Skip to main content

GLSL ES (OpenGL ES Shading Language)_WebGL Notes 9

Free2016-01-02#JS#WebGL#webgl着色器#GLSL ES#着色器语言#webgl着色器语法#webgl存储限定字

Comprehensive understanding of GLSL ES language, including syntax rules, data types, flow control, functions, etc.

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

  1. Case sensitive

  2. Statement ends must have semicolons

  3. Start execution from main function

  4. Cannot omit return value type in function declarations (no return value is void, C language can omit, but here cannot)

  5. 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

  1. Variable declaration

Same as C language, type + variable name, variable naming rules also same, basic types only int, float and bool

  1. 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

  1. 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

  1. 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"

Comments

No comments yet. Be the first to share your thoughts.

Leave a comment