WebGL实践篇(六)透视投影下的三维图形及其投影矩阵

一 透视投影的实现 

我们日常观看三维物体的方式就是透视投影,而透视投影符合“近大远小”的原则,那该如何实现呢?方法就是将裁剪控件中的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”!透视投影矩阵就完成啦~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值