An overview of the
This is a new series, learn OpengGl Es, actually is “OpenGl Es Application Development Practice Guide Android volume” study notes, interested can directly read this book, of course, this will record their own understanding, the following only as notes, in case you forget later
The first nine chapters of the book will then be analyzed in turn
Android OpenGl Es learning (a) : create an OpenGl Es program
Android OpenGl Es learning (2) : define vertices and shaders
Android OpenGl Es learning (3) : build shaders
Android OpenGl Es learning (4) : add color
Android OpenGl Es learning (5) : adjust the aspect ratio
Android OpenGl Es Learning (vi) : Enter 3d
Android OpenGl Es learning (7) : Using textures
Android OpenGl Es Learning (8) : Build simple objects
Add touch feedback to Android OpenGl Es
The end result is a simple game of hockey, something like this
Define the vertices
So here we see the final result, but first let’s make it simple, one rectangle, one line, two points
We see that a rectangle is actually made up of four vertices, and then we connect the four vertices into a line
Points, lines, triangles
So in OpenGl you can only draw points, sublines, triangles, so you can’t draw a rectangle directly, you have to break up a rectangle into triangles, and then form a rectangle again
As shown in the figure, a rectangle is divided into two triangles
Define vertices in your code
In your code these vertices are represented as floating-point arrays. Because they are two-dimensional coordinates, each vertex is recorded with two floating-point arrays, one marking the X-axis position and one marking the Y-axis position. This array is often called an attribute array
float[] tableVertices = {
// First triangle
0f.0f.9f.14f.0.14f.// Second triangle
0f.0f.9f.0f.9f.14f};Copy the code
This array represents two triangles, each in counterclockwise order, with four vertices, each of which shares two vertices
Add lines and points
float[] tableVertices = {
// First triangle
0f.0f.9f.14f.0.14f.// Second triangle
0f.0f.9f.0f.9f.14f./ / line
0f.7f.9f.7f./ / points
4.5 f.2f.4.5 f.12f
};
Copy the code
Make the data available to OpengL
We have defined vertices above, but our Java code runs on a virtual machine, while OpengL runs on local hardware. So how do we make Java data available to OpengL?
- The first one is JNI, if you need to know about it, check out my previous blog
- The second is to change the way memory is allocated. Java has a special set of classes that allocate local chunks of memory and copy Java data into local memory
Let’s look at the code
private final int BYTES_PER_FLOAT = 4;
FloatBuffer verticeData = ByteBuffer.allocateDirect(tableVertices.length * BYTES_PER_FLOAT)
.order(ByteOrder.nativeOrder())
.asFloatBuffer()
.put(tableVertices);
verticeData.position(0);
Copy the code
Each floating-point number has 32 bits of precision and each byte has 8 bits of precision, so each floating-point number takes up 4 bytes
methods | describe |
---|---|
ByteBuffer.allocateDirect | Allocates a block of local memory, the size of which is passed in from outside |
order(ByteOrder.nativeOrder()) | Tells the buffer to organize content in local byte order |
asFloatBuffer() | We want to operate on Float, and calling this method returns FloatBuffer |
put() | Fill in the data |
position() | Moves the data subscript to the specified position |
Opengl pipeline
Now that OpengL has the data, before they can draw the rectangle to the screen, they also need to pass it through opengL’s pipelines. This step requires the use of shaders. These shaders tell the GRAPHICS processing unit (CPU) how to draw the data
- Vertex shader: Generates the final position of each vertex. It executes the position once for each vertex. Once the position is determined, OpengL can assemble these vertices into points, lines, and triangles
- Fragment shader: Generates the final color for each fragment that consists of points, lines, and triangles. It performs this once for each fragment. A fragment is a small, rectangular area of a single color, similar to a pixel on a computer screen
Once the final colors are generated, OpengL writes them to a block of memory called a frame buffer, which Android then displays on the screen
Rasterization technique
Mobile devices display is composed of hundreds of millions of independent panel parts, they are called pixels, each of these pixels have the ability to display one of the millions of colors, each pixel is actually made up of three separate subcomponents, they emit red, blue, green light, because each pixel is very small, people’s eyes, would have been red, green and blue mix tube, It creates a huge amount of color, and you only need as many pixels to show what you want
Opengl through rasterization process, each of the points, lines, triangles, decomposed into lots of small fragments, pixel mapping to a mobile device, so as to generate the image, these small fragments is similar to that of the pixels on the screen, each containing a single pure color, to show the color, each fragment has four components, red, blue, green, to represent the color, The Alpha component is used to indicate transparency
GLSL
Opengl Shader Language(GLSL) is an opengL Shader Language used to create vertex shaders. If you want to learn more about this Language, please refer to the OpengL Shader GLSL Manual
Basic data types
type | instructions |
---|---|
void | An empty type that does not return any value |
bool | Boolean type true, false |
int | A signed integer |
float | A signed floating point number |
vec2, vec3, vec4 | N-dimensional floating point vector, including 2, 3, and 4 elements of the floating point vector |
bvec2, bvec3, bvec4 | N-dimensional Boolean vectors, Boolean vectors with 2, 3, and 4 elements |
ivec2, ivec3, ivec4 | N-dimensional orthopaedics vector, orthopaedics vector with 2, 3, and 4 elements |
mat2, mat3, mat4 | 2✖️2, 3✖️ 3,4 ✖️4, floating point matrix |
sampler2D | 2 d texture |
samplerCube | Cube texture |
Basic structures and arrays
type | describe |
---|---|
structure | Struct type-name{} is similar to a c language structure |
An array of | Float Foo [3] GLSL only supports 1-dimensional arrays, which can be members of a structure |
Variable qualifier
The modifier | describe |
---|---|
none | (omitted by default) Local variables are readable and writable, and function inputs are such variables |
const | Declare the arguments of a variable or function as read-only types |
attribute | There can only be vertex shaders, which are typically used to hold vertex or normal data and can be read from a buffer |
uniform | Shader cannot change uniform variables at runtime. The first version is used to place transformation matrices, materials, and lighting parameters passed to the shader by the program |
varying | Responsible for data transfer between Vertex and fragment |
Parameter qualifier
Function parameters are passed by default as copies, that is, value passes. Any variable passed to a function is copied and passed internally. We can also add qualifiers to achieve the effect of passing by reference
type | describe |
---|---|
The default | The in qualifier is used by default |
in | What is passed to a function is a copy of the parameter. Modifying the parameter value inside the function does not affect the parameter variable itself |
out | The value of a parameter is not passed to the function, but if you change the value inside the function, the value of the parameter will change after the function ends |
inout | The arguments passed to the function are references, and when the value is changed internally, the arguments change |
function
GLSL allows functions to be declared on the outermost side of a program. Functions cannot be nested, cannot be called recursively, and must declare a return value type (void if there is no return value), otherwise like C functions
vec4 getPosition(a){
vec4 v4 = vec4(0..0..0..1.);
return v4;
}
void doubleSize(inout float size){
size= size*2.0 ;
}
void main(a) {
float psize= 10.0;
doubleSize(psize);
gl_Position = getPosition();
gl_PointSize = psize;
}
Copy the code
Type conversion
GLSL can use constructors for display type conversions
bool t= true;
bool f = false;
int a = int(t); //true converts to 1 or 1.0
int a1 = int(f);//false converts to 0 or 0.0
float b = float(t);
float b1 = float(f);
bool c = bool(0);//0 or 0.0 is converted to false
bool c1 = bool(1);// Non-0 converts to true
bool d = bool(0.0);
bool d1 = bool(1.0);
Copy the code
Precision limit
GLSL, when rasterizing, does a lot of floating point operations that the device may not be able to handle, so GLSL offers three floating-point precision options that can be selected for different devices
Add highP mediump lowp before variable to declare precision
lowp float color;
varying mediump vec2 Coord;
lowp ivec2 foo(lowp mat3);
highp mat4 m;
Copy the code
In addition to the precision qualifier, we can also specify the precision to be used by default. If a variable does not use a precision qualifier, the default precision is used. The default precision qualifier is placed at the beginning of the shader code, as in:
precision highp float;// Defaults to high-precision float
precision mediump int;// Default medium precision int
Copy the code
Invariant keyword:
This can cause some problems, especially when vertex shader is passing values to fragment shader. In addition to this, we can use #pragma STDGL invariant(all) to ensure that all outputs are uniform, which limits the compiler’s optimization and reduces performance
#pragma STDGL invariant(all) // All output variables are invariant
invariant varying texCoord; // VARYING is declared as invariant when passing data
Copy the code
Built-in special variables
GLSL uses some special built-in variables to communicate with the hardware, which can be roughly divided into two types. One type is input, which is responsible for sending data to the hardware, and the other type is output, which is responsible for sending data back to the program for convenient programming needs
In the vertex shader
The built-in variable for output
variable | describe |
---|---|
highp vec4 gl_Position; | Gl_Position places vertex coordinate information |
mediump float gl_PointSize; | Gl_PointSize Specifies the size of the drawing point |
In the fragment shader
A built-in variable of type input
variable | describe |
---|---|
mediump vec4 gl_FragCoord; | The relative positions of the elements in the framebuffer |
bool gl_FrontFacing; | Flags whether the current icon is part of the front icon |
mediump vec2 gl_PointCoord; | The interpolated texture coordinates range from 0.0 to 1.0 |
Built-in variable of type output
variable | describe |
---|---|
mediump vec4 gl_FragColor; | The relative positions of the elements in the framebuffer |
mediump vec4 gl_FragData[n] | Sets the color of the current slice, using the glDrawBuffers data array |
Built-in function
Refer to the link above and I won’t continue writing here
Create a vertex shader
First we create a raw folder in res, then create a new file vertex_shader.glsl, and write the code in GLSL
attribute vec4 a_Position;
void main() {
gl_Position = a_Position;
gl_PointSize=10.0;
}
Copy the code
For each vertex we define, the vertex shader is called once, and when it is called, it accepts the position of the current vertex in the gl_Position property
The main function is the entrance to the shader, and all it needs to do is assign the position defined earlier to the specified output variable gl_Position, and this shader assigns a value to gl_Position, and OpengL takes the value stored in gl_Position as the final position of the vertex, and assembles those vertices into points, lines, triangle
Create the fragment shader
Let’s create a new file fragment_shader.glsl
precision mediump float;
uniform vec4 u_Color;
void main() {
gl_FragColor = u_Color;
}
Copy the code
The first line sets the precision
Uniform Unlike the property that sets one for every vertex, a UNIFORM uniform will make every vertex use the same value unless we change them
The main function is the entrance to the shader, and then assigns the uniform definition of color to the special output variable gl_FragColor