DirectX考点归纳

Direct3D游戏编程入门教程第2版 考点归纳
试卷的考题类型有:单选题、多选题、名词解释/简答题、分析题等。

第01章 Direct3D/DirectX历史
DirectX8有哪些新特性:
1 Point spite(点精灵)
2 3D体纹理
3 对direct3DX库的改进
4 N-patches
5 顶点shader和像素shader

Point spite可以生成:火花、爆炸、雨、雪等效果。

3D体纹理可以完成:光照效果、点光、聚光效果、大气效果。

DirectX8实用库增强的DirectX7的辅助功能:
1枚举设备配置
2设置一个设备
3统一处理全屏模式或窗口模式
4处理调整大小的操作
5向量和矩阵的计算操作
6简化图像文件的加载和纹理的创建
7绘制简单的形状、精灵和立方体贴图(cube map)。

DirectX8提供的新的功能:
1、蒙皮网格(skinned mesh)
2、多分辨率的细节级别(Level-Of-Detail, LOD)几何体
3、.X文件的高阶表面数据(higher-order surface data)支持
4、N-patches(可以在图形卡的帮助下提高模型的精度,即传入一个低精度多边形模型数据,完成镶嵌操作(加入更多的多边形))

DirectX8.0支持的shader:顶点shader标准vs_1_1 和像素shader标准ps_1_1
DirectX8.1支持的shader:                 像素shader标准ps_1_2、ps_1_3、ps_1_4
DirectX9支持的shader:像素shader标准ps_2_0、ps_2_x、ps_2_a 、ps_3_0


DirectX9引入了:
HLSL(High-Level Shader Language)
剪裁测试(scissor test)
行抗锯齿(line anti-aliasing)
球形映射texgen
双面模板(two-sided stencil),允许给封闭几何体添加阴影体(shadow volume)
多重渲染目标(multiple render target)的渲染能力(G-buffer)
多重元素纹理(multi-element texture)
位移贴图(displacement map)
24位颜色精度支持
根据监视器gamma支持来改变gamma值

片断颜色(fragment color)->剪裁测试->alpha测试


第02章 HAL和COM概述
HAL (Hardware Abstraction Layer,硬件抽象层)

HAL和COM技术使Direct3D能在不同硬件上发挥最快速度的同时,具有一个通用的接口和向后兼容的能力:
1 具备一致的API才有生存力
2 若硬件不支持Direct3D所要求的一些特性,应有一种相对低效的机制来保证程序能继续运行
3 接口的变革应尽可能降低程序员学习新接口难度
所有使用早期DirectX版本开发的游戏都保证能在将来的DirectX版本下运行

游戏根据HAL所提供的特性来开启或关闭相应的特征,过程如下:
1游戏检查HAL的能力;
2游戏适当地开启或关闭某些特征;
3若某些特征被关闭,便会以一种低效的方法来实现,可能是模拟某个特征。这通常会导致图像质量降低。

Dircet3D使用HAL通过DDI(Device Driver Interface,设备驱动接口)来访问图形硬件。要检查DDI的版本,可运行DirectX Diagnostic Tool(DirectX 诊断工具)

HAL支持硬件加速的光栅化、硬/软件顶点处理。

HAL有4种不同的顶点处理方法:
SW VP(软件顶点处理 D3DCREATE_SOFTWARE_VERTEXPROCESSING)
Mixed VP(混合顶点处理 D3DCREATE_MIXED_VERTEXPROCESSING)
HW VP(硬件顶点处理 D3DCREATE_HARDWARE_VERTEXPROCESSING)
Pure VP(纯顶点处理 D3DCREATE_PURE_DEVICE)

注册一个插件式软件设备的函数是:IDirect3D9::RegisterSoftwareDevice

COM的优点:
1、COM接口永远不会改变(永远无法通过任何方式修改);
2、COM是独立于语言的(language-independent);
3、只能访问一个COM组件的方法,而不是它的数据。

COM是一种基于二进制(binary-based)的编程模型

使用顶点着色器的步骤
1.编写和编译顶点着色器
2.创建一个IDircet3DVertexShader接口来代表基于编译过后的着色器代码的着色器
3.用IDirect3DDevice9::SetVertextShader方法开启这个顶点着色器


第03章 编程约定
为了编译DirectX程序,必须导入下列程序库:
1、d3dx9.lib
2、d3dxof.lib
3、d3d9.lib
4、winmm.lib
5、dxguid.lib

注意点:
1、Direct3D运行时COM对象和DLL必须由Windows注册和加载。DirectX安装程序会为你做这件事;
2、必须在Windows项目中包含先前提及的库,才能正确链接到所调用的包装方法;
3、必须在源文件和IDE的包含路径条目表格中都包含正确的头文件。

LPDIRECT3D9:IDirect3D9接口指针
Direct3DCreate9():创建一个IDirect3D9的COM对象,并从中获取一个接口指针

命名约定中的前缀和类型:P19

OutputDebugString()函数是一个调试辅助函数,可以在程序运行期间将调试信息输出到IDE的调试窗口中。

DirectX 9.0 SDK提供的宏SUCCEEDED()和FAILED()可以简化检查返回码的工作。

并不是所有方法都会在成功后返回S_OK,所以不要试图将HRESULT与S_OK进行比较判断。若没有什么需要做的,一个函数可能会返回S_FALSE来表示它什么都没做。


第04章 3D基础、Gouraud着色、纹理映射基础
在默认情况下,Direct3D引擎使用左手坐标系。

x:宽度,y:高度,z:深度

创建一个顶点结构,包含位置、漫反射、法线、顶点坐标信息:
typedef Struct
{
  D3DVECTOR3 vPosition; //位置
  DWORD dwDidiffuse; //漫反射
  D3DVECTOR3 vNormal; //法线
  FLOAT u, v; //纹理坐标
}MYVERTEX;
创建一个顶点结构,包含位置、纹理、法线信息:
typedef Struct Vertex
{
  D3DVECTOR3 vPosition; //位置
  D3DVECTOR3 vTexture; //纹理
  D3DVECTOR3 vNormal; //法线
} MYVERTEX;

描述一个物体的朝向需要至少4个向量(LOOK、UP、RIGHT 、POSITION):
LOOK:定义了物体所指的方向;若要飞机横滚(roll),则需将其绕着LOOK向量旋转

UP:表示那个方向对于物体来说是“上”或“下”;若要改变飞机所面对的方向(yaw:航向、偏航),则需将其绕着UP向量旋转

RIGHT:定义物体右侧指向;若要改变飞机的俯仰度(pitch),则需将其绕着RIGHT向量旋转

POSITION:定义物体的位置;要使物体沿着LOOK向量方向移动,则需改变POSITION向量。

任何3D物体都是由面构成。
所有模型和3D世界都可被分解为:点、线和三角形。

一个由若干三角形构成的物体称为:网格(mesh)。

注意:Direct3D会剔除背面。这种背面剔除(Backface Culling)特性可删除所有背对着观看者或照相机的三角形,即那些在默认情况下按逆时针次序的顶点构成的三角形。平均而言,在任何特定时刻,游戏世界中的一半三角形是背对着照相机的,因此利用该特性有助于缩短渲染时间。根据要渲染的对象不同,有时可以用D3DCULL_NONE标志来关闭背面剔除。

为了进行背面剔除,需要计算一个三角形两条边的夹积,夹积结果是一个平面(由三角形的那两条边构成)垂直的向量,该向量就是平面的面法线,它的方向决定了这个三角形是面朝照相机的还是背向照相机的。

多边形:指至少由3个顶点定义的n边封闭形状。最简单的多边形就是三角形。

法线是垂直于面的向量,可用于定义一个面的朝向或可见侧。

顶点法线垂直于一个面,并且位于顶点之上,具有一个单位的长度,顶点法线被用于Gouraud着色。

面法线垂直于面,并且位于面的中央。面法线经常被用于Flat着色。

着色是进行光照计算并根据计算结果确定像素颜色的过程。这些像素颜色稍后将用于在屏幕上表示物体(更专业地说,着色是光栅器对像素进行上色的方法)。
Flat着色是对每一个面进行着色,也就意味着将使用位于三角形中央的面法线来计算光照。
Gouraud着色用顶点法线计算,效果更光滑,这是因为顶点法线的方向随着顶点的不同而不同。Gouraud着色是Direct3D中的默认模式。

纹理坐标空间中,u表示水平方向,v表示垂直方向。

一个典型的顶点结构,由位置向量、RHW(Reciprocal of Homogeneous W,齐次W的倒数)、纹理的tu、tv坐标组成。
Struct Vertex
{
  FLOAT x,y,z,rhw; //2D顶点坐标
  FLOAT tu,t v; //纹理坐标
}


第05章 基础
CD3DApplication
ConfirmDevice()方法是最先被执行的,它用于检查显卡的能力。
以下代码的作用?答:用于检查显卡是否支持1.1版的顶点shader。
HRESULT CMyD3DApplication::ConfirmDevice
(
  D3DCAPS9 *pCaps, //
  DWORD dwBehavior, //
  D3DFORMAT adapterFormat, //
  D3DFORMAT backBufferFormat //同显示的像素格式
)
{
//检查硬件是否支持顶点shader
If((dwBehavior & D3DCREATE_HARDWARE_VERTEXPROCESSING)||(dwBehavior & D3DCREATE_MIXED_VERTEXPROCESSING))
{
//如果不支持,则采用软件顶点shader
If(pCaps->VertexShaderVersion < D3DVS_VERSION(1,1))
return E_FAIL; //一个错误返回值
}
return S_OK; //返回OK状态
}

调用顺序:
1、ConfirmDevice()
2、OneTimeSceneInit() 一次性永久初始化,完成任何一次性的资源分配
3、InitDeviceObjects()
4、RestoreDeviceObjects()
在应用程序运行期间,会循环执行以下函数:
FrameMove() 用于容纳所有动画相关的代码,包括更新矩阵、纹理坐标、对象坐标以及其他一些根据时间而变化的行为。
Render() 作为3D渲染的入口点在渲染每一帧时被调用
以上2个函数时每个框架应用程序的主循环。
FinalCleanup() 用于销毁为几何数据分配的内存、删除文件对象等。


用户改变窗口大小时,框架将会调用:
InvalidateDeviceObjects():当游戏关闭、或用户改变窗口大小、或改变设备,都将调用它。
RestoreDeviceObjects()重新创建InvalidateDeviceObjects()所销毁的东西。

如果用户通过按F2或点击“File->Change Device”来更改设备(HAL或REF)的话,框架会调用:
InvalidateDeviceObjects()
DeleteDeviceObjects()
InitDeviceObjects()
RestoreDeviceObjects()

如果用户退出应用程序,框架会调用:
InvalidateDeviceObjects()
DeleteDeviceObjects()
FinalCleanup():框架最后调用的一个函数。

InvalidateDeviceObjects() - RestoreDeviceObjects()是一对相对应的函数。

DeleteDeviceObjects() - InitDeviceObjects()是一对相对应的函数。

OneTimeSceneInit() - FinalCleanup()是一对相对应的函数。

可变(灵活)顶点格式(Flexible vertex format, FVF)用于构造自定义顶点数据。

HRESULT IDirect3DDevice9::CreateVertexBuffer
(
UINT Length, //顶点缓冲区的大小,此数字是顶点结构体大小的倍数
DWORD Usage, //顶点缓冲的类型或处理方法,0=通常
DWORD FVF, //可变顶点格式
D3DPOOL Pool, //指定顶点缓冲要存储的存储器位置(显卡、系统存储器)和管理方式
IDirect3DVertexBuffer9 **ppVertexBuffer, //顶点缓冲区指针地址
HANDLE *pHandle //保留参数,置为0或NULL
);

FVF描述符标志:
D3DFVF_XYZ:包含3D坐标,告知系统游戏引擎没有变换过顶点,要求Direct3D做。
D3DFVF_XYZRHW:包含2D坐标,告知系统游戏引擎已对顶点作过了变换,Direct3D不需要再做。
D3DFVF_NORMAL:包含法线
D3DFVF_DIFFUSE:包含一个漫反射色分量
D3DFVF_SPECULAR:包含一个镜面反射色分量
D3DFVF_TEX1:包含一个纹理坐标对

p41当使用固定功能流水线时,顶点元素和顶点输入寄存器的映射关系是固定的。因此特定的顶点成分(如位置或法线)必然会被放到顶点输入存储器的特定寄存器中去。所以,顶点结构中的各个成分必须要以一种特定的次序来放置,如下所示:
1、变换过的/未变换的顶点位置,浮点数x、y、z坐标;
2、RHW,齐次w坐标的倒数(仅适用于已变换过的顶点);
3、混合(Blending)权重值,为1~5之间的浮点数;
4、顶点法线,浮点数x、y、z构成的法线坐标;
5、顶点的点大小;
6、漫反射颜色,漫反射的RGBA值;
7、镜面反射颜色,镜面反射的RGBA值;
8、纹理坐标集合1~8,浮点数u和v构成的坐标值。
例:
typedef struct SObjVertex
{
  FLOAT x, y, z; //位置
  FLOAT nx, ny, nz; //法线
  DWORD diffuse; //漫反射颜色
  DWORD specular; //镜面反射颜色
  FLOAT tu, tv; //第1对纹理坐标
  FLOAT tu2, tv2, tw2; //第2对纹理坐标
  FLOAT tu3, tv3; //第3对纹理坐标
  FLOAT tu4, tv4; //第4对纹理坐标
} SObjVertex;
…..
const DWORD g SObjVertexFVF =(D3DFVF_XYZ| D3DFVF_DIFFUSE| D3DFVF_SPECULAR| D3DFVF_NORMAL| D3DFVF _TEX4|D3DFVF _TEXCOORDSIZE2(0)| D3DFVF _TEXCOORDSIZE3(1)| D3DFVF _TEXCOORDSIZE2(2)| D3DFVF _TEXCOORDSIZE2(3))
D3DFVF _TEX4表示Direct3D引擎将会使用到4个顶点坐标。D3DFVF _TEXCOORDSIZE*指定了每组纹理坐标的值的个数(2或3)和纹理坐标的次序。比如:第2组纹理坐标由3个浮点值分量组成。

在Direct3D8.0中引入了一个资源管理器,专门负责对纹理和几何数据实施统一管理。它可用于管理mipmap、体贴图、立方体贴图、顶点缓冲器和索引缓冲器。

在Direct3D中,选择一种管理资源的方法(或定义用于保存资源的存储器类型)称为“选择一个池(choosing a pool)”。在DirectX9中有4种不同类型的池:
typedef enum_D3DPOOL
{
D3DPOOL_DEFAULT =0; //资源存储在显卡或AGP存储器中
D3DPOOL_MANAGED =1; //托管池,资源存储在显卡或AGP存储器中,
              //并且留一份拷贝在系统内存中
D3DPOOL_SYSTEMMEM =2; //系统池,资源存储在系统内存中,
                //这种池的优势在于设备丢失后,资源不需要重新创建。
D3DPOOL_SCRATCH =3; //临时池,资源存储在系统内存中,
              //并且当设备丢失后,将无法重新创建它。
D3DPOOL_FORCE_DWORD =0x7fffffff; //
} D3DPOOL;

p45对一个资源同一时刻只能锁定1次,那怕第2次锁定是一个非重叠的区域也不行。
HRESULT IDirect3DDevice9::Lock
(
UINT OffsetToLock, //顶点缓冲器的锁定操作的起始偏移量
UINT SizeToLock, //锁定区域的大小,为顶点的数量乘以一个顶点的大小
          // OffsetToLock和SizeToLock都是0时,为整个缓冲
VOID **ppbData, //可以读写的缓冲区的指针
DWORD Flags //对如何使用顶点缓冲器给出的提示,
        //=0让驱动程序决定如何对顶点缓冲器的访问如何进行优化
);
Flags的值:
1、D3DLOCK_DISCARD,覆盖顶点缓冲器中的所有位置
2、D3DLOCK_READONLY,应用程序不会写入顶点缓冲器
3、D3DLOCK_NOOVERWRITE,顶点缓冲器中的数据不会被覆盖
4、D3DLOCK_NOSYSLOCK,允许在Lock()期间改变显示模式
5、D3DLOCK_NO_DIRTY_UPDATE,对锁定的顶点缓冲器不添加dirty区域

IDirect3DDevice9::Lock()和IDirect3DDevice9::Unlock()是一对加锁、解锁函数,对顶点缓冲区的内存操作,必须通过Lock()和Unlock()一起配合来实现。

SetStreamSource()函数是用于从一条或多条数据流中装配每个顶点,这些顶点被Direct3D用于流水线的不同处理环节。p46
HRESULT SetStreamSource
(
UINT StreamNumber, //渲染数据流序号,根据硬件不同,最多可以有16条不同的数据流
IDirect3DVertexBuffer9 *pStreamData, //进行绑定链接的顶点缓冲区指针,
                      //提供了该流的数据
UINT OffsetInBytes, //进行绑定链接的渲染数据流的起始位置(字节计)
UINT Stride //渲染数据流中一个顶点所占的内存大小(字节数)
);

SetFVF()声明当前的渲染数据流中的可变顶点格式,DirectX9引入,仅用于固定功能的流水线,如果使用了一个顶点shader,则必须用SetVertexShader()来代替:
HRESULT IDirect3DDevice9::SetFVF()
(
DWORD FVF //渲染数据流中的可变顶点格式
);

HRESULT IDirect3DDevice9::SetVertexShader()函数用于设置当前的顶点渲染函数:
HRESULT IDirect3DDevice9::SetVertexShader
(
IDirect3DVertexShader9* pShader //要设置的顶点渲染对象,禁用=NULL
);
如果要禁用顶点渲染,使用标准Direct3D渲染程序,则应将pShader赋值为NULL,并调用IDirect3DDevice9::SetFVF(),设置可变顶点格式。

Direct3D图元绘制函数:
HRESULT IDirect3DDevice9::DrawPrimitive
(
D3DPRIMITIVETYPE PrimitiveType, //图元类型
UINT StartVertex, //起始点位置
UINT PrimitiveCount //图元数目
);

图元(PrimitiveType)的类型:
D3DPT_POINTLIST
D3DPT_LINELIST
D3DPT_LINESTRIP
D3DPT_TRIANGLELIST
D3DPT_TRIANGLESTRIP
D3DPT_TRIANGLEFAN

p52使用一张纹理贴图的步骤:
1定义纹理坐标变量
2调用CreateTexture()创建纹理
3使用SetTexture()设置纹理
5使用 SetTextureStageState()设置适当的纹理阶段操作
6调用SetFVF()
7调用SetStreamSource()
8调用 DrawPrimitive()
9释放

创建纹理
HRESULT IDirect3DDevice9:: CreateTexture
(
UINT Width, //纹理的宽度,以像素表示
UINT Height, //纹理的高度,以像素表示
UINT Levels, //纹理的多级渐进纹理序列级数
DWORD Usage, //纹理的使用方法
D3DFORMAT Format, //纹理图形的像素格式
D3DPOOL Pool, //顶点缓冲区的内存位置
IDirect3DTexture9** ppTexture, //纹理对象接口指针的地址
HANDLE* pHandle //保留参数,置为0
)

HRESULT IDirect3DDevice9::SetTexture
(
DWORD Stage, //指定关联的纹理序号
IDirect3DBaseTexture9 *pTexture //纹理接口指针
);

SetTextureStageState()作用是控制纹理渲染状态,主要用于Direct3D指定纹理颜色与顶点颜色的混合方法。
HRESULT IDirect3DDevice9:: SetTextureStageState
(
DWORD Stage, //指定当前设置的贴图纹理层ID
D3DTEXTURESTAGESTATETYPE Type, //纹理渲染状态类型枚举值
DWORD Value //纹理渲染状态值
)

创建索引缓冲区函数:
HRESULT IDirect3DDevice9::CreateIndexBuffer
(
UINT Length, //要创建的索引缓冲区的大小,以字节数计算
DWORD Usage, //索引缓冲区属性
D3DFORMAT Format, //索引数组的元素格式,它可以是D3DFMT_INDEX16,
            //表示16位整数索引;或者是D3DFMT_INDEX32,表示32位整数索引。
D3DPOOL Pool, //枚举类型,指定索引缓冲区的内存位置
IDirect3DIndexBuffer9*ppIndexBuffer, //索引缓冲区的指针地址
HANDLE* pHandle //保留参数,置为0
);

IDirect3DDevice9::SetIndices()函数设置当前绘制的索引数组:
HRESULT IDirect3DDevice9:: SetIndices
(
IDirect3DIndexBuffer9 *pIndexData //欲设置的索引缓冲区
);

DrawIndexedPrimitive()函数绘制当前的索引数组:
HRESULT IDirect3DDevice9:: DrawIndexedPrimitive
(
D3DPRIMITIVETYPE Type, //图元类型
INT BaseVertexIndex, //要绘制的索引缓冲区的起始地址
UINT MinIndex, //索引数组中最小的索引值
UINT NumVertices, //要绘制的索引数组中的顶点数目
UINT StartIndex, //表示从索引数组中的第几个元素开始绘制图元
UINT PrimitiveCount //要绘制的图元数目
);


先整理到第5章,后面的稍后啊~~把这些都看熟了就能过了,不用背~~嘿嘿~~

nbkjb2007-05-04 16:14


edwardwewe2007-05-08 23:17
第06章 动画初步
***p103-105
6.1.1世界变换
矩阵(Matrix)
Direct3D将矩阵存储为D3DMATRIX结构中:
typedef struct_D3DMATRIX
{
D3DVALUE _11, _12, _13, _14;
D3DVALUE _21, _22, _23, _24;
D3DVALUE _31, _32, _33, _34;
D3DVALUE _41, _42, _43, _44;
} D3DMATRIX

单位矩阵:
1 0 0 0
0 1 0 0
0 0 1 0
0 0 0 1

D3DMATRIX mat;
mat._11 = mat._22 = mat._33 = mat._44 =1.0f;
mat._12 = mat._13 = mat._14 =0.0f;
mat._21 = mat._23 = mat._24=0.0f;
mat._31 = mat._32 = mat._34=0.0f;
mat._41 = mat._42 = mat._43=0.0f;

单位矩阵函数:
D3DMATRIX *D3DXMatrixIdentity(D3DMATRIX *pOut);

A11 A12 A13 0
A21 A22 A23 0
A31 A32 A33 0
A41 A42 A43 1

平移:
1 0 0 0
0 1 0 0
0 0 1 0
Tx Ty Tz 1
矩阵平移函数:
D3DMATRIX *D3DXMatrixTranslation
{
D3DMATRIX *pOut, //输出矩阵
FLOAT x, //X坐标偏移量
FLOAT y, //Y坐标偏移量
FLOAT z, //Z坐标偏移量
}

inline VOID D3DXMatrixTranslation(D3DXMATRIX* m, FLOAT tx, FLOAT ty, FLOAT tz)
{
D3DXMatrixIdentity(&m);
m._41 =tx; m._42 = ty; m._43 = tz;
}

绕X轴旋转矩阵:
1 0   0   0
0 cosθ sinθ 0
0 -sinθ cosθ0
0 0   0   1
伪代码实现:
mat._22 = cosf(theta)
mat._23 = sinf(theta)
mat._32 = -sinf(theta)
mat._33 = cosf(theta)

D3DMATRIX *D3DXMatrixRotationX
{
D3DMATRIX *pOut, //输出矩阵
FLOAT Angle //X轴上的旋转弧度值
}

inline VOID D3DXMatrixRotationX (D3DXMATRIX* mat, FLOAT fRads)
{
D3DXMatrixIdentity(mat);
mat._22 = cosf(fRads);
mat._23 = sinf(fRads);
mat._32= -sinf(fRads);
mat._33 = cosf(fRads);
}

绕Y轴旋转矩阵:
cosθ 0 -sinθ 0
0   1   0   0
sinθ 0 cosθ 0
0   0   0   1
伪代码实现:
mat._11 = cosf(theta)
mat._13 = -sinf(theta)
mat._31 = sinf(theta)
mat._33 = cosf(theta)

inline VOID D3DXMatrixRotationY (D3DXMATRIX* mat, FLOAT fRads)
{
D3DXMatrixIdentity(mat);
mat._11 = cosf(fRads);
mat._13 = -sinf(fRads);
mat._31 = sinf(fRads);
mat._33 = cosf(fRads);
}

D3DMATRIX *D3DXMatrixRotationY
{
D3DMATRIX *pOut, //输出矩阵
FLOAT Angle //Y轴上的旋转弧度值
}

绕Z轴旋转矩阵:
cosθsinθ 0 0
-sinθcosθ 0 0
0   0   1   0
0   0   0   1
伪代码实现:
mat._11 = cosf(theta)
mat._12 = sinf(theta)
mat._21 = -sinf(theta)
mat._22= cosf(theta)

inline VOID D3DXMatrixRotationZ (D3DXMATRIX* mat, FLOAT fRads)
{
D3DXMatrixIdentity(mat);
mat._11 = cosf(fRads);
mat._12 = sinf(fRads);
mat._21 = -sinf(fRads);
mat._22 = cosf(fRads);
}

D3DMATRIX *D3DXMatrixRotationZ
{
D3DMATRIX *pOut, //输出矩阵
FLOAT Angle //Z轴上的旋转弧度值
}

缩放:
Sx 0 0 0
0 Sy 0 0
0 0 Sz 0
0 0   0 1
矩阵缩放函数:
D3DMATRIX *D3DXMatrixScaling
{
D3DMATRIX *pOut, //输出矩阵
FLOAT sx, //X缩放
FLOAT sy, //Y缩放
FLOAT sz //Z缩放
}

D3DMATRIX* D3DXMatrixScaling (D3DXMATRIX* pOut, FLOAT sx, FLOAT sy, FLOAT sz)
{
pOut->_11 =sx; pOut->_22 =sy; pOut->_33 =sz; pOut->_44 =1;
return pOut;
}

将矩阵合并:
D3DMATRIX *D3DXMatrixMultiply
{
D3DMATRIX *pOut, //输出矩阵
CONST D3DMATRIX *pM1, //源矩阵1
CONST D3DMATRIX *pM2 //源矩阵2
}

D3DMATRIX *D3DXMatrixMultiply
(
D3DMATRIX *pOut,
CONST D3DMATRIX *pM1,
CONST D3DMATRIX *pM2
)
{
FLOAT pM[16];
ZeroMemory(pM, sizeof(D3DMATRIX));
for(WORD i=0; i<4; i++)
  for(WORD j=0; j<4; j++)
  for(WORD k=0; k<4; k++)
    pM[4*i+j] += pM1[4*i+k] * pM2[4*k+j];
memcpy(pOut, pM, sizeof(D3DXMATRIX));
return(pOut);
}

p77注意:使用旋转函数来旋转三维空间中的物体时,可能会导致丢失一维,这便是“万向节锁(gimbal lock)”现象。大多数游戏只用得“2.5维”就够了。对于万向节锁的一种解决方法是:通过向量来实现三维空间中物体的旋转、或使用四元数。

6.1.1观察变换
观察变换用于描述一个观察者在场景中的位置和朝向。
世界矩阵和观察矩阵的主要区别在于3个朝向向量的存储方式。世界矩阵以行的次序存储它们,而观察矩阵以列的次序存储。

世界坐标系和摄像机坐标系都是3维坐标系,但屏幕上显示的都是2维画面,因此需要进行3维坐标系道2维坐标系的转换。将3维坐标系变换为2维坐标系的一个最简便的方法就是去掉一个轴,例如,如果所有顶点的Z值都是0,只有X,Y值,3维坐标系也就自然而然地变成了2维坐标系,这种变换叫做:正交投影(Orhographic Projection),只适合CAD、CAM等非常有限的领域。事实上,现在常用的投影变换是透视投影(Perspective Projection),摄像机的距离不同,投影的比率也不同,这样可以表现出物体的距离感。

在3维空间绘制2个物体时,需要变换它们自己的局部坐标系,将它们引入同一个坐标系,也就是世界坐标系。将局部坐标系变换为世界坐标系的矩阵称为:变换矩阵(Transform Matrix),也可以缩略为TM。
设置世界坐标系变换:SetTransform(D3DTS_WORLD, &matWorld);

观察变换/摄像机变换:以摄像机为基准点,将世界坐标系变换为摄像机坐标系的过程
摄像机变换矩阵运算函数:
D3DMatrix* D3DMatrixLookAtLH
(
D3DMatrix* pout, //存放变换后矩阵的矩阵结构体
CONST D3DVECTOR3* pEye, //眼睛的位置,就是观察者在三维坐标系中的坐标;
                //观察点的位置坐标
CONST D3DVECTOR3* pAt, //观察的方向,这也是一个点的坐标值,
                //函数通过pEye参数表示的点坐标,
              //到pAt参数的点坐标方向来确定观察的方向。
            //所以pAt参数可以是从观察点向观察方向上的任何一个点的坐标值
              //被观察点的位置坐标
CONST D3DVECTOR3* pUp //虚拟相机(观察者)的向上向量,通常为(0,1,0)
)

确定物体的位置至少需要:摄像机位置的世界坐标、摄像机观察位置的世界坐标、摄像机的上方向量。

设置摄像机变换:SetTransform(D3DTS_VIEW, &matView);

D3D渲染管道(Rendering Pipeline),表现了从最初的局部坐标系到最终坐标系的变换过程:
顶点->     世界矩阵 ->摄像机矩阵->投影矩阵 ->变换后的顶点
局部坐标系->世界坐标系->视图坐标系->摄影坐标系->视图区坐标系

D3DXVec3Dot()计算两个3D向量的点积:
FLOAT D3DXVec3Dot
(
CONST D3DXVECTOR3 *pV1, //指向D3DXVECTOR3 结构的源向量
CONST D3DXVECTOR3 *pV2 //指向D3DXVECTOR3 结构的源向量
);

D3DXMatrixInverse()计算矩阵的逆矩阵:
D3DXMATRIX *WINAPI D3DXMatrixInverse
(
D3DXMATRIX *pOut, //指向D3DXMATRIX结构的逆矩阵
FLOAT *pDeterminant, //指向FLOAT 类型的对角线[1,1][2,2][3,3][4,4]积,
            //如果不需要,可以设置为 NULL。
CONST D3DXMATRIX *pM //指向 D3DXMATRIX 结构源矩阵
);

p88 D3DMatrixLookAtLH实现
p93代码分析

6.1.3投影变换
投影变换矩阵运算函数:
D3DMATRIX *D3DXMatrixPerspectiveFovLH
(
  D3DMATRIX *pOut, //用来存放变换后矩阵的矩阵结构体
  FLOAT fovY, //field of view,视域,视角,在y轴上的成像度,单位是弧度。
        //指投影视野的宽度,这个数越大,能够看到的范围就越多
  FLOAT Aspect, //截头体的纵横比
  FLOAT zn, //近裁剪平面的Z(深度)值(near clipping plane),
      //可以观察到的三维世界的最近距离。
  FLOAT zf //远裁剪平面的Z(深度)值(far clipping plane),
      //可以观察到的三维世界的最远距离。
);

设置投影变换:SetTransform(D3DTS_PROJECTION, &matProj);

6.1.4使用视口
typedef struct _D3DVIEWPORT9
{
DWORD X; //视口左上角X坐标,StartX
DWORD Y; //视口左上角Y坐标,StartY
DWORD Width; //视口宽度
DWORD Height; //视口高度,X、Y、Width、Height值对应于视口矩阵
float MinZ; //视口内景物的最小深度值,0.0f~1.0f之间,一般设为0.0f
float MaxZ; //视口内景物的最大深度值,0.0f~1.0f之间,一般设为1.0f
}D3DVIEWPORT9;
通常将MinZ和MaxZ分别设为0.0f和1.0f,这样可以渲染深度缓冲器中整个深度范围内的值。如果将这两个值都设为0.0f,则强制系统在前景中进行渲染。

HRESULT IDirect3DDevice9::SetViewport
(
CONST D3DVIEWPORT9 *pViewport //存放新视口的地址
);
SetViewport()相当于把投影空间的顶点P(x,y,z,1)乘以矩阵Mviewport:
Width/2     0         0       0
0         -Height/2     0       0
0         0         MaxZ-MinZ   0
StartX+Width/2 StartY+Height/2 MinZ     0

因此屏幕上P’(x’, y’)的坐标等于:
x’ = x*Width/2 + StartX+Width/2;
y’ =-y*Height/2 + StartY+Height/2;
z’ =z*(MaxZ-MinZ) + MinZ; z’被用来进行裁剪
SetViewport()定义了窗口中的一个区域,随后绘图操作的结果都将显示在该矩阵区域内。如果一个新视口定义在设备边界之外,那么SetViewport()则会失败。请注意,视口矩阵的高宽比应与投影矩阵所定义的高宽比相匹配,否则物体看上去会像被挤压过一样。

HRESULT IDirect3DDevice9::GetViewport
(
D3DVIEWPORT9 *pViewport //存放视口的地址
);
HRESULT IDirect3DDevice9::SetTransform
{
D3DTRANSFORMSTATETYPE State, //可取:D3DTRANSFORMSTATE_WORLD、
                    //D3DTRANSFORMSTATE_VIEW、
                    //D3DTRANSFORMSTATE_PROJECTION
CONST D3DMATRIX *pMatrix //指向变换矩阵的内存地址
};

绘制字符串:
INT IDDXFont::DrawText
(
LPD3DXSPITE pSprite, //指定字符串所属的对象接口,默认值是0,表示在当前窗口绘制
LPCSTR pString, //指定将要绘制的字符串
INT Count, //指定绘制的个数,-1表示自动绘制到字符串结束为止
LPRECT pRect, //指定字符串绘制的矩形区域位置
DWORD Format, //在矩形区域的摆放属性
D3DCOLOR Color //指定字符串显示的颜色值,属于D3DCOLOR结构。
)

6.1.5深度缓冲(Depth Buffering)p99
渲染场景最有效的方法是:只渲染那些可以被观察者看到的像素。如果渲染了那些看不到的像素(比如,房间里那些通过窗口无法看到的物体),那么这种多余的操作称为:覆绘(overdrawing)。

深度复杂度(depth complexity)是对场景中覆绘数量的一种度量值,它表示在所有渲染的像素中最终可见像素的比例。例如,一个场景的深度复杂度为3,则表示被渲染的像素数量是屏幕上实际可见像素数量的3倍。也就是说,在某种特定的帧速率情况下,需要以3倍于无覆绘的填充率来显示一个场景。覆绘事使3D游戏变得低效率的一个主要原因。

Direct3D深度缓冲器可以防止覆绘。

HRESULT Clear
(
DWORD Count,//矩形数量
  const D3DRECT *pRects,//矩形指针
  DWORD Flags,//要清除的缓冲类型(Z缓冲器、渲染目标、模板缓冲器)
  float Z,//Z缓冲设置的值
DWORD Stencil //模板缓冲设置的值
)

在一个透视观察环境中,世界空间的距离值与深度缓冲器中的值(Z值)不是成比例对应的。也就是说任意2个相邻的深度值之间的距离并不是相等的,其中较小的深度值间的距离比较相近些。

如果丢失了深度缓冲的精度,那么就极有可能会发生渲染错误。而深度精度取决于深度缓冲器的位数。不过这些问题通常只会发生在Z缓冲器中,解决该问题的方便就是选择更高精度的深度缓冲器格式,比如24位。

W缓冲器:减少了Z缓冲器在处理远距离物体时所遇到的问题,并且无论对于多远或多近的物体都具有稳定的执行性能。
一个基于W算法的深度缓冲器与Z缓冲器算法相比,它可以更均匀地分布于近裁剪面与远裁剪面之间。这样的好处就是解决了近/远裁剪面间的距离比例问题。使在仍能获得相对精确的深度缓冲的前提下,支持更大的深度范围。缺点是有时对于较近的物体会显露出表面被隐藏的视觉错误、不如Z缓冲那样广泛地为硬件所支持。

通过SetRenderState()函数可以切换为W缓冲器:
m_pd3dDevice-> SetRenderState(D3DRS_ZENABLE, D3DZB_USEW); //w-buffer
使用下列代码可以检查是否支持W缓冲器:
if(d3dCaps.RasterCaps & D3DPRASTERCAPS_WBUFFER)
m_bDeviceSupportW = TRUE;
else
m_bDeviceSupportW = FALSE;

SetRenderState()
HRESULT IDirect3DDevice9::SetRenderState
(
D3DRENDERSTATETYPE State,
DWORD Value
);

typedef enum D3DRENDERSTATETYPE
{
  D3DRS_ZENABLE = 7,
  D3DRS_FILLMODE = 8,
  D3DRS_SHADEMODE = 9,
  D3DRS_ZWRITEENABLE = 14,
  D3DRS_ALPHATESTENABLE = 15,
  D3DRS_LASTPIXEL = 16,
  D3DRS_SRCBLEND = 19,
  D3DRS_DESTBLEND = 20,
  D3DRS_CULLMODE = 22,
  D3DRS_ZFUNC = 23,
  D3DRS_ALPHAREF = 24,
  D3DRS_ALPHAFUNC = 25,
  D3DRS_DITHERENABLE = 26,
  D3DRS_ALPHABLENDENABLE = 27,
  D3DRS_FOGENABLE = 28,
  D3DRS_SPECULARENABLE = 29,
  D3DRS_FOGCOLOR = 34,
  D3DRS_FOGTABLEMODE = 35,
  D3DRS_FOGSTART = 36,
  D3DRS_FOGEND = 37,
  D3DRS_FOGDENSITY = 38,
  D3DRS_RANGEFOGENABLE = 48,
  D3DRS_STENCILENABLE = 52,
  D3DRS_STENCILFAIL = 53,
  D3DRS_STENCILZFAIL = 54,
  D3DRS_STENCILPASS = 55,
  D3DRS_STENCILFUNC = 56,
  D3DRS_STENCILREF = 57,
  D3DRS_STENCILMASK = 58,
  D3DRS_STENCILWRITEMASK = 59,
  D3DRS_TEXTUREFACTOR = 60,
  D3DRS_WRAP0 = 128,
  D3DRS_WRAP1 = 129,
  D3DRS_WRAP2 = 130,
  D3DRS_WRAP3 = 131,
  D3DRS_WRAP4 = 132,
  D3DRS_WRAP5 = 133,
  D3DRS_WRAP6 = 134,
  D3DRS_WRAP7 = 135,
  D3DRS_CLIPPING = 136,
  D3DRS_LIGHTING = 137,
  D3DRS_AMBIENT = 139,
  D3DRS_FOGVERTEXMODE = 140,
  D3DRS_COLORVERTEX = 141,
  D3DRS_LOCALVIEWER = 142,
  D3DRS_NORMALIZENORMALS = 143,
  D3DRS_DIFFUSEMATERIALSOURCE = 145,
  D3DRS_SPECULARMATERIALSOURCE = 146,
  D3DRS_AMBIENTMATERIALSOURCE = 147,
  D3DRS_EMISSIVEMATERIALSOURCE = 148,
  D3DRS_VERTEXBLEND = 151,
  D3DRS_CLIPPLANEENABLE = 152,
  D3DRS_POINTSIZE = 154,
  D3DRS_POINTSIZE_MIN = 155,
  D3DRS_POINTSPRITEENABLE = 156,
  D3DRS_POINTSCALEENABLE = 157,
  D3DRS_POINTSCALE_A = 158,
  D3DRS_POINTSCALE_B = 159,
  D3DRS_POINTSCALE_C = 160,
  D3DRS_MULTISAMPLEANTIALIAS = 161,
  D3DRS_MULTISAMPLEMASK = 162,
  D3DRS_PATCHEDGESTYLE = 163,
  D3DRS_DEBUGMONITORTOKEN = 165,
  D3DRS_POINTSIZE_MAX = 166,
  D3DRS_INDEXEDVERTEXBLENDENABLE = 167,
  D3DRS_COLORWRITEENABLE = 168,
  D3DRS_TWEENFACTOR = 170,
  D3DRS_BLENDOP = 171,
  D3DRS_POSITIONDEGREE = 172,
  D3DRS_NORMALDEGREE = 173,
  D3DRS_SCISSORTESTENABLE = 174,
  D3DRS_SLOPESCALEDEPTHBIAS = 175,
  D3DRS_ANTIALIASEDLINEENABLE = 176,
  D3DRS_MINTESSELLATIONLEVEL = 178,
  D3DRS_MAXTESSELLATIONLEVEL = 179,
  D3DRS_ADAPTIVETESS_X = 180,
  D3DRS_ADAPTIVETESS_Y = 181,
  D3DRS_ADAPTIVETESS_Z = 182,
  D3DRS_ADAPTIVETESS_W = 183,
  D3DRS_ENABLEADAPTIVETESSELLATION = 184,
  D3DRS_TWOSIDEDSTENCILMODE = 185,
  D3DRS_CCW_STENCILFAIL = 186,
  D3DRS_CCW_STENCILZFAIL = 187,
  D3DRS_CCW_STENCILPASS = 188,
  D3DRS_CCW_STENCILFUNC = 189,
  D3DRS_COLORWRITEENABLE1 = 190,
  D3DRS_COLORWRITEENABLE2 = 191,
  D3DRS_COLORWRITEENABLE3 = 192,
  D3DRS_BLENDFACTOR = 193,
  D3DRS_SRGBWRITEENABLE = 194,
  D3DRS_DEPTHBIAS = 195,
  D3DRS_WRAP8 = 198,
  D3DRS_WRAP9 = 199,
  D3DRS_WRAP10 = 200,
  D3DRS_WRAP11 = 201,
  D3DRS_WRAP12 = 202,
  D3DRS_WRAP13 = 203,
  D3DRS_WRAP14 = 204,
  D3DRS_WRAP15 = 205,
  D3DRS_SEPARATEALPHABLENDENABLE = 206,
  D3DRS_SRCBLENDALPHA = 207,
  D3DRS_DESTBLENDALPHA = 208,
  D3DRS_BLENDOPALPHA = 209,
  D3DRS_FORCE_DWORD = 0x7fffffff,
} D3DRENDERSTATETYPE, *LPD3DRENDERSTATETYPE;

typedef enum D3DSHADEMODE
{
  D3DSHADE_FLAT = 1,
  D3DSHADE_GOURAUD = 2,
  D3DSHADE_PHONG = 3,
  D3DSHADE_FORCE_DWORD = 0x7fffffff,
} D3DSHADEMODE, *LPD3DSHADEMODE;


nbkjb2007-05-09 18:16
要是有能运行的小程序多好呀, 看文字太抽象了.


edwardwewe2007-05-15 01:16
第09章 使用高级shader语言进行shader编程
p157图9.2
顶点shader可在功能上取代传统的变换与光照阶段(Transform and Lighting stage, T&L),它位于镶嵌(tesselation)阶段后,剔除(culling)与剪裁(clipping)阶段之前。镶嵌阶段结束后,便将一个顶点的数据传递给顶点shader,顶点可能包括位置、纹理坐标、顶点颜色、法线等数据。顶点shader不能创建或删除顶点,他处理结束后,至少要输出一个顶点中的位置数据。

像素shader可以在功能上取代传统的多纹理(multitexturing)阶段,它位于光栅化(rasterization)阶段之后。它从光栅器、顶点shader、应用程序、纹理贴图几处获取数据。

纹理贴图不仅可以存储颜色数据,还可用于存储所有对像素shader中的算法有用的数值信息。

像素shader的计算结果可以写入一个渲染目标,然后将该渲染目标设置为后续渲染的输入纹理,这种技术称为:渲染到渲染目标(rendering to the render target)。

像素shader通常输出一个float4类型的值。如果像素shader同时对几个渲染目标进行渲染,则最多可输出4个float4值,这种技术称为:多渲染目标(Multiple Render Targets)或简称MRT。

9.3.1环境光照
全局光照模型(global lighting model):在环境光照模型中,所有光线都均匀地从各个方向照射到物体上,这种光照模型在空间中是没有位置的。

I=Aintensity*Acolor
intensity=光照的强度
color=光的颜色

更为复杂的环境光的光照公式:
I=Aintensity*Acolor+Diffuse+Specular

在高级语言中可以这样书写环境成分:
float4 Acolor = {1.0, 0.15, 0.15, 1.0}
float Aintensity =0.5;
return Aintensity*Acolor;
-> return float4(0.5, 0.075, 0.075, 1.0);

下面这段代码中的VS()和PS()函数的作用?这段代码用的是什么技术?p159:
float4x4 matWorldViewProj;
struct VS_OUTPUT{ float4 Pos: POSITION;};

VS_OUTPUT VS(float4 Pos: POSITION)
{
VS_OUTPUT Out = (VS_OUTPUT)0;
Out.Pos = mul(Pos, matWorldViewProj); //transform Position
return Out;
}

float4 PS() : COLOR
{
return float4(0.5, 0.075, 0.075, 1.0);
}
答:
源代码起始处的VS_OUTPUT结构描述了顶点shader的输出值。
VS()函数是一个顶点shader函数,具有VS_OUTPUT类型的返回值。圆括号内的输入参数Pos,用POSITION语义标识了该输入数据是一个位置数据。
顶点shader的输入之和输出值都用到了POSITION语义。第2处POSITION语义标识了顶点shader的返回值也是一个位置数据(顶点shader必须至少要输出一个位置数据),该数据将作为后续像素shader的输入值。
顶点shader使用matWorldViewProj矩阵来变换顶点位置,matWorldViewProj矩阵在此作为一个由程序提供的常量。最后顶点shader输出位置值。

像素shader函数PS()的返回值总是float4。这里使用语义COLOR标注了返回值,所以编译器会始终将该返回值作为颜色之来处理。此处像素shader没有显式的输入值,因为其名称PS后的括号内是空的。
这段代码用了顶点shader和像素shader技术。

9.3.2漫反射光照P160
在漫反射光照模型中,需要考虑到光的位置,也就是说光在空间中是有位置概念的,这就是所谓的“位置光照模型(positional lighting model)”。

而在环境光照模型中,光在空间中是没有位置的,因此是当作全局条件来处理的,这是漫反射光照模型与环境光照模型之间的主要区别。

漫反射光照的另一个特性是光的反射与观察者的位置无关。因此,在漫反射光照模型中一个物体的表面在所有方向上的反射都是一样。这就是漫反射光照被普遍应用于模拟不光滑表面的原因。
漫反射光照模型遵循Lambert定理。这个定理借助于描述光位置的向量L和描述物体顶点法线的向量N来描述。

N.L = ||N||*||L||*cosα
->N.L = cosα

I=Aintensity*Acolor + Dintensity*Dcolor*N.L + Specular
Aintensity=环境光(Ambient)的强度
Acolor=环境光的颜色
Dintensity=漫反射光的强度
Dcolor=漫反射(Diffuse)光的颜色
Specular:镜面反射

可简化为:
I=A + D*N.L + Specular

分析下面这段代码p61:
float4x4 matWorldViewProj;
float4x4 matWorld;
float4 vecLightDir;

struct VS_OUTPUT
{
float4 Pos: POSITION;
float3 Light: TEXCOORD0;
float3 Norm: TEXCOORD1;
};

VS_OUTPUT VS(float4 Pos: POSITION, float Normal : NORMAL)
{
VS_OUTPUT Out = (VS_OUTPUT)0;
Out.Pos = mul(Pos, matWorldViewProj); //transform Position
Out.Light = normalize(vecLightDir); //output light vector
Out.Norm = normalize(mul(Normal, matWorld)); //transform Normal and normalize it
return Out;
}

float4 PS(float3 Light : TEXCOORD0, float3 Norm : TEXCOOD1) : COLOR
{
float4 diffuse = (1.0f, 0.0f, 0.0f, 1.0f);
float4 ambient = (0.1, 0.0, 0.0, 1.0);
return ambient + diffuse * saturate(dot(Light, Norm));
}
答:这个顶点shader使用了顶点地址、顶点法线作为其输入数据。“世界-观察-投影”矩阵、世界矩阵、光向量都是由应用程序提供的,分别通过matWorldViewProj、matWorld、vecLightDir传递给顶点shader。本例的顶点shder VS通过变量Light和Norm输出了向量N和L。这两个向量都在顶点shader中通过内部函数normalize()作了归一化。该函数返回归一化的向量v=v/length(v),如果v的长度为0,则结果不确定。Normal向量的变换是通过乘以世界矩阵完成的。函数mul(a,b)用于做a和b的矩阵乘法。函数saturate()将参数值限制在区间[0,1]函数中。函数dot(a,b)用于做a和b的点积。

9.3.3镜面反射光照p163
镜面反射(Specular)光照需要考虑观察者的位置,漫反射(Diffuse)光照需要考虑光向量的位置,而环境光照(Ambient)则什么位置都不需要考虑。

使用2个向量来计算镜面反射成分:观察者向量V(描述了观察者或照相机的方向)和反射向量R(描述了光向量的反射方向)。V和R的夹角β。V越与R靠拢,反射光就越亮。可使用cos(β)^n来描述镜面反射,指数n表示光泽属性。
镜面反射模型:
R.V = ||R||*||V||*cosβ
cosβ = R.V
如果其中两个向量都是一个单位的长度,则R.V可用cosβ替代。因此镜面反射便可描述为(R.V)^n
R = 2*(N.L)*N -L
整个phong镜面反射的光照公式如下:
I = Aintensity * Acolor + Dintensity * Dcolor * N.L + Sintensity * Scolor * (R.V)^n
可以简化为:I = A + D * N.L + (R.V)^n
源代码如下:
float4x4 matWorldViewProj;
float4x4 matWorld;
float4 vecLightDir;
float4 vecEye;

struct VS_OUTPUT
{
float4 Pos: POSITION;
float3 Light: TEXCOORD0;
float3 Norm: TEXCOORD1;
float3 View: TEXCOORD2;
};

VS_OUTPUT VS(float4 Pos: POSITION, float Normal : NORMAL)
{
VS_OUTPUT Out = (VS_OUTPUT)0;
Out.Pos = mul(Pos, matWorldViewProj); //transform Position
Out.Light = vecLightDir; //output light vector
float3 PosWorld = normalize(mul(Pos, matWorld));
Out.View = vecEye –PosWorld; //V;观察者向量=眼睛位置-在世界空间中的顶点位置
Out.Norm = mul(Normal, matWorld); //N
return Out;
}
float4 PS(float3 Light : TEXCOORD0, float3 Norm : TEXCOOD1, float3 View : TEXCOORD2) : COLOR
{
float4 diffuse = (1.0f, 0.0f, 0.0f, 1.0f);
float4 ambient = (0.1f, 0.0f, 0.0f, 1.0f);

float3 Normal = normalize(Norm);
float3 LightDir = normalize(Light);
float3 ViewDir = normalize(View);
float4 diff = saturate(dot(Normal, LightDir)); //漫反射组件:N.L

//R = 2*(N.L)*N -L
float3 Reflect = normalize(2 * diff * Normal - LightDir);
float4 specular = pow(saturate(dot(Reflect, ViewDir)), 8); //(R.V)^n; n=8

// I = A + D * N.L + (R.V)^n
return ambient + diffuse * diff + specular;
}
应用程序向顶点shader提供了几个常量:matWorldViewProj矩阵、matWorld矩阵、眼睛位置vecEye、光向量vecLightDir。在顶点shader中,观察者向量V是通过在世界空间中将眼睛位置减去顶点位置得到的。顶点shader输出的内容为:位置向量、光向量、法线向量、观察者向量,这些向量将作为后续像素shader的输入值。3个输入向量都是在像素shader中通过normalize()作归一化的。
本例的像素shader实现了Phong光照计算,它先计算了漫反射,这行代码位于归一化输入向量之后。此处通过saturate()函数将参数值限制在区间[0,1]中。接着重复利用漫反射的计算结果来计算反射向量。使用pow()函数来计算镜面反射的乘幂,它被声明为pow(x,y),返回x^y。这个函数只能在像素shader ps_2_0或更高版本中使用。

9.3.4自身遮蔽阴影项(Self-Shadowing Term)p167
通常将镜面反射光照模型和一个自阴影项一起使用,从而获得一种几何体自身遮蔽的阴影效果。如果光向量被几何体挡住了,该项便会将光的亮度设为0。还可以将其用于线性地缩放光的亮度。这有助于在使用凹凸贴图时减少像素跳跃感(popping)。
自身遮蔽阴影项光照公式:
I = A + S * (D * N.L + (R.V)^n)
如果漫反射为NULL,则自阴影项将使光照模型中的漫反射成分和镜面反射成为也减少为NULL。漫反射成分还可用于减弱镜面反射成分的强度。
代码如下:
float4x4 matWorldViewProj;
float4x4 matWorld;
float4 vecLightDir;
float4 vecEye;

struct VS_OUTPUT
{
float4 Pos: POSITION;
float3 Light: TEXCOORD0;
float3 Norm: TEXCOORD1;
float3 View: TEXCOORD2;
};

VS_OUTPUT VS(float4 Pos: POSITION, float Normal : NORMAL)
{
VS_OUTPUT Out = (VS_OUTPUT)0;
Out.Pos = mul(Pos, matWorldViewProj); //transform Position
Out.Light = vecLightDir; //output light vector
float3 PosWorld = normalize(mul(Pos, matWorld));
Out.View = vecEye –PosWorld; //V;观察者向量=眼睛位置-在世界空间中的顶点位置
Out.Norm = mul(Normal, matWorld); //N
return Out;
}
float4 PS(float3 Light : TEXCOORD0, float3 Norm : TEXCOOD1, float3 View : TEXCOORD2) : COLOR
{
float4 diffuse = (1.0f, 0.0f, 0.0f, 1.0f);
float4 ambient = (0.1, 0.0, 0.0, 1.0);

float3 Normal = normalize(Norm);
float3 LightDir = normalize(Light);
float3 ViewDir = normalize(View);
float4 diff = saturate(dot(Normal, LightDir)); //漫反射组件:N.L

//计算自身遮蔽阴影项
//S = saturate(4* N.L)
float shadow = saturate(4*diff);

float3 Reflect = normalize(2 * diff * Normal - LightDir); // R = 2*(N.L)*N -L
float4 specular = pow(saturate(dot(Reflect, ViewDir)), 8); //(R.V)^n; n=8

// I = ambient + shadow * (Dcolor * N.L + (R.V)^n)
return ambient + shadow * ( diffuse * diff + specular);
}

9.3.5凹凸映射(Bump Mapping)p168
凹凸映射可以产生一种表面好似高低起伏的几何体的假象。
凹凸贴图是一种图像文件(如*.dds或*.tga),它与普通的颜色贴图相比,除了保存颜色值外还保存法线,这些法线将取代顶点法线参与光照计算。在最常见的凹凸映射技术中,这些法线都保存在“纹理空间”或“切线空间(tangent space)”中。由于通常是在模型空间或世界空间中处理光向量的,所以为了获得正确的结果,就必须将光向量变换到与凹凸贴图的法线相同的空间中去。这需要借助于一个纹理或切线空间坐标系来完成该变换。
获取这种纹理空间坐标系最简单的方法就是使用Direct3D实用程序库Direct3DX提供的D3DXComputeNormals()与D3DXComputeTangent()函数。

计算纹理空间坐标系:若3个三角形共享一个顶点,先计算面法线/面切线,然后将这3个三角形的向量相加以形成顶点法线/顶点切线。在当前示例程序的顶点shader中,对于纹理空间坐标系中的UVW向量分别采取:使用顶点法线代表W向量;借助于D3DXComputeTangent()函数来计算U向量;使用cross()函数计算W和U向量的叉积来获取V向量。
代码如下:
float4x4 matWorldViewProj;
float4x4 matWorld;
float4 vecLightDir;
float4 vecEye;

struct VS_OUTPUT
{
float4 Pos: POSITION; //位置
float2 Tex: TEXCOORD0; //2D纹理坐标
float3 Light: TEXCOORD1; //光向量
float3 View: TEXCOORD2; //观察者向量
};

VS_OUTPUT VS(float4 Pos: POSITION, float2 Tex : TEXCOORD , float3 Normal : NORMAL, float3 Tangent : TANGENT)
{
VS_OUTPUT Out = (VS_OUTPUT)0;
Out.Pos = mul(Pos, matWorldViewProj); //transform Position

//计算3x3变换矩阵
//从世界空间变换到切线空间
float3x3 worldToTangentSpace;
worldToTangentSpace[0] = mul(Tangent, matWorld);
worldToTangentSpace[1] = mul(cross(Tangent, Normal), matWorld);
worldToTangentSpace[2] = mul(Normal, matWorld);

Out.Tex = Tex.xy;
Out.Light.xyz = mul(worldToTangentSpace, vecLightDir); //L
float3 PosWorld = normalize(mul(Pos, matWorld));
float3 Viewer = vecEye – PosWorld; //V
Out.View = mul(worldToTangentSpace, Viewer);

return Out;
}

float4 PS(float2 Tex : TEXCOORD0, float3 Light : TEXCOORD1, float3 View : TEXCOOD2) : COLOR
{
float4 color = tex2D(ColorMapSampler, Tex); //获取颜色贴图
float3 bumpNormal = 2 * (tex2D(BumpMapSampler, Tex) – 0.5); //凹凸贴图

float3 LightDir = normalize(Light); //L
float3 ViewDir = normalize(View); //V
float4 diff = saturate(dot(bumpNormal, LightDir)); //漫反射组件

//计算自身遮蔽阴影项
//S = saturate(4* N.L)
float shadow = saturate(4*diff);

float3 Reflect = normalize(2 * diff * bumpNormal - LightDir); // R

//利用与color.w比较,限制镜面反射范围的光泽贴图
float4 specular = min(pow(saturate(dot(Reflect, ViewDir)), 3), color.w); //(R.V)^n; n=3

return 0.2 * color + shadow * ( color * diff + specular);
}
在顶点shader中创建了一个由U、V、W(=N)构成的3x3矩阵,并通过它将L和V变换到纹理空间中去。此例像素shader使用了一张颜色贴图和一张凹凸贴图,通过tex2D()来访问它们。tex2D()函数被声明为tex2D(s,t),其中s是采样器对象,t是一个2D纹理坐标。而texCUBE(s,t)用来获取一张立方体贴图。贯穿整个像素shader都是使用凹凸贴图中的法线来取代顶点中的法线,像素shader对从法线贴图中取出的值施以偏移(-0.5)和缩放(*2.0)。施以这样的偏移是必须的,因为法线贴图是以无符号纹理格式存储的,其中每个值都是在[0,1]区间内,而存在这样的限制主要是为了兼容老式硬件。因此,必须将这些法线还原到有符合区间中去。
此例像素shader还限制了镜面反射只能发生在地球模型的水域范围中。这是借助于min()函数和一张光泽贴图(gloss map)来完成的,其中光泽贴图存储在颜色贴图的alpha值中。min()被定义为min(a,b),它的作用是在a和b中选出较小者。
在return语句中,环境光这一项被替换成了一个来自于颜色贴图的颜色值,并减少了该颜色值的强度,这样做是为了保证,即使自阴影项减弱了漫反射和镜面反射成分,至少仍可以看到一个较黑暗的地球。

9.3.6点光(Point Light)p170
与一个定向光源的平行光线不同,点光源的光线是从点所在位置均匀地向各个方向散播。
合理的衰减效果:
attenuation = 1 – d*d //d为距离
代码表示为:
attenuation = 1 – (x * x + y * y + z * z)
attenuation = 1 –((x/r)^2 + (y/r)^2 + (z/r)^2))

相关代码如下:
float4x4 matWorldViewProj;
float4x4 matWorld;
float4 vecLightDir;
float4 vecEye;

struct VS_OUTPUT
{
float4 Pos: POSITION; //位置
float2 Tex: TEXCOORD0; //2D纹理坐标
float3 Light: TEXCOORD1; //光向量
float3 View: TEXCOORD2; //观察者向量
};

VS_OUTPUT VS(float4 Pos: POSITION, float2 Tex : TEXCOORD , float3 Normal : NORMAL, float3 Tangent : TANGENT)
{
VS_OUTPUT Out = (VS_OUTPUT)0;
Out.Pos = mul(Pos, matWorldViewProj); //transform Position

//计算3x3变换矩阵
//从世界空间变换到切线空间
float3x3 worldToTangentSpace;
worldToTangentSpace[0] = mul(Tangent, matWorld);
worldToTangentSpace[1] = mul(cross(Tangent, Normal), matWorld);
worldToTangentSpace[2] = mul(Normal, matWorld);

Out.Tex = Tex.xy;
Out.Light.xyz = mul(worldToTangentSpace, vecLightDir); //L
float3 PosWorld = normalize(mul(Pos, matWorld));
float3 Viewer = vecEye – PosWorld; //V
Out.View = mul(worldToTangentSpace, Viewer);

float LightRange = 1.0

//点光
Out.Att = Light * LightRange;

return Out;
}

float4 PS(float2 Tex : TEXCOORD0, float3 Light : TEXCOORD1, float3 View : TEXCOOD2, float3 Att : TEXCOORD3 ) : COLOR
{
float4 color = tex2D(ColorMapSampler, Tex); //获取颜色贴图
float3 bumpNormal = 2 * (tex2D(BumpMapSampler, Tex) – 0.5); //凹凸贴图

float3 LightDir = normalize(Light); //L
float3 ViewDir = normalize(View); //V
float4 diff = saturate(dot(bumpNormal, LightDir)); //漫反射组件

//计算自身遮蔽阴影项
//S = saturate(4* N.L)
float shadow = saturate(4*diff);

float3 Reflect = normalize(2 * diff * bumpNormal - LightDir); // R

//利用与color.w比较,限制镜面反射范围的光泽贴图
float4 specular = min(pow(saturate(dot(Reflect, ViewDir)), 3), color.w); //(R.V)^n; n=3

//衰减
float4 Attenuation = mul(Att, Att);

//colormap*(self-shadow-term*diffuse+ambient)+self-shadow-term*specular)*(1-Attenuation)
return (color * (shadow * diff + ambient) + shadow * specular) * (1 - Attenuation);
}
降低LightRange参数可增加光照的范围,而增加这个值则会使光照范围缩小。在像素shader中衰减值与自身相乘,因为这样做比使用指数二次方更有效率。像素shader的最后一行将衰减值乘以光照计算的结果,从而便可根据光源的距离来增加或减少光照的强度。


edwardwewe2007-05-16 00:45
第11章 使用文件
定义一种3D场景文件需要解决的基本问题:
1、如何存储构成场景的各个对象(场景数据具有2种关系性:层次关系:比如父子关系、兄弟关系和关联关系:比如网格附着到框架(frame)、材质附着到网格等)
2、如何存储这些对象之间的关系
3、如何保存用户自定义的数据类型
4、向前和向后的兼容性

11.2 X文件格式
用户自定义的数据类型:X文件是由模板(template)驱动的,模板定义了如何存储一个数据对象,这样用户便可自己来定义具体的格式。预定义的模板位于rmxftmpl.h和rmxftmpl.x中,模板的标识符都在rmxfguid.h中,通用文件d3dfile.cpp包含了这2个头文件。
层次关系:模板所允许的数据类型称为“可选成员(optional member)”,这些可选成员作为数据对象的子对象来保存。子对象可以是另一种数据对象、或对一个先前数据对象的引用、或是一个二进制的对象。

11.2.1首部(Header)p199
xof 0303txt 0032
xof 0303txt 0064

11.2.2网格(Mesh)p199
网格(Mesh)文件包含:材质、法线、纹理、变换矩阵。
模板template由4部分组成:第1部分是一个独一无二的名称,可以由数字、字符、下划线构成,但不能以数字开头。第2部分是一个UUID(Universally Unique Identifier:全球惟一标识符)。第3部分由各个数据项构成。第4部分用于控制面板的限制程度,一个模板可以是开放的、闭合的或受限的。开放模板可以包含所有其他数据类型,封闭模板不可以包含任何其他的数据类型,受限模板只可以包含特定的数据类型。

11.2.3网格材质列表(MeshMaterialList)
Material数据对象保存:材质颜色、镜面反射的指数、镜面反射颜色、自发光颜色。

11.3使用X文件p216
HRESULT D3DXLoadMeshFromX
(
LPCTSTR pFilename, //name;X文件路径和名称字符串
DWORD Options, //D3DXMESH_SYSTEMMEM;指定生成的多边形网格属性
LPDERECT3DDEVICE9 pDevice’ //m_pd3dDevice;Direct3D设备指针
LPD3DXBUFFER* ppAdjacency, //NULL;存储邻近多边形信息的LPD3DXBUFFER地址
LPD3DXBUFFER* ppMaterials, //NULL;存储模型材质的LPD3DXBUFFER地址
LPD3DXBUFFER* ppEffectInstances, //NULL;存储模型特殊效果实例的LPD3DXBUFFER地址
DWORD* pNumMaterials, //NULL;存储模型材质数目的内存地址
LPD3DXMESH* ppMesh //&pMeshSystem;存储生成的多边形网格的内存地址
);
第1个参数pFilename是一个指向文件路径的指针。第2个参数Options用于指定顶点缓冲器或索引缓冲器的内存池类型和一些其他标志。第3个参数应传入一个指向设备的指针。由于不需要任何领接数据,所以第4个参数ppAdjacency为NULL。同样也不需要材质数据和效果实例的数据,所以接下来的3个参数(ppMaterials、ppEffectInstances、pNumMaterials)也为NULL。最后一个参数ppMesh返回一个指向网格的指针。


edwardwewe2007-05-17 14:37
第07章 纹理映射基础P109
SetSamplerState()用于设置采样器状态。其中设置镶嵌器单元(tessellator unit)用于处理高阶(higher-order)表面;Bezier patches、N-patches等状态,用于采样置换贴图(displacement map)。p111

SetSamplerState()可用于指定纹理的过滤方式、贴夹方式(tiling/clamping)、MIPLOD方式等。有2项与固定功能的多纹理单元相关的设备能力参数:MaxTextureBlendStages(表示允许应用程序使用的纹理混合阶段(texture-blending stage)的最大数量,MaxTextureBlendStages表示驱动程序认为存在几个D3DTSS_和D3DSAMP_混合阶段)和MaxSimultaneousTextures(纹理的最大数量,表示可以在同一时刻使用几张不同的纹理)。使用额外的混合阶段而不使用额外的纹理是允许的。

HRESULT SetSamplerState
(
DWORD Sample, //纹理阶段号,对应于在SetTexture()方法中所使用的数字
D3DSAMPLERSTATETYPE Type, //以D3DSAMP_为前缀的采样器状态类型之一
DWORD Value //采样器状态的值
);

typedef enum D3DSAMPLERSTATETYPE
{
  D3DSAMP_ADDRESSU = 1,
  D3DSAMP_ADDRESSV = 2,
  D3DSAMP_ADDRESSW = 3,
  D3DSAMP_BORDERCOLOR = 4,
  D3DSAMP_MAGFILTER = 5, //放大
  D3DSAMP_MINFILTER = 6, //缩小
  D3DSAMP_MIPFILTER = 7,
  D3DSAMP_MIPMAPLODBIAS = 8,
  D3DSAMP_MAXMIPLEVEL = 9,
  D3DSAMP_MAXANISOTROPY = 10,
  D3DSAMP_SRGBTEXTURE = 11,
  D3DSAMP_ELEMENTINDEX = 12,
  D3DSAMP_DMAPOFFSET = 13,
  D3DSAMP_FORCE_DWORD = 0x7fffffff,
} D3DSAMPLERSTATETYPE, *LPD3DSAMPLERSTATETYPE;

SetTextureStageState
(
DWORD Stage, //纹理阶段号
D3DTEXTURESTATETYPE Type, //以D3DTSS_为前缀的阶段状态类型之一
DWORD Value //阶段状态的值
);

SetTextureStageState()方法处理以下内容很有用:
1、不同的纹理坐标
2、颜色操作
3、alpha操作
4、凹凸映射(bump mapping)/环境映射操作(environment mapping)
这些操作之对固定功能的多纹理单元有效,不能将它们与像素shader连用。

Direct3D采用了一种一般化的编址方案,其中纹理地址由[0.0, 1.0]区间内的坐标值组成。

最多使用8组纹理坐标(D3DFVF_TEX1~ D3DFVF_TEX8)。

在0.0~1.0范围以外的值将如何改变纹理的外观取决于所选择的纹理寻址模式(texture-addressing mode):包装(wrap)、镜像(mirror)、夹持(clamp)、边框颜色(border color)、一次镜像(mirroronce)。

包装纹理寻址模式(Warp texture-addressing mode)在纹理坐标的每个整数相接处重复纹理,这是Direct3D所使用的默认模式。
m_pd3dDevice->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP);
m_pd3dDevice->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP);

镜像纹理寻址模式(Mirror texture-addressing mode)使Direct3D在每个整数边界上对纹理作一次镜像,即图素在0.0~0.1范围之外的区域作了翻转。这种方式中,纹理是沿着每根轴作镜像的。
m_pd3dDevice->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_MIRROR);
m_pd3dDevice->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_ MIRROR);

夹持纹理寻址模式(Clamp texture-addressing mode)只将纹理应用到多边形上一次,然后对于超出部分涂上纹理边缘像素的颜色。
m_pd3dDevice->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
m_pd3dDevice->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_ CLAMP);

当设置了边框颜色纹理寻址模式(Border texture-addressing mode),则纹理周围会出现一个你所选定颜色的边框。
m_pd3dDevice->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_BORDER);
m_pd3dDevice->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_ BORDER);
m_pd3dDevice->SetSamplerState(0, D3DSAMP_BORDERCOLOR, 0x00aaaaaa);

在一次镜像纹理寻址模式(Mirroronce texture-addressing mode)中,纹理在-1.0~1.0范围内作镜像,而在该范围之外作夹持。
m_pd3dDevice->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_MIRRORONCE);
m_pd3dDevice->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_ MIRRORONCE);

包装纹理寻址模式用于决定如何映射[0.0, 1.0]边界之外的图素坐标,而纹理包装用于决定如何在纹理坐标之间作插值计算。

a和b之间的最短线路一般要穿越纹理的中间部分。如果启用了v方向上的纹理包装,则光栅器将在v方向的最短路径上进行插值处理。如果启用了u方向上的纹理包装,则光栅器将在u方向的最短路径上进行插值处理

要为流水线中的每个纹理阶段启用纹理包装,必须通过D3DRS_WRAPx来设置渲染状态,其中x为要设置的纹理阶段号(从0到7)。要启动某个方向上的纹理包装,则需包含表示u方向的D3DWRAP_U或表示v方向的D3DWRAP_V。

在某个方向上启动纹理包装时,Direct3D将纹理视作围绕着一个圆柱体而包装起来的。

为了将一张纹理包裹到一个球体上,就需要同时对2个方向上使用包装。可通过调用以下函数来启动第一个纹理阶段在u和v方向上的纹理包装:
m_pd3dDevice->SetSamplerState(D3DRS_WRAP0, D3DWRAP_U, D3DWRAP_V);
在启动纹理包装后将使[0.0, 1.0]区间之外的纹理坐标成为无效值,所以纹理包装不能与各种纹理寻址模式共用。

7.5 纹理过滤和抗锯齿(Anti-Aliasing)
纹理过滤方法:
最近点采样
线性纹理过滤
各向异性(anisotropic)纹理过滤。

过滤是指通过给定的uv坐标从纹理贴图中获取图素的一种方法。

放大(magnification)会导致“阶梯效果,俗称锯齿”和缩小(minification)会导致“像素抖动”,看上去会让人晕眩。

减少放大或缩小所造成的视觉错误的4种过滤:点采样、双线性过滤(在Direct3D中称为“线性过滤”)、三线性过滤(在Direct3D中称为“线性过滤+mipmap”)、各向异性过滤。

mipmap是由一系列纹理组成,其中每张纹理是一张分辨率逐步降低的图像。mipmap序列中每一级的高度和宽度都是前一级高度和宽度的一半,这些层级既可以是正方形也可以是长方形。

如果提供给CreateTexture()方法正确的参数,就可以自动地产生和使用mipmap了。CreateTexture()使用到了D3DXCreateTextureFromEx()方法。

使用mipmap可以确保无论是靠近还是远离纹理,纹理都可以保持它们原有的真实性和质量,从而减少了放大和缩小所带来的负面效果。

1、将一张256*256的纹理过滤到一个2*2的多边形上,需要较长时间
2、mipmap是一种可有效降低纹理内存流量的方法。

Direct3D会自动挑选出合适的mip层级,即选出一个图素与像素的比值最接近于1的mip层级。

最近点采样是一种最简单的过滤器。它将纹理坐标对齐到最接近的整数,再将那个位于整数坐标上的纹理像素作为最终的颜色。对于处理那些与屏幕上多边形图像大小差不多的纹理来说是一种快速、高效的方法。Direct3D会将区间[0.0, 1.0]内的浮点纹理坐标映射到区间为[-0.5, n-0.5]的整数纹理空间中,其中n是一张特定尺寸的纹理上图素的个数。由此得到的结果再四舍五入为一个最接近的整数,不过这可能会在处理图素边界时发生一些错误。

双线性纹理过滤:计算相对于采样点距离最近的4个图素的平均值。双线性过滤首先计算出一个图素地址,该地址通常不是一个整数,接着求得该地址最接近的整数地址,并找到这个整数地址所在的纹理像素,然后计算出最接近于这个采样点的上下左右图素的加权平均值。
可通过以下代码实现:
m_pd3dDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
m_pd3dDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);

也可以将D3DSAMP_MIPFILTER,作为SetSamplerState()的第二个参数,表示在两个mipmap级之间也使用纹理过滤器,默认值为D3DTEXF_NONE。如果给出了D3DTEXF_LINEAR,即表示使用三线性纹理过滤,或称“mipmap过滤”。
m_pd3dDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
m_pd3dDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
m_pd3dDevice->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);

三线性纹理过滤:对于每个像素,三线性过滤会选择两张最接近的mipmap,将它们双线性过滤为两张理想大小的mipmap,然后根据理想的mip级组合这两张过滤后的mipmap中的对应像素。例如,理想的mip级是3.4,那么组合的过程就是将第3张mipmap中的0.6倍(1-0.4)的对应像素加上第4张mipmap中的0.4倍的对应像素。

双线性过滤和三线性过滤的一个缺点:它们都使用正方形采样区域来采样图素。如果纹理是侧着朝向你的话,则会发生一种称为:各向异性的失真效果。

各向异性过滤:Direct3D根据一个屏幕像素的伸张度(elongation:长度除以宽度)来测量一个纹理像素的各向异性,该屏幕像素将被反向映射回纹理空间中。当一个屏幕像素在反向映射后被伸张了,则各向异性过滤将采样更多的图素以减少由标准线性过滤所造成的模糊。
m_pd3dDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_ANISOTROPIC);
m_pd3dDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_ ANISOTROPIC);
m_pd3dDevice->SetSamplerState(0, D3DSAMP_MAXANISOTROPY, 16);

FSAA(Full Screen Anti-Aliasing),全屏抗锯齿,通过调整图像中每条斜线周围的亮度来隐藏其上的锯齿效果,它沿着这些边缘产生局部模糊的效果,并淡化原本阶梯层级似的外观。全屏多采样对每个像素进行多次采样,这些不同的样本被混合后输出到屏幕,这便为抗锯齿或其他效果提供了改进的图像质量。

多屏蔽采样(maskable multisampling)是在光栅化阶段完成的,它仅影响三角形和三角形组,不影响直线。它能提高多边形边缘的分辨率,以及深度测试和模板测试的分辨率。
D3DMULTISAMPLE_NONMASKABLE

多采样可以实现的特殊效果:
指定只有某些样本才受特定渲染通道的影响、允许运动模糊的模拟(simulation of motion blur)、景深聚焦效果(depth-of-field focus effect)、反射模糊(reflection blur)等。

alpha混合(blending)
使用alpha混合可将图元颜色与先前存储在帧缓冲器中的像素的颜色组合起来(于帧缓冲器相混合)。

alpha混合可用于:模拟半透明的物体、组合两幅图像、实现一些特殊效果(如力场、火焰、离子束、光照映射)。

alpha混合最大的优点就是允许多次渲染(multipass rendering;意味着以不同技术多次对同一几何体进行渲染),并允许将这些渲染通道混合在一起。

alpha混合是像素流水线的最后一个步骤。执行alpha混合就是组合2个颜色:源颜色(source color)和目的颜色(destination color)。源颜色是准备绘制到帧缓冲器中的像素,目的颜色是已存在于帧缓冲器中的像素。
FinalColor = SourcePixelColor * SourceBlendFactor + DestPixelColor * DestBlendFactor
SourcePixelColor来自于某个将被渲染到当前像素位置上的图元;DestPixelColor则来自于帧缓冲器中的当前像素位置。通过改变SourceBlendFactor(D3DRS_SRCBLEND)和DestBlendFactor(D3DRS_DESTBLEND)标志可以产生各种预期的效果。FinalColor是混合后最终写入帧缓冲器的颜色。

如果想使alpha混合不起作用,只是绘制像素而不考虑帧缓冲器中已存在的内容,则可以这样做:FinalColor = SourcePixelColor * 1.0 + DestPixelColor * 0.0
可以简化为:FinalColor = SourcePixelColor
相关源代码如下:
m_pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
m_pd3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE);
m_pd3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ZERO);
如果想要将源颜色和目的颜色相乘,则需把SourceBlendFactor设为0.0、DestBlendFactor设为SourcePixelColor。
FinalColor = SourcePixelColor * 0.0 + DestPixelColor * SourcePixelColor
相关源代码如下:
m_pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
m_pd3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ZERO);
m_pd3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_SRCCOLOR);

产生透明的立方体、彩色玻璃般效果的方程式:
FinalColor = SourcePixelColor * SourceColor + DestPixelColor * InverseSourceColor
其中InverseSourceColor为:
(1-SourcePixelColorred, 1-SourcePixelColorgreen, 1-SourcePixelColorblue, 1-SourcePixelColoralpha)

m_pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
m_pd3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ SRCCOLOR);
m_pd3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCCOLOR);

alpha混合要求不少额外的数学运算和存储器访问,因此有必要在用到alpha混合时才打开它,不用时就关闭它。
m_pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);

显卡是否支持alpha混合,可以通过检查特定的能力标志为来确定:
if(0 == (pCaps->DestBlendCaps & D3DBLENDCAPS_INVSRCCOLOR)) &&
(0 == (pCaps->SrcBlendCaps & D3DBLENDCAPS_SRCCOLOR)) &&
……..
D3DBLENDCAPS_*标志用于检查显卡是否支持各种D3DBLEND_*标志。D3DCAPS9结构成员DestBlendCaps或SrcBlendCaps可用于检查设备是否具有支持各项D3DRS_DESTBLEND/D3DRS_SRCBLEND参数的能力。


edwardwewe2007-05-18 17:20
第08章 使用多重纹理
***p149-151
哪些应用到多重纹理?
黑暗映射、发光映射、细节映射。

多次渲染(Mulitpass Rendering)
多重纹理(mulitexturing):在一次渲染中访问到2张或更多纹理。

颜色操作时通过D3DTSS_COLOROP来设置的。大致代码如下:
m_pd3dDevice->SetRenderState(0, D3DTSS_COLORARG1, arg);
m_pd3dDevice->SetRenderState(0, D3DTSS_COLORARG2, arg);
m_pd3dDevice->SetRenderState(0, D3DTSS_COLOROP, op);

其中可用的op参数,位于D3DTEXTUREOP枚举中:
typedef enum D3DTEXTUREOP
{
  D3DTOP_DISABLE = 1,
  D3DTOP_SELECTARG1 = 2,
  D3DTOP_SELECTARG2 = 3,
  D3DTOP_MODULATE = 4,
  D3DTOP_MODULATE2X = 5,
  D3DTOP_MODULATE4X = 6,
  D3DTOP_ADD = 7,
  D3DTOP_ADDSIGNED = 8,
  D3DTOP_ADDSIGNED2X = 9,
  D3DTOP_SUBTRACT = 10,
  D3DTOP_ADDSMOOTH = 11,
  D3DTOP_BLENDDIFFUSEALPHA = 12,
  D3DTOP_BLENDTEXTUREALPHA = 13,
  D3DTOP_BLENDFACTORALPHA = 14,
  D3DTOP_BLENDTEXTUREALPHAPM = 15,
  D3DTOP_BLENDCURRENTALPHA = 16,
  D3DTOP_PREMODULATE = 17,
  D3DTOP_MODULATEALPHA_ADDCOLOR = 18,
  D3DTOP_MODULATECOLOR_ADDALPHA = 19,
  D3DTOP_MODULATEINVALPHA_ADDCOLOR = 20,
  D3DTOP_MODULATEINVCOLOR_ADDALPHA = 21,
  D3DTOP_BUMPENVMAP = 22,
  D3DTOP_BUMPENVMAPLUMINANCE = 23,
  D3DTOP_DOTPRODUCT3 = 24,
  D3DTOP_MULTIPLYADD = 25,
  D3DTOP_LERP = 26,
  D3DTOP_FORCE_DWORD = 0x7fffffff,
} D3DTEXTUREOP, *LPD3DTEXTUREOP;

D3DTOP_DISABLE:无效
D3DTOP_SELECTARG1:第1个被选择的参数

D3DTOP_SELECTARG2:第2个被选择的参数


D3DTOP_MODULATE
将参数的每个成分相乘

D3DTOP_MODULATE2X
将参数的每个成分相乘,并将乘积左移1位(等同于乘以2的效果)以使其变得更亮

D3DTOP_MODULATE4X
将参数的每个成分相乘,并将乘积左移2位(等同于乘以4的效果)以使其变得更亮

D3DTOP_ADD
Add the components of the arguments.

D3DTOP_ADDSIGNED
Add the components of the arguments with a - 0.5 bias, making the effective range of values from - 0.5 through 0.5.

D3DTOP_ADDSIGNED2X
Add the components of the arguments with a - 0.5 bias, and shift the products to the left 1 bit.

D3DTOP_SUBTRACT
Subtract the components of the second argument from those of the first argument.

D3DTOP_ADDSMOOTH
Add the first and second arguments; then subtract their product from the sum.

D3DTOP_BLENDDIFFUSEALPHA
使用由每个顶点计算而得的alpha插值来线性混合本纹理阶段。

D3DTOP_BLENDTEXTUREALPHA
使用本阶段纹理中的alpha来线性混合本纹理阶段。

D3DTOP_BLENDFACTORALPHA
使用通过D3DRS_TEXTUREFACTOR 渲染状态设置的alpha标量来线性混合本纹理阶段。

D3DTOP_BLENDTEXTUREALPHAPM
使用一个预乘alpha来线性混合本纹理阶段。

D3DTOP_BLENDCURRENTALPHA
使用来自上一纹理阶段的alpha来线性混合本纹理阶段。

D3DTOP_PREMODULATE
D3DTOP_PREMODULATE is set in stage n. The output of stage n is arg1. Additionally, if there is a texture in stage n + 1, any D3DTA_CURRENT in stage n + 1 is premultiplied by texture in stage n + 1.
D3DTOP_MODULATEALPHA_ADDCOLOR
D3DTSS_COLOROP 参数,使用第1个参数alpha来调制第2个参数的颜色,然后将结果加上第1个参数。Result = Arg1RGB+ Arg1Alpha* Arg2RGB

D3DTOP_MODULATECOLOR_ADDALPHA
Modulate the arguments; then add the alpha of the first argument. This operation is supported only for color operations (D3DTSS_COLOROP).

D3DTOP_MODULATEINVALPHA_ADDCOLOR
Similar to D3DTOP_MODULATEALPHA_ADDCOLOR, but use the inverse of the alpha of the first argument. This operation is supported only for color operations (D3DTSS_COLOROP).

D3DTOP_MODULATEINVCOLOR_ADDALPHA
Similar to D3DTOP_MODULATECOLOR_ADDALPHA, but use the inverse of the color of the first argument. This operation is supported only for color operations (D3DTSS_COLOROP).

D3DTOP_BUMPENVMAP
Perform per-pixel bump mapping, using the environment map in the next texture stage, without luminance. This operation is supported only for color operations (D3DTSS_COLOROP).
D3DTOP_BUMPENVMAPLUMINANCE
Perform per-pixel bump mapping, using the environment map in the next texture stage, with luminance. This operation is supported only for color operations (D3DTSS_COLOROP).
D3DTOP_DOTPRODUCT3
Modulate the components of each argument as signed components, add their products; then replicate the sum to all color channels, including alpha. This operation is supported for color and alpha operations.

In DirectX 6 and DirectX 7, multitexture operations the above inputs are all shifted down by half (y = x - 0.5) before use to simulate signed data, and the scalar result is automatically clamped to positive values and replicated to all three output channels. Also, note that as a color operation this does not updated the alpha it just updates the RGB components.
However, in DirectX 8.1 shaders you can specify that the output be routed to the .rgb or the .a components or both (the default). You can also specify a separate scalar operation on the alpha channel.
D3DTOP_MULTIPLYADD
完成一个累积乘法(multiply-accumulate)操作。它取2个参数相乘,然后将乘积加上另一个输入/源参数,最后将和作为结果。 Result = Arg0+ Arg1* Arg2
SRGBA = Arg1 + Arg2 * Arg3
D3DTOP_LERP
根据第1个源参数指定的比例计算第2、3个源参书的线性插值。
Result = (Arg0)*Arg1 +(1-Arg0)*Arg2
SRGBA = (Arg1) * Arg2 + (1- Arg1) * Arg3.
D3DTOP_FORCE_DWORD
Forces this enumeration to compile to 32 bits in size. Without this value, some compilers would allow this enumeration to compile to a size other than 32 bits. This value is not used.

每种颜色操作可接受最多3个颜色参数:D3DTSS_COLORARG0、D3DTSS_COLORARG1、D3DTSS_COLORARG2,可以将这些颜色参数设为以下值:
D3DTA_DIFFUSE,使用漫反射颜色,这种颜色是在使用Gouraud着色时,对顶点成分中的漫反射颜色作插值计算而得的;
D3DTA_SPECULAR,使用镜面反射颜色,这种颜色是在使用Gouraud着色时,对顶点成分中的镜面反射颜色作插值计算而得的;
D3DTA_TFACTOR,使用纹理系数,可事先通过渲染状态D3DRS_TEXTUREFACTOR调用SetRenderState()来设置的纹理系数;
D3DTA_TEXTURE,使用本文里阶段的纹理颜色;
D3DTA_CURRENT,使用上一个混合阶段的结果;
D3DTA_TEMP,使用一个用于读/写的临时寄存器颜色;
D3DTA_ALPHAREPLICATE,复制alpha信息;
D3DTA_COMPLEMENT,对颜色参数取反。

8.2.1 黑暗映射(Darkl Mapping)p134
逐顶点(per-vertex)计算方式的缺点:参与计算和实际计算的是三角形的每个顶点,而不是对每个像素进行的渲染。这导致如果顶点未受到光照,或受到很少光照(比如光源在三角形中央或靠近中央时,整个三角形只有非常少的光亮,并且它的中央会有一个亮点),则无法计算出正确的三角形面的颜色。

将2张纹理的颜色相乘称为:“光照映射(light mapping)”,由于这种技术经常被用于使一张纹理变暗,所以也称“黑暗映射”。
实例代码:
m_pd3dDevice->SetTexture(0, m_pWallTexture);
m_pd3dDevice->SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0);
m_pd3dDevice->SetTextureStageState (0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
//将D3DTA_TEXTURE作为D3DTSS_COLORARG1的值
m_pd3dDevice->SetTextureStageState (0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);

m_pd3dDevice->SetTexture(1, m_pEnvTexture);
m_pd3dDevice->SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 0);
m_pd3dDevice->SetTextureStageState (1, D3DTSS_COLORARG1, D3DTA_TEXTURE);
m_pd3dDevice->SetTextureStageState (1, D3DTSS_ COLORARG2, D3DTA_CURRENT);
m_pd3dDevice->SetTextureStageState (1, D3DTSS_COLOROP, D3DTOP_MODULATE);
第1个阶段(阶段0)的默认操作是D3DTOP_MODULATE;其他纹理阶段的默认操作是D3DTOP_DISABLE。

下面的代码和上面的效果相同,通过多次渲染技术:
//Set texture for the cube
m_pd3dDevice->SetTextureStageState (0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
m_pd3dDevice->SetTextureStageState (0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
m_pd3dDevice->SetTextureStageState (D3DRS_ALPHABLENDENABLE, FALSE);
m_pd3dDevice->SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0);
m_pd3dDevice->SetTexture(0, m_pWallTexture);
//draw polygon
//Set darkmap
m_pd3dDevice->SetTextureStageState (D3DRS_ALPHABLENDENABLE, TRUE);
m_pd3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ ZERO);
m_pd3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_SRCCOLOR);
m_pd3dDevice->SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0);
m_pd3dDevice->SetTexture(1, m_pEnvTexture);
//draw polygon

其中alpha混合公式:
FinalColor = SourcePixelColor * 0.0 + DestPixelColor * SourcePixelColor

8.2.3 混合纹理和材质漫反射颜色p138
基础贴图+漫反射插值 实例代码:
m_pd3dDevice->SetTexture(0, m_pWallTexture);
m_pd3dDevice->SetTextureStageState (0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
m_pd3dDevice->SetTextureStageState (0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
m_pd3dDevice->SetTextureStageState (0, D3DTSS_COLOROP, D3DTOP_ADD);

在固定功能的流水线中,有3种获取漫反射颜色的途径,即从以下途径获取:
材质:D3DMCS_MATERIAL
漫反射顶点颜色:D3DMCS_COLOR1
镜面反射顶点颜色:D3DMCS_COLOR2

从何处获取漫反射颜色取决于SetRenderState()函数中的D3DRS_DIFFUSEMATERIALSOURCE参数:
SetRenderState(D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_MATERIAL)
默认颜色源为漫反射顶点颜色:D3DMCS_COLOR1。如果还未选择颜色,则默认颜色为不透明的白色。

8.2.4 混合黑暗贴图和材质漫反射颜色p140
(基础贴图*光的颜色)*黑暗贴图 实例代码:
m_pd3dDevice->SetTexture(0, m_pWallTexture);
m_pd3dDevice->SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0);
m_pd3dDevice->SetTextureStageState (0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
m_pd3dDevice->SetTextureStageState (0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
m_pd3dDevice->SetTextureStageState (0, D3DTSS_COLOROP, D3DTOP_MODULATE); //相乘
如果没有光照,则纹理颜色会乘以0,因此便看不到墙壁纹理了,只能看到第二张纹理。
m_pd3dDevice->SetTexture(1, m_pEnvTexture);
m_pd3dDevice->SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 0);
m_pd3dDevice->SetTextureStageState (1, D3DTSS_COLORARG1, D3DTA_TEXTURE);
m_pd3dDevice->SetTextureStageState (1, D3DTSS_ COLORARG2, D3DTA_CURRENT);
m_pd3dDevice->SetTextureStageState (1, D3DTSS_COLOROP, D3DTOP_MODULATE);
如果第1张纹理是不可见的,则第1阶段的值0与第2阶段的颜色相乘,还是0(黑色)。当你处于一个真正漆黑的地方时,由于光线微弱,你是无法看到周围物体的颜色的,因此以上代码对于模拟这种情况很有用。

使用多次渲染技来实现相同的效果:
//Set texture for the cube
m_pd3dDevice->SetTextureStageState (0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
m_pd3dDevice->SetTextureStageState (0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
m_pd3dDevice->SetTextureStageState (0, D3DTSS_COLOROP, D3DTOP_MODULATE);
m_pd3dDevice->SetTextureStageState (D3DRS_ALPHABLENDENABLE, FALSE);
m_pd3dDevice->SetTexture(0, m_pWallTexture);
//draw polygon
//Set darkmap
m_pd3dDevice->SetTextureStageState (D3DRS_ALPHABLENDENABLE, TRUE);
m_pd3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ ZERO);
m_pd3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_SRCCOLOR);
m_pd3dDevice->SetTexture(1, m_pEnvTexture);
//draw polygon

其中alpha混合公式:
FinalColor = SourcePixelColor * 0.0 + DestPixelColor * SourcePixelColor

8.2.5 发光映射(Glow Mapping)p140
发光映射与黑暗映射正好相反。它对于创建具有独立于基础贴图的发光部分的物体很有用,比如发光二极管、按钮、建筑物内的灯光、太空船上的灯光等。发光映射仅影响基础贴图上的发光区域,而不应影响到其余部分。因此需要对发光效果做加法。
m_pd3dDevice->SetTexture(0, m_pWallTexture);
m_pd3dDevice->SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0);
m_pd3dDevice->SetTextureStageState (0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
m_pd3dDevice->SetTextureStageState (0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
//Set Glow Mapping
m_pd3dDevice->SetTexture(1, m_pEnvTexture);
m_pd3dDevice->SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 0);
m_pd3dDevice->SetTextureStageState (1, D3DTSS_COLORARG1, D3DTA_TEXTURE);
m_pd3dDevice->SetTextureStageState (1, D3DTSS_ COLORARG2, D3DTOP_SELECTARG1);
m_pd3dDevice->SetTextureStageState (1, D3DTSS_COLOROP, D3DTOP_ADD);

使用多次渲染技来实现相同的效果:
//Set texture for the cube
m_pd3dDevice->SetTextureStageState (0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
m_pd3dDevice->SetTextureStageState (0, D3DTSS_COLORARG2, D3DTA_ SELECTARG1);
m_pd3dDevice->SetTextureStageState (D3DRS_ALPHABLENDENABLE, FALSE);
m_pd3dDevice->SetTexture(0, m_pWallTexture);
//draw polygon
//Set darkmap
m_pd3dDevice->SetTextureStageState (D3DRS_ALPHABLENDENABLE, TRUE);
m_pd3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ ONE);
m_pd3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE);
m_pd3dDevice->SetTexture(1, m_pEnvTexture);
//draw polygon

其中alpha混合公式:
FinalColor = SourcePixelColor * 1.0 + DestPixelColor * 1.0

8.2.6 细节映射(Detail Mapping)p142
  基础贴图(或是第一张纹理)的颜色未经修改便作为第二个纹理阶段中的第二个参数。然后通过D3DTOP_ADDSIGNED将灰色的细节纹理与基础贴图相加。这个操作本质上是做一个加法,只是使用了有符号的颜色值(-127~128)来代替平时使用的无符号值(0~255)。在ADDSIGNED两张纹理是,-127为黑色,128为白色。它将参数的每个成分相加后,再加上-0.5的偏移量,从而使有效值域变为-0.5~0.5。
  在细节贴图中较亮的灰色图素将使基础贴图变得更亮,而较暗的灰色图素则会使基础贴图变暗。细节贴图将会使箱面呈现出一张粗糙的表面,从而使之看上去更为真实。
通过以下代码来使用细节映射:
m_pd3dDevice->SetTexture(0, m_pWallTexture);
m_pd3dDevice->SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0);
m_pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
m_pd3dDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
//Set Detail Map
m_pd3dDevice->SetTexture(1, m_pDetailTexture);
m_pd3dDevice->SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 0);
m_pd3dDevice->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE);
m_pd3dDevice->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT);
m_pd3dDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_ADDSIGNED);

通过多次渲染也可以实现同样的效果,如下所示:
m_pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
m_pd3dDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
m_pd3dDevice->SetTextureStageState(D3DRS_ALPHABLENDENABLE, FALSE);
m_pd3dDevice->SetTexture(0, m_pWallTexture);
//draw polygon
//set darkmap
m_pd3dDevice->SetTextureStageState(D3DRS_ALPHABLENDENABLE, TRUE);
m_pd3dDevice->SetTextureStageState(D3DRS_SRCBLEND, D3DBLEND_DESTCOLOR);
m_pd3dDevice->SetTextureStageState(D3DRS_DESTBLEND, D3DBLEND_SRCCOLOR);
m_pd3dDevice->SetTexture(1, m_pDetailTexture);
//draw polygon

其中alpha混合公式如下所示:
FinalColor = SourcePixelColor * DestPixelColor + DestPixelColor * SourcePixelColor

纹理阶段操作会导致对所有颜色操作及以下内容作线性混合:
1、迭代的漫反射alpha
2、当前迭代的纹理alpha
3、alpha标量(用D3DRS_TFACTOR设置)
4、预乘(pre-multiplied)的纹理alpha
5、前一阶段计算出的alpha

alpha操作时通过D3DTSS_ALPHAOP来设置的。大致代码如下:
m_pd3dDevice->SetRenderState(1, D3DTSS_ALPHAARG1, arg);
m_pd3dDevice->SetRenderState(1, D3DTSS_ALPHAARG2, arg);
m_pd3dDevice->SetRenderState(1, D3DTSS_ALPHAOP, op);

其中可用的op参数:
D3DTOP_DISABLE:无效
D3DTOP_SELECTARG1:第1个被选择的参数

D3DTOP_SELECTARG2:第2个被选择的参数


D3DTOP_MODULATE
将参数的每个成分相乘

D3DTOP_MODULATE2X
将参数的每个成分相乘,并将乘积左移1位(等同于乘以2的效果)以使其变得更亮

D3DTOP_MODULATE4X
将参数的每个成分相乘,并将乘积左移2位(等同于乘以4的效果)以使其变得更亮

D3DTOP_ADD
Add the components of the arguments.

D3DTOP_ADDSIGNED
Add the components of the arguments with a - 0.5 bias, making the effective range of values from - 0.5 through 0.5.

D3DTOP_ADDSIGNED2X
Add the components of the arguments with a - 0.5 bias, and shift the products to the left 1 bit.

D3DTOP_SUBTRACT
Subtract the components of the second argument from those of the first argument.

D3DTOP_MULTIPLYADD
完成一个累积乘法(multiply-accumulate)操作。它取2个参数相乘,然后将乘积加上另一个输入/源参数,最后将和作为结果。 Result = Arg0+ Arg1* Arg2
SRGBA = Arg1 + Arg2 * Arg3
D3DTOP_LERP
根据第1个源参数指定的比例计算第2、3个源参书的线性插值。
Result = (Arg0)*Arg1 +(1-Arg0)*Arg2
SRGBA = (Arg1) * Arg2 + (1- Arg1) * Arg3.
每种alpha操作可接受最多3个颜色参数:D3DTSS_ALPHAARG0、D3DTSS_ALPHAARG1、D3DTSS_ALPHAARG2,可以将这些颜色参数设为以下值:
D3DTA_DIFFUSE,使用漫反射颜色,这种颜色是在使用Gouraud着色时,对顶点成分中的漫反射颜色作插值计算而得的;
D3DTA_SPECULAR,使用镜面反射颜色,这种颜色是在使用Gouraud着色时,对顶点成分中的镜面反射颜色作插值计算而得的;
D3DTA_TFACTOR,使用纹理系数,可事先通过渲染状态D3DRS_TEXTUREFACTOR调用SetRenderState()来设置的纹理系数;
D3DTA_TEXTURE,使用本文里阶段的纹理颜色;
D3DTA_CURRENT,使用上一个混合阶段的结果;
D3DTA_TEMP,使用一个用于读/写的临时寄存器颜色;
D3DTA_COMPLEMENT,对alpha参数取反。

alpha值可以存储在顶点、材质、纹理贴图中,alpha值0表示完全透明,1表示不透明,其余0~1之间的值表示不同程度的半透明。
在固定功能的流水线中,有3种获取alpha的途径:
从纹理中获取alpha值: D3DTA_TEXTURE
从材质颜色中获取alpha值:D3DMCS_MATERIAL、D3DTA_DIFFUSE
从漫反射顶点颜色中获取alpha值:D3DMCS_COLOR1、D3DTA_DIFFUSE

从何处获取alpha值取决于SetRenderState()函数中的D3DRS_DIFFUSEMATERIALSOURCE参数:

如果要从一张纹理中获取alpha值,应将D3DTA_TEXTURE作为作为alpha参数:
m_pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);

使用材质颜色中的alpha值,应将D3DTA_DIFFUSE作为alpha参数,并确保已设置了D3DRS_DIFFUSEMATERIALSOURCE= D3DMCS_MATERIAL:
m_pd3dDevice->SetRenderState(D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_MATERIAL);
m_pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE);

如果要使用来自于顶点颜色中的alpha值,应将D3DTA_DIFFUSE作为alpha参数,并确保D3DRS_DIFFUSEMATERIALSOURCE= D3DMCS_COLOR1(这是默认值):
m_pd3dDevice->SetRenderState(D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_COLOR1);
m_pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE);

如果未用SetRenderState()设置D3DMCS_x参数,则从默认来源获取漫反射颜色。

alpha调制
将绿色的环境光与材质alpha相调制,可以模拟夜视镜观察的效果:
D3DCOLOR d3dclrAmbientLightColor = D3DRGBA(0.0f, 1.0f, 0.0f, 1.0f);
m_pd3dDevice->SetRenderState(D3DRS_AMBIENT, d3dclrAmbientLightColor);

可以使用以下代码来调制环境光与纹理颜色:
m_pd3dDevice->SetTexture (0, D3DTextr_GetSurface(“Wall.bmp”));
// 基础贴图*漫反射颜色
m_pd3dDevice->SetTextureStageState (0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
m_pd3dDevice->SetTextureStageState (0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
m_pd3dDevice->SetTextureStageState (0, D3DTSS_COLOROP, D3DTOP_MODULATE);
// alpha值*漫反射颜色
m_pd3dDevice->SetTextureStageState (0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
m_pd3dDevice->SetTextureStageState (0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);
m_pd3dDevice->SetTextureStageState (0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
这个过程可描述为:基础贴图∞漫反射插值∞alpha∞漫反射插值

//用ConfirmDevice()检查3D硬件对多纹理的支持情况:
if(pCaps->MaxSimultaneousTextures<2)
return E_FAIL;

//检查各项显卡能力:
if(0 == (pCaps->DestBlendCaps & D3DBLENDCAPS_INVSRCCOLOR)) && //alpha blending
(0 == (pCaps->SrcBlendCaps & D3DBLENDCAPS_SRCCOLOR)) &&
(0 == (pCaps->TextureOpCaps & D3DTEXOPCAPS_ADDSIGNED)) && //texture blending
(0 == (pCaps->TextureOpCaps & D3DTEXOPCAPS_MODULATE)) &&
(0 == (pCaps->TextureOpCaps & D3DTEXOPCAPS_ MODULATE2X)) &&
(0 == (pCaps->TextureOpCaps & D3DTEXOPCAPS_ MODULATE4X)) &&
(0 == (pCaps->TextureOpCaps & D3DTEXOPCAPS_ADD)) &&
(0 == (pCaps->TextureAddressCaps & D3DTADDRESSCAPS_CLAMP)) &&
(0 == (pCaps->TextureAddressCaps & D3DTADDRESSCAPS_BORDER)) &&
(0 == (pCaps->TextureAddressCaps & D3DTADDRESSCAPS_MIRRORONCE)) )
return E_FAIL;

D3DPOOL_MANAGED


edwardwewe2007-05-21 01:16
第10章 更多高级shader效果p172
Shader有哪些应用?
环境光照、漫反射光照、镜面反射光照、自身遮蔽阴影项、凹凸映射、点光、立方体环境映射、动态折射/反射环境映射、凹凸的动态折射/反射环境映射、阴影体。

10.1使用立方体贴图
立方体环境映射(cube environment mapping):用于在物体的表面上反射其周围环境。

反射映射(reflection mapping),环境映射(environment mapping)

通过将一系列预先定义的图像塑造成包裹着物体的几个贴图形状,便可以在空间中为一个点定义出其位置上的反射。最常见的包裹着物体的预定义图像称为“立方体环境贴图”,或者简称“立方体贴图”。

立方体贴图:以一个立方体的6个面来表现环境,这个立方体用于包裹场景中的一个或多个物体。

生成立方体贴图的方法是,将照相机放置到需要实现反射的物体位置上,然后分别在6个方向上拍摄快照(正x、负x、正y、负y、正z、负z)。每张快照都应具有90度的视域(FOV,field-of-view)和正方形的高宽比。如此,将这些图像作为立方体贴图的6个面,然后将立方体的6个面紧密地缝合起来便可以创建出一张全方位的全景图。

借助于一个入射线向量I来创建一个访问立方体贴图的向量R,该向量与眼睛向量(从顶点岛眼镜的向量)相反。

I是一个从眼睛到顶点的向量(与眼睛向量相反)。入射光向量。
R=I-2*N*(I.N)
被反射的向量R=入射光向量I-2*法线向量N*(入射光向量和法线向量的点积I.N)

R=R1+R2
R1=I-R2

R=I-2*R2
R2= N*(I.N)

R=I-2*N*(I.N)

实现:
float4x4 matWorldViewProj;
float4x4 matWorld;
float4x4 matView;
float4 vecEye;
texture CubeMap;

samplerCUBE CubeMapSampler = sampler_state
{
Texture = <CubeMap>;
MinFilter = Linear;
MagFilter = Linear;
AddressU = Wrap;
AddressV = Wrap;
};

struct VS_OUTPUT
{
  float4 Pos : POSITION;
   float3 Reflect: TEXCOORD4;
};

VS_OUTPUT VS(float4 Pos : POSITION, float2 Tex : TEXCOORD, float3 Normal : NORMAL)
{
  VS_OUTPUT Out = (VS_OUTPUT)0;    
  Out.Pos = mul(Pos, matWorldViewProj);    // transform Position
  float3 Norm = normalize(mul(Normal, matWorld));
 
  // get a vector toward the camera/eye -> V
  float3 PosWorld = normalize(mul(Pos, matWorld));
float3 Incident = normalize(PosWorld - vecEye);
//I;入射线向量=在世界空间中的顶点位置-眼睛位置
 
   // Reflection Vector for cube map: R = I - 2*N * (I.N)
   Out.Reflect = normalize(reflect(Incident, Norm));
//    Out.Reflect = normalize(Incident - 2 * Norm * dot(Incident, Norm));
   
  return Out;
}

float4 PS(float3 Ref : TEXCOORD4) : COLOR
{
   return texCUBE(CubeMapSampler, Ref);  
}
顶点位置通过应用程序提供的世界、观察和投影矩阵来变换,法线则通过世界矩阵来变换。入射线I由顶点位置减去眼睛位置计算而得。归一化的反射向量则借助于reflect()函数计算而得,此函数的实现如下:
float3 reflect(float3 I, float3 N)
{
return I-2*N*dot(I, N);
}
给定入射线方向I和表面法线N,reflect()返回反射向量R。在像素shader中,函数texCUBE()从一个立方体贴图中采样一个图素,并将其作为输出,其中第2个参数为3D查找向量,这里将其指定为顶点shader中计算出的反射向量。

10.3折射/反射环境映射
float4x4 matWorldViewProj;
float4x4 matWorld;
float4x4 matView;
float4 vecEye;
texture CubeMap;

samplerCUBE CubeMapSampler = sampler_state
{
Texture = <CubeMap>;
MinFilter = Linear;
MagFilter = Linear;
AddressU = Wrap;
AddressV = Wrap;
};

struct VS_OUTPUT
{
  float4 Pos : POSITION;
   float3 Reflect: TEXCOORD4;
  float3 Reflect2: TEXCOORD5;
};

VS_OUTPUT VS(float4 Pos : POSITION, float2 Tex : TEXCOORD, float3 Normal : NORMAL)
{
  VS_OUTPUT Out = (VS_OUTPUT)0;    
  Out.Pos = mul(Pos, matWorldViewProj);    // transform Position
  float3 Norm = normalize(mul(Normal, matWorld));
 
  // get a vector toward the camera/eye -> V
  float3 PosWorld = normalize(mul(Pos, matWorld));
float3 Incident = normalize(PosWorld - vecEye);
//I;入射线向量=在世界空间中的顶点位置-眼睛位置
 
   // Reflection Vector for cube map: R = I - 2*N * (I.N)
  Out.Reflect = normalize(Incident - 2 * Norm * dot(Incident, Norm));
   
float3 ShortNorm = mul(Norm, 0.4); //缩短法线

  Out.Reflect2 = normalize(Incident - 2 * Norm * dot(Incident, ShortNorm));

  return Out;
}

float4 PS(float3 Ref : TEXCOORD4, float3 Ref2 : TEXCOORD5) : COLOR
{
   float4 tex1 = texCUBE(CubeMapSampler, Ref);
  float4 tex2 = texCUBE(CubeMapSampler, Ref2);
return tex2*0.5+ tex1;
}
在顶点shader中,法线被缩短后储存到变量ShortNorm中,然后将这个变量传递给reflect()函数来计算折射向量。使用一个较大的常量值(接近1.0)会使结果看上去更接近于反射;而较小的值(接近0)则会使结果更接近于向量I,由此可产生一种缩放的效果。
反射值和折射值随后被传到像素shader中,像素shader用它们分2次读取立方体贴图。然后,将通过折射向量读取到的立方体贴图的颜色值乘以0.5,以此来降低颜色的亮度。最后,将这个值与通过反射向量读取到的颜色值相加。

10.3.1动态折射/折射环境映射p179

10.3.2凹凸的动态折射/反射环境映射p181
float4x4 matWorldViewProj;    
float4x4 matWorld;    
float4 vecEye;
float4 vecLightDir;

texture ColorMap;
sampler ColorMapSampler = sampler_state
{
  Texture = <ColorMap>;
  MinFilter = Linear;
  MagFilter = Linear;
  MipFilter = Linear;  
  AddressU = Clamp;
  AddressV = Clamp;
};

texture BumpMap;
sampler BumpMapSampler = sampler_state
{
  Texture = <BumpMap>;
  MinFilter = Linear;
  MagFilter = Linear;
  MipFilter = Linear;  
  AddressU = Clamp;
  AddressV = Clamp;
};

texture CubeMap;
samplerCUBE CubeMapSampler = sampler_state
{
  Texture = <CubeMap>;
  MinFilter = Linear;
  MagFilter = Linear;
  AddressU = Wrap;
  AddressV = Wrap;
};

struct VS_OUTPUT
{
  float4 Pos : POSITION;
  float2 Tex : TEXCOORD0;
  float3 Light : TEXCOORD1;
  float3 View : TEXCOORD2;
   float3 Reflect: TEXCOORD3;
   float3 Reflect2: TEXCOORD4;
};

VS_OUTPUT VS(float4 Pos : POSITION, float2 Tex : TEXCOORD, float3 Normal : NORMAL, float3 Tangent : TANGENT )
{
  VS_OUTPUT Out = (VS_OUTPUT)0;    
  Out.Pos = mul(Pos, matWorldViewProj);    // transform Position
 
  // compute the 3x3 tranform matrix
  // to transform from world space to tangent space
  float3x3 worldToTangentSpace;
  worldToTangentSpace[0] = mul(Tangent, matWorld);
  worldToTangentSpace[1] = mul(cross(Tangent, Normal), matWorld);
  float3 Norm = mul(Normal, matWorld);
  worldToTangentSpace[2] = Norm;
   
  Out.Tex = Tex.xy;
  // output light vector
  float3 Light = normalize(vecLightDir);
  Out.Light.xyz = mul(worldToTangentSpace, Light);  

  // get a vector toward the camera/eye -> V
  float3 PosWorld = normalize(mul(Pos, matWorld));
  float3 Incident = normalize(PosWorld - vecEye);
  float3 Viewer2 = normalize(vecEye - PosWorld); // view vector for bump map  
  Out.View = mul(worldToTangentSpace, Viewer2);

  // Reflection Vector for cube map: R = I - 2*N * (I.N)
   Out.Reflect = normalize(reflect(Incident, Norm));

   float3 ShortNorm = mul(Norm, 0.4);
   
   // Reflection Vector for cube map: R = I - 2*N * (I.N)
   Out.Reflect2 = normalize(reflect(Incident, ShortNorm));
   
   return Out;
}

float4 PS(float2 Tex: TEXCOORD0, float3 Light : TEXCOORD1,
float3 View : TEXCOORD2, float3 Ref : TEXCOORD3,
float3 Ref2 : TEXCOORD4) : COLOR
{
  float4 color = tex2D(ColorMapSampler, Tex);                    // fetch color map
  float4 bumpNormal = 2 * (tex2D(BumpMapSampler, Tex) - 0.5); // fetch bump map
  float4 tex1 = texCUBE(CubeMapSampler, Ref);
  float4 tex2 = texCUBE(CubeMapSampler, Ref2);
   
   // light vector  
  float3 LightDir = normalize(Light);
  float3 ViewDir = normalize(View);
   
  // diffuse component
  float4 diff = saturate(dot(bumpNormal, LightDir));
 
  // compute self-shadowing term
  float shadow = saturate(4 * diff);
 
   // compute R vector
  float3 Reflect = normalize(2 * diff * bumpNormal - LightDir);
   
   // gloss map in color.w used to restrict specular reflection on non-glossy regions
  float4 spec = min(pow(saturate(dot(Reflect, ViewDir)), 8), color.w);

  return 0.2 * color + shadow * ((color * diff + spec)+ ((tex2 * 0.5 + tex1) * 0.05));    
}

10.4 使用阴影p184
一个具有阴影的场景需要有:一个用于逐点或逐像素计算的光源、一个抛投阴影的物体(称为遮光板)、一个被投射阴影的表面(接受者)。

阴影本身由2部分构成:本影(umbra),阴影中央最黑暗的部分;半影(penumbra)由阴影的外部和边缘部分构成的较柔和的一部分。

10.4.1阴影体(Shadow Volume)p185


edwardwewe2007-05-21 16:25
第12章 使用*.md3文件p219
关键帧(key frame)是一种廉价的动画处理方法。缺点是处理高度镶嵌的(high-tesselated)模型需要大量的存储空间。

一个.md3模型通常由身体的3个部分(head.md3、upper.md3、lower.md3)和一个武器模型组成的。

头部(head)组件的名称以h_开头、上身(upper)组件以u_开头,下身(lower)组件以l_开头。

.md3将角色动画存储在关键帧中。animation.cfg存储着动画方面的信息,它用于指导对关键帧的调用。

单选题:可以通过3个不同的md3文件来提供3种不同精细程度的模型。在Quake3中,对这3种精细程度的文件命名规范是:对于最低精细度的模型,在其文件名后加一个2;对于中等精细度的模型,在文件名后面加一个1;对于最高精细度的模型,则不需要加任何额外的数字。因此,文件名head.md3 、head1.md3 、head2.md3分别对应于最高精度、中等精度、最低精度。

头部、上身、下身这3个躯体部分之间通过所谓的标记(tag)来彼此连接(attach)。每个.md3模型一般会有3个标记。在头颈底部,通过tag_head标记将头部连接到上身的上方。在脊椎底部,通过tag_torso标记将上身连接到腿上。武器通过tag_weapon连接到模型的手上。每个模型至少会有一个标记。例如head.md3会有一个标记tag_head,upper.md3有tag_head、tag_torso和tag_weapon,lower.md3有tag_torso。

每个连接.md3文件的标记都是通过*.skin文件中的一条记录指定的。将皮肤(即.tga或.jpg格式的纹理贴图文件)连接到几何体上也是通过*.skin文件完成的。

Quake3模型使用的文件格式:p222
1、.md3:通常为head.md3、upper.md3、lower.md3
2、图标(肖像)文件:icon_default.tga
3、动画文件:animation.cfg
4、皮肤文件:.tga或.jpg
5、标记文件:.skin
6、shader文件:比如:models.shader
7、声音文件:.wav

.md3格式:p233
1、.md3首部
2、.md3骨骼帧(boneframe)
3、.md3标记
4、.md3网格(包括Mesh首部、Mesh纹理名称、Mesh三角形、Mesh纹理坐标、Mesh顶点)

p233-p257请关注一下,呵呵。

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值