一 透视投影的实现
我们日常观看三维物体的方式就是透视投影,而透视投影符合“近大远小”的原则,那该如何实现呢?方法就是将裁剪控件中的X和Y除以Z即可。Z就是深度值,也就是说距屏幕的远近。随着Z值变大X和Y的值就会变小,那么绘制出来的图形也会变小,距离也就变远了。那Z值的范围是-1到1,如何控制距离远近呢?就可以利用一个因子factor与Z相乘来控制距离啦。
二 透视投影在顶点着色器中的体现
1.将factor因子加入到着色器当中
<script id="vertex-shader-3D" type="x-shader/x-vertex">
attribute vec4 a_position;
attribute vec4 a_color;
uniform mat4 u_matrix;
uniform float u_fudgeFactor;
varying vec4 v_color;
void main(){
vec4 position = u_matrix * a_position;
//加1是为了zToDivideBy的范围从[-1,1]变成[0,2*fudgeFactor],这样更容易理解缩放(如小于1为缩,大于1为放)
float zToDivideBy = 1.0 + u_fudgeFactor * position.z;
gl_Position = vec4(position.xy / zToDivideBy,position.zw);
v_color = a_color;
}
</script>
2.设置fudgeFactor(初始值设为1)
var factorUniformLocation = webgl.getUniformLocation(program, "u_fudgeFactor");
webgl.uniform1f(factorUniformLocation, fudgeFactor);
结果如下:
3.优化着色器代码
WebGL会将我们提供给gl_Position的四个参数值自动除以w,因此可以直接将zToDivideBy放到w参数的位置。
gl_Position = vec4(position.xyz,zToDivideBy);
再进一步简化就是将其换成矩阵的形式(w' = z * fudgeFactor + 1):
makeZtoWMatrix: function(fudgeFactor) {
return [
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, fudgeFactor,
0, 0, 0, 1
]
},
那么顶点着色器就变成了这样:
<script id="vertex-shader-3D" type="x-shader/x-vertex">
attribute vec4 a_position;
attribute vec4 a_color;
uniform mat4 u_matrix;
varying vec4 v_color;
void main(){
gl_Position = u_matrix * a_position;
v_color = a_color;
}
</script>
矩阵的设置也稍微发生了一点点变化:
var matrix = m4.makeZtoWMatrix(fudgeFactor);
matrix = m4.multiply(matrix, m4.projection(webgl.canvas.clientWidth, webgl.canvas.clientHeight, 400));
结果如下:
三 透视投影矩阵
在我们将z的滑块值调整到(-webgl.canvas.height,webgl.canvas.height)的范围之后,会发现z的值在负某值或正某值的时候“F”的部分或者全部会消失在我们的视野当中,这就引入了“视锥”的概念,在视锥体之外的东西我们是看不见的(超出了裁剪空间Z的范围[-1,1])。因此我们可以通过投影矩阵将三维图元映射(投影)到裁剪空间内,这样我们就能看见啦。
投影矩阵的设置(至于每个矩阵参数的设置还是得好好扒原理啦~):
perspective: function (fieldofViewInRadians, aspect, near, far) {
var f = Math.tan(Math.PI * 0.5 - 0.5 * fieldofViewInRadians);
var rangInv = 1.0 / (near - far)
return [
f / aspect, 0, 0, 0,
0, f, 0, 0,
0, 0, (near + far) * rangInv, -1,
0, 0, near * far * rangInv * 2, 0
]
},
透视投影矩阵里面每个参数的含义可以参考这份博客,会加深理解:
(26条消息) 26 WebGL的透视投影矩阵_暮志未晚Webgl的博客-CSDN博客_webgl投影矩阵https://blog.csdn.net/qq_30100043/article/details/72842498从这篇博客中我们可以知道fieldofViewInRadians参数就是我们的垂直视角,而它切分称为一半就对应为中心到Y=-1与Y=1,而X的值则需通过乘以aspect获取,Znear对应裁剪空间中的Z=-1,Zfar对应裁剪空间中的Z=1,需要注意裁剪空间中的坐标与屏幕空间中的坐标之间的区别。
调整fov,Znear,Zfar都会改变视锥体的大小从而控制我们的视野范围。
设置矩阵参数:
var matrix = m4.perspective(fieldofViewInRadians, webgl.canvas.clientWidth / webgl.canvas.clientHeight, 1, 2000);
调完之后我们发现,咦,"F"咋看不见了咧?
这就是因为我们Z的原因啦,正常咱们的Z轴的正方向是朝向屏幕外的,但是我们的视角(假设坐标为(0,0,0)点)是朝向屏幕内的,这就导致咱们的Z值为0的F消失在了我们的视野当中。就比如图上的这样,离得太近了(z值越靠近0)离得太远了(z值越靠近负无穷大)我们都看不见,因为不在视锥体内部。
那么就需要将“F"的Z值平移到负值才能看见,让我们来拉一下滑块吧。
怎么倒过来啦,那就从根源解决,直接改”F"的位置吧。
将~ 一个离我们超近的巨大“F”!透视投影矩阵就完成啦~