• 【渲染学习随笔】三角形的顶点绕序与背面消隐


    今天依然在研究  BlauHimmel 大神的光栅器代码 [1]。在自己实现的时候发现自己的颜色填充渲染只渲染了一半的三角形,还有部分渲染部分被截去了。经前辈指点,问题应该是出在三角形的顶点绕序:因为三角形的顶点在生成 Mesh 的时候排列混乱,导致摄像机没法分清平面的正面背面,从而影响了渲染效果。

    所以打算学习并总结一下这个问题(以下内容总结自《腾讯游戏学院:渲染管线之背面消隐[2]):

    • 什么是背面消隐

    在渲染由多个三角形网格所组成的物体时,在将物体渲染到屏幕时,我们需要根据相机的视角删除不需要渲染的网格(或多边形)。这些网格是相机视角无法看到的网格部分,比如说在物体背向相机的网格(相机看不到的面)。

    • 顶点绕序与背面消隐的关系

    由于背面消隐的基本数学计算是根据被渲染物体的每个面的法向量来计算的,而每个面的法向量又基于三角形的顶点来计算。三角形的法向量计算方式如下:

    // 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;

    背面消隐的图片讲解请看《腾讯游戏学院:渲染管线之背面消隐》[2]

    以上的完整伪代码如下:

     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)即可,或者直接用该顶点与法向量做点乘即可。

    参考材料:

    [1] BlauHimmel --- Simple-3D-Renderer: https://github.com/BlauHimmel/Simple-3D-Renderer

    [2] 腾讯游戏学院:渲染管线之背面消隐:https://gameinstitute.qq.com/article/detail/289434

  • 相关阅读:
    MQTT---HiveMQ源代码具体解释(四)插件载入
    HTTP访问控制(CORS)
    java实现限流
    Orika的使用姿势,Orika java bean copy 框架
    uploadify是通过flash上传,服务器获取type为application/octet-stream
    ThinkPHP 5使用 Composer 组件名称可以从https://packagist.org/ 搜索到
    3种方法轻松处理php开发中emoji表情的问题
    解决thinkphp设置session周期无效的问题
    如何使用沙箱测试单笔转账到支付宝账号(php版) https://openclub.alipay.com/read.php?tid=1770&fid=28
    微信jssdk批量添加卡券接口
  • 原文地址:https://www.cnblogs.com/thdt/p/13676061.html
Copyright © 2020-2023  润新知