2014年2月14日星期五(DEMO7-4,3D坦克)

先看区别:

1, 相机可旋转了,上个例子是固定相机

2, 进行的不是背面消除,而是物体剔除

3, 渲染的不是单个物体,而是多个物体的渲染列表。列表中,是单物体多位置

先看物体剔除操作,

物体剔除是的位置在世界坐标系之后,在背面消除之前,即流水线目前是这样子的:

模型坐标->局部坐标到世界坐标转换->世界坐标->物体消除->背面消除->世界坐标到相机坐标转换->相机坐标->投影坐标->屏幕坐标

 

其中物体剔除,可以按照包围球测试,判断是否在视景体内,(当然,包围球测试并非一直有效,或许要用包围盒或者其它几何体)

有三种情况

1, 物体完全在视景体之外

2, 物体完全在视景体之内

3, 物体部分在视景体之内

包围球半径是物体包含顶点离物体中心的最远距离,

为计算方便,包围球在相机坐标中转换,相机设定为90度。球取6个点,分别与正负X,正负Y轴,正负Z轴的平行点,测试目的是为了排除整个球体,

判断点p(X,Y,Z)是否位于视景体外的通用规则为

if( (z>far_z ) || ( z<near_z ) ||   //远近裁剪面

( fabs(x)<z ) ||                       //左右裁剪面

( fabs(y) <z ) )                       //上下裁剪面

{

//物体不在视景体内

}

先定义几个剔除标志,根据各个轴

 

 

//剔除标志

#define CULL_OBJECT_X_PLANE                     0x0001            //根据左右裁剪面进行剔除

#define CULL_OBJECT_Y_PLANE                     0x0002            //根据上下裁剪面进行剔除

#define CULL_OBJECT_Z_PLANE                     0x0004            //根据远近裁剪面进行剔除

#define CULL_OBJECT_XYZ_PLANES              ( CULL_OBJECT_X_PLANE | CULL_OBJECT_Y_PLANE | CULL_OBJECT_Z_PLANE )

 

 

int ddraw_liushuixian::Cull_OBJECT4DV1( OBJECT4DV1_PTR obj, CAM4DV1_PTR cam, int cull_flags,ddraw_math math )

{

//将物体包围球球心变换为相机坐标

POINT4D                            sphere_pos;        //用于存储包围球球心变换后的坐标

//对点进行变换

math.Mat_Mul_VECTOR4D_4X4( & obj->world_pos, & cam->mcam, & sphere_pos );

//根据剔除标记对物体执行剔除操作

if (cull_flags & CULL_OBJECT_Z_PLANE)

{

    // cull only based on z clipping planes

 

    // test far plane

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

    } // end if

 

} // end if

 

if (cull_flags & CULL_OBJECT_X_PLANE)

{

    // cull only based on x clipping planes

    // we could use plane equations, but simple similar triangles

    // is easier since this is really a 2D problem

    // if the view volume is 90 degrees the the problem is trivial

    // buts lets assume its not

 

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

    } // end if

} // end if

 

if (cull_flags & CULL_OBJECT_Z_PLANE)

{

    // cull only based on z clipping planes

 

    // test far plane

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

    } // end if

 

} // end if

 

if (cull_flags & CULL_OBJECT_Y_PLANE)

{

    // 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_height*sphere_pos.z/cam->view_dist;

 

    if ( ((sphere_pos.y-obj->max_radius) > z_test)  || // right side

        ((sphere_pos.y+obj->max_radius) < -z_test) )  // left side, note sign change

    {

        SET_BIT(obj->state, OBJECT4DV1_STATE_CULLED);

        return(1);

    } // end if

} // end if

 

return ( 0 );

}

现在,可以逐步进行,

首先定义了一行物体的个数和物体间距

 

// object defines

#define NUM_OBJECTS     2       // number of objects on a row

#define OBJECT_SPACING  250     // spacing between objects

 

设置摄像机位置

POINT4D  cam_pos = {0,200,0,1};

物体不放缩

VECTOR4D vscale={1.0,1.0,1.0,1},

因为要渲染一群坦克,所以用渲染列表

RENDERLIST4DV1         render_list;

 

在Game_Init()中,初始化发生改变如下:

将远切面改为1000

liushuixian.Init_CAM4DV1( *math, &cam,      // the camera object

                  CAM_MODEL_EULER, // euler camera model

                  &cam_pos,  // initial camera position

                  &cam_dir,  // initial camera angles

                  NULL,      // no initial target

                  50.0,      // near and far clipping planes

                  1000.0,

                  90.0,      // field of view in degrees

                  WINDOW_WIDTH,   // size of final screen viewport

                  WINDOW_HEIGHT);

                 

加载坦克模型

         liushuixian.Load_OBJECT4DV1_PLG( & obj, "tank1.plg", & vscale, & vpos, & vrot );

设定模型

         obj.world_pos.x                                                                      = 0;

         obj.world_pos.y                                                                      = 0;

         obj.world_pos.z                                                              = 400;

 

在Game_Main()中,每帧先重置列表

liushuixian.Reset_RENDERLIST4DV1( &render_list);

重置角度

 

 

         ang_x                                                                     = 0;

         ang_y                                                                     = 1;

         ang_z                                                                     = 0;

 

通过按键控制摄像机朝向

if ( KEYDOWN( VK_DOWN))

         {

                  cam.dir.x                                              += 1;

         }

         else

         if ( KEYDOWN( VK_UP))

         {

                  cam.dir.x                                              -= 1;

         }

 

         if ( KEYDOWN( VK_LEFT))

         {

                  cam.dir.y                                              += 1;

         }

         else

         if ( KEYDOWN( VK_RIGHT))

         {

                  cam.dir.y                                              -= 1;

         }

 

旋转角度矩阵

mrot =  math->Build_XYZ_Rotation_MATRIX4x4(  ang_x, ang_y, ang_z );

上个DEMO,位置是这么设定的

 

         liushuixian.Transform_OBJECT4DV1( &obj, &mrot, TRANSFORM_LOCAL_ONLY, 1, * math );

 

    现在由于是列表,

         for ( int x = - NUM_OBJECTS / 2; x < NUM_OBJECTS / 2; x++ )

         {

                  for ( int z = - NUM_OBJECTS / 2; z < NUM_OBJECTS / 2; z++)

                  {

                          liushuixian.Reset_OBJECT4DV1( & obj );

                          //设置物体位置

                          obj.world_pos.x                                   = x * OBJECT_SPACING + OBJECT_SPACING / 2;

                          obj.world_pos.y                                   = 0;

                          obj.world_pos.z                           = 500 + z * OBJECT_SPACING + OBJECT_SPACING / 2;

//判断是否物体消除,没有的话,则转到世界坐标系,并插入渲染列表

         if ( ! liushuixian.Cull_OBJECT4DV1( & obj, & cam, CULL_OBJECT_XYZ_PLANES))

                          {

                                   liushuixian.Model_To_World_OBJECT4DV1( & obj, * math );

                                   liushuixian.Insert_POLYF4DV1_RENDERLIST4DV1( & render_list, & obj );

                          }

接下来,改动的,还有个消除列表背面,这里没有,要加上,

 

void ddraw_liushuixian::Remove_Backfaces_RENDERLIST4DV1(RENDERLIST4DV1_PTR rend_list, CAM4DV1_PTR cam, ddraw_math math )

{

 

         // process each poly in mesh

         for (int poly=0; poly < rend_list->num_polys; poly++)

         {

                  // acquire polygon

                  POLYF4DV1_PTR curr_poly = rend_list->poly_ptrs[poly];

 

                  // is this polygon valid?

                  // test this polygon if and only if it's not clipped, not culled,

                  // active, and visible and not 2 sided. Note we test for backface in the event that

                  // a previous call might have already determined this, so why work

                  // harder!

                  if ( ( curr_poly == NULL) ||

                          !(curr_poly->state & POLY4DV1_STATE_ACTIVE) ||

                          (curr_poly->state & POLY4DV1_STATE_CLIPPED ) ||

                          (curr_poly->attr  & POLY4DV1_ATTR_2SIDED)    ||

                          (curr_poly->state & POLY4DV1_STATE_BACKFACE) )

                  {

                         

                          continue;

                  }

 

        

 

                  VECTOR4D u, v, n;

 

                  // build u, v

                  math.VECTOR4D_Build(curr_poly->tvlist[0],curr_poly->tvlist[1], &u);

                  math.VECTOR4D_Build(curr_poly->tvlist[1],curr_poly->tvlist[2], &v);

 

                  // compute cross product

                  math.VECTOR4D_CROSS(&u, &v, &n);

 

                  // now create eye vector to viewpoint

                  VECTOR4D view;

                  math.VECTOR4D_Build(&curr_poly->tvlist[0], &cam->pos, &view);

 

                  // and finally, compute the dot product

                  float dp = math.VECTOR4D_DOT(&n, &view);

 

                  // if the sign is > 0 then visible, 0 = scathing, < 0 invisible

                  if (dp <= 0.0 )

                          SET_BIT(curr_poly->state, POLY4DV1_STATE_BACKFACE);

 

        

         } // end for poly

         fclose(fp);

} // end Remove_Backfaces_OBJECT4DV1

 

 

剩下部分都改成列表

        

         liushuixian.Build_CAM4DV1_Matrix_Euler( *math, & cam, CAM_ROT_SEQ_ZYX );

         liushuixian.Remove_Backfaces_RENDERLIST4DV1( & render_list, &cam, * math );

        

         liushuixian.World_To_Camera_RENDERLIST4DV1(  *math, &render_list, & cam );

         liushuixian.Camera_To_Perspective_RENDERLIST4DV1( &render_list, & cam );

         liushuixian.Perspective_To_Screen_RENDERLIST4DV1( &render_list, & cam );

        

         ddraw->DDraw_Lock_Back_Surface();

         liushuixian.Draw_RENDERLIST4DV1_Wire16( *math, & render_list, ddraw->getbackbuffer(), ddraw->getbacklpitch() );

 

 

加上个将物体插入列表中的函数,

 

int ddraw_liushuixian::Insert_OBJECT4DV1_RENDERLIST4DV1( RENDERLIST4DV1_PTR rend_list, OBJECT4DV1_PTR obj, int insert_local /* = 0 */ )

{

         if ( ! ( obj->state & OBJECT4DV1_STATE_ACTIVE ) ||

                  ( obj-> state & OBJECT4DV1_STATE_CULLED ) ||

                  ! ( obj->state & OBJECT4DV1_STATE_VISIBLE))

         {

                  return ( 0 );

         }

         for ( int poly = 0; poly < obj->num_polys; poly++)

         {

                  POLY4DV1_PTR              curr_poly                             = & obj->plist[poly];

                  if ( ! ( curr_poly->state & POLY4DV1_STATE_ACTIVE) ||

                             ( curr_poly->state & POLY4DV1_STATE_CLIPPED) ||

                             ( curr_poly->state & POLY4DV1_STATE_BACKFACE ))

                         

                  {

                          continue;

                  }

                  POINT4D_PTR                          vlist_old                              = curr_poly->vlist;

                  if ( insert_local )

                  {

                          curr_poly->vlist                                             = obj->vlist_local;

 

                  }

                  else

                  {

                          curr_poly->vlist                                             = obj->vlist_trans;

                  }

                  if ( ! Insert_POLYF4DV1_RENDERLIST4DV1( rend_list, curr_poly))

                  {

                          curr_poly->vlist                                             = vlist_old;

                          return ( 0 );

 

                  }

                  curr_poly->vlist                                                     = vlist_old;

                 

         }

         return ( 1 );

 

}

当然,还要加上添加顶点到列表中,

int ddraw_liushuixian::Insert_POLY4DV1_RENDERLIST4DV1(RENDERLIST4DV1_PTR rend_list, POLY4DV1_PTR poly, ddraw_math math)

{

         if( rend_list->num_polys >= RENDERLIST4DV1_MAX_POLYS )

                  return ( 0 );

 

         rend_list->poly_ptrs[rend_list->num_polys]                                   = &rend_list->poly_data[rend_list->num_polys];

        

         rend_list->poly_data[rend_list->num_polys].state                 = poly->state;

         rend_list->poly_data[rend_list->num_polys].attr                   = poly->attr;

         rend_list->poly_data[rend_list->num_polys].color                = poly->color;

 

         math.VECTOR4D_COPY( & rend_list->poly_data[rend_list->num_polys].tvlist[0], & poly->vlist[poly->vert[0]] );

         math.VECTOR4D_COPY( & rend_list->poly_data[rend_list->num_polys].tvlist[1], & poly->vlist[poly->vert[1]] );

         math.VECTOR4D_COPY( & rend_list->poly_data[rend_list->num_polys].tvlist[2], & poly->vlist[poly->vert[2]] );

 

         math.VECTOR4D_COPY( & rend_list->poly_data[rend_list->num_polys].vlist[0], & poly->vlist[poly->vert[0]] );

         math.VECTOR4D_COPY( & rend_list->poly_data[rend_list->num_polys].vlist[1], & poly->vlist[poly->vert[1]] );

         math.VECTOR4D_COPY( & rend_list->poly_data[rend_list->num_polys].vlist[2], & poly->vlist[poly->vert[2]] );

 

         if( rend_list->num_polys == 0 )

         {

                  rend_list->poly_data[0].next                                                  = NULL;

                  rend_list->poly_data[0].prev                                                  = NULL;

         }

         else

         {

                  rend_list->poly_data[rend_list->num_polys].next                 = NULL;

                  rend_list->poly_data[rend_list->num_polys].prev                 = & rend_list->poly_data[rend_list->num_polys-1];

                  rend_list->poly_data[rend_list->num_polys-1].next     = & rend_list->poly_data[rend_list->num_polys];

 

         }

         rend_list->num_polys++;

 

         return ( 1 );

}

 

 

经过一系列调整,OK了

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值