绕过 if else

       不废话直接上代码吧,对于代码

if (x == 0) {
    y += 5;
}

       其实可以优化成这个样子

vec4 when_eq(vec4 x, vec4 y) {
    return 1.0 - abs(sign(x - y));
}

y += 5 * when_eq(x, 0);

如果使用HLSL,则应用 step 函数取代GLSL中的 sign 函数

       对于代码

if (x == 0) {
    b = a1;
} else {
    b = a2;
}

       可以优化成

vec4 when_neq(vec4 x, vec4 y) {
    return abs(sign(x - y));
}

b = mix(a1, a2, when_neq(x, 0));

如果使用HLSL,则应用 lerp 函数取代GLSL中的 mix 函数

常用关系运算符优化

//relation operator
vec4 when_eq(vec4 x, vec4 y) {
    return 1.0 - abs(sign(x - y));
}

vec4 when_neq(vec4 x, vec4 y) {
    return abs(sign(x - y));
}

vec4 when_gt(vec4 x, vec4 y) {
    return max(sign(x - y), 0.0);
}

vec4 when_lt(vec4 x, vec4 y) {
    return max(sign(y - x), 0.0);
}

vec4 when_ge(vec4 x, vec4 y) {
    return 1.0 - when_lt(x, y);
}

vec4 when_le(vec4 x, vec4 y) {
    return 1.0 - when_gt(x, y);
}

常用逻辑运算符优化

//logical operator;
vec4 and(vec4 a, vec4 b) {
    return a * b;
}

vec4 or(vec4 a, vec4 b) {
    return min(a + b, 1.0);
    // or
    // return max(a, b);
}

vec4 xor(vec4 a, vec4 b) {
    return (a + b) % 2.0;
}

vec4 not(vec4 a) {
    return 1.0 - a;
}

给浮点数求余数,HLSL应使用 fmod 函数;GLSL应使用 modf 函数,且只有openGL 3.x/openGL ES 3.x 中才能使用

使用MAD

       MAD 是英文 multiply, then add 的缩写。指的意思是GPU中,编译器可以把一个乘法和一个加法合并成一条指令(类似AMD的FMA指令)。比如

result = 0.5 * (1.0 + variable); // NG
result = 0.5 + 0.5 * variable;

       再举个稍微复杂的例子

// Without MAD
myOutputColor.xyz = myColor.xyz;
myOutputColor.w = 1.0;
gl_FragColor = myOutputColor;

// With MAD
const vec2 constantList = vec2(1.0, 0.0);
gl_FragColor = mycolor.xyzw * constantList.xxxy + constantList.yyyx;

使用Swizzle

in vec4 in_pos;
// The following two lines:
gl_Position.x = in_pos.x;
gl_Position.y = in_pos.y;
// can be simplified to:
gl_Position.xy = in_pos.xy;

多使用shader内置函数

       有很多shader内置函数比如 mix(lerp)dot ,都是优化过的,GPU可以在一个时钟周期内完成(“single-cycle”)

Linear Interpolation

vec3 colorRGB_0, colorRGB_1;
float alpha;
resultRGB = colorRGB_0 * (1.0 - alpha) + colorRGB_1 * alpha;

// The above can be converted to the following for MAD purposes:
resultRGB = colorRGB_0  + alpha * (colorRGB_1 - colorRGB_0);

// GLSL provides the mix function. This function should be used where possible:
resultRGB = mix(colorRGB_0, colorRGB_1, alpha);

Dot products

vec3 fvalue1;
result1 = fvalue1.x + fvalue1.y + fvalue1.z;
vec4 fvalue2;
result2 = fvalue2.x + fvalue2.y + fvalue2.z + fvalue2.w;

// This is essentially a lot of additions. 
// Using a simple constant and the dot-product operator, we can have this:
const vec4 AllOnes = vec4(1.0);
vec3 fvalue1;
result1 = dot(fvalue1, AllOnes.xyz);
vec4 fvalue2;
result2 = dot(fvalue2, AllOnes);

多数shader内置函数都很快,但也存在特例,比如 discard , floor

使用 Index Buffer

       在绘制图元时,永远使用Index Buffer而不是单纯使用三角化后的Vertex Buffer。在openGL中就是使用 glDrawElements 而不要用 glDrawArrays

       首先,使用索引缓冲可以极大减少CPU向 vertex shader 传送数据的大小,减少缓冲压力。其次,对于多次引用到的顶点,其顶点计算结果可以被缓存。

If you are using indexed rendering, then it gets complicated. It’s more-or-less 1:1, each vertex having its own VS invocation. However, thanks to post-T&L caching, it is possible for a vertex shader to be executed less than once per input vertex.(原文)