WebGL打开 3D 世界的大门(六):透视投影

这一节我们来介绍透视投影,那什么是透视投影呢?和我们说的正射投影有什么不同呢?在现实生活中距离我们越远的物体就看起来越小,这就是透视投影,就是在z轴方向上,距离我们越远的物体,在表现上来说就越小,这一节我们先写一个简单的透视投影,然后讨论一下透视矩阵,以及透视矩阵的生成过程,最后用透视矩阵实现E的展示。

一个简单的透视算法

我们可以这样想,一个最简单的透视算法就是用一个点的X,Y坐标除以Z坐标就行了,这就能实现到我们距离越远,距离越小的效果。我们在第5章的基础上修改顶点着色器,如下

ini 复制代码
float z_half = a_position.z * 0.005;
vec3 t_position = vec3(a_position.x/z_half,a_position.y/z_half,a_position.z);
// vec4(a_position.xy / z, z, 1.0);
vec3 position = (matrix_translate * matrix_rotateX * matrix_rotateY * matrix_rotateZ * matrix_scale * vec4(t_position, 1)).xyz;

至于为什么要乘以0.005,那是由于我们的z值有点大,并且这个算法不准确,取了一个经验值。效果如下:

可以较为明显的看到3D的透视效果。感兴趣的同学可以自行调节参数再试试其他的。很明显我们的透视算法并不好,太简单了。见到到就不太正确,下面我们介绍真能整的透视变换。

透视矩阵的生成

关于什么是透视,首先要上图,一图胜千言。

这两张图来源于计算机图形学这本书。我们要有一个感性的认识就行了。什么是感性的认识呢,从数学上来讲就是要知道相关系数,看图说话,首先在z轴方向的大小肯定和z轴距离有关,其次和上下夹角ɑ有关,再次和左右∂夹角有关,所以生成矩阵函数的肯定是这样的 Z = f(z,ɑ,∂);具体到底怎么计算的,其实不在我们的讨论范围,我们又不是搞数学的,我们你需要的是结果。结果如下:

以上内容来源于deepseek。然后感性分析一下,嗯应该是对的。然后我们在代码里面根据这个信息,构造透视矩阵。

使用透视矩阵

然后将透视矩阵放到我们以前的代码中。主要代码:

ini 复制代码
/**
 * 构造透视投影矩阵(列主序)
 * @param {number} fov - 垂直视野角(弧度制,如 Math.PI/4)
 * @param {number} aspect - 宽高比(width / height)
 * @param {number} near - 近裁剪面距离(必须 > 0)
 * @param {number} far - 远裁剪面距离(必须 > near)
 * @returns {Float32Array} 4x4 透视矩阵(列主序)
 */
function createPerspectiveMatrix(fov, aspect, near, far) {
  // 1. 计算垂直方向的缩放因子(基于视野角)
  const f = 1.0 / Math.tan(fov / 2); // 相当于 1/tan(fov/2)

  // 2. 计算深度范围倒数(用于非线性深度映射)
  const rangeInv = 1.0 / (near - far);

  // 3. 初始化列主序矩阵(16元素 Float32Array)
  const matrix = new Float32Array(16);

  // 第一列
  matrix[0] = f / aspect; // x 缩放(受宽高比影响)
  matrix[1] = 0;
  matrix[2] = 0;
  matrix[3] = 0;

  // 第二列
  matrix[4] = 0;
  matrix[5] = f;          // y 缩放
  matrix[6] = 0;
  matrix[7] = 0;

  // 第三列(深度处理)
  matrix[8] = 0;
  matrix[9] = 0;
  matrix[10] = (far + near) * rangeInv;  // 非线性深度映射
  matrix[11] = -1;                       // 透视除法关键

  // 第四列
  matrix[12] = 0;
  matrix[13] = 0;
  matrix[14] = 2 * far * near * rangeInv; // 深度平移
  matrix[15] = 0;

  return matrix;
}

效果如下:

代码地址: gitee.com/feng-lianxi...

总结

有了透视矩阵我们就可以建立摄像机的概念了,这样你就会对three.js的摄像机场景画布,有更清晰的概念。下一节我们就介绍摄像机。

相关推荐
叹一曲当时只道是寻常3 分钟前
Softhub软件下载站实战开发(十六):仪表盘前端设计与实现
前端·golang
超级土豆粉8 分钟前
npm 包 scheduler 介绍
前端·npm·node.js
bug爱好者9 分钟前
原生小程序如何实现跨页面传值
前端·javascript
随笔记12 分钟前
uniapp开发的小程序输入框在ios自动填充密码,如何欺骗苹果手机不让自动填充
前端·ios·app
bug爱好者18 分钟前
原生微信小程序最实用的工具函数合集
前端·javascript
3Katrina21 分钟前
JS事件机制详解(2)--- 委托机制、事件应用
前端·javascript·面试
Allen Bright26 分钟前
【CSS-15】深入理解CSS transition-duration:掌握过渡动画的时长控制
前端·css
张鑫旭27 分钟前
40岁老前端2025年上半年都学了什么?
前端
前端wchen29 分钟前
Vue 3 组件通信实战系列(一)父子组件通信的标准姿势:Props 与 Emit(含实战与进阶技巧)
前端·vue.js
Jerry Lau42 分钟前
go go go 出发咯 - go web开发入门系列(一) helloworld
开发语言·前端·golang