Android OpenGl Es learning (two): define vertices and shaders

Overview

This is a new series. Learning OpengGl Es is actually the study notes of the "OpenGl Es Application Development Practice Guide Android Volume". If you are interested, you can directly read this book. Of course, this will record your own understanding. The following are only notes. In case you forget later

Later, the first nine chapters of the book will be analyzed and recorded in turn

Android OpenGl Es learning (1): Create an OpenGl es program

Android OpenGl Es learning (two): define vertices and shaders

Android OpenGl Es learning (3): Compile shaders

Android OpenGl Es learning (four): add color

Android OpenGl Es learning (5): adjust the aspect ratio

Android OpenGl Es learning (6): enter the three-dimensional

Android OpenGl Es learning (7): use texture

Android OpenGl Es learning (eight): build simple objects

Android OpenGl Es learning (9): add touch feedback

The final goal is to realize a simple game of hockey, like this

Define vertices

We can see that the above is the final effect. First, let’s implement it simply, a rectangle, a line, and two dots.

We see that a rectangle is actually composed of 4 vertices, and then the four vertices are connected into a line

Point, line, triangle

In OpenGl, only points, sublines, and triangles can be drawn, so we can’t draw rectangles directly. We need to decompose the rectangle into several triangles, and then compose them into rectangles.

As shown in the figure, just divide a rectangle into two triangles

Define vertices in code

In the code, these vertices will be represented by an array of floating-point numbers. Because it is a two-dimensional coordinate, each vertex needs to be recorded with two floating-point numbers, one marking the x-axis position and the other marking the y-axis position. This array is usually called Attribute array

  float[] tableVertices = {
    
    
                //第一个三角
                0f, 0f,
                9f, 14f,
                0,14f,
                //第二个三角
                0f,0f,
                9f, 0f,
                9f, 14f,
        };

This array represents two triangles, each triangle is represented counterclockwise, a total of four vertices, two triangles share two vertices

Add lines and points

   float[] tableVertices = {
    
    
            //第一个三角
            0f, 0f,
            9f, 14f,
            0, 14f,
            //第二个三角
            0f, 0f,
            9f, 0f,
            9f, 14f,

            //线
            0f, 7f,
            9f, 7f,

            //点
            4.5f, 2f,
            4.5f, 12f
    };

Make data available to opengl

We have defined the vertex above, but our java code runs on a virtual machine, and opengl runs on local hardware, so how can we make java data available to opengl?

  • The first is JNI, if you need to understand, you can read my previous blog
  • The second is to change the way of memory allocation. Java has a special collection of classes that can allocate local memory blocks and copy Java data to 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);

Each floating-point number has 32-bit precision, and each byte has 8-bit precision, so each floating-point number occupies 4 bytes

method description
ByteBuffer.allocateDirect Allocate a piece of local memory, the allocation size is passed in from outside
order(ByteOrder.nativeOrder()) Tell the buffer to organize the content in local byte order
asFloatBuffer() We want to operate Float, calling this method will return FloatBuffer
put() Data input
position() Move the data subscript to the specified position

opengl pipeline

Now OpenGL has the data. Before drawing the rectangle to the screen, they also need to pass it in the OpenGL pipeline. This step requires the use of shaders, which will tell the graphics processing unit (CPU) How to draw data, there are two shaders we need to define

  • Vertex shader: Generate the final position of each vertex, and execute it once for each vertex. Once the position is determined, OpenGL can assemble these vertices into points, lines and triangles.
  • Fragment shader: Generate the final color for each fragment that composes points, lines, and triangles. It will execute it once for each fragment. A fragment is a small, single-color rectangular area, similar to a computer screen One pixel of

Once the final colors are generated, OpenGL will write them to a block of memory called the frame buffer, and then Android will display the frame buffer on the screen

Rasterization technology

The display screen of a mobile device is composed of thousands of individual small components. They are called pixels. Each of these pixels has the ability to display one of millions of colors. In fact, each pixel is composed of three They are composed of independent sub-components. They emit red, blue, and green light. Because each pixel is small, the human eye will mix the red, green and blue tubes to create a huge amount of color. Pixels can display the picture you want

OpenGL decomposes each point, line, and triangle into a large number of small fragments through the process of rasterization, and maps them to the pixels of the mobile device to generate images. These small fragments are similar to pixels on the display screen, and each contains a single In order to represent the color, each segment has 4 components, red, blue, and green to represent the color, and the alpha component is used to represent the transparency

GLSL

To create a vertex shader, you must first understand the OpenGL shading language OpenGl Shader Language(GLSL). If you want to learn more about changing this language, you can refer to this OpenGL shader GLSL Chinese manual
. Some basic information is recorded below.

Basic data type

Types of Description
void Empty type, does not return any value
bool Boolean type true, false
int Signed integer
float Signed floating point
vec2, vec3, vec4 n-dimensional floating point number vector, including 2, 3, 4 element floating point vector
bvec2, bvec3, bvec4 n-dimensional Boolean vector, a Boolean vector containing 2, 3, 4 elements
ivec2, ivec3, ivec4 n-dimensional shaping vector, shaping vector containing 2, 3, 4 elements
mat2, mat3, mat4 2✖️2, 3✖️3, 4✖️4, floating point matrix
sampler2D 2D texture
samplerCube Cube texture

Basic structure and array

Types of description
structure struct type-name{} is similar to the structure in the c language
Array float foo[3] glsl only supports 1-dimensional arrays, which can be members of structures

Variable qualifier

Modifier description
none (It can be omitted by default) The local variables are readable and writable, and the input parameters of the function are such variables
const Declare that the parameters of a variable or function are of read-only type
attribute Only vertex shader (vertex shader) can exist, generally used to save vertex or normal data, and data can be read in the buffer
uniform The shader cannot change the uniform variables at runtime. The first version is used to place the transformation matrix, material and lighting parameters passed by the program to the shader.
varying Mainly responsible for transferring data between vertex and fragment

Parameter qualifier

The parameters of the function are passed in the form of copy by default, that is, by value. Any variable passed to the function will have a copy of its value and then handed over to the function for processing. We can also add qualifiers to achieve reference. Delivered effect

Types of description
default The in qualifier is used by default
in What is actually passed into the function is a copy of the parameter. Modifying the parameter value in the function will not affect the parameter variable itself
out The value of the parameter will not be passed to the function, but if the value is modified inside the function, the value of the parameter will change after the function ends
inout The parameter passed into the function is a reference. If the value is modified inside the function, the parameter will also change

function

GLSL allows functions to be declared in the outermost part of the program, functions cannot be nested, cannot be called recursively, and the return value type must be declared (return void if there is no return value), which is the same as the c function in other respects

vec4 getPosition(){
    
     
    vec4 v4 = vec4(0.,0.,0.,1.);
    return v4;
}
 
void doubleSize(inout float size){
    
    
    size= size*2.0  ;
}
void main() {
    
    
    float psize= 10.0;
    doubleSize(psize);
    gl_Position = getPosition();
    gl_PointSize = psize;
}

Type conversion

GLSL can use the constructor to display type conversion

bool t= true;
bool f = false;
 
int a = int(t); //true转换为1或1.0
int a1 = int(f);//false转换为0或0.0
 
float b = float(t);
float b1 = float(f);
 
bool c = bool(0);//0或0.0转换为false
bool c1 = bool(1);//非0转换为true
 
bool d = bool(0.0);
bool d1 = bool(1.0);

Precision limit

When GLSL is rasterizing, it will perform a large number of floating-point operations, which may not be able to bear the equipment, so GLSL provides three floating-point precisions, which can be selected according to different devices.

Add before the variable highp mediump lowpto declare the precision

lowp float color;
varying mediump vec2 Coord;
lowp ivec2 foo(lowp mat3);
highp mat4 m;

In addition to the precision qualifier, we can also specify the precision used by default. If a variable does not use a precision qualifier, the default precision will be used. The default precision qualifier is placed in the initial position of the shader code, such as:

precision highp float;//默认高精度float
precision mediump int;//默认中精度int

invariant keywords:

Since the shader will do some internal optimizations when compiling, it may cause the same operation to be inconsistent in different shaders, which will cause some problems, especially when the vertex shader passes values ​​to the fragment shader, so we need to use invariantThe keyword requires that the calculation results must be consistent. In addition to this, we can also use #pragma STDGL invariant(all)commands to ensure that all outputs are consistent, which will limit the optimization of the compiler and reduce performance

#pragma STDGL invariant(all) //所有输出变量为 invariant
invariant varying texCoord; //varying在传递数据的时候声明为invariant

Built-in special variables

GLSL uses some special built-in variables to communicate with the hardware. They are roughly divided into two types. One is the input type, which is responsible for sending data to the hardware, and the other is the output type, which is responsible for returning data to the program to facilitate programming needs.

In the vertex shader

Built-in variables of output

variable description
highp vec4 gl_Position; gl_Position place vertex coordinate information
mediump float gl_PointSize; gl_PointSize 绘制点的大小

在fragment shader中

input类型的内置变量

变量 描述
mediump vec4 gl_FragCoord; 片元在framebuffer画面的相对位置
bool gl_FrontFacing; 标志当前图元是不是正面图元的一部分
mediump vec2 gl_PointCoord; 经过插值计算后的纹理坐标,点的范围是0.0到1.0

output类型内置变量

变量 描述
mediump vec4 gl_FragColor; 片元在framebuffer画面的相对位置
mediump vec4 gl_FragData[n] 设置当前片点的颜色,使用glDrawBuffers数据数组

内置函数

参考上面链接吧,这里就不继续写了

创建顶点着色器

首先我们在res新建一个raw文件夹,然后新建一个文件vertex_shader.glsl,然后用GLSL书写代码

 attribute vec4 a_Position;

  void main() {
      gl_Position =  a_Position;
      gl_PointSize=10.0;
   }

我们定义的每一个顶点,顶点着色器都会被调用一次,当他被调用时,他会在gl_Position属性接受当前顶点的位置

main函数是着色器的入口,他需要做的就是把前面定义的位置,赋值到指定输出变量gl_Position,这个着色器给gl_Position赋值,opengl会把gl_Position储存的值当做顶点的最终位置,然后把这些顶点组装成点,线,三角形

创建片段着色器

我们新建一个文件fragment_shader.glsl

 precision mediump float;
 uniform vec4 u_Color;
   void main() {
        gl_FragColor = u_Color;
    }

第一行设置精度

uniform 不像属性每个顶点都要设置一个,一个uniform会让每个顶点都使用同一个值,除非我们改变他们

main函数是着色器的入口,然后把uniform定义的颜色赋值到特殊的输出变量gl_FragColor

Guess you like

Origin blog.csdn.net/qq_34760508/article/details/108716294