【渲染学习随笔】三角形的顶点绕序与背面消隐
今天依然在研究
在渲染由多个三角形网格所组成的物体时,在将物体渲染到屏幕时,我们需要根据相机的视角删除不需要渲染的网格(或多边形)。这些网格是相机视角无法看到的网格部分,比如说在物体背向相机的网格(相机看不到的面)。
-
顶点绕序与背面消隐的关系
由于背面消隐的基本数学计算是根据被渲染物体的每个面的法向量来计算的,而每个面的法向量又基于三角形的顶点来计算。三角形的法向量计算方式如下:
// p1, p2, p3, 为三角形的三个顶点 Vector3 v1 = p2.position - p1.position; Vector3 v2 = p3.position - p2.position; // 计算法向量 Vector3 normal = Vector3.Cross(v1, v2);
其中,所有三角形的三个顶点必须严格按照逆时针或者顺时针的顺序存储,否则将会影响相机判断那个面面对相机需要渲染,哪些面需要剔除。
在得到面的法向量后,我们需要求得观察向量(相机到三角形某一顶点的向量)与法向量的夹角来判断这个面是否要渲染。若夹角小于 90 度,则这个面需要被渲染。若大于等于 90 度则不渲染。计算方式如下:
// 观察向量 Vector 3 viewDir = p1.position - camera.position; // 判断是否渲染 if(Vector3.Dot(normal, viewDir) < 90){ // render code ... return true; } return false;
背面消隐的图片讲解请看《
1 bool BackFaceCulling(Vertex p1, Vertex p2, Vertex p3){ 2 // p1, p2, p3, 为三角形的三个顶点 3 Vector3 v1 = p2.position - p1.position; 4 Vector3 v2 = p3.position - p2.position; 5 // 计算法向量 6 Vector3 normal = Vector3.Cross(v1, v2); 7 8 // 观察向量(平面上的任意一点,可以是三角形的顶点) 9 Vector 3 viewDir = p1.position - camera.position; 10 // 判断是否渲染 11 if(Vector3.Dot(normal, viewDir) < 0){ 12 // render code ... 13 return true; 14 } 15 return false; 16 }
-
实现注意
在 BlauHimmel 和韦易笑大神们的渲染器代码的基础上实现了背面消隐,虽然算法比较简单,但有以下几个点在实现的时候需要注意:
-
一定,一定,一定检查三角形顶点的绕序!!!
在实现过程中,我花了好久去对比每个三角形面的顶点绕序,确保他们是在正面面对我时逆时针旋转排列的。这个地方一定要注意,一定是要把模型的每个面在面对自己的情况下检查顶点绕序。但因为在我的代码中,模型是一个自己构造的正方体,比较简单,但也因为顶点绕序混乱花了不少时间去重新排列顶点绕序。
-
在相机观察空间(View Space) 里面进行法向量的判断和观察向量的计算
其实三角形平面法向量的计算只需要在进行投影变换之前(在 3D 变 2D 之前)在世界坐标空间(World Space)和相机观察空间(View Space)进行计算就好,因为一旦进行了投影变换,顶点深度坐标就被改变了,则无法算出正确的法向量。
我在这里之所以选择在相机空间中原因是因为相机空间中的相机坐标为 (0,0,0),即相机坐标即为世界坐标原点,这使得观察向量的计算只需要面上的一个顶点减去 (0,0,0)即可,或者直接用该顶点与法向量做点乘即可。
BlauHimmel --- Simple-3D-Renderer:
https://gameinstitute.qq.com/article/detail/289434
渲染管线之背面消隐: