更新于 

可视范围

可视范围(visible range)

LookAtTrianglesWithKeys 程序中,三角形的一角被残忍地咬掉了。

三角形界的灵异照片
三角形界的灵异照片

这是因为没有设置 可视范围
WebGL只显示可视范围内的区域。

可视范围(正射类型)

不绘制可视范围外的对象,是基本的降低程序开销的手段。

为什么要设置可视范围?

因为大型网游earth Online最初就是这么设计的(大雾)!
earth Online设计者考虑到这一点,给了你视线范围,要不让你随便朝天上扫一眼,就能看到月球上跳舞的小人吗?

心外无物,心外无事,心外无理

虽然earth Online中的哲学分支可谓复杂纷纭,
但是要理解WebGL世界中的可视距离机制,主观唯心主义观点可能会对你有所启发:

存在即被感知 ——【英】贝克莱

人类本身也只能看到眼前的东西,水平视角大约200度左右。

可视空间(view volume)

有两类常用的可视空间:

正射投影

orthographic projection

  • 长方形可视(盒状)空间
  • 物体看上去大小与其所在位置没有关系
  • 可以方便地比较场景中物体大小
  • 常用于平面绘图等技术
透视投影

perspective projection

  • 四棱锥(金字塔)可视空间
  • 使三维场景看上去更有深度感,更自然
  • 真实世界也是透视投影
盒状可视空间

近裁剪面远裁剪面 之间的空间就是 盒状可视空间
只有在此空间内的物体会被显示出来。

如果裁剪面和canvas画布的宽高比不同会怎么样?

答: 会按照canvas的宽高对画面进行压缩,物体会被扭曲。

定义盒状可视空间 Matrix4.setOrtho()

设置投影的矩阵被称为 正射投影矩阵(orthographic projection matrix)

OrthoView

该案例支持键盘控制可视空间深度大小。

OrthoView Code
偏转视点观察OrthoView运行效果
偏转视点观察OrthoView运行效果
观察三角形消失顺序与盒空间厚度的关系
观察三角形消失顺序与盒空间厚度的关系
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="./OrthoView.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
let VSHADER_SOURCE = `
attribute vec4 a_Position;
attribute vec4 a_Color;
uniform mat4 u_ProjMatrix;
varying vec4 v_Color;
void main(){
gl_Position = u_ProjMatrix * 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 (!gl) {
console.log('failed to get context');
return;
}
if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
console.log('failed to init shaders');
return;
}
let n = initVertexBuffers(gl);
if (n < 0) {
console.log('failed to init vertex buffers');
return;
}
gl.clearColor(0.0, 0.0, 0.0, 1.0);

let u_ProjMatrix = gl.getUniformLocation(gl.program, 'u_ProjMatrix');
let projMatrix = new Matrix4();

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

let g_nf = [0.0, 0.5];
function keydown(ev,gl,n,u_ProjMatrix,projMatrix,nf) {
switch (ev.keyCode) {
case 39: g_nf[0] += 0.01; break;//右键
case 37: g_nf[0] -= 0.01; break;//左键
case 38: g_nf[1] += 0.01; break;//上键
case 40: g_nf[1] -= 0.01; break;//下键
default: break;
}
draw(gl, n, u_ProjMatrix, projMatrix, nf);
}

function draw(gl,n,u_ProjMatrix,projMatrix,nf) {
projMatrix.setOrtho(-1, 1, -1, 1, g_nf[0], g_nf[1]);
gl.uniformMatrix4fv(u_ProjMatrix, false, projMatrix.elements);
gl.clear(gl.COLOR_BUFFER_BIT);
// nf.innerHTML = 'near:' + Math.round(g_nf[0] * 100) / 100 +
// ',far:' + Math.round(g_nf[1] * 100) / 100;
nf.innerHTML = 'near:' + g_nf[0].toFixed(2) + ',far:' + g_nf[1].toFixed(2);
gl.drawArrays(gl.TRIANGLES, 0, n);
}

function initVertexBuffers(gl) {
let verticesColor = new Float32Array([
// 红色三角形
0.0, 0.5, -0.4, 1.0, 0.4, 0.4,
-0.5, -0.5, -0.4, 1.0, 0.4, 0.4,
0.5, -0.5, -0.4, 0.0, 0.4, 0.4,
// 绿色三角形
-0.4, 0.4, -0.2, 0.4, 1.0, 0.4,
0.4, 0.4, -0.2, 0.4, 1.0, 0.4,
0.0, -0.6, -0.2, 0.4, 0.0, 0.4,
// 蓝色三角形
0.0, 0.5, 0.0, 0.4, 0.4, 1.0,
-0.5, -0.5, -0.0, 0.4, 0.4, 1.0,
0.5, -0.5, -0.0, 0.4, 0.4, 0.0
]);
let n = 9;
let FSIZE = verticesColor.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, verticesColor, 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;
}
near、far与三角形消失的关系
Js中对固定位小数特殊的处理方式
1
2
3
// 方法1:Math.round
let digit = Math.pow( 100, 2);
let result = Math.round(num * digit)/digit;
1
2
3
// 方法2 toFix
let digit = 2;
let result = num.toFixed(digit);
ShapeZ VauleShow Condition
0.0near<=0.00 && far>=0.00
-0.2near<=0.20 && far>=0.20
-0.4near<=0.40 && far>=0.40

补上三角形缺掉的角

简述:即推远远裁剪面,本例主要介绍视图矩阵与正射投影矩阵的混合使用

正射矩阵和视图投影矩阵的使用顺序
1
gl_Position = ProjMatrix * ViewMatrix * a_Position;
  1. 得到顶点在视图坐标系下的坐标
  2. 使用正射投影矩阵计算可视范围
LookAtTrianglesWithKeys_ViewVolume
三角形整形前
三角形整形前
三角形整型后
三角形整型后

可视空间与canvas的比例冲突

比例冲突会导致压缩变形
画布大小不变,可视空间横截面缩减一半

运行效果: 图形变为原来大小的两倍,超出可视空间部分被裁剪。

1
projMatrix.setOrtho(-0.5,0.5,-0.5,0.5,0.0,1.0)
画布大小不变,可视空间比例改变

运行效果: 图形在宽度上被拉伸导致变形,超出可视空间部分被裁剪。

1
projMatrix.setOrtho(-0.3,0.3,-1.0,1.0,0.0,1.0)