Preface
After [previous notes](/articles/纹理映射(贴图)-webgl 笔记 8/), we finished the most basic part of WebGL, this note comprehensively introduces GLSL ES syntax, relatively independent, then it's matrices matrices matrices (transformation, perspective, lighting, controlling complex models, etc., all about matrices)
I. Overview
GLSL ES is formed based on GLSL (OpenGL Shading Language), after deleting and simplifying some features, target platforms are consumer electronics and embedded devices, such as smartphones, game consoles, etc., ES version mainly reduces hardware power consumption, reduces performance overhead
P.S. Actually WebGL doesn't support all features of GLSL ES, supports a subset of GLSL ES 1.00 version
II. Basic Syntax Rules
-
Case sensitive
-
Statement ends must have semicolons
-
Start execution from main function
-
Cannot omit return value type in function declarations (no return value is void, C language can omit, but here cannot)
-
Comment syntax consistent with C language (single line //, multi-line /* */)
III. Variables and Basic Data Types
1. Basic Data Types
Only supports 2 basic data types:
-
Numeric types: integers, floating point numbers
-
Boolean types: true and false 2 boolean constants
Note: Doesn't support strings
2. Variables
- Variable declaration
Same as C language, type + variable name, variable naming rules also same, basic types only int, float and bool
- Type conversion
No implicit type conversion, also doesn't support type suffixes like 3f, but provides type conversion functions: int(), float() and bool(), all only accept remaining 2 basic data types
- Operators
Doesn't support bitwise operations, others consistent with C language, also supports ternary selection, logical AND (&&) and logical OR (||) also have short-circuit characteristics
- Scope
Consistent with C language, declared inside functions are local variables, declared outside are global variables
IV. Complex Data Types
1. Vectors (vec)
Supports 2, 3, 4 dimensional vectors, divided into 3 categories by component data type:
-
vec2, vec3, vec4: components are floating point numbers
-
ivec2, ivec3, ivec4: components are integers
-
bvec2, bvec3, bvec4: components are boolean values
Constructor names consistent with type names, for example vec4(1.0) returns 4-dimensional vector [1.0, 1.0, 1.0, 1.0], if only pass one parameter like this, will assign all components to that value, if passed parameters more than one but less than needed parameter count, will error. For example, vec4(1.0) and vec4(1.0, 1.0, 1.0, 1.0) both fine, while vec4(1.0, 1.0) and vec4(1.0, 1.0, 1.0) will error
Additionally, can pass vectors to construct new vectors, or combine existing vectors to create new vectors, for example:
vec3 v3 = vec3(0.0, 0.5, 1.0); // [0.0, 0.5, 1.0]
vec2 v2 = vec2(v3); // [0.0, 0.5],截取 v3 的前两个分量
vec4 v4 = vec4(v2, vec4(1.5)); // [0.0, 0.5, 1.5, 1.5],组合 v2 和新矢量 [1.5, 1.5, 1.5, 1.5]
In short, parameters can come from basic values or other vectors, but if parameter count insufficient and count not 1 will error
Accessing vector components has 2 ways, as follows:
v4 = vec4(1, 2, 3, 4);
// .component name
v4.x, v4.y, v4.z, v4.w // homogeneous coordinates
v4.r, v4.g, v4.b, v4.a // color values
v4.s, v4.t, v4.p, v4.q // texture coordinates
// [] operator
v4[0], v4[1], v4[2], v4[3]
Dot component name way just to add semantics, equivalent to bracket operator, can understand as aliases, for example v4[0]'s aliases are v4.x, v4.r and v4.s. More interestingly, can also combine use, for example v4.xz returns 2-dimensional vector, but at this time component names cannot mix (v4.sz is incorrect)
Note: Values in brackets must be constant index values, either integer literals, or const modified variables, loop indices (explain in flow control part), or expressions composed of these 3
2. Matrices (mat)
Only supports 2, 3, 4 dimensional square matrices, only supports floating point type components: mat2, mat3, mat4
Special note: Matrix elements are column-major order, for example:
mat4 m4 = mat4(
1, 2, 3, 4,
5, 6, 7, 8,
9, 10, 11, 12,
13, 14, 15, 16
);
// Generated matrix is:
1 5 9 13
2 6 10 14
3 7 11 15
4 8 12 16
May be very different from imagination, but indeed is like this, similarly, matrix constructors can also accept other vectors or matrices, no matter where parameters come from, ultimately this set of numbers will according to column-major order construct matrix, for example:
vec2 v2_1 = vec2(1, 2);
vec2 v2_2 = vec2(3, 4);
mat2 m2 = mat2(v2_1, v2_2);
// Generated matrix is:
1 3
2 4
Similarly, if parameters insufficient and parameter count more than 1, will error
Accessing matrix elements generally uses bracket operator, for example:
m4[0] // 1st column elements, 4-dimensional vector
m4[0][1] // 1st column 2nd row element, basic value
m4[0].y // Same as above
Note: Similarly, values in brackets must also be constant index values
3. Structures (struct)
Similar to C language structures, as follows:
// Declare custom structure type
struct light {
vec4 color;
vec3 pos;
};
// Declare structure type variables
light l1, l2; // Equivalent to C language struct light l1, l2;
// Can also declare structure type variables while declaring structure like C language
struct light {
vec4 color;
vec3 pos;
} l3;
After declaring structure will automatically generate same name constructor, parameter order must be consistent with member order in structure, for example:
light l4 = light(vec4(1.0), vec3(0.0));
Can directly access structure variable members with dot operator, structure itself only supports assignment (=) and 2 comparison operators (==, !=), if 2 structure members and order all same, then equal
4. Arrays (xArray)
Only supports 1-dimensional arrays, and doesn't support pop, push etc. operations, array declaration way consistent with C language, for example:
float a[10];
vec4 arr[3];
Similarly, values in brackets can only be constant index values, and arrays cannot initialize while declaring, must explicitly assign each element
5. Samplers (sampler)
Can access textures through sampler variables, sampler variables only 2 types: sampler2D and samplerCube, and sampler variables can only be uniform variables, for example:
uniform sampler2D u_Sampler;
Only value that can assign to sampler variables is texture unit number, for example gl.uniformi(u_Sampler, 0) passes texture unit number 0 to shader, so sampler variables limited quantity, fragment shader maximum 8, vertex shader has no sampler variables
Additionally, except =, == and !=, sampler variables cannot participate in operations as operands
V. Vector Operations and Matrix Operations
Vectors and matrices only support == and != in comparison operators, operation assignment (+=, -=, *=, /=) operations acting on vectors and matrices actual effect is operation assignment on each component
1. Vector and Floating Point Operations
v2 + f; // v2[0] + f
// v2[1] + f
2. Vector Operations
v2_1 + v2_2; // v2_1[0] + v2_2[0]
// v2_1[1] + v2_2[1]
3. Matrix and Floating Point Operations
m2 + f; // m2[0] + f
// m2[1] + f
// m2[2] + f
// m2[3] + f
4. Matrix Right Multiply Vector
m3 * v3; // m3[0][0] * v3[0] + m3[1][0] * v3[1] + m3[2][0] * v3[2]
// m3[0][1] * v3[0] + m3[1][1] * v3[1] + m3[2][1] * v3[2]
// m3[0][2] * v3[0] + m3[1][2] * v3[1] + m3[2][2] * v3[2]
5. Matrix Left Multiply Vector
v3 * m3; // v3[0] * m3[0][0] + v3[1] * m3[0][1] + v3[2] * m3[0][2]
// v3[0] * m3[1][0] + v3[1] * m3[1][1] + v3[2] * m3[1][2]
// v3[0] * m3[2][0] + v3[1] * m3[2][1] + v3[2] * m3[2][2]
6. Matrix Multiply Matrix
m3a * m3b; // m3a[0][0] * m3b[0][0] + m3a[1][0] * m3b[0][1] + m3a[2][0] * m3b[0][2]
// m3a[0][0] * m3b[1][0] + m3a[1][0] * m3b[1][1] + m3a[2][0] * m3b[1][2]
// m3a[0][0] * m3b[2][0] + m3a[1][0] * m3b[2][1] + m3a[2][0] * m3b[2][2]
// m3a[0][1] * m3b[0][0] + m3a[1][1] * m3b[0][1] + m3a[2][1] * m3b[0][2]
// m3a[0][1] * m3b[1][0] + m3a[1][1] * m3b[1][1] + m3a[2][1] * m3b[1][2]
// m3a[0][1] * m3b[2][0] + m3a[1][1] * m3b[2][1] + m3a[2][1] * m3b[2][2]
// m3a[0][2] * m3b[0][0] + m3a[1][2] * m3b[0][1] + m3a[2][2] * m3b[0][2]
// m3a[0][2] * m3b[1][0] + m3a[1][2] * m3b[1][1] + m3a[2][2] * m3b[1][2]
// m3a[0][2] * m3b[2][0] + m3a[1][2] * m3b[2][1] + m3a[2][2] * m3b[2][2]
VI. Flow Control
1. Branching
if-else structure usage consistent with C language and js, but no switch statements
2. Loops
Only supports for loops, and can only define loop variables in initialization expression (position before first semicolon in for(;;)), for example:
for (int i = 0; i < 10; i++) {
//...
}
Only allows one loop variable, and loop variables can only be int or float, and condition expression (position between 2 semicolons in for(;;)) must be loop variable comparison with integer constant, and inside loop body, loop variables cannot be assigned
Many restrictions, is to let compiler able to inline expand for loops
continue, break usage consistent with js, additionally, there's a discard, can only use in fragment shader, indicates discard current fragment, directly process next fragment
VII. Functions
Basically consistent with C language, but cannot return arrays, if return custom structures, structure members also cannot have arrays, for example:
float luma(vec4 color) {
return 0.2126 * color.r + 0.7162 * color.g + 0.0722 * color.b;
}
Similarly, need declare first, then call, otherwise must declare function signature before calling, for example:
float luma(vec4); // Declare function signature
void main() {
luma(color);
}
float luma...
Additionally, recursion not allowed, this restriction also for compiler able to inline expand functions
In GLSL ES there are several parameter qualifier words, as follows:
in value passing, can omit, default is value passing
const in value passing, cannot modify parameters inside function
out address passing
inout address passing, passed parameters must already be initialized
VIII. Storage Qualifiers
attribute, uniform, varying, differences as follows:
- attribute
Can only appear in vertex shaders, can only be global variables, types can only be float or vectors and matrices with float components. WebGL environment at least supports 8 attribute variables, used to represent per-vertex information
- uniform
Can only be global variables, can use in vertex shaders and fragment shaders, can be any type except arrays and structures. WebGL environment at least supports fragment shader appearing 16 uniform variables, vertex shader is 128, used to represent data shared by all vertices, all fragments
Special: If vertex shader and fragment shader declare same name uniform variables, then will be shared by 2 shaders
- varying
Can only be global variables, types can only be float or vectors and matrices with float components. WebGL environment at least supports 8 varying variables, used to pass data from vertex shader to fragment shader, but not directly pass, has interpolation process
Additionally, there's const qualifier, but not commonly used
IX. Precision Qualifiers
Introducing precision qualifiers is to help shader programs improve runtime efficiency, reduce memory usage. Generally adopts medium precision:
#ifdef GL_ES
precision mediump float; // Can also be highp and lowp
#endif
This statement indicates all floating point numbers encountered later without declared precision use medium precision, only float type has no set default precision, so in shader source programs must first set float default precision then use float type
Whether fragment shader supports high precision needs device support, can detect through checking macro: GL_FRAGMENT_PRECISION_HIGH
X. Preprocessor Directives
Similar to C language, commonly used 3 preprocessor directives as follows:
// 1
#if condition expression
If condition expression true, execute this part
#endif
// 2
#ifdef macro
If macro exists, execute this part
#endif
// 3
#ifndef macro
If macro doesn't exist, execute this part
#endif
// Define macro
#define macro name macro content
// Undefine macro
#undef macro name
More useful is: #version 101 can specify use GLSL ES1.01 version, this directive must be at shader top, before can only be blank or comments
Reference Materials
- "WebGL Programming Guide"
No comments yet. Be the first to share your thoughts.