更新于 

可视空间(透视投影)

定义透视投影可视空间

如何表达透视投影可视空间?

答: 视点、视线、近裁剪面、远裁剪面,宽高比。

透视投影矩阵(perspective projection matrix)
setPerspective(30.0,1.0,1.0,100)
  • fov = 30.0
  • aspect = 1.0
  • near = 1.0
  • far = 100.0
PerspectiveView Code
PerspectiveView运行效果
PerspectiveView运行效果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="../examples/lib/cuon-matrix.js"></script>
<script src="../examples/lib/cuon-utils.js"></script>
<script src="../examples/lib/webgl-debug.js"></script>
<script src="../examples/lib/webgl-utils.js"></script>
<script src="./PerspectiveView.js"></script>
</head>
<body onload="main()">
<canvas id="webgl" width="400" height="400"></canvas>
<p id="nearFar"></p>
</body>
</html>
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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
let VSHADER_SOURCE = `
attribute vec4 a_Position;
attribute vec4 a_Color;
uniform mat4 u_ViewMatrix;
uniform mat4 u_ProjMatrix;
varying vec4 v_Color;
void main(){
gl_Position = u_ProjMatrix * u_ViewMatrix * a_Position;
v_Color = a_Color;
}
`;

let FSHADER_SOURCE = `
#ifdef GL_ES
precision mediump float;
#endif
varying vec4 v_Color;
void main(){
gl_FragColor = v_Color;
}
`;

function main() {
let canvas = document.getElementById('webgl');
let nf = document.getElementById('nearFar');
let gl = getWebGLContext(canvas);
if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
console.log('failed to init shaders');
return;
}
gl.clearColor(0.0, 0.0, 0.0, 1.0);
let n = initVertexBuffers(gl);

let u_ViewMatrix = gl.getUniformLocation(gl.program, 'u_ViewMatrix');
let viewMatrix = new Matrix4();
viewMatrix.setLookAt(
0, 0, 5,
0, 0, -100,
0, 1, 0
);
gl.uniformMatrix4fv(u_ViewMatrix, false, viewMatrix.elements);
let u_ProjMatrix = gl.getUniformLocation(gl.program, 'u_ProjMatrix');
let projMatrix = new Matrix4();

document.onkeydown = function (ev) {
keydown(canvas,gl,ev,n,u_ProjMatrix,projMatrix,nf);
}
draw(canvas,gl,n,u_ProjMatrix,projMatrix,nf);
}

let g_fov = 30.0;
let g_far = 100.0;
function keydown(canvas,gl,ev,n,u_ProjMatrix,projMatrix,nf) {
switch (ev.keyCode) {
case 37: g_far -= 1.0; break;
case 38: g_fov += 1.0; break;
case 39: g_far += 1.0; break;
case 40: g_fov -= 1.0; break;
default: break;
}
draw(canvas,gl, n, u_ProjMatrix, projMatrix, nf);
}

function draw(canvas,gl,n,u_ProjMatrix,projMatrix,nf) {
projMatrix.setPerspective(g_fov, canvas.width / canvas.height, 1.0, g_far);
gl.uniformMatrix4fv(u_ProjMatrix, false, projMatrix.elements);
nf.innerHTML = 'fov:' + Math.round(g_fov * 10) / 10 + ' far:' + Math.round(g_far * 10) / 10;
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLES, 0, n);
}

function initVertexBuffers(gl) {
let verticesColors = new Float32Array([
//* 右侧3个三角形
// 绿色-最后
0.75, 1.0, -4.0, 0.4, 1.0, 0.4,
0.25, -1.0, -4.0, 0.4, 1.0, 0.4,
1.25, -1.0, -4.0, 1.0, 0.4, 0.4,
// 黄色-中间
0.75, 1.0, -2.0, 1.0, 1.0, 0.4,
0.25, -1.0, -2.0, 1.0, 1.0, 0.4,
1.25, -1.0, -2.0, 1.0, 0.4, 0.4,
// 蓝色-最前
0.75, 1.0, 0.0, 0.4, 0.4, 1.0,
0.25, -1.0, 0.0, 0.4, 0.4, 1.0,
1.25, -1.0, 0.0, 1.0, 0.4, 0.4,

//* 左侧3个三角形
// 绿色-最后
-0.75, 1.0, -4.0, 0.4, 1.0, 0.4,
-1.25, -1.0, -4.0, 0.4, 1.0, 0.4,
-0.25, -1.0, -4.0, 1.0, 0.4, 0.4,
// 黄色-中间
-0.75, 1.0, -2.0, 1.0, 1.0, 0.4,
-1.25, -1.0, -2.0, 1.0, 1.0, 0.4,
-0.25, -1.0, -2.0, 1.0, 0.4, 0.4,
// 蓝色-最前
-0.75, 1.0, 0.0, 0.4, 0.4, 1.0,
-1.25, -1.0, 0.0, 0.4, 0.4, 1.0,
-0.25, -1.0, 0.0, 1.0, 0.4, 0.4,
]);
let n = 18;
let FSIZE = verticesColors.BYTES_PER_ELEMENT;
let a_Position = gl.getAttribLocation(gl.program, 'a_Position');
let a_Color = gl.getAttribLocation(gl.program, 'a_Color');
let vertexColorBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexColorBuffer);
gl.bufferData(gl.ARRAY_BUFFER, verticesColors, gl.STATIC_DRAW);
gl.vertexAttribPointer(a_Position, 3, gl.FLOAT, false, 6 * FSIZE, 0);
gl.enableVertexAttribArray(a_Position);
gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, 6 * FSIZE, 3 * FSIZE);
gl.enableVertexAttribArray(a_Color);
return n;
}
透视矩阵的作用

透视矩阵实际上对三角形进行了 两次变换

  1. 缩小变换: 根据三角形与视点距离按比例对三角形进行缩小变换。
  2. 平移变换: 使三角形贴近视线。

因此我们可以手动对盒式空间中的图形进行几何变换,使其达到具有深度的视觉效果。

盒状可视空间(Canonical View Volume) 与金字塔状可视空间的关系
盒状可视空间(Canonical View Volume) 与金字塔状可视空间的关系

共冶一炉

PerspectiveView有什么问题是需要优化的?

答: 左右两列三角形对称,完全可以由一列三角形平移获得。

  1. 准备虚线三角形顶点数据
  2. 沿X轴正方向平移0.75单位,绘制出结果
  3. 沿X轴负方向平移0.75单位,绘制出结果
mvp矩阵
  • 模型矩阵(Model Matrix)
  • 视图矩阵(View Matrix)
  • 投影矩阵(Project Matrix)
1
gl_Position = 投影矩阵 * 视图矩阵 * 模型矩阵 * a_Position;
PerspectiveView_mvp Code
和PerspectiveView运行结果一样
和PerspectiveView运行结果一样
1
2
3
4
5
6
7
8
9
10
11
12
let VSHADER_SOURCE = `
attribute vec4 a_Position;
attribute vec4 a_Color;
uniform mat4 u_ViewMatrix;
uniform mat4 u_ProjMatrix;
uniform mat4 u_ModelMatrix;
varying vec4 v_Color;
void main(){
gl_Position = u_ProjMatrix * u_ViewMatrix * u_ModelMatrix * a_Position;
v_Color = a_Color;
}
`;
1
2
3
4
5
6
7
8
let u_ModelMatrix = gl.getUniformLocation(gl.program, 'u_ModelMatrix');
let modelMatrix = new Matrix4();
modelMatrix.setTranslate(-0.75, 0, 0);
gl.uniformMatrix4fv(u_ModelMatrix, false, modelMatrix.elements);
gl.drawArrays(gl.TRIANGLES, 0, n);
modelMatrix.setTranslate(0.75, 0, 0);
gl.uniformMatrix4fv(u_ModelMatrix, false, modelMatrix.elements);
gl.drawArrays(gl.TRIANGLES, 0, n);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
let verticesColors = new Float32Array([
//* 右侧3个三角形
// 绿色-最后
0.0, 1.0, -4.0, 0.4, 1.0, 0.4,
-0.5, -1.0, -4.0, 0.4, 1.0, 0.4,
0.5, -1.0, -4.0, 1.0, 0.4, 0.4,
// 黄色-中间
0.0, 1.0, -2.0, 1.0, 1.0, 0.4,
-0.5, -1.0, -2.0, 1.0, 1.0, 0.4,
0.5, -1.0, -2.0, 1.0, 0.4, 0.4,
// 蓝色-最前
0.0, 1.0, 0.0, 0.4, 0.4, 1.0,
-0.5, -1.0, 0.0, 0.4, 0.4, 1.0,
0.5, -1.0, 0.0, 1.0, 0.4, 0.4,
]);
PerspectiveView_mvp需要注意的部分
性能真的提高了吗?

答: 虽然减少了顶点的个数,但是增加了drawArrays的次数,因此实际使用时性能不一定提高。

可不可以把矩阵的计算拉出着色器?

答: 被拉出的矩阵被称为 模型视图投影矩阵

关键代码行
1
mvpMatrix.set(projMatrix).multiply(viewMatrix).multiply(modelMatrix);