美文网首页unity3D技术分享Unity分享征服Unity3d
3D游戏编程大师技巧(2) 3D线框引擎

3D游戏编程大师技巧(2) 3D线框引擎

作者: 云木unity | 来源:发表于2019-03-08 13:35 被阅读6次
    EngineMain_Tank.png EngineMain_Box.png

    目录

    学习了前七章的vs工程, github地址: https://github.com/GeWenL/My3DGameEngine

    1. 本书坐标是行矩阵还是列矩阵?
    2. Model变换 : 局部坐标到世界坐标
    3. View变换:世界坐标到相机坐标
    4. Projection变换 + 投影到Screen:相机坐标到透视坐标再到屏幕坐标
    5. 渲染线框
    6. 背面剔除 与 物体剔除

    一、本书坐标是行矩阵还是列矩阵?

    unity使用的是列矩阵,本书中的坐标是行矩阵还是列矩阵?

    为什么要关注这个问题?

    顶点坐标执行变换的顺序是缩放、旋转、位移。
    列矩阵:TRS * POS == (T(R(S * POS)))从右到左执行
    行矩阵:POS *SRT

    Unity中旋转矩阵的顺序是:(基于self坐标系)

    • 旋转后不改变坐标系(官方) zxy :(My * (Mx * (Mz * POS)))
    • 旋转后改变坐标系 yxz :(Mz * (Mx * (My * POS)))

    从Mat_Mul_VECTOR4D_4X4函数中可以看出,用坐标va的行乘以矩阵mb的列。是行矩阵[x,y,x,w]。

    void Mat_Mul_VECTOR4D_4X4(VECTOR4D_PTR  va, 
                              MATRIX4X4_PTR mb,
                              VECTOR4D_PTR  vprod)
    {
    // this function multiplies a VECTOR4D against a 
    // 4x4 matrix - ma*mb and stores the result in mprod
    // the function makes no assumptions
    
        for (int col=0; col < 4; col++)
            {
            // compute dot product from row of ma 
            // and column of mb
            float sum = 0; // used to hold result
    
            for (int row=0; row<4; row++)
                 {
                 // add in next product pair
                 sum+=(va->M[row]*mb->M[row][col]);
                 } // end for index
    
            // insert resulting col element
            vprod->M[col] = sum;
    
            } // end for col
    
    } // end Mat_Mul_VECTOR4D_4X4
    

    二、Model变换

    顶点坐标执行变换的顺序是缩放、旋转、位移。

    1. 示例版本demoII7_3.cpp的实现方式:
    1. 缩放:在load模型的时候就对局部坐标进行缩放,非矩阵。变换后存放在vlist_local局部信息中。
      Load_OBJECT4DV1_PLG(&obj, "cube2.plg",&vscale, &vpos, &vrot);
    // scale vertices
    obj->vlist_local[vertex].x*=scale->x;
    obj->vlist_local[vertex].y*=scale->y;
    obj->vlist_local[vertex].z*=scale->z;
    
    1. 旋转:旋转矩阵,变换后存放在vlist_local局部信息中。

    在示例中,是遵循xyz的顺序:

    // generate rotation matrix around y axis
    Build_XYZ_Rotation_MATRIX4X4(x_ang, y_ang, z_ang, &mrot);
    
    // rotate the local coords of single polygon in renderlist
    Transform_OBJECT4DV1(&obj, &mrot, TRANSFORM_LOCAL_ONLY,1);
    
    1. 位移:在load模型的时候就记录局部坐标系原点相对世界坐标系的坐标world_pos。非矩阵变换。变换后存放在vlist_trans信息中。
    Load_OBJECT4DV1_PLG(&obj, "cube2.plg",&vscale, &vpos, &vrot);
    // set position of object 设置物体位置
    obj->world_pos.x = pos->x;
    obj->world_pos.y = pos->y;
    obj->world_pos.z = pos->z;
    obj->world_pos.w = pos->w;
    
    // perform local/model to world transform
    Model_To_World_OBJECT4DV1(&obj);
    VECTOR4D_Add(&obj->vlist_local[vertex], &obj->world_pos, &obj->vlist_trans[vertex]);
    
    2. 我使用缩放矩阵、旋转矩阵、位移矩阵的版本:(注意顺序:POS *SRT)
    static MATRIX4X4 mRot; // general rotation matrix
    static MATRIX4X4 mScale;
    static MATRIX4X4 mTrans;
    static MATRIX4X4 mTemp;
    static MATRIX4X4 mSRT;
    // generate rotation matrix around y axis
    Build_XYZ_Rotation_MATRIX4X4(x_ang, y_ang--, z_ang, &mRot);
    Build_Model_To_World_MATRIX4X4(&vpos, &mTrans);
    Mat_Init_4X4(&mScale, vscale.x, 0, 0, 0,
        0, vscale.y, 0, 0,
        0, 0, vscale.z, 0,
        0, 0, 0, 1);
    Mat_Mul_4X4(&mScale,&mRot, &mTemp);
    Mat_Mul_4X4(&mTemp, &mTrans, &mSRT);
    Transform_OBJECT4DV1(&obj, &mSRT, TRANSFORM_LOCAL_TO_TRANS, 1);
    

    三、View变换

     // generate camera matrix
    Build_CAM4DV1_Matrix_Euler(&cam, CAM_ROT_SEQ_ZYX);
    Mat_Mul_4X4(&mt_inv, &mrot, &cam->mcam);
    

    视图变换只包含位移矩阵和旋转矩阵,没有缩放矩阵。

    World_To_Camera_OBJECT4DV1(&obj, &cam);

    四、Projection变换 + 投影到Screen

    1. 示例版本demoII7_3.cpp的实现方式:
    // apply camera to perspective transformation
    Camera_To_Perspective_OBJECT4DV1(&obj, &cam);
    
    // apply screen transform
    Perspective_To_Screen_OBJECT4DV1(&obj, &cam);
    

    这两次变换都不是基于矩阵变换。


    Projection变换.png
    投影到Screen.png
    2. 我的矩阵版本:结合view矩阵、Projection矩阵、Screen矩阵。
    MATRIX4X4 mVP; // view矩阵 * Projection矩阵 * Screen矩阵
    MATRIX4X4 mPer;
    MATRIX4X4 mScr;
    MATRIX4X4 mPerScr;// Projection矩阵 * Screen矩阵
    Build_Camera_To_Perspective_MATRIX4X4(&cam, &mPer);
    Build_Perspective_To_Screen_MATRIX4X4(&cam, &mScr);
    Mat_Mul_4X4(&mPer, &mScr, &mPerScr);
    Mat_Mul_4X4(&(&cam)->mcam, &mPerScr, &mVP);
    Transform_OBJECT4DV1(&obj, &mVP, TRANSFORM_TRANS_ONLY, 1);
    Convert_From_Homogeneous4D_OBJECT4DV1(&obj);
    

    五、渲染线框

    1. Draw_OBJECT4DV1_Wire16(&obj, back_buffer, back_lpitch);// render the object
    2. Draw_Clip_Line16
    3. Draw_Line16
    4. UCHAR *back_buffer = NULL; // secondary back buffer

    六、背面剔除 与 物体剔除

    1. 背面剔除:在世界空间下处理
    // remove backfaces
    Remove_Backfaces_OBJECT4DV1(&obj, &cam);
    
    1. 计算三角形法线(通过叉乘):u = p0->p1, v=p0->p2, 法线n=uxv

       // we need to compute the normal of this polygon face, and recall
       // that the vertices are in cw order, u = p0->p1, v=p0->p2, n=uxv
       VECTOR4D u, v, n;
      
       // build u, v
       VECTOR4D_Build(&obj->vlist_trans[vindex_0], &obj->vlist_trans[vindex_1], &u);
       VECTOR4D_Build(&obj->vlist_trans[vindex_0], &obj->vlist_trans[vindex_2], &v);
      
       // compute cross product
       VECTOR4D_Cross(&u, &v, &n);
      
    2. 计算三角形法线和相机视线dot 点积

       // now create eye vector to viewpoint
       VECTOR4D view;
       VECTOR4D_Build(&obj->vlist_trans[vindex_0], &cam->pos, &view);
      
       // and finally, compute the dot product
       float dp = VECTOR4D_Dot(&n, &view);
      
    3. 点积<=0,表示>=90度,不可见,设置为隐藏

      • a·b>0 方向基本相同,夹角在0°到90°之间
      • a·b=0 正交,相互垂直
      • a·b<0 方向基本相反,夹角在90°到180°之间
        // if the sign is > 0 then visible, 0 = scathing, < 0 invisible
        if (dp <= 0.0)
            SET_BIT(curr_poly->state, POLY4DV1_STATE_BACKFACE);
    
    2. 物体剔除:

    使用物体的中心和最大半径来创建包围球,测试是否在左、右、上、下、远、近六个裁切面内,即是否在视椎体内。


    将物体从3D渲染管线中剔除.png

    示例是在世界空间下检测;也可以在相机空间下处理。
    因为都要进行M、V变换,只是前者用临时变量存储变换结果。

    物体剔除.png
    1. 将球心变换为相机坐标 transform the center of the object's bounding sphere into camera space
    POINT4D sphere_pos; // hold result of transforming center of bounding sphere
    
    // transform point
    Mat_Mul_VECTOR4D_4X4(&obj->world_pos, &cam->mcam, &sphere_pos);
    
    1. 远、近裁切面检测 cull only based on z clipping planes

       if (((sphere_pos.z - obj->max_radius) > cam->far_clip_z) ||
           ((sphere_pos.z + obj->max_radius) < cam->near_clip_z))
       {
           SET_BIT(obj->state, OBJECT4DV1_STATE_CULLED);
           return(1);
       }
      
    2. 左、右裁切面检测:test the the right and left clipping planes against the leftmost and rightmost points of the bounding sphere

       float z_test = (0.5)*cam->viewplane_width*sphere_pos.z / cam->view_dist;
      
       if (((sphere_pos.x - obj->max_radius) > z_test) || // right side
           ((sphere_pos.x + obj->max_radius) < -z_test))  // left side, note sign change
       {
           SET_BIT(obj->state, OBJECT4DV1_STATE_CULLED);
           return(1);
       }
      
    3. 上、下裁切面检测:test the the top and bottom clipping planes against the bottommost and topmost points of the bounding sphere

        float z_test = (0.5)*cam->viewplane_height*sphere_pos.z / cam->view_dist;
    
        if (((sphere_pos.y - obj->max_radius) > z_test) || // top side
            ((sphere_pos.y + obj->max_radius) < -z_test))  // bottom side, note sign change
        {
            SET_BIT(obj->state, OBJECT4DV1_STATE_CULLED);
            return(1);
        }
    

    相关链接:

    1. 3D游戏编程大师技巧(1) 源码与配置项目 https://www.jianshu.com/p/85d754620d8b

    相关文章

      网友评论

        本文标题:3D游戏编程大师技巧(2) 3D线框引擎

        本文链接:https://www.haomeiwen.com/subject/kwejuqtx.html