OpenGL ES-Shader Language Basics

Written in front:

        This article is some sorting out of my learning Shader, and the source of the information is "OpenGL ES 3.X Game Development Volume 1". If you don’t know the basics of OpenGL ES, you can read my other blog OpenGL ES Introduction - Noun Literacy icon-default.png?t=M276https://juejin.cn/post/7080801741008011277 The opinions are not correct, and criticisms and corrections are welcome.

1. Data type

1.1, scalar

        Scalars are like our basic data types from the perspective of Java/C language, but there are no byte/char/short/long/double.

  • bool boolean true/false
  • int/uint integer/unsigned integer
  • float floating point data

Compared with Java/C-based data types, there is no generation gap at all

1.2, vector

        From the perspective of Java/C language, a vector is like our one-dimensional array. The data type of the array is also the type of scalar, and the size of the volume is from 2 to 4. For example, a volume is 3 in Java. The floating-point array is float[3], so the representation in the shader language is vec3. In addition to the floating-point type, there are also bool/int/uint type vectors corresponding to the scalar. The details are as follows:

Vectors mainly store one-dimensional array type data such as color, position, and texture coordinates. Assume that a vector is declared

vec3 my_vec;

There are two ways we visit

  • my_vec.r/my_vec.g/my_vec.b my_vec.x/my_vec.y/my_vec.z my_vec.s/my_vec.t/my_vec.q

        In fact, r, g, b, a/x, y, z, w/s, t, p, and q are all accessible subscripts corresponding to 0, 1, 2, and 3. It’s just possible that for better readability we are used to accessing color vectors with r, g, b, a, accessing coordinate vectors with x, y, z, w, and accessing texture vectors with s, t, p, q. If only *vec2 Then you can only use the first two bits, and *vec4 can use four bits to access.

  • my_vec[0]/my_vec[1]/my_vec[2] This idea is actually an array idea.

1.3. Matrix

        From the perspective of Java/C, a matrix looks like a two-dimensional array, which is actually the same as the matrix understanding in our linear algebra. The size of its two-dimensional volume is from 2 to 4, and its access is consistent with the access method of a two-dimensional array. All matrix types are as follows:

        mat2 and mat2x2 are actually a data type, but the expression is different, mat3/mat4 ​​are the same

1.4. Sampler

        A sampler is a class of data types specialized for texture processing. In general, a sampler variable represents a pair or a set of texture maps, and all sampler types are as follows:

        The sampler is not initialized inside the shader program, but passed in through the Java/C layer for assignment.

1.5. Structure

        It is basically the same as the C language, except that the data type is defined by the shader language.

1.6, array

        It is basically the same as the Java/C language, except that the data type is defined by the shader language.

2. Initialization

        Personally, I think the initialization of the shader language is more flexible. The scalar initialization is basically the same as the Java/C method, and the vector/matrix/array types are all like Java/C++ constructors.

There are also some flexible techniques for matrix initialization, which are specifically divided into the following situations.

  • Each element of the matrix can use literal constants or variables during initialization, and can also be directly obtained from other vectors.
  • If the matrix only has values ​​on the diagonal and are the same during initialization, you can initialize the matrix by giving a literal constant.
  • When the number of rows and columns (N×N) of the matrix M1 is less than the number of rows and columns (M×M) of the matrix M2 in the constructor during initialization, that is, N<M, the element values ​​of the matrix M1 are the N x N corresponding elements in the upper left corner of the matrix M2 value.
  • The number of rows and columns (N×M) of matrix M1 at initialization is different from the number of rows and columns (P×Q) of matrix M2 in the constructor, and the maximum value between P and Q is greater than the maximum value between N and M (assuming M1 is mat2×3, M2 is mat4×2), the values ​​of N×N elements in the upper left corner of matrix M1 are the values ​​of N×N elements in the upper left corner of matrix M2, and the values ​​of other rows of matrix M1 are 0.
  • When the number of rows and columns (N×N) of matrix M1 during initialization is less than the number of rows and columns (M×M) of matrix M2 in the constructor, that is, N>M, the values ​​of the M×M elements in the upper left corner of matrix M1 are the corresponding elements of matrix M2 The value of the remaining diagonal elements in the lower right corner of matrix M1 is 1, and the remaining elements of matrix M1 are 0.

However, the data types declared         by some special qualifiers in/uniform/out/const cannot be initialized. Due to the existence of built-in variables and functions of the shader, in order to avoid naming conflicts, we should not use variables and functions starting with gl_.

Three, qualifier

3.1, storage qualifier

        The variable declared in in mainly receives data from the rendering pipeline in the vertex shader, and is passed in and assigned by the host program (Java/C), so it itself does not allow initialization. In the fragment shader, in mainly receives data from the vertex shader.

        out means to output data to the outside. For the vertex shader, the output data can be specified by using the out qualifier, while the fragment shader receives it through the in qualifier, and the declared variable names should be consistent.

        Uniform can be used in vertex or fragment shaders, and can modify all basic data types. The effect is similar to in variables. For example, if we want to pass in a value dynamically from the outside, we can use uniform to receive it inside the shader. The host program Assignment is made by getting the reference position inside the shader program.

3.2, interpolation qualifier

3.3、layout

        layout is usually used together with (location=n) to specify the location of in/out/uniform type parameters. Generally, if location is not specified, the program will automatically specify location in sequence, then we need code to get location.

3.4. Accuracy

        When using the floating-point data type, the floating-point data type can be directly declared in the vertex shader, but we must specify the floating-point precision in the fragment shader, otherwise a compilation error will occur. We can improve the drawing effect or avoid compilation errors by specifying the precision. There are three types of precision:

lowp low precision / mediump medium precision / highp high precision

4. Built-in variables

        Built-in variables are some variables built into the shader language, we don't need to declare them, we can use them directly. So at the beginning, I was also baffled, obviously there is a variable that has not been declared why it can run.

4.1. Vertex shader built-in variables

4.1.1. Built-in input variables

4.1.2. Built-in output variables

4.2. Fragment shader built-in variables

4.2.1. Built-in input variables

  • gl_FragCoord (vec4 type) contains the coordinates x, y, z and 1/w of the current fragment in the viewport (as shown in Figure 4-3). Where x and y are the two-dimensional coordinates of the fragment relative to the viewport. If the size of the viewport is 800*480 (unit is pixel), then the value range of x is 0~800, the value range of y is 0~480, and the z part is the depth value of the fragment.
  • gl_FrontFacing is a Boolean built-in variable. By reading the value of this built-in variable, it can be judged whether the fragment being processed belongs to the front face of the primitive corresponding to the fragment generated in the rasterization stage. If it belongs to the front, the value of gl_FrontFacing is true, otherwise it is false. It is generally used to develop applications related to double-sided lighting functions. For primitives such as points and line segments that have no front and back sides, the generated fragments will be considered to be front by default. For triangle primitives, the front and back sides depend on the winding settings in the application and the specific winding conditions of the vertices in the primitive.
  • gl_PointCoord is a built-in variable of type vec2. When the point sprite is enabled, the value of gl_PointCoord represents the texture coordinate of the fragment in the current primitive, and its value ranges from 0.0 to 1.0. If the current primitive is not a point or point sprites are not enabled, the value of gl_PointCoord is undefined.

4.2.2. Built-in output variables

        gl_FragDepth, whose value is the depth value of the fragment, allows developers to assign it since OpenGL ES 3.0, and then send it into the depth buffer to participate in subsequent calculations.

4.3. Built-in constants

4.4. Built-in uniform variables

V. Summary

        The operators, process control, and function declarations in the shader language are roughly the same as those in the C language. Anyone with a C language foundation can understand it, so no description is given. In fact, there are a large number of built-in functions to deal with built-in variables. We can look up the built-in functions we need to use in the early stage through a similar table lookup method. The space is too large, so we will not describe them here. If you need a PDF of this book, you can leave an email in the comment area (ps: the source of resources is the Internet, only for personal study and not for commercial use)

Guess you like

Origin blog.csdn.net/qq_37841321/article/details/123908866