1.3.0 预备
1.HAL,硬件抽象层,由D3DDEVTYPE_HAL指定
2.REF,参考光栅设备
3.COM,组件对象模型,使之向下兼容,视为C++类。
1.3.1 表面
4.IDirect3DSurface9,描述表面。
(1)LockRect:获取指向表面存储区的指针,通过指针对每一个像素进行读写。
(2)UnlockRect:执行完LockRect后,必须调用此来解锁。
(3)GetDesc:填充结构D3DSURFACE_DESC来获取表面的描述信息。
1.3.2 多重采样
5.多重采样:用于平滑块状图像的技术。常用于全屏反走样。
D3DMULTISAMPLE_TYPE:一系列枚举常量值,用于表示对表面进行多重采样的级别。
(1)D3DMULTISAMPLE_NONE:禁止多重采样。
(2)D3DMULTISAMPLE_1_SAMPLE....~_16_~:指定了了从1~16级的多重采样
(3)与特定多重采样类型相关的质量水平,用DWORD描述。
用IDirect3d9::CheckDeviceMultiSampleType来检查设备是否支持。
1.3.3 像素格式
在创建表面或者纹理时,要指定Direct3D资源的像素格式,由D3DFORMAT来定义:
(1)D3DFMT_R8G8B8:每个像素24位,8红8绿8蓝。
(2)D3DFMT_X8R8G8B8:每个像素32位,8未加使用~。
(3)D3DFMT_A8R8G8B8:每个像素32位,8位分给Alpha~。
(4)D3DFMT_A16B16G16R16F:每个像素64位,是一种浮点数类型的像素格式。
(5) D3DFMT_A32B32G32R32F:每个像素128位,浮点。
1.3.4 内存池
Direct3D资源可以放入许多类型的内存池中。类型可有D3DPOOL枚举类型来表示:
(1) D3DPOOL_DEFAULT:默认值,放入最合适的类型的存储区。存储区可以是显存,AGP
存储区,系统存储区。调用函数IDrect3DDevice9::Reset之前,必须对默认内存池中的
资源销毁或释放。然后再资源重新初始化。
(2) D3DPOOL_MANAGED:放入此处的资源将交给Direct3D管理。
(3) D3DPOOL_SYSTEMMEM:指定将资源放入系统存储区中。
(4) D3DPOOL_SCRATCH:指定将资源放入系统存储区中。不受图形设备的制约,设备无法访
问,资源之间可相互复制。
1.3.5 交换链和页面置换
Direct3D为活着一个表面集合。
交换链:两三个表面组成的集合。用接口IDirect3DSwapChain9表示。
用途:和页面置换技术,用于生成更加平滑的动画。
表面1(前台)《-------》表面2(后台)
while(1)
在后台缓存中绘制;
提交后台缓存的内容;
1.3.6 深度缓存
for 定义:只含有特定像素的深度信息,不含图像数据的表面。为每一个像素都保留了一个深度项。
for 用于进行深度测试,判定某一物体的那些像素位于另一个物体之前。z-缓存。
for 深度缓存的格式决定了深度测试的精度。
1 D3DFMT_D32 32位
2 D3DFMT_D23S8 指定24位深度缓存,其中8位保留供模板缓存使用。
3 D3DFMT_D24X8 24位
4 D3DFMT_X4S4 指定24位深度缓存,其中4位保留~。
5 D3DFMT_D16 16位
1.3.7 定点运算
for 顶点是3D几何学中基本元素。有两种不同方式进行顶点运算:
1.软件顶点运算,都可以使用
2.硬件顶点运算,必须图形卡的支持,优先考虑,不占CPU(支持变换和光照)
1.3.8 设备性能 DevCaps(device capabilities)
for Direct3D提供的每一项性能都对应于结构D3DCAP9中的一个数据成员。
for 以某一具体硬件为基础,初始化一个D3DCAP9类型的实例,然后在程序中检查相应的数据成员来
判断设备是否支持某项特性。
1.4 Direct3D的初始化
步骤
1.获取接口IDrect3D的指针。获取系统中物理硬件设备的信息并创建接口IDirectDDevice9,
代表了我们用来显示3D图形的物理硬件设备。
2.检查设备性能(D3DCAPS9),判断主显卡是否支持硬件顶点运算。
3.初始化D3DPRESENT_PARAMETERS结构的一个实例。通过里面的变量来指定即将创建的结构
IDirectDDevice9的特性。
4.利用已初始化的D3DPRESENT_PARAMETER结构创建IDirectDDevice9对象(代表显示3D图形
的物理硬件设备)。
1.4.1 获取接口IDirect3D9的指针
for IDirect3D9 * _d3d9;
_d3d9 = Direct3DCreate9 (D3D_SDK_VERSION);
for 函数Direct3DCreate9参数必须是D3D_SDK_VERSION。
for 用途:
1.设备枚举,获取系统中显卡的各种信息。
2.创建IDirect3DDevice9类型。
1.4.2 校验硬件顶点运算
for 创建一个代表主显卡的IDirect3DDevice9类型对象时,必须指定使用该对象进行顶
点运算的类型(硬软件)。所以要先检查是否支持。
for 要检查,必须根据主显卡的性能参数初始化一个IDirect3DDevice9的对象。
for HRESULT IDirect3D9::GetDeviceCaps(
UINT Adapter,
D3DDEVTYPE DeviceType,
D3DCAPS9 * pCaps
);
1.Adapter 指定物理显卡的序号。
2.DeviceType 指定设备类型(A 硬件设备(D3DDEVTYPE_HAL),
B 软件设备(D3DDEVTYPE_REF)).
3.pCaps 返回已初始化的设备性能结构实例。
for D3DCAPS caps;
d3d9->GetDeviceCaps(
D3DADAPTER_DEFAULT,
deviceType,
&caps);
//能使用硬件顶点运算吗?
int vp = 0;
if( caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT){
//我们能!
vp = D3DCREATE_HARDWARE_VERTEXPROCESSING;
}else{
//我们不能,只能用软件咯.
vp = D3DCREATE_SOFTWARE_VERTEXPROCESSING;
}
1.4.3 填充D3DPRESENT_PARAMETER 结构
for 太长啦看书48页。
typedef struct _D3DPRESENT_PARAMETERS_{
BALH BALH;
}
D3DPRESENT_PARAMETERS d3dpp;
d3dpp.blah = blah;
慢慢填;
1.4.4 创建IDirect3DDevice9接口
for D3DPRESENT_PARAMETERS结构填充后,我们用如下方法创建IDirect3DDevice9:
HRESULT IDirect3D9::CreateDevice(
UINT Adapter,
D3DDEVTYPE DeviceType,
HWND hFocusWindow,
DWORD BehaviorFlags,
D3DPRESENT_PARAMETERS *pPresentationParameters,
IDirect3DDevice9 ** ppReturnedDeviceInterface,
);
1.Adapter 指定物理显卡的序号。
2.DeviceType 指定设备类型(A 硬件设备(D3DDEVTYPE_HAL),
B 软件设备(D3DDEVTYPE_REF))。
3.hFocusWindow 与设备相关的窗口句柄。设备所要进行绘制的目标窗口。与
D3DPRESENT_PARAMETERS结构的数据成员d3dpp.hDeviceWindow 为同一个句柄。
4.BehaviorFlags 该参数可为D3DCREATE_HARDWARE_VERTEXPROCESSING 或者
D3DCREATE_SOFTWARE_VERTEXPROCESSING.
5.pPresentationParameters 初始化了的D3DPRESENT_PARAMETERS类型的实例。
6.ppReturnedDeviceInterface 返回所创建的设备。
for 调用示例
IDirect3DDevice9 * device = 0;
hr = d3d->CreateDevice(
D3DADAPTER_DEFAULT,
D3DDEVTYPE_HAL,
hwnd,
D3DCREATE_HARDWARE_VERTEXPROCESSING,
&d3dpp,
&device);
if(FAILED(hr)){
::MessageBox(0,"CreateDevice()-FAILED",0,0);
return 0;
}
1.5 例程:Direct3D的初始化
1.5.1 d3dUtility.h/cpp
1.InitD3D:初始化程序主窗口,执行1.4节中的初始化过程。得到指向创建好的
IDirect3DDevice9接口的指针。该函数允许我们制定窗口的尺寸以及运行模式(窗口或全屏)。
2.EnterMsgLoop:封装了应用程序的消息循环。接受一个指向显示函数(实现绘制功能)的
函数指针。在空闲处理(idle processing)期间显示场景。
3.WndProc:应用程序主窗口的窗口过程函数(window procedure)。
4.Relefore:方便的释放COM接口并将其设置为NULL。
5.Delete:方便的删除自由堆中的对象,并将对象指针赋为NULL。
1.5.2 例程框架
for 例程框架:组织例程结构的一般形式。
1.bool Setup() 全部设置和初始化都在该函数进行,如分配资源,检查设备性能,设
置应用程序状态。
2.void Cleanup() 释放在Setup中分配的任何资源。
3.bool Display(float timeDelta) 实现全部的绘制代码,相邻帧之间应执行的操作,
timeDelta是相邻帧间的时间差。
1.5.3 例程:D3D初始化
for IDirect3DDevice9::Clear声明如下:
HRESULT IDirect3DDevice9::Clear(
DWORD Count,
const D3DRECT *pRects,
DWORD Flags,
D3DCOLOR Color,
float Z,
DWORD Stencil);
1.Count:pRect数组中矩形的数目。
2.pRects:所要执行清除的表面。我们可清除下列表面中的一个或多个:
D3DCLEAR_TARGET:绘制目标表面,通常指后台缓存。
D3DCLEAR_ZBUFFER:深度缓存。
D3DCLEAR_STENCIL:模板缓存。
3.Color:指定将绘制目标体设置为何种颜色。
4.Z:深度缓存所要设定的值。
5.Stencil:模板缓存所要设定的值。
2 绘制流水线
ps 绘制流水线的功能是:在给定3D场景和指定的虚拟摄像机的几何描述时,创建一副2D图像。
2.1 模型表示
for 场景:物体或模型的几何。
for 三角形网格:任何物体都可以用三角形网格逼近表示,三角形网格是构建物体模型的基本单元。
(同义词,多边形,图元,网格几何元)
for 描述三角形单元:指定3个顶点的位置。
描述物体时,指定构成该物体的三角形单元列表。
2.1.1 定点格式
for 定点:除了包含空间信息外,还有其他附加属性。颜色,法线。
for //位置,颜色属性
struct ColorVertex{
float _x,_y,_z;
DWORD _color;
};
//位置,法线,纹理坐标
strcut NormalTexVertex{
float _x,_y,_z;
float _nx,_ny,_nz;
float _u,_v;
};
for 灵活顶点格式:用它标记的组合来描述顶点的组织结构。如:
//对应于该定点格式的顶点结构,包含了位置属性和漫反射属性。
#define FVF_COLOR (D3DFVF_XYZ | D3DFVF_DIFFUSE)
//对应于该顶点格式的顶点结构,包含了位置属性,法线,纹理坐标。
#define FVF_NORMAL_TEX (D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX1)
for 必须满足的约定:灵活定点格式标记的指定顺序必须与顶点结构中相应类型数据定义的顺序
保持一致。
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.2 世界坐标系
for 每个模型都位于自身的局部坐标系中,所有局部坐标系组织在一起构成世界坐标系。
for 世界变换:1.局部坐标系中的物体通过世界变换运算变换到世界坐标系中。
包括平移,旋转,比例运算。分别用于设定该物体在世界坐标系中的位置,
方向,模型大小。
2. 世界变换用一个矩阵来表示,并由IDirect3DDevice9::SetTransform方法来加以应用。
第一个参数表示变换的类型,如要世界变换,就设置为D3DTS_WORLD.
第二个参数表示所采用的世界变换矩阵。
for 例子:
//生成cube的矩阵,想把立方体的中心位于世界坐标系中的点(-3,2,6)上
D3DXMATRIX cubeWorldMatrix;
D3DXMatrixTranslation(&cubeWorldMatrix,-3.0f, 2.0f, 6.0f);
//生成球体的矩阵,想把球体的中心位于(5,0,-2)上
D3DXMATRIX sphereWorldMatrix;
D3DXMatrixTranslation(&sphereWorldMatrix,5.0f, 0.0f, -2.0f);
//立方体转换!
Device->SetTransform(D3DTS_WORLD, &cubeWorldMatrix);
drawCube();
//球体转换!
Device->SetTransform(D3DTS_WORLD, sphereWorldMatrix);
drawSphere();
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);
2.3.8 视口变换
for 视口变换:我的任务是将顶点坐标从投影窗口转换到屏幕的一个矩形区域中,该矩形区域称为
视口。
for 视口:视口结构用D3DVIEWPORT9来表示:
typedef struct D3DVIEWPORT9{
DWORD X;
DWORD Y;
DWORD Width;
DWORD Height;
DWORD MinZ;
DWORD MaxZ;
}D3DVIEWPORT9;
for 视口例子:
D3DVIEWPORT9 vp = {0, 0, 640, 480, 0, 1};
Device ->SetViewport(& vp);
2.3.9 光栅化
for 光栅化:我的任务是为了绘制每个三角形单元,如何计算构成三角形单元的每个像素的颜色值。
2.4 小结
for 局部坐标系--->世界坐标系---->观察坐标系--->投影窗口--->视口
第3章 Direct3D的绘制
3.1 顶点缓存与索引缓存
for 顶点缓存:包含顶点数据的连续内存空间。
索引缓存:包含索引数据的连续内存空间。
for 不是用数组储存的!是放在显存中,使用显存中的数据比内存中更快。
for 顶点缓存:
HRESULT IDirect3DDevice9::CreateVertexBuffer(
UINT Length,
DWORD Usage,
DWORD FVF,
D3DPOOL Pool,
IDirect3DVertexBuffer9 ** ppVertexBuffer,
HANDLE* pSharedHandle
);
for 索引缓存:
HRESULT IDirect3DDevice9::CreateIndexBuffer(
UINT Length,
DWORD Usasge,
D3DFORMAT Format,
D3DPOOL Pool,
IDirect3DIndexBuffer9 ** ppIndexBuffer,
HANDLE *pSharedHandle
);
for 参数解析:
1. Length:为缓存分配的字节数。比如8 * sizeof(Vertex);Vertex是定义顶点的结构。
2. Usage:指定关于如何使用缓存的一些附加属性。可为0。或以下组合:
D3DUSAGE_DYNAMIC:将缓存设为动态缓存。
D3DUSAGE_POINTS:规定缓存将用于存储点图元。仅用于顶点缓存。
D3DUSAGE_SOFTWAREPROCESSING:软件顶点运算方式。
D3DUSAGE_WRITEONLY:只写。
3. FVF:存储在顶点缓存中顶点的灵活定点格式。
4. Pool:容纳缓存的内存池。
5. ppVertexBuffer:接受所创建的顶点缓存的指针。
6. pSharedHandle:不使用,设为0.
7. Format:指定索引的大小。如D3DFMT_INDEX16,D3DFMT_INDEX32
8. ppIndexBuffer:用于接受所创建的索引缓存的指针。
for 静态缓存:放置在显存中,一般用来存储不需要经常访问修改的(地图,地形)
for 使用创建缓存的例子:
//可容纳8个Vertex类型的,静态顶点缓存
IDirect3DVertexBuffer9* vb;
device->CreateVertexBuffer(
8 * sizeof( Vertex ),
0,
D3DFVF_XYZ,
D3DPOOL_MANAGED,
&vb,
0);
//可容纳36个16位索引的动态缓存
IDirect3DIndexBuffer9* ib;
device->CreateIndexBuffer(
36 * sizeof(WORD),
D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY,
D3DFMT_INDEX16,
D3DPOOL_MANAGED,
&ib,
0);
3.1.2 访问缓存内容
for 我们需要获得指向缓存内部存储区的指针,才能访问顶点缓存或索引缓存中的数据。
for 用Lock来获取指针,记住unlock。
for Lock的声明:
HRESULT IDirect3DVertexBuffer9::Lock(
UINT OffsetToLock,
UINT SizeToLock,
BYTE **ppbData,
DWORD Flags);
HRESULT IDirect3DIndexBuffer9::Lock(
同上;);
for 参数解析:
1.OffsetToLock:自缓存的起始点到开始锁定的位置的偏移量。
2.SizeToLock:所要锁定的字节数。
3.ppbData:指向被锁定的存储区起始位置的指针。
4.Flags:我描述了锁定的方式,可以是0,也可以是下面的组合:
D3DLOCK_DISCARD:仅用于动态缓存。它指示硬件将缓存内容丢弃,并返回一个指向重新分配
的缓存的指针。允许我们访问新分配的内存时,硬件能够继续使用被丢弃的
缓存中的数据进行绘制。
D3DLOCK_NOOVERWRITE:仅用于动态缓存。只能追加,不能覆盖。
D3DLOCK_READONLY:只读。
for 例子:
Vertex * vertices;
_vb->Lock(0, 0, (void**)&vertices, 0);
vertices[0] = Vertex(-1.0f, 0.0f, 2,0f);
vertices[1] = Vertex(0.0f, 1.0f, 2.0f);
vertices[2] = Vertex(1.0f, 0.0f, 2.0f);
_vb -> Unlock();
3.1.3 获取顶点缓存和索引缓存的信息
for 举个栗子:
D3DVERTEXBUFFER_DESC vbDescription;
vertexBuffer->GetDesc(&vbDescription);
D3DINDEXBUFFER_DESC ibDescripttion;
indexBuffer->GetDesc(&ibDescription);
for D3DVERTEXBUFFER_DESC 和 D3DINDEXBUFFER_DESC结构定义如下:
typedef struct _D3DVERTEXBUFFER_DESC{
D3DFOMAT Format;
D3DRESUORCETYPE Type;
DWORD Usage;
D3DPOOL Pool;
UINT Size;
DWORD FVF;
}D3DVERTEXBUFFER_DESC;
typedef struct _D3DINDEXBUFFER_DESC{
D3DFORMAT Format;
D3DRESUORCETYPE Type;
DWORD Usage;
D3DPOOL Pool;
UINT Size;
DWORD FVF;
}D3DINDEXBUFFER_DESC;
3.2 绘制状态
for Direct3D封装了多种绘制状态,这些绘制状态将影响几何体的绘制方式。有默认值。
for 举个设置绘制状态的栗子:
HRESULT IDirect3DDevice9::SetRenderState(
D3DRENDERSTATETYPE State,
DWORD Value;
);
for 本章我们用线框模式,所以我们应该用这种方式:
_device->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME);
3.3 绘制的准备工作
for 绘制之前准备工作:
1.指定数据输入源。将顶点缓存和数据流进行链接,实际上是将几何体的信息传输到绘制流水线中。
//用下面的方法设置顶点数据流的输入源:
HRESULT IDirect3DDevice9::SetStreamSource(
UINT StreamNumber,
IDirect3DVertexBuffer9 * pStreamData,
UINT OffsetInBytes,
UINT Stride
);
(1) StreamNumber:标示与顶点缓存建立链接的数据流。不使用多个流,设置为0。
(2) pStreamData:指向我们希望与给定数据流建立链接的顶点缓存的指针。
(3) OffsetInBytes:自数据流的起始点算起的一个偏移量。指定了起始位置。
(4) Stride:每个元素的大小。
2.设置顶点格式。
_device->SetFVF(D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1);
3.设置索引缓存。任意时刻只允许使用一个索引缓存。所以需要切换。
_device->SetIndices(_ib);
3.4 使用顶点缓存和索引缓存进行绘制
for 绘制方法:用DrawPrimitive或DrawIndexedPrimitive将待绘几何体的信息通过绘制流水线传输。
这两个方法从顶点数据流中获取顶点信息,并从当前设定的索引缓存中提取索引信息。
3.4.1 IDirect3DDevice9::DrawPrimitive
for 该方法可用于绘制未使用索引信息的图元。
HRESULT IDirect3DDevice9::DrawPrimitive(
D3DPRIMITIVETYPE PrimitiveTypes,
UINT StartVertex,
UINT PrimitiveCount
);
1.PrimitiveTypes:所要绘制的图元类型。因为我们将使用三角形,设置为D3DPT_TRIANGLELIST.
2.StartVertex:顶点数据流中表示顶点数据读取起点的元素的索引。
3.PrimitiveCount:绘制的数量。
for 举个栗子:
_device->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 4);
3.4.2 IDirect3DDevice9::DrawIndexedPrimitive
for 函数声明:
HRESULT IDirect3DDevice9::DrawIndexedPrimitive(
D3DPRIMITIVETYPE Type,
INT BaseVertexIndex,
UINT MinIndex,
UINT NumVertices,
UINT StartIndex,
UINT PrimitiveCount
);
for 参数解析:
1.Type:图元类型。
2.BaseVertexIndex:为索引增加一个基数。
3.MinIndex:允许引用的最小索引值。
4.NumVertices:本次调用中将引用的顶点总数。
5.StartIndex:起点索引。
6.PrimitiveCount:要绘制的总数。
for 举个栗子:
_device->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, 8, 0, 12);
for 局部索引缓存的内容应于局部顶点缓存中的顶点一致。
3.4.3 Begin/End Scene
for 所有方法必须在IDirect3DDevice9::VeginScene 和 IDirect3DDevice9::EndScene 构成的方法之间
_device -> BeginScene();
_device->DrawPrimitive(..);
_device->EndScene();
3.5 D3DX几何体
for D3DX库提供了如下6个网格创建函数:
D3DXCreateBox,D3DXCreateSphere,D3DXCreateCylinder~
for 上述方法都使用D3DX网格数据结构ID3DXMesh和ID3DXBuffer接口。
for 调用例子:
HRESULT D3DXCreateTeapot(
LPDIRECT3DDEVICE9 pDevice,
LPD3DXMESH* ppMesh,
LPD3DXBUFFER* ppAdjacency
);
ID3DXMesh * mesh = 0;
D3DXCreateTeapot(_device, & mesh, 0);
for 绘制例子:
_device->BeginScene();
_mesh->DrawSubset(0);
_device->EndScene();
//记住释放
_mesh ->Release();
_mesh = 0;
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来表示。
5.2 材质
for 材质:我能允许你们定义物体表面对各种颜色光的反射比例。
for 结构声明:
typedef struct D3DMATERIAL9{
D3DCOLORVALUE Diffuse;
D3DCOLORVALUE Ambient;
D3DCOLORVALUE Specular;
D3DCOLORVALUE Emissive;
float Power;
}D3DMATERIAL9, *LPD3DMATERIAL9;
for 参数解析:
1.Diffuse:材质对漫射光的反射率。
2.Ambient:材质对环境光的反射率。
3.Specular:材质对镜面光的反射率。
4.Emissive:增强亮度。
5.Power:指定高光点的锐度。
for 举个栗子:
D3DMATERIAL9 red;
::ZeroMemory (&red, sizeof(red));
red.Diffuse = D3DXCOLOR(1.0f, 0.0f, 0.0f, 1.0f);
red.Ambient = D3DXCOLOR(1.0f, 0.0f, 0.0f, 1.0f);
red.Specular = D3DXCOLOR(1.0f, 0.0f, 0.0f, 1.0f);
red.Emissive = D3DXCOLOR(0.0f, 0.0f, 0.0f, 1.0f);
red.Power = 5.0f;
5.2 材质
for 对当前材质设定:
IDirect3DDevice9::SetMaterial(CONST D3DMATERIAL9 * pMaterial);
5.3 顶点法线
for 定义:顶点法线描述的是构成多边形的各个顶点的法线。
for 注意,顶点法线与表面法线不一定相同。
for 计算法线:假定顶点指定顺序是顺时针。
void ComputNormal(
D3DXVECTOR3 *p0,
D3DXVECTOR3 *p1,
D3DXVECTOR3 *p2,
D3DXVECTOR3 *out)
{
D3DXVECTOR3 u = *p1 - *p0;
D3DXVECTOR3 v = *p2 - *p0;
D3DXVEC3Cross(out, &u, &v);
D3DXVEC3Normalize(out, out);
}
for 面片法向量不能产生很平滑的效果。更好的是求均值。
for 重新规范化:
Device->SetRenderState(D3DRS_NORMALIZENORMALS. true);
5.4 光源
for 点光源:我在世界坐标系中有固定的位置。向所有方向发射光线。
for 方向光:我没有位置信息,发射的光线相互平行的沿某一特定方向传播。
for 聚光灯:锥形。
for 光源用结构D3DLIGHT9表示
typedef struct D3DLIGHT9{
D3DLIGHTTYPE Type;
D3DCOLORVALUE Diffuse;
D3DCOLORVALUE Specular;
D3DCOLORVALUE Ambient;
D3DVECTOR Positon;
D3DVECTOR Direction;
float Range;
float Falloff;
float Attenuation0;
float Attenuation1;
float Attenuation2;
float Theta;
float Phi;
}D3DLIGHT9,*LPD3DLIGHT;
for 参数解析:
1.Type:光源类型,可以是D3DLIGHT_POINT,D3DLIGHT_SPOT,D3DLIGHT_DIRECTIONAL.
2.Diffuse:漫射光的颜色。
3.Specular:镜面光的颜色。
4.Ambient:环境光的颜色。
5.Positon:光源在世界坐标系中的位置。对方向光无意义。
6.Direction:传播的方向。对点光源无意义。
7.Range:最大光程。对方向光无意义。
8.Attenuation:光强随距离的衰减方式。不用于方向光。
9.Falloff:仅用于聚光灯。定义了衰减方式。一般为1.0f。
10.Theta:仅用于聚光灯。内部圆锥的圆锥角。弧度。
11.Phi:仅用于聚光灯。外部锥形的圆锥角。弧度。
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轴的正方向是竖直向下的。
6.2 创建并启用纹理
for 纹理的导入:纹理常从磁盘中的图像 文件读入,然后在加载到IDirect3DTexture9.
for 从文件到接口的函数:
HRESULT D3DXCreateTextureFromFile(
LPDIRECT3DDEVICE9 pDevice,
LPCTSTR pSrcFile,
LPDIRECT3DTEXTURE9 * ppTexture
);
for 调用例子:
//比如我们想从名字为fuck.jpg的图片中创建纹理
IDirect3DTexture9 * _stonewall;
D3DXCreateTextureFromFile(_device, "fuck.jpg", &_stonewall);
//设置当前纹理
HRESULT IDirect3DDevice9::SetTexture(
DWORD Stage,//几重纹理
IDirect3DBaseTexture9 * pTexture
);
Device->SetTexture(0, _stonewall);
for 如果不想使用纹理,则把第二个参数也设置为0。
6.3 纹理过滤器
for 纹理过滤:纹理会适应屏幕,所以会放大缩小产生畸变,所以我能克服畸变。
for 有三种纹理过滤器:
1. 最近点采样:默认的方式。处理速度最快,效果最差。
2. 线性纹理过滤:推荐
3. 各向异性纹理过滤:可以产生最好的结果,但比较复杂
6.4 多级渐进纹理
for 多级渐进纹理:由某一纹理创建一系列分辨率逐渐减小的纹理图像,并且对每种分辨率下的纹理所采用的
过滤方式进行定制,以便保留那些较重要的细节。
for 产生原因:屏幕上的三角形与纹理三角形大小不一致,为了消除差异。
6.4.1 多级渐进纹理过滤器
for 多级渐进纹理过滤器:我控制Direct3D使用多级渐进纹理的方式。设置如下:
Device -> SetSamplerState(0, D3DSAMP_MIPFILTER, Filter);
for Filter 可取以下值:
D3DTEXF_NONE:禁用多级渐进纹理过滤器。
D3DTEXF_POINT:Direct3D将选择尺寸与屏幕三角形最接近的那一级纹理,一旦选择,Direct3D就会
用指定的放大过滤器和缩小过滤器对该纹理进行过滤。
D3DTEXF_LINEAR:Direct3D将取与屏幕三角形尺寸最接近的两个纹理级,用过滤器进行过滤,然后再
对这两级进行线性组合。
6.4.2 使用多级渐进纹理
for D3DXCreateTextureFromFile将为你创建一个多级渐进纹理链。
for Direct3D还自动从链中选择与屏幕三角形最匹配的一级。
6.5 寻址模式
for 有时纹理坐标超出了[0,1],Direct3D定义了四种来处理超出的纹理映射模式:
1.重复寻址模式。wrap
2.边界寻址模式。border
3.箝位寻址模式。clamp
4.镜像寻址模式。mirror
6.6 例程:纹理四边形
for 步骤:
1.构造顶点,并制定纹理坐标。
2.用函数D#D3DXCreateTextureFromFile 为 IDirect3DTexture9 接口加载一种纹理。
3.设置缩小过滤器,放大过滤器和多级渐进纹理过滤器。
4.绘制物体前,用IDirect3DDevice9::SetTexture
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分量的纹理元的一
个额外的位集合。
7.3.2 指定Alpha来源
for 默认:纹理的Alpha通道。
没有Alpha通道:顶点颜色。】
for 设置Alpha值:
//在着色期间计从漫颜色中计算Alpha
Device->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE);
Device->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
//从Alpha通道获取Alpha
Device->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
Device->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
7.4 用DirectX Texture Tool创建Alpha通道
7.5 例程
for 融合步骤:
1.设置融合因子D3DRS_SRCBLEND和D3DRS_DESTBLEND.
2.如果使用了Alpha分量,还需要指定其来源。
3.启用Alpha融合绘制状态。
8 模板
for 模板缓存:我是一个用于获得某种特效的离屏缓存。我的像素与后台缓存和深度缓存的像素是一一对应
的。
for 功能:决定是否将某个像素写入后台中。可用模板缓存来阻止物体影响在非镜面区域中的绘制。
8.1 模板缓存的使用
for 启用和禁用模板缓存:
Device->SetRenderState(D3DRS_SENCILENABLE, true);
Device->SetRenderState(D3DRS_STENCILENABLE, false);
for 清空模板缓存为默认值:
Device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER | D3DCLEAR_STENCEL,0xff000000,1.0f,0);
8.1.1 模板缓存格式的查询
for 模板缓存与深度缓存一同创建, 共享一个离屏的表面缓存。如下三种深度/模板缓存格式:
1. D3DFMT_D24S8:32位的深度/模板缓存, 每个像素24位深度缓存,8位模板缓存。
2. D3DFMT_D24X4S4:32位的~,每个像素24位深度缓存,4位模板缓存,4位不使用。
3. D3DFMT_D15S1:16位,15位深度,1位模板。
8.1.2 模板测试
for 我们可以用模板缓存来阻止对后台缓存中某些特定区域进行绘制。
for 模板测试:我来判定是否将某个像素写入后台缓存的决策过程称为模板测试。
表达式:(ref & mask) ComparisonOperation (value & mask);
for 每个像素都需进行模板测试。模板测试需要如下两个操作数:
1. 左操作数(LHS = ref & mask),由程序定义的模板参考值ref和模板掩码mask通过位运算得到。
2. 右操作数(RHS = value & mask),当前测试的像素的模板缓存中的数值value和模板掩码mask位运算得到。
for 然后根据comparison operation所指定的比较规则对LHS和RHS进行比较。true写入。
8.1.3 模板测试的控制
for 我们可以对模板参考值,模板掩码,比较函数进行设定。
1.模板参考值:我的ref的默认值为0,可用D3DRS_STENCILREF来改变该值。
Device->SetRenderState(D3DRS_STENCILREF, 0x1);
2.模板掩码:我用来屏蔽ref和value变量中的某些位。我的默认值为0xffffffff,表示不屏蔽任何位。
可根据绘制状态D3DRS_STENCILMASK来修改掩码。
Device->SetRenderState(D3DRS_STENCILMASK, 0x0000ffff);
3.模板值:我是当前待测试像素在模板缓存中的位置。我们不能显式的单独设置模板值,但是能对
模板缓存进行清空操作。
4.比较运算:通过D3DRS_STENCILFUNC来设置比较运算函数。可取自如下:
typedef enum D3DCMPFUN{
D3DCMP_NEVER = 1,
D3DCMP_LESS = 2,
D3DCMP_EQUAL = 3,
D3DCMP_LESSEQUAL = 4,
D3DCMP_GRETER = 5,
D3DCMP_NOTEQUAL = 6,
D3DCMP_ALWAYS = 8, //模板测试总是成功的
D3DCMP_FORCE_DWORD = 0X7FFFFFFF,
}D3DCMPFUNC, *LPD3DCMPFUNC;
8.1.4 模板缓存的更新
for 除了决定一个具体像素是否应该写入后台缓存,我们还可以基于以下3种可能的情形定义如何更新:
1.若像素模板测试失败,可设置为D3DRS_STENCILFAIL:
Device->SetRenderState(D3DRS_STENCILFAIL, StencilOperation);
2.若像素深度测试失败,可设置为D3DRS_STENCILZFAIL:
Device->SetRenderState(D3DRS_STENCILZFAIL, StencilOperation);
3.若模板与深度均成功,设置为D3DRS_STENCILPASS:
Device->SetRenderState(D3DRS_STENCILPASS, StencilOperation);
for StencilOperation可取以下预定义常量:
1.D3DSTENCILOP_KEEP:保留当前值。
2.D3DSTENCILOP_ZERO:模板缓存中的值设为0.
3.D3DSTENCILOP_INCRSAT:增加模板缓存中的对应值,若超过最大,取最大。
4.D3DSTENCILOP_DECRSAT:减小模板缓存中的对应值,若小于最小,取最小。
5.D3DSTENCILOP_INVERT:对应值按位取反。
6.D3DSTENCILOP_INCR:增加,若超过最大,取0.
7.D3DSTENCILOP_DECR:减小,小于最小,取最大。
8.1.5 模板写掩码
for 模板写掩码:我可以屏蔽将写入模板缓存的任何值的某些位。用D3DRS_STENCILWRITEMASK来设定。
默认值为0xffffffff.
Device->SteRenderState(D3DRS_STENCILWRITEMASK, Ox0000ffff);
8.2 例程:镜面效果
8.2.1 成像中的数学问题
for 下列函数用于创建相对于任意平面的镜像变换矩阵。
D3DXMATRX * D3DXMatrixReflect(
D3DXMATRIX *pOut;
CONST D3DXPLANE * pPlane;
);
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的指针。