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

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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271

推荐阅读更多精彩内容