chapter 5 Modeling <从www.yixuanli.com转载>
这一章讲解的主要是怎样向direct3D 描述场景。场景里面的物体都是由很多组primitives组成的,primitive包括point, line, triangles。 由于三角形也是一个面,所以一组三角形就可以模拟出一个平滑的表面。一个顶点除了位置信息,面法线外,还有很多额外的信息绑定到这个顶点,如顶点颜色值,纹理坐标值,顶点blend weight,任意的shader数据等等。 这些数据是通过一组数据流传递给Device,每一个流都对应一个顶点buffer。
Direct3D 提供了两种方法来描述一个物体的形状,可以用参数把面的精细度定义成真实面的一组三角形。设备一次只描述场景中的一个primitive 。
建模场景
所有的3D 渲染都在在场景里面完成的,BeginScene标志着一个场景的开始,紧接着是图形渲染。EndScene标志着场景渲染的结束。设备则是被创建以后就可以设置它的属性和状态,他们不受场景的影响。StretchRect 不是一个渲染方法,能被应用在场景之外。
可视性
"Z buffer"是一个判断可视性的算法,它工作在Render Target的每个像素上,而不是每个模型上。 当一个模型被光栅化之后,每一个像素就会有一个Z 值,来表示这个像素离照相机的距离。这个算法也不是完美的,虽然对于不透明的像素还能处理的很好,但是如处理那些果透明的或者半透明的像素,它就有缺陷了。幸运的是,我们可以先画出所有不透明的物体,然后把透明的物体从后到前排序,然后用painter算法画它们。 这个方法并不完美,它确实能减少artifacts,但是仍然使用了快速Z buffer硬件。
depth/ stencil buffer 存放着每个像素离照相机的距离。 一个depth/stencil buffer是通过设备的D3DPRESENT_PARAMETERS来创建的,或者显示的调用CreateDepthStencilSurface。GetDepthStencilSurface可以取得设备的depth/stencil surface。为了利用Z buffer来解决可视性问题,设置RS Z enable 为 D3DZB_TRUE, RS Z Write Enable为True 和 RS Z Func 为D3DCMP_LESS。
如果 D3DCAPS9::RasterCaps 的 D3DPRASTERCAPS_ZBUFFERLESSHSR被设置,它意味着用另外一种方法做可视性判断,可视性判断是与硬件相关,这种情况下render targe的depth/stencil surface 为空,而且设置RS Z enable为 D3DZB_TRUE.
Render Targets
当光栅处理结束后,像素流就被产生。它的目的地就是设备的render target。 swap chain上的所有的back buffer 都是合理的render targets,但是render target并不仅仅限制于back buffer的surface。当设备被创建或者reset的时候,render target就是默认swap chain的back buffer 0。当调用present的时候,render target就跳到下一个back buffer,这样present 返回的以后,render target又是back buffer 0。
可以设置render target为一个表面,这样允许设备直接渲染到一个图像表面。非swap chain 上的render target surface 可以通过调用函数CreateRenderTarget。
SetRenderTarget ,GetRenderTarget, Clear 方法用于对render target surface。
Primitive Type
一般包括D3DPT_POINTLIST, D3DPT_LINELIST, D3DPT_LINESTRIP,D3DPT_TRIANGLELIST,D3DPT_TRIANGLESTRIP, D3DPT_TRIANGLEFAN。
顶点数据
场景数据是由定义物体形状和外表的数据组成。我们已经看到形状可以由primitive和顶点坐标确定。用于固定流水线的数据要么是FVF格式的数据,要么是顶点shader声明格式的数据。顶点shader程序可以在每个顶点上使用任意的数据。每个与这个顶点相关的数据都被称作一个顶点component。利用FVF格式的数据,所有的数据必须在一个单一的流里面。但是顶点shader声明可以把这些components分成多个流。
除了选择固定功能处理和可编程顶点处理外,应用程序可以把数据直接提供给设备,忽略所有的顶点处理。
灵活顶点格式(FVF)
顶点FVF是一个DWORD,它包含一个或者多个描述在内存里面顶点组件,如:D3DFVF_XYZ, D3DFVF_XYZRHW, D3DFVF_XYZB1,等等。
位置相关的FVF: D3DFVF_XYZ, D3DFVF_XYZRHW,D3DFVF_XYZB1, D3DFVF_XYZB3,D3DFVF_XYZB4 和 D3DFVF_XYZB5。B值是它的blending weight。
D3DFVF_PSIZE 与点精灵有关,描述了点精灵的大小。
D3DFVF_TEXn标记纹理坐标的数目,D3DFVF_TEXCOORDSIZEN 纹理坐标的维度。
GetFVF和SetFVF用于设备管理它的FVF。
顶点声明
用顶点声明,可以将顶点数据分成多个流,每个流都包含一个或多个顶点组件数据。顶点声明可以被用于固定功能处理也可以用于可编程处理。
顶点声明是由一组D3DVERTEXELEMENT9结构的数组创建的。数组里面的每个元素代表顶点里面一个顶点component。元素在数组里面的顺序会影响流里面数据的分布。但是,顶点组件顺序可以存放在内存里面的任意位置。
typedef struct _D3DVERTEXELEMENT9
{
WORD Stream; //
WORD Offset;
BYTE Type; //数据的类型,如果D3DECLTYPE_FLOAT3,理论上所有的数据在被传入到顶点处理之前都要是4值的向量,如果不是,则会在y,z补0,w补1。
BYTE Method; // 用于顶点的tessellation。
BYTE Usage; // 如: D3DECLUSAGE_POSITION。
BYTE UsageIndex; //用于有同一个Usage的索引,如哪个texture stage。
}
顶点声明的接口是IDirect3DVertexDeclaration9,它有两个只读的方法,GetDeclaration和GetDevice。
当使用固定过程的流水线用顶点声明的时候,每个顶点组件必须被映射到一个特定的usage。你能使用多个stream,不是这个流必须遵循FVF的顺序和顶点类型。
注意:顶点声明总是存放在系统内存,所以当设备重启之后它不需要恢复。
Vertex Buffer
Vertex buffer 资源用于存储应用程序的顶点数据。Direct3D 的接口是IDirect3DVertexBuffer9。
interface IDirect3DVertexBuffer9 : IUnknown
{
HRESULT GetDesc(D3DVERTEXBUFFER_DESC* value);
HRESULT Lock(UINT offset, UINT size, BYTE** data , DWORD flags);
HRESULT Unlock();
}
创建一个包含size 个字节的vertex buffer,可以通函数CreateVertexBuffer();
HRESULT CreateVertexBuffer(UINT size, DWORD usage, D3DPOOL pool, DWORD fvf,IDirect3DVertexBuffer9 ** reuslt, Handle * unused);
如果fvf参数为0 ,它将创建一个non-FVF vertex buffer,其内容将使用顶点shader声明。 unused必须是NULL。 usage 包括D3DUSAGE_DONOTCLIP, D3DUSAGE_DYNAMIC, D3DUSAGE_NPATCHES,D3DUSAGE_POINTS, D3DUSAGE_RTPATCHES, D3DUSAGE_SOFTWAREPROCESSING, D3DUSAGE_WRITEONLY.
D3DUSAGE_DONOTCLIP指这些顶点不需要clipping; D3DUSAGE_NPATCHES, D3DUSAGE_POINTS, D3DUSAGE_RTPATCHES 指这个顶点buffer将分别被用于画 N-Patches, 点 sprite,higher order suface patches;D3DUSAGE_SOFTWAREPROCESSING 指顶点用于软件处理;D3DUSAGE_WRITEONLY指这个buffer不能读取,只能写入;D3DUSAGE_DYNAMIC指应用程序将会改边里面的内容,如果不指定这个标记,顶点buffer将是静态的。
如果D3DCAPS9::DevCaps的D3DDEVCAPS_TLVVERTEXSYSTEMMEMORY 或者 D3DDEVCAPS_TLVVERTEXVIDEOMEMORY 被设置,设备能够使用video 或者 system内存中的而且包含被转换顶点的vertex buffer。通常static vertex buffer是分配在设备内存中的,然而动态顶点buffer则是在系统内存中或者AGP内存,可以被CPU直接访问。动态顶点buffer必须要在系统内存池中分配,或者在默认内存池中分配。
IDirect3DVertexBuffer9::GetDesc将会返回一个D3DVERTEXBUFFER_DESC的结构。
typedef struct _D3DVERTEXBUFFER_DESC
{
D3DFORMAT Format; //对于vertex buffer,将永远是D3DFMT_VERTEXDATA;
D3DRESOURCETYPE Type;
DWORD Usage;
D3DPOOL Pool;
UINT Size;
DWORD FVF;
}
IDirect3DVertexBuffer::Lock提供对包含数据的直接访问。对buffer 数据的访问必须与它的参数flags相一致。 flags 包括D3DLOCK_DISCARD,D3DLOCK_NOOVERWRITE, D3DLOCK_NOSYSLOCK, D3DLOCK_READONLY. D3DLOCK_DISCARD指应用程序不care以前的内容,这个以前的内容将会被抛弃;D3DLOCK_NOOVERWRITE 指应用程序不能重写里面的数据;D3DLOCK_DISCARD和D3DLOCK_NOOVERWRITE只能用于动态顶点buffer。对于动态几何的场景,不正确lock将对render的性能影响很大。
被索引的Primitives
我们已经知道在描述一个形状的时候有很多重复的顶点,如果我们为每个顶点分配一个序号,并且把所有不同的顶点按照期序号缓存起来,这样当我们制作一个primitive的时候,我们只需要提供primitive 顶点序号即可。这样我们节省了很大存储空间。
Direct3D通过IDirect3DIndexBuffer9 接口来管理index buffer。这个接口的方法与IDirect3DVertexBuffer类似,这里就不做详细讲解了。
顶点Shader
设备的顶点shader的属性是在固定功能顶点处理和可编程顶点处理之中选择。这些属性就是一个顶点shader的接口指针,它是通过SetVertexShader和GetVertexShader来管理。设置这个接口指针为空,表示选择固定功能的处理,合理的接口指针,它将是意味着可编程的顶点处理。
HRESULT GetVertexShader(IDirect3DVertexShader9 ** value);
HRESULT GetVertexShader(IDirect3DVertexShader9 * value);
可以通过CreateVertexShader创建一个顶点shader,
HRESULT CreateVertexShader(const WORD * function, IDirect3DVertexShader9 ** result);
interface IDirect3DVertexShader9 : IUnknown
{
HRESULT GetDevice(IDirect3DDevice9 ** value);
HRESULT GetFunction(void * value, DWORD * size);
}
|