3d渲染
3D渲染流程
总览图
空间
概念
空间实质上是指描述物体之间信息的坐标系,某些信息只在特定的坐标系中才有意义。而同时,空间也被分为不同的种类,同一个物体在不同的空间中会存在不同的表示。
种类
本地空间
世界上任何物体都有自己的本地坐标,本地坐标所描述的空间谓之本地空间,本地空间在简化模型处理工作上相当有用,在本地空间中创建模型比在世界空间中创建模型要简单,不需要考虑在世界坐标系中相对于其他物体的位置、大小和方向关系。
世界空间
在完成各种模型的创建后,需要把这些模型组合到一起,形成一个场景。在这个场景里使用一个统一的坐标系来定位物体的位置,而这个坐标系所决定的空间称为世界空间。
视图空间
Direct3D允许在场景中设置一个虚拟的摄像机,决定看到的场景,由虚拟摄像机观察到的空间称为视图空间
视口
视口即是屏幕上的一个矩形区域。在游戏中,视口可以时整个屏幕,也可以是屏幕的一部分或者处于串口模式下的窗口的客户区。通常,视口坐标都是以像素为单位取值的。
图形渲染管线
图形渲染管线即渲染三维对象的流水线,如同现实生活中摄像的过程,将现实世界的物体通过摄像机的拍摄显示在二维的相片上,所不同的是在Direct3D中把相片换成了屏幕。
图形渲染管线图
Direct3D中渲染三维对象的过程可分为两个阶段。
1)坐标变换和光照(Transforming and Lighting 简称T&L)阶段。
2)光栅化处理阶段。
T&L阶段
每个对象的顶点被从一个抽象的、浮点坐标空间转换到基于像素的屏幕空间,并根据场景中光源和物体表面的材质对物体顶点应用不同类型的光照效果。这个阶段在Direct3D中通常称为顶点变换流水线。
世界变换(本地空间->世界空间)
将三维对象从本地空间变换到世界空间,旨在把独立的物体放在一个统一的坐标系内,组合成一个完整的场景,在世界变换中其主要完成的是对模型变换的操作,涵盖平移、旋转、缩放。
顶点坐标变换都是通过变换矩阵实现的。
Direct3D的设置世界变换的代码:
//g_pDevice为有效的Direct3D设备指针,matWorld代表一个变换矩阵
g_pDevice->SetTransform(D3DTS_WORLD, &matWorld);
在D3D中,描述空间三维变换的变换矩阵是4×4的形式的,因此当xyz描述的是一个点坐标的时候齐次坐标w的值为1,描述的是方向时为0。
平移矩阵
平移矩阵的通式可以表示为:
伸缩矩阵
伸缩矩阵的通式可以表示为:
旋转矩阵
点围绕X轴旋转矩阵的通式可以表示为:
点围绕y轴旋转矩阵的通式可以表示为:
点围绕Z轴旋转矩阵的通式可以表示为:
变换矩阵生成函数
所谓由于物体的各种变换而需乘以的矩阵称为变换矩阵(Transform Matrix)缩写为TM;D3D中,通常不会单独对一个矩阵对象的属性进行相应要求的设置,而是通过函数来生成对应的变换矩阵。
平移
// 建立一个矩阵,平移(x, y, z)
D3DXMATRIX* WINAPI D3DXMatrixTranslation(D3DXMATRIX *pOut, FLOAT x, FLOAT y, FLOAT z);
示例:
//1.平移
D3DXMATRIX matTrans;//平移矩阵
D3DXMatrixIdentity(&matTrans);//单位化矩阵
/*matTrans._41 = -2.0f;
matTrans._42 = 1.0f;
matTrans._43 = 10.0f;*/
D3DXMatrixTranslation(&matTrans, 1.0f, 1.0f, 0.0f);
伸缩
// 构建一个按(sx, sy, sz)缩放的矩阵
D3DXMATRIX* WINAPI D3DXMatrixScaling(D3DXMATRIX *pOut, FLOAT sx, FLOAT sy, FLOAT sz);//pout为转换矩阵地址 xyz为坐标值
示例:
//3.缩放
D3DXMATRIX matscale;//缩放矩阵
D3DXMatrixIdentity(&matScale);//单位化矩阵
/*matScale._11 = 1.1f;
matScale._22 = -2.0f;
matScale._33 = 2.0f;*/
D3DXMatrixScaling(&matScale, 2.0f, 2.0f, 2.0f);
旋转
//angle为旋转角度
// 建立一个绕X轴旋转的矩阵
D3DXMATRIX* WINAPI D3DXMatrixRotationX(D3DXMATRIX *pOut, FLOAT Angle);
// 建立一个绕Y轴旋转的矩阵
D3DXMATRIX* WINAPI D3DXMatrixRotationY(D3DXMATRIX *pOut, FLOAT Angle);
// 建立一个绕Z轴旋转的矩阵
D3DXMATRIX* WINAPI D3DXMatrixRotationZ(D3DXMATRIX *pOut, FLOAT Angle);
// 建立一个绕任意轴旋转的矩阵
D3DXMATRIX* WINAPI D3DXMatrixRotationAxis(D3DXMATRIX *pOut, const D3DXvector3 *pV, FLOAT Angle);
// 用四元数构建一个矩阵
D3DXMATRIX* WINAPI D3DXMatrixRotationQuaternion(D3DXMATRIX *pOut, CONST D3DXQUATERNION *pQ);
// 用指定的绕Y轴角度,绕X轴角度和Z轴角度构建一个旋转矩阵
D3DXMATRIX* WINAPI D3DXMatrixRotationYawPitchRoll(D3DXMATRIX *pOut, FLOAT Yaw, FLOAT Pitch, FLOAT Roll);
示例:
//2.旋转
D3DXMATRIX matRotX;//旋转矩阵
D3DXMatrixIdentity(&matRotX);//单位化矩阵
static float angle = 0.0f;
angle += 0.1f;
/*matRotX._22 = cos(angle);
matRotX._33 = cos(angle);
matRotX._23 = sin(angle);
matRotX._32 = -sin(angle);*/
D3DXMatrixRotationX(&matRotX, angle += 0.1f);
//D3DXMatrixRotationY(&matRotY, angle += 0.1f);
//D3DXMatrixRotationZ(&matRotZ, angle += 0.1f);
视图变换
将摄像机平移到世界空间的坐标原点(即变化坐标系,以摄像机点作为世界坐标的原点)并把它的方向旋转至朝向z轴的正方向,此时,世界空间中所有的物体都将随着摄像机的变换做相同的变换,这个变换就叫做视图变换。
Direct3D的设置视图变换的代码:
//g_pDevice为有效的Direct3D设备指针,matWorld代表一个视图变换矩阵
g_pDevice->SetTransform(D3DTS_VIEW, &matView);
视图矩阵生成函数:
// 建立一个视图矩阵(左手系的)
D3DXMATRIX* WINAPI D3DXMatrixlookatLH(D3DXMATRIX *pOut, // 输出用于视图变换的矩阵
CONST D3DXVECTOR3 *pEye, // 摄像机的位置
CONST D3DXVECTOR3 *pAt,// 摄像机朝向的位置
CONST D3DXVECTOR3 *pUp);// 摄像机的正方向
示例:
D3DXMATRIX matView;//视图矩阵
D3DXMatrixIdentity(&matView);//单位化矩阵
D3DXVECTOR3 eye(0.0f, 0.0f, -10.0f);//眼睛位置
D3DXVECTOR3 lookAt(0.0f, 0.0f, 0.0f);//眼睛看向的位置
D3DXVECTOR3 up(0.0f, 1.0f, 0.0f);//头顶方向
D3DXMatrixLookAtLH(&matView, &eye, &lookAt, &up);//构造视图矩阵
g_pd3dDevice->SetTransform(D3DTS_VIEW, &matView);
背面拣选
3D世界中每个多边形的面有两个表面,一个标为正面,一个为背面。通常,背面总是不可见的。在Direct3D中使用背面拣选,只渲染物体的正面,而不渲染背面,提高渲染的效率。
在使用背面拣选时,需要事先指定多边形的正面和反面。Direct3D中默认顶点以顺时针方向(在观察坐标系中)形成的三角形为正面,以逆时针方向形成的三角形为背面。
设置多边形的正面和背面的函数为SetRenderState:
//g_pDevice为有效的Direct3D设备指针
g_pDevice->SetRenderState(D3DRS_CULLMODE, value);
参数value的取值:
取值 |
说明 |
D3DCULL_NONE |
不进行背面拣选,两面都渲染 |
D3DCULL_CW |
设置顺时针排列的多边形为背面 |
D3DCULL_CCW |
设置逆时针排列的多边形为背面,系统默认值 |
光照
光源在世界空间中定义的,但是经过视图空间变换后,光源就变换到视图空间中。
在视图空间中,光源可以用来照亮场景中的物体,这样场景看起来就更加真实。
裁剪
只有当几何图形处于可视的范围之内才需要被渲染。需要拣选那些超出可视范围的多边形,这个过程被称为裁剪。
把可视范围作为一个虚拟的几何物体,称为可视体。裁剪过程中可能出现3种情况:
1)图形完全在可视体内部,保持不变,被送入下一个阶段。
2)图形完全在可视体外部,被裁剪掉,不再进入下一个阶段。
3)图形部分在可视体内部,将物体分为两个部分,可视体内部的被保留,可视体外部的被裁剪。
投影变换
把摄像机观察到的三维景像显示在二维的平面上,这种三维到二维的变换就是投影变换。在Direct3D中,主要的投影变换有两种类型:正交平行投影和透视投影。
1)正交平行投影:
物体靠近或远离摄像机时,屏幕上物体显示的大小并不发生变化;
2)透视投影:
如果物体朝远离摄像机的方向移动,屏幕上显示的物体就越来越小,当物体靠近摄像机时,它会变的越来越大。
正交平行投影
在Direct3D中,正交投影的观察范围(可视体)是一个矩形的正交六面体,只有在六面体范围内的物体才会被显示出来。
Direct3D的设置视图变换的代码:
g_pDevice->SetTransform(D3DTS_PROJECTION, &matProj);//matProj是一个可视体
可视体的生成函数:
D3DXMATRIX *D3DXMatrixOrthoLH(
D3DXMATRIX *pOut, //输出用于正交投影的变换矩阵
FLOAT w, //可视体宽度
FLOAT h, //可视体高度
FLOAT zn, //可视体离摄像机的最近距离
FLOAT zf //可视体离摄像机的最远距离
);
透视投影
在Direct3D中,透视投影的观察范围(可视体)是一个平截台体。
观察平截面的概念是,锥体的尖头位于虚拟摄像机的位置,而摄像机朝向该锥体的底部,将锥体的4个侧面向屏幕的四边投影,并切除远近裁剪平面位置锥体的前后部分。得到的观察平截面代表投影空间在渲染场景中的可见部分。
Direct3D的设置透视投影的代码:
g_pDevice->SetTransform(D3DTS_PROJECTION, &matProj);//可视体的矩阵
可视体的生成函数:
//Aspect参数表示平截台体的横纵比。如果不等于屏幕的横纵比,物体将会出现变形
D3DXMATRIX *D3DXMatrixPerspectiveFovLH(
D3DXMATRIX *pOut, //输出用于透视投影的变换矩阵
FLOAT fovY, //摄像机镜头的夹角(在y轴上的成像角度)
FLOAT Aspect, //平截台体的横纵比
FLOAT zn, //近平截面的距离
FLOAT zf //远平截面的距离
);
视口变换
坐标变换的最后一步是通过定义屏幕显示区域的实际宽、高等参数,将顶点从投影坐标变换为最终显示的以像素为单位的屏幕坐标。
使用IDirect3DDevice9::SetViewport()函数为设置Direct3D视口的函数:
HRESULT SetViewport(CONST D3DVIEWPORT9 *pViewport); //pViewport视口
视口信息:
typedef struct _D3DVIEWPORT9 {
Dword X; //视口区域的左上角x坐标
dword Y; //视口区域的左上角y坐标
DWORD Width; //视口区域的宽度
DWORD Height; //视口区域的高度
float MinZ; //视口内景物的最小深度值,0-1.0之间,通常为0
float MaxZ; //视口内景物的最大深度值,0-1.0之间,通常为1
} D3DVIEWPORT9;
光栅化
经过变换和投影之后的顶点、颜色以及纹理坐标(全部来自几何阶段)将会进入光栅阶段,光栅阶段的主要目的就是给每个像素正确的配色以便正确的渲染整个图像,即把屏幕空间的二维顶点变换为屏幕上的像素。
在光栅化阶段中,对变换后的顶点执行纹理映射、颜色求和、雾计算、裁剪测试、alpha测试、模板测试、深度测试、混合、抖动、逻辑操作多步计算。