更新于 

切换着色器

一个着色器够用吗?

这个问题就好像别人问你:
一天一顿饭能吃饱吗?一年一双袜子够穿吗?笔记本4G内存够用吗?
答案自然是 可以,但没必要,不同的物体经常需要使用不同的着色器绘制。

如何实现切换着色器

切换步骤

说是切换步骤,实际上每个绘制过程的步骤都是完全分离,互不影响的。

1.准备阶段A

准备用来绘制 A物体 的着色器 ShaderA 的代码

2.准备阶段B

准备用来绘制 B物体 的着色器 ShaderB 的代码

3.创建阶段A

调用 createProgram() ,使用 ShaderA 创建出着色器程序对象 ProgramA

4.创建阶段B

调用 createProgram() ,使用 ShaderB 创建出着色器程序对象 ProgramB

5.绘制A①

调用 gl.useProgram() 指定 ProgramA

6.绘制A②

通过缓冲区对象向 ShaderA 传入attribute变量并开启

7.绘制A③

绘制A

8.绘制B①

调用 gl.useProgram() 指定 ProgramB

9.绘制B②

通过缓冲区对象向 ShaderB 传入attribute变量并开启

10.绘制B③

绘制B

ProgramObject Code
ProgramObject运行效果
ProgramObject运行效果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
// 第一步:准备着色器
// 纯色立方体-顶点着色器
let SOLID_VSHADER_SOURCE = `
attribute vec4 a_Position;
attribute vec4 a_Normal;
uniform mat4 u_MvpMatrix;
uniform mat4 u_NormalMatrix;
varying vec4 v_Color;
void main(){
vec3 lightDirection = vec3(0.0, 0.0, 1.0);
vec4 color = vec4(0.0,1.0,0.0,1.0);
gl_Position = u_MvpMatrix * a_Position;
vec3 normal = normalize(vec3(u_NormalMatrix * a_Normal));
float nDotL = max(dot(normal,lightDirection), 0.0);
v_Color = vec4(color.rgb * nDotL, color.a);
}
`
// 纯色立方体-片元着色器
let SOLID_FSHADER_SOURCE = `
#ifdef GL_ES
precision mediump float;
#endif
varying vec4 v_Color;
void main(){
gl_FragColor = v_Color;
}
`
// 纹理立方体-顶点着色器
let TEXTURE_VSHADER_SOURCE = `
attribute vec4 a_Position;
attribute vec4 a_Normal;
attribute vec2 a_TexCoord;
uniform mat4 u_MvpMatrix;
uniform mat4 u_NormalMatrix;
varying vec2 v_TexCoord;
varying float v_NdotL;
void main(){
vec3 lightDirection = vec3(0.0, 0.0, 1.0);
gl_Position = u_MvpMatrix * a_Position;
v_TexCoord = a_TexCoord;
vec3 normal = normalize(vec3(u_NormalMatrix * a_Normal));
v_NdotL = max(dot(normal, lightDirection), 0.0);
}
`
// 纹理立方体-片元着色器
let TEXTURE_FSHADER_SOURCE = `
#ifdef GL_ES
precision mediump float;
#endif
uniform sampler2D u_Sampler;
varying vec2 v_TexCoord;
varying float v_NdotL;
void main(){
vec4 color = texture2D(u_Sampler, v_TexCoord);
gl_FragColor = vec4( color.rgb*v_NdotL, color.a );
}
`
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// 纯色立方体着色器 切换与attribute变量初始化
function drawSolidCube(gl, program, o, x, angle, viewProjMatrix) {
gl.useProgram(program);

initAttributeVariable(gl, program.a_Position, o.vertexBuffer);
initAttributeVariable(gl, program.a_Normal, o.normalBuffer);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, o.indexBuffer);
drawCube(gl, program, o, x, angle, viewProjMatrix);
}

// 纹理立方体着色器 切换与attribute变量初始化
function drawTexCube(gl,program,o,texture,x,angle,viewProjMatrix) {
gl.useProgram(program);

initAttributeVariable(gl, program.a_Position, o.vertexBuffer);
initAttributeVariable(gl, program.a_TexCoord, o.texCoordBuffer);
initAttributeVariable(gl, program.a_Normal, o.normalBuffer);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, o.indexBuffer);

gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, texture);

drawCube(gl, program, o, x, angle, viewProjMatrix);
}

// 用来注入attribute buffer的通用函数
function initAttributeVariable(gl, a_attribute, buffer) {
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.vertexAttribPointer(a_attribute, buffer.num, buffer.type, false, 0, 0);
gl.enableVertexAttribArray(a_attribute);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
// 初始化vertex buffer
function initVertexBuffers(gl) {
let vertices = new Float32Array([ // Vertex coordinates
1.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0,-1.0, 1.0, 1.0,-1.0, 1.0, // v0-v1-v2-v3 front
1.0, 1.0, 1.0, 1.0,-1.0, 1.0, 1.0,-1.0,-1.0, 1.0, 1.0,-1.0, // v0-v3-v4-v5 right
1.0, 1.0, 1.0, 1.0, 1.0,-1.0, -1.0, 1.0,-1.0, -1.0, 1.0, 1.0, // v0-v5-v6-v1 up
-1.0, 1.0, 1.0, -1.0, 1.0,-1.0, -1.0,-1.0,-1.0, -1.0,-1.0, 1.0, // v1-v6-v7-v2 left
-1.0,-1.0,-1.0, 1.0,-1.0,-1.0, 1.0,-1.0, 1.0, -1.0,-1.0, 1.0, // v7-v4-v3-v2 down
1.0,-1.0,-1.0, -1.0,-1.0,-1.0, -1.0, 1.0,-1.0, 1.0, 1.0,-1.0 // v4-v7-v6-v5 back
]);

let normals = new Float32Array([ // Normal
0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, // v0-v1-v2-v3 front
1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, // v0-v3-v4-v5 right
0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, // v0-v5-v6-v1 up
-1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, // v1-v6-v7-v2 left
0.0,-1.0, 0.0, 0.0,-1.0, 0.0, 0.0,-1.0, 0.0, 0.0,-1.0, 0.0, // v7-v4-v3-v2 down
0.0, 0.0,-1.0, 0.0, 0.0,-1.0, 0.0, 0.0,-1.0, 0.0, 0.0,-1.0 // v4-v7-v6-v5 back
]);

let texCoords = new Float32Array([ // Texture coordinates
1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, // v0-v1-v2-v3 front
0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, // v0-v3-v4-v5 right
1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.0, 0.0, // v0-v5-v6-v1 up
1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, // v1-v6-v7-v2 left
0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, // v7-v4-v3-v2 down
0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0 // v4-v7-v6-v5 back
]);

let indices = new Uint8Array([ // Indices of the vertices
0, 1, 2, 0, 2, 3, // front
4, 5, 6, 4, 6, 7, // right
8, 9,10, 8,10,11, // up
12,13,14, 12,14,15, // left
16,17,18, 16,18,19, // down
20,21,22, 20,22,23 // back
]);

let o = new Object();

// 将顶点数据写入buffer
o.vertexBuffer = initArrayBufferForLaterUse(gl, vertices, 3, gl.FLOAT);
o.normalBuffer = initArrayBufferForLaterUse(gl, normals, 3, gl.FLOAT);
o.texCoordBuffer = initArrayBufferForLaterUse(gl, texCoords, 2, gl.FLOAT);
o.indexBuffer = initElementArrayBufferForLaterUse(gl, indices, gl.UNSIGNED_BYTE);
o.numIndices = indices.length;
gl.bindBuffer(gl.ARRAY_BUFFER, null);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);

return o;

}

// 初始化texture
function initTextures(gl, program) {
let texture = gl.createTexture();
let image = new Image();
image.onload = function () {
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1); // Flip the image Y coordinate
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);

gl.useProgram(program);
gl.uniform1i(program.u_Sampler, 0);

gl.bindTexture(gl.TEXTURE_2D, null);
};
image.src = '../img/sky.jpg';
return texture;
}
// 初始化ARRAY_BUFFER类型buffer内存
function initArrayBufferForLaterUse(gl,data,num,type) {
let buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
buffer.num = num;
buffer.type = type;
return buffer;
}

// 用于创建ELEMENT_ARRAY_BUFFER的通用函数
function initElementArrayBufferForLaterUse(gl,data,type) {
let buffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, data, gl.STATIC_DRAW);
buffer.type = type;
return buffer;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 用于进行变换的矩阵
let g_modelMatrix = new Matrix4();
let g_mvpMatrix = new Matrix4();
let g_normalMatrix = new Matrix4();

// 绘制立方体的通用函数
function drawCube(gl,program,o,x,angle,viewProjMatrix) {
g_modelMatrix.setTranslate(
x, 0, 0
).rotate(
20, 1, 0, 0
).rotate(
angle, 0, 1, 0
);
// 计算normalMatrix
g_normalMatrix.setInverseOf(g_modelMatrix);
g_normalMatrix.transpose();
gl.uniformMatrix4fv(program.u_NormalMatrix, false, g_normalMatrix.elements);

g_mvpMatrix.set(viewProjMatrix);
g_mvpMatrix.multiply(g_modelMatrix);
gl.uniformMatrix4fv(program.u_MvpMatrix, false, g_mvpMatrix.elements);

gl.drawElements(gl.TRIANGLES, o.numIndices, gl.UNSIGNED_BYTE,0);
}
let ANGLE_STEP = 30;
let last = Date.now();
function animate(angle) {
let now = Date.now();
let ellapse = now - last;
last = now;
return ((angle + ANGLE_STEP * ellapse / 1000)) % 360;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
function main() { 
let canvas = document.getElementById('webgl');
let gl = getWebGLContext(canvas);

// 创建着色器程序对象
let solidProgram = createProgram(gl, SOLID_VSHADER_SOURCE, SOLID_FSHADER_SOURCE);
let texProgram = createProgram(gl, TEXTURE_VSHADER_SOURCE, TEXTURE_FSHADER_SOURCE);

// 获取attribute和uniform对象
solidProgram.a_Position = gl.getAttribLocation(solidProgram, 'a_Position');
solidProgram.a_Normal = gl.getAttribLocation(solidProgram, 'a_Normal');
solidProgram.u_MvpMatrix = gl.getUniformLocation(solidProgram, 'u_MvpMatrix');
solidProgram.u_NormalMatrix = gl.getUniformLocation(solidProgram, 'u_NormalMatrix');

texProgram.a_Position = gl.getAttribLocation(texProgram, 'a_Position');
texProgram.a_TexCoord = gl.getAttribLocation(texProgram, 'a_TexCoord');
texProgram.a_Normal = gl.getAttribLocation(texProgram, 'a_Normal');
texProgram.u_MvpMatrix = gl.getUniformLocation(texProgram, 'u_MvpMatrix');
texProgram.u_NormalMatrix = gl.getUniformLocation(texProgram, 'u_NormalMatrix');
texProgram.u_Sampler = gl.getUniformLocation(texProgram, 'u_Sampler');

// 初始化attribute buffer
let cube = initVertexBuffers(gl);
let texture = initTextures(gl, texProgram);

gl.enable(gl.DEPTH_TEST);
gl.clearColor(0.0, 0.0, 0.0, 1.0);

let viewProjMatrix = new Matrix4();
viewProjMatrix.setPerspective(
30.0, canvas.width / canvas.height, 1.0, 100.0
).lookAt(
0.0, 0.0, 15.0,
0, 0, 0,
0, 1, 0
);
let currentAngle = 0.0;
let tick = function () {

gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
currentAngle = animate(currentAngle);
drawSolidCube(gl, solidProgram, cube, -2.0, currentAngle, viewProjMatrix);
drawTexCube(gl, texProgram, cube, texture, 2.0, currentAngle, viewProjMatrix);
window.requestAnimationFrame(tick);
}
tick();
}
切换着色器可以用来做什么?

答: 在同一个场景中绘制出各种不同效果组合。

案例待补充