您的位置:首页 > 其它

2015年1月13日星期二(11-1深度缓存和可见性简介)

2016-01-10 18:51 387 查看
新的一年,开始新的篇章,终于进入第11章了。必须首要搞这个。争取今年搞完,我也相信,会对3D学习不是浪费时间。而是内力提升。
在以前,用画家算法进行多边形排序,即根据渲染列表中的每个多边形的平均、最小或最大值进行排序,然后,再从后到前的顺序绘制多边形。
大多数情况下,是正确的。但是,在多边形相互贯通、包含大小各异的几何体时,画家算法可能失效,
引入Z缓存(即像素级画家算法):将对像素进行排序,
算法如下:
For(渲染列表中的每个多边形) begin
1,对其进行光栅化,生成xi,yi,zi,
For(每组xi,yi,zi)
2,if( zi < zbuffer[xi,yi], then
Zbuffer[xi,yi]= zi,然后将像素显示到屏幕上,Plot(xi,yi).
End for

获取多边形的Z值方法:
根据多边形所在的平面;ax+by+cz+d=0.求得z值。
然后可以进行插值运算。

Z缓存的问题:
上述三角形的(x,y,z)是3D空间推导进行,即世界做表空间或相机坐标空间,而Z缓冲是在屏幕空间的投影坐标(x_screen,y_screen),而x_screen = d * x/z; y_screen= d * y /z,可知,Z值不是线性的,而1/z是线性的。
首先定义z缓存的变量和数据结构
//为z缓存数据结构定义的常量
#define
ZBUFFER_ATTR_16BIT 16
#define
ZBUFFER_ATTR_32BIT 32

//Z缓存数据结构
typedef
struct ZBUFFERV1_TYP
{
int
attr; //z缓存的属性,位或位
UCHAR *
zbuffer; //指向Z缓存的指针
int
width; //z缓存的宽度,单位为像素
int
height; //z缓存的高度,单位为像素
int
sizeq; //实际的四元组大小(每个四元组为个字节)
} ZBUFFERV1, *ZBUFFERV1_PTR;


创建z缓存


int DDRAW_LIUSHUIXIAN_TEXTURE::Create_Zbuffer(ZBUFFERV1_PTRzb,
int width,
int height, int
attr)
{
//指针zb是否有效
if(!zb )
return0;

//是否已经给zb指向的ZBUFFERV1分配了内存
if(zb->zbuffer)
free(zb->zbuffer);

//设置字段
zb->width =width;
zb->height =height;
zb->attr =attr;

//z缓存是位的还是位的
if(attr &
ZBUFFER_ATTR_16BIT)
{
//计算四元组数
zb->sizeq =width *
height/ 2;

//分配内存
if((
zb->zbuffer= (
UCHAR * ) malloc(width *
height* sizeof(
SHORT) ) ) )
return1;
else
return0;
}
else
if(attr &
ZBUFFER_ATTR_32BIT)
{
//计算四元组数
zb->sizeq =width *
height;

//分配内存
if((
zb->zbuffer= (
UCHAR * ) malloc(width *
height* sizeof(
INT) ) ) )
return1;
else
return0;
}
else
return0;
}

每次写入一个32位UINT值的方式填充/设置内存
void
ddraw_math::Mem_Set_QUAD(
void* dest,
USHORTdata, int
count )
{
_asm
{
movedi,
dest ;
movecx,
count ;
moveax,
data ;
repstosd ;
}
}
清空/填充z缓存


void
DDRAW_LIUSHUIXIAN_TEXTURE::Clear_Zbufffer(ZBUFFERV1_PTRzb,
UINT data)
{
ddraw_mathmath;
math.Mem_Set_QUAD( (
void* ) zb->zbuffer,data,
zb->sizeq );
}

删除z缓存,

int DDRAW_LIUSHUIXIAN_TEXTURE::Delete_Zbuffer(ZBUFFERV1_PTRzb)
{
if(zb )
{
//释放内存
if(zb->zbuffer)
free(zb->zbuffer);

//清除内存中的数据
memset((
void * ) zb,0,
sizeof( ZBUFFERV1) );
return1;
}
else
return0;
}

首先,更新恒定Shader处理函数

以GouraudShader处理函数,不进行RGB颜色插值,每个像素都使用相同的颜色,并将对某个颜色分量进行插值的代码改为对Z值进行插值的代码。



void
ddraw_math::Draw_Triangle_2DZB_16(
POLYF4DV2_PTR
face, //指向多边形面的指针

UCHAR * _dest_buffer,//指向视频缓存的指针

int mem_pitch,
//每行占用多少字节(,640)

UCHAR * _zbuffer,
//指向z缓存的指针

int zpitch )

中,把U,I坐标改为Z坐标,其余没变。



对于Gouraud shader,只需加入z值的代码即可。


void
ddraw_math::Draw_Gouraud_TriangleZB16(
POLYF4DV2_PTR face,
//指向多边形的指针

UCHAR * _dest_buffer,//指向视频缓存的指针

int mem_pitch,
//每行占据的字节数

UCHAR *
_zbuffer, //指向z缓存的指针

int
zpitch ) //z缓存每行占用的字节数


对于支持固定着色的纹理映射函数。,也是插入Z代码,并Z缓存比较。

void
ddraw_math::Draw_Textured_TriangleZB16(
POLYF4DV2_PTR
face, //指向多边形的指针

UCHAR * _dest_buffer,//指向视频缓存的指针

int mem_pitch,
//每行占据的字节数

UCHAR * _zbuffer,
//指向z缓存的指针

int zpitch )
//z缓存每行占用的字节数

对于支持恒定着色的纹理映射函数,更新Z缓冲将通过Z缓冲的绘制

void
ddraw_math::Draw_Textured_TriangleFSZB16(
POLYF4DV2_PTR
face, //指向多边形的指针

UCHAR * _dest_buffer,//指向视频缓存的指针

int mem_pitch,
//每行占据的字节数

UCHAR * _zbuffer,
//指向z缓存的指针

int zpitch)
//z缓存每行占用的字节数
对于接下来进行更新渲染列表函数,进行绘制的各个路径。

void
DDRAW_LIUSHUIXIAN_TEXTURE:: Draw_RENDERLIST4DV2_SolidZB16(ddraw_math
math2,RENDERLIST4DV2_PTR
rend_list,UCHAR *
video_buffer,int
lpitch,

UCHAR *
zbuffer,

int
zpitch)
{
POLYF4DV2
face;

for(int
poly =0; poly <
rend_list->num_polys;
poly++)
{
//获得当前多边形
POLYF4DV2_PTR
curr_poly =
rend_list->poly_ptrs[poly];


//当且仅当多边形没有被剔除或者裁剪掉,同时处于活动状态且可见时,才对其进行变换)
if(!(
curr_poly->state&
POLY4DV2_STATE_ACTIVE ) ||
( curr_poly->state&
POLY4DV2_STATE_CLIPPED ) ||
( curr_poly->state&
POLY4DV2_STATE_BACKFACE ) )
continue;
//进入下一个多边形

//先测试纹理,
if(rend_list->poly_ptrs[poly]->attr&
POLY4DV2_ATTR_SHADE_MODE_TEXTURE )
{
face.tvlist[0].x =(
int ) rend_list->poly_ptrs[poly]->tvlist[0].x;
face.tvlist[0].y = (
int) rend_list->poly_ptrs[poly]->tvlist[0].y;
face.tvlist[0].z = (
int) rend_list->poly_ptrs[poly]->tvlist[0].z;
face.tvlist[0].u0 = (
int) rend_list->poly_ptrs[poly]->tvlist[0].u0;
face.tvlist[0].v0 = (
int) rend_list->poly_ptrs[poly]->tvlist[0].v0;

face.tvlist[1].x = (
int) rend_list->poly_ptrs[poly]->tvlist[1].x;
face.tvlist[1].y = (
int) rend_list->poly_ptrs[poly]->tvlist[1].y;
face.tvlist[1].z =(
int ) rend_list->poly_ptrs[poly]->tvlist[1].z;
face.tvlist[1].u0 = (
int) rend_list->poly_ptrs[poly]->tvlist[1].u0;
face.tvlist[1].v0 = (
int) rend_list->poly_ptrs[poly]->tvlist[1].v0;

face.tvlist[2].x = (
int) rend_list->poly_ptrs[poly]->tvlist[2].x;
face.tvlist[2].y = (
int) rend_list->poly_ptrs[poly]->tvlist[2].y;
face.tvlist[2].z = (
int) rend_list->poly_ptrs[poly]->tvlist[2].z;
face.tvlist[2].u0 = (
int) rend_list->poly_ptrs[poly]->tvlist[2].u0;
face.tvlist[2].v0 = (
int) rend_list->poly_ptrs[poly]->tvlist[2].v0;

face.texture =rend_list->poly_ptrs[poly]->texture;

if(rend_list->poly_ptrs[poly]->attr&
POLY4DV2_ATTR_SHADE_MODE_CONSTANT )
{
math2.Draw_Textured_TriangleZB16(&
face, video_buffer,lpitch,
zbuffer,zpitch );
}
else
{
face.lit_color[0] =
rend_list->poly_ptrs[poly]->lit_color[0];

math2.Draw_Textured_TriangleFSZB16(&
face, video_buffer,lpitch,
zbuffer,zpitch );
}

}
else
if((
rend_list->poly_ptrs[poly]->attr&
POLY4DV2_ATTR_SHADE_MODE_FLAT )
|| ( rend_list->poly_ptrs[poly]->attr&
POLY4DV2_ATTR_SHADE_MODE_CONSTANT ) )
{
//使用固定着色
face.lit_color[0] =
rend_list->poly_ptrs[poly]->lit_color[0];

//设置顶点坐标
face.tvlist[0].x =(
float ) rend_list->poly_ptrs[poly]->tvlist[0].x;
face.tvlist[0].y =(
float ) rend_list->poly_ptrs[poly]->tvlist[0].y;
face.tvlist[0].z =(
float ) rend_list->poly_ptrs[poly]->tvlist[0].z;

face.tvlist[1].x =(
float ) rend_list->poly_ptrs[poly]->tvlist[1].x;
face.tvlist[1].y =(
float ) rend_list->poly_ptrs[poly]->tvlist[1].y;
face.tvlist[1].z =(
float ) rend_list->poly_ptrs[poly]->tvlist[1].z;

face.tvlist[2].x =(
float ) rend_list->poly_ptrs[poly]->tvlist[2].x;
face.tvlist[2].y =(
float ) rend_list->poly_ptrs[poly]->tvlist[2].y;
face.tvlist[2].z =(
float ) rend_list->poly_ptrs[poly]->tvlist[2].z;

math2.Draw_Triangle_2DZB_16(&
face, video_buffer,lpitch,
zbuffer,zpitch );



}
else
if(
rend_list->poly_ptrs[poly]->attr &
POLY4DV2_ATTR_SHADE_MODE_GOURAUD)
{
face.tvlist[0].x =(
float ) rend_list->poly_ptrs[poly]->tvlist[0].x;
face.tvlist[0].y =(
float ) rend_list->poly_ptrs[poly]->tvlist[0].y;
face.tvlist[0].z =(
float ) rend_list->poly_ptrs[poly]->tvlist[0].z;
face.lit_color[0] =
rend_list->poly_ptrs[poly]->lit_color[0];

face.tvlist[1].x =(
float ) rend_list->poly_ptrs[poly]->tvlist[1].x;
face.tvlist[1].y =(
float ) rend_list->poly_ptrs[poly]->tvlist[1].y;
face.tvlist[1].z =(
float ) rend_list->poly_ptrs[poly]->tvlist[1].z;
face.lit_color[1] =
rend_list->poly_ptrs[poly]->lit_color[1];

face.tvlist[2].x =(
float ) rend_list->poly_ptrs[poly]->tvlist[2].x;
face.tvlist[2].y =(
float ) rend_list->poly_ptrs[poly]->tvlist[2].y;
face.tvlist[2].z =(
float ) rend_list->poly_ptrs[poly]->tvlist[2].z;
face.lit_color[2] =
rend_list->poly_ptrs[poly]->lit_color[2];

math2.Draw_Gouraud_TriangleZB16(&
face, video_buffer,lpitch,
zbuffer,zpitch );
}




}


}

优化部分基本过时,主要看算法,看看例子如何运行。

首先定义了个庞大的物体,(因为要Z缓冲排序,所以物体较多。)
#define
CAM_DECEL 0.25
#define
MAX_SPEED 20
#define
NUM_OBJECTS 5
#define
NUM_SCENE_OBJECTS 500
#define
UNIVERSE_RADIUS 2000

摄像机改变位置
POINT4D
cam_pos ={0,0,0,1};
物体名和当前物体序号
char *
object_filenames[NUM_OBJECTS] ={
"cube_flat_01.cob", "cube_gouraud_01.cob",
"cube_flat_textured_01.cob","sphere02.cob","sphere03.cob",};

int
curr_object =2;
每个物体的位置
POINT4D
scene_objects[NUM_SCENE_OBJECTS];
摄像机速度和Z缓冲设置。

float
cam_speed =0;
ZBUFFERV1
zbuffer;

在Game_Init()中,
设置摄像机参数

liushuixian.Init_CAM4DV1( *
math,&cam,
// the camera object
CAM_MODEL_EULER,// the euler model
&cam_pos,
// initial camera position
&cam_dir,
// initial camera angles
&cam_target,
// no target
15.0, // near and farclipping planes
12000.0,
120.0, // field ofview in degrees
SCREEN_WIDTH,
// size of finalscreen viewport

SCREEN_HEIGHT);
加载物体
for(int
index_obj= 0; index_obj <
NUM_OBJECTS;index_obj++ )
{

liushuixian_texture.Load_OBJECT4DV2_COB( *
math,matiGroup, &obj_array[index_obj],
object_filenames[index_obj],
&vscale, &vpos, &vrot,
VERTEX_FLAGS_SWAP_YZ|
VERTEX_FLAGS_TRANSFORM_LOCAL);

}

设置当前物体


curr_object = 2;
obj_work = &
obj_array[curr_object];

加载场景

liushuixian_texture.Load_OBJECT4DV2_COB( *
math,matiGroup, &obj_scene,"cube_gouraud_01.cob",

&vscale, &vpos, &vrot,
VERTEX_FLAGS_SWAP_YZ|
VERTEX_FLAGS_TRANSFORM_LOCAL);


看了一下DEMO的例子效果,感觉不错,所以把4个灯光全加上
white.rgba =
_RGBA32BIT(255,255,255,0);
gray.rgba =
_RGBA32BIT(100,100,100,0);
black.rgba =
_RGBA32BIT(0,0,0,0);
red.rgba =
_RGBA32BIT(255,0,0,0);
green.rgba =
_RGBA32BIT(0,255,0,0);
blue.rgba =
_RGBA32BIT(0,0,255,0) ;

light.Init_Light_LIGHTV2( *
math,lightGroup,AMBIENT_LIGHT_INDEX,LIGHTV2_STATE_ON,
LIGHTV2_ATTR_AMBIENT,gray,
black,black,
NULL,NULL, 0, 0, 0, 0, 0, 0 );

VECTOR4D
dlight_dir = { -1, 0, -1, 1 };
light.Init_Light_LIGHTV2( *
math,lightGroup,INFINITE_LIGHT_INDEX,LIGHTV2_STATE_ON,
LIGHTV2_ATTR_INFINITE,black,
gray,black,
NULL,& dlight_dir, 0, 0, 0, 0, 0, 0 );

VECTOR4D
plight_pos = { 0, 200, 0, 1 };
light.Init_Light_LIGHTV2( *
math,lightGroup,POINT_LIGHT_INDEX,LIGHTV2_STATE_ON,
LIGHTV2_ATTR_POINT,black,
green,black, &
plight_pos,NULL, 0, 0.002, 0, 0, 0, 1 );

VECTOR4D
slight2_dir = { -1, 0, -1, 1 };
VECTOR4D
slight2_pos = { 0, 1000, 0, 1 };
light.Init_Light_LIGHTV2( *
math,lightGroup,SPOT_LIGHT2_INDEX,LIGHTV2_STATE_ON,
LIGHTV2_ATTR_SPOTLIGHT2,black,
red,black, &
slight2_pos,& slight2_dir, 0, 0.001, 0, 0, 0, 1 );


设置Z缓冲

liushuixian_texture.Create_Zbuffer(&
zbuffer, SCREEN_WIDTH,SCREEN_HEIGHT,
ZBUFFER_ATTR_32BIT);

在每帧中,设定灯光旋转
staticfloat
plight_ang= 0, slight_ang = 0;

物体旋转
staticfloat
x_ang= 0, y_ang = 0,
z_ang= 0;
Z缓冲模式
static int
z_buffer_mode =1;
忘了背面消除,这里补上。

int DDRAW_LIUSHUIXIAN_TEXTURE::Cull_OBJECT4DV2(
OBJECT4DV2_PTRobj,
CAM4DV1_PTRcam,
int cull_flags,ddraw_mathmath )
{
//将物体包围球球心变换为相机坐标
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[obj->curr_frame]) >
cam->far_clip_z)||
((sphere_pos.z +
obj->max_radius[obj->curr_frame])<
cam->near_clip_z))
{
SET_BIT(obj->state,
OBJECT4DV2_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
floatz_test = (0.5)*cam->viewplane_width*sphere_pos.z/cam->view_dist;

if( ((sphere_pos.x-obj->max_radius[obj->curr_frame])>
z_test) || // right side
((sphere_pos.x+obj->max_radius[obj->curr_frame])<
-z_test) ) // left side, notesign change
{
SET_BIT(obj->state,
OBJECT4DV2_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
floatz_test = (0.5)*cam->viewplane_height*sphere_pos.z/cam->view_dist;

if( ((sphere_pos.y-obj->max_radius[obj->curr_frame])>
z_test) || // right side
((sphere_pos.y+obj->max_radius[obj->curr_frame])<
-z_test) ) // left side, notesign change
{
SET_BIT(obj->state,
OBJECT4DV2_STATE_CULLED);
return(1);
} //end if
} //end if

return( 0 );
}

根据Z缓冲,按照Z的大小不同图示如下:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: