2.2.2 三角形单元
for 三角形单元列表:为了构建一个物体,需要创建一个描述形状和轮廓的三角形单元列表。
包含了我们希望绘制的每个独立三角形的数据。
2.1.3 索引
for 因为三角形单元之间会共享许多公共定点,所以需要索引。
for 原理:创建一个顶点列表和索引列表。顶点列表包含了全部独立顶点,索引列表包含了指向顶点
列表的索引。索引规定了构建为了三角形单元,各顶点应该按何种方式来组织。
//顶点列表
Vertex vertexList[4] = {v0, v1,v2, v3};
//索引列表
WORD indexList[6] = {0,1,2
0,2,3};
2.2 虚拟摄像机
for 摄像机:指定了场景对观察者的可见部分,即我们将依据那部分3D场景来创建2D图像。
摄像机有一定的位置和方向,定义了可见的空间体积。
for 视域体:显示屏为矩形。位于视域体之外的物体时不见的。丢弃这类数据的运算过程称为剪裁。
for 投影窗口:是一个2D区域,位于视域体中的3D几何体通过投影映射到该区域。xy坐标min(-1,-1)
max(1,1)。
2.3 绘制流水线
for 绘制流水线定义:建立了3D场景的几何描述,并设置好虚拟摄像机,下面的任务就是在显示器
中建立该场景的2D表示。这一系列运算统称为绘制流水线。
for 坐标变换:有几个阶段的任务是将几何体从一个坐标系变换至另一个坐标系。Direct3D可帮助
我们进行这类运算。
1.我们仅仅需要提供描述坐标变换的变换矩阵。
2.使用IDirect3DDevice9 -> SetTransform方法,有两个参数,一个用来描述
变换类型,一个用来描述变换矩阵。
比如:Device->SetTransform(D3DTS_WORLD,&worldMatrix);
2.3.1 局部坐标系
for 局部坐标系(建模坐标系)定义:定义构成物体的三角形单元列表的坐标系。
for 好处:简化建模过程,无需考虑位置,大小,相对朝向。
2.3.3 观察坐标系
for 取景变换:我们将摄像机变换至世界坐标系的原点,并将其旋转,使摄像机的光轴与世界坐标
系Z轴方向一致。同时,世界空间中的所有几何体都随摄像机一同变换。
for 观察坐标系:我们称变换后的几何体位于观察坐标系。
for 取景变换的矩阵(观察矩阵)由如下D3DX函数计算得到:
D3DXMATRIX *D3DXMatrixLookAtLH(
D3DXMATRIX* pOut,
CONST D3DXVECTOR3* pEye, //摄像机在世界坐标系中的位置。
CONST D3DXVECTOR3* pAt, //指定了世界坐标系中的被观察点。
CONST D3DXVECTOR3* pUp //世界坐标系中表示“向上”方向的向量。
);
for 例子:
//摄像机在(5,3,-10),观察点为世界坐标系的原点
D3DXVECTOR3 position(5.0f, 3.0f, -10.0f);
D3DXVECTOR3 targetPoint(0.0f, 0.0f, 0.0f);
D3DXVECTOR3 worldUp(0.0f, 1.0f, 0.0f);
D3DXMATRIX V;
D3DXMatrixLookAtLH(&V, &positon, &targetPoint, &worldUp);
for 然后在用IDirect3DDevice9::SetTransform,变换类型参数指定为D3DTS_VIEW:
Device->SetTransform(D3DTS_VIEW, &V);
2.3.4 背面消隐
for 每个多边形都有两个侧面,我们将其中一个侧面标记为正面,另一个侧面标记为背面。
背面是不可见的,摄像机总是禁止进入物体内部的实体空间。
for 正面朝向多边形:正面朝向摄像机。
背面朝向多边形:正面偏离摄像机的多边形。
for 背面消隐:正面朝向多边形遮挡了位于其后的背面朝向多边形,所以将其剔除,这称为背面消隐。
for 默认(在观察坐标系中):顶点排序顺序为 顺时针 的三角形单元是正面朝向的。 顶点排序顺序为 逆时针 的三角形单元是背面朝向的。
for 非默认:由于某些原因默认的消隐方式不能满足,我们可以修改绘制状态(D3DRS_CULLMODE)
来达到目的。
Device->SetRenderState(D3DRS_CULLMODE,Value);
for Value可取以下值:
1.D3DCULL_NONE 完全禁用背面消隐。
2.D3DCULL_CW 只对顺时针绕序的三角形单元进行消隐。
3.D3DCULL_CCW 默认值,只对逆时针绕序的三角形单元进行消隐。
2.3.5 光照
for 光源是在世界坐标系中定义的,但必须经过取景变换至观察坐标系方可使用。
2.3.6 剪裁
for 剪裁:将那些位于视域体外的几何体剔除掉。
for 三角形单元与视域体的相对关系:
1.完全在内部:如果三角形单元完全在视域体内,便被保留并转向下一阶段的处理。
2.完全在外部:如果三角形单元完全在视域体外,便被剔除。
3.部分在内(部分在外):三角形单元在内的部分保留,在外的部分剔除。
2.3.7 投影
for 观察坐标系:我的任务是获取3D场景的2D表示!
for 投影:从n维变换为n-1维的过程。
for 投影有多种方式,我们只关注,透视投影。
for 透视投影:会产生"透视缩短"的效果,即近大远小。这类投影使得我们可以用2D图像表示3D
场景。
for 投影变换:我定义了视域体,并负责将视域体中的几何体投影到投影窗口中。
for 得到投影矩阵:用D3DX函数 D3DXMatrixPerspectiveFovLH,功能是根据视域体的描述信息,
创建一个投影矩阵。
for 函数声明:
D3DXMATRIX * D3DXMatrixPerspectiveFovLH(
D3DXMATRIX &pOut,
FLOAT fovY,
//视域体角度,用弧度表示
FLOAT Aspect, //纵横比:屏宽/屏高
FLOAT zn,
//到近平面的距离
FLOAT zf //到远平面的距离
);
for 纵横比:因为从方形的投影窗口到矩形的显示屏的变换会导致拉伸畸变。纵横比是显示屏纵横
两维尺寸的比率,校正了由方形到矩形的映射发生的畸变。
for 应用投影矩阵例子:
//变换类型指定为D3DTS_PROJECTION
//视域角:90度
//近裁剪面到坐标原点的距离:1,远裁剪面到原点的距离:1000
D3DXMATRIX proj;
D3DXMatrixPerspectiveFovLH(
&proj, PI * 0.5f, (float)width/(float)height, 1.0f, 1000.0f);
Device->SetTransform(D3DTS_PROJECTION, &proj);
3.6 例程:三角形,立方体,茶壶,D3DXCreate*
for Setup函数创建顶点缓存和索引缓存,然后对缓存进行锁定,将构成立方体的顶点数据以及构成立方体的
三角形单元的索引数据分别写入顶点缓存和索引缓存。然后将摄像机沿Z轴负方向平移,然后投影变换
然后将填充模式的绘制状态设为线框模式。
4 颜色
4.1 颜色表示 for 颜色:RGB数据用D3DCOLOR保存,实际上与DWORD类型完全相同。共32位。 for 计算:通过D3DCOLOR_ARGB宏帮我们进行计算。 for 调用举例: D3DCOLOR brightRed = D3DCOLOR_ARGB(255,255,0,0);//第四个是Alpha分量 for 也可以用D3DCOLOR_XRGB来代替D3DCOLOR_ARGB。前者不接受Alpha参数。 for 还有一种存储颜色数据的方法:D3DCOLORVALUE。用百分比(0~1的浮点数) for 结构声明: Typedef struct D3DCOLORVALUE{ float r; float g; float b; float a; }D3DCOLORVALUE; for 还可以用D3DXCOLOR代替D3DCOLORVALUE,它还有一些有用的构造函数和重载运算符。二者可互相转换。
4.2 顶点颜色
for 图元的颜色由顶点颜色决定。
for 举例:
struct ColorVertex{
float x, y, z;
D3DCOLOR color;
static const DWORD FVF;
}
const DWORD ColorVertex::FVF = D3DFVF_XYZ | D3DFVF_DIFFUSE;
4.4 着色
for 在光栅化的过程中,需要对多边形进行着色。
for 注意,不能用D3DCOLORVALUE.
for 两种着色方式:平面着色和Gouraud着色。
for 平面着色:颜色由第一个顶点决定,其他会被忽略。
ColorVertex T[3];
T[0] = D3DCOLOR_XRGB(255, 0, 0);
T[1] = D3DCOLOR_XRGB(0,255, 0);
T[2] = D3DCOLOR_XRGB(255, 0, 0);
for Gouraud着色:平滑着色。颜色由顶点线性插值得到。
for 如何设置着色模式:
Device -> SetRenderState(D3DRS_SHADEMODE, D3DSHADE_FLAT);
Device -> SetRenderState(D3DRS_SHADEMODE, D3DSHADE_GOURAUD);
4.4 例程
for d3dUtility.cpp 和 d3dUtility.h都得有
5 光照
5.1 光照的组成
for 环境光:是经过其他表面反射到达物体的光。
for 漫射光:我沿着特定的方向传播。沿所有方向均匀传播。无需考虑观察者的位置。
for 镜面光:我沿着特定的方向传播。我达到一个表面时,将严格地沿着另一个方向反射,从而形成
只能在一定角度范围内才能观察到的高亮度照射。
1. 若想启用:
Device->SetRenderState(D3DRS_SPECULARENABLE, true);
for 每种光都可以用结构D3DCOLORVALUE或者D3DXCOLOR来表示。
for 对光源进行注册:
Device->SetLight(
0,
& light);
//开关控制
Device->LightEnable(
0,
true);
5.5 例程:光照
5.7 小结
for Direct3D支持三种光源模型:方向光,点光源,聚光灯。每种光能产生三种光,环境光,漫射光,镜面光。
6 纹理映射
for 纹理映射技术:我们能将图像数据映射到三角形单元中,这种功能可以显著地增加所绘制场景的细节和真实感。
for 纹理用接口IDirect3DTexture9
6.1 纹理坐标
for 纹理坐标系:水平方向的u轴,垂直方向的v轴。
for 纹理元:用坐标对(u,v)标识的纹理元素。 for v轴的正方向是竖直向下的。
7 融合技术
for 融合技术:我能把当前要进行光栅化的像素的颜色与先前已光栅化并处于同一位置的像素颜色进行合成。
for 即是正在处理的图元颜色值与存储在后台缓存中的像素颜色值进行合成。
7.1 融合方程
for 先绘制那些不需要进行融合的物体。然后要进行融合的物体按照相对于摄像机的深度值进行排序。
for 启动融合运算:
Devicep->SetRenserState(D3DRS_ALPHABLENDENABLE, true);
7.2 融合因子
for 通过设定源融合因子和目标融合因子,我们可创建一系列不同的融合效果。
for 设定因子例子:
Device->SetRenderState(D3DRS_SRCBELND, Source);//源融合因子
Device->SetRenderState(D3DRS_DESTBLEND, Detination);//目标融合因子
for Source和Destionation可取下列融合因子:
见书121.
7.3 透明度
for Alpha分量:我来指定像素的透明度。
for 为了用Alpha分量,必须把融合因子设为默认值(D3DBLEND_SRCALPHA,D3DBLEND_INVERCALPHA).
7.3.1 Alpha通道
for 我们通过从纹理的Alpha通道中获取Alpha信息。Alpha通道是保留了额存储了Alpha分量的纹理元的一
个额外的位集合。
8.2.2 镜面效果实现概述
for 只有当物体位于镜面之前才绘制。但是为了简化,我们随时都进行绘制,但借助模板缓存,我们可以
阻止某些特定区域绘制到后台缓存中。用模板缓存来阻止不在镜面中的绘制。
for 步骤
1.绘制整个场景,不绘制茶壶。
2.将模板缓存清为0.
3.绘制构成镜面的图元。给镜面做标记。模板测试设置为总是成功。若测试通过,模板缓存替换为1。
将模板缓存中与镜面对应区中的像素设为1。其余为0.
4.将茶壶的映像绘制到后台缓存和值为1的模板缓存中。
8.2.3 代码解析
for RenderMirror函数:我首先将构成镜面的图元绘制到模板缓存中,然后仅当茶壶的映像位于镜面中时,
才绘制出来。
8.3.3 阴影矩阵
for 由平行光产生的阴影实质上是物体在平面np + d = 0上沿特定方向上的平行投影。
for 对于点光源,阴影实际上是以光源为视点时,物体在平面np+d=0上的透视投影。
for 将顶点到其投影点s的变换用矩阵来表述。
for 我们可以用同一个矩阵同时表示正交投影和透视投影。
for 用以下函数来创建阴影矩阵:
D3DXMATRIX * D3DXMatrixShadow(
D3DXMATRIX * pOut,
CONST D3DXVECTOR * pLight,
CONST D3DXPLAINE * pPlane
);
8.3.4 使用模板缓存防止二次融合
for 二次融合:有时会有多个像素点重叠在一起,使用透明度后,会有部分偏暗。
for 解决方法:模板缓存。我们将模板测试设置为只接受第一次得到绘制的那些像素。设置标记。
8.3.5 代码解析
for D3DFVF_TEX0表示没有纹理坐标。、
for LPDIRECT3DDEVICE9 长指针 long pointer.....
for memset:作用是在一段内存块中填充某个给定的值,它是对较大的结构体或数组进行清零操作的一种最快方法.
void *memset(void *s, int ch, size_t n);
将s所指向的某一块内存中的前n个 字节的内容全部设置为ch指定的ASCII值, 第一个值为指定的内存地址,块的大小由第三个参数指定,这个函数通常为新申请的内存做初始化工作, 其返回值为指向s的指针。