发现关于Direct3D的好文章.

nisus_lee 2007-11-19 04:27:57
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);
}

...全文
1195 8 打赏 收藏 转发到动态 举报
写回复
用AI写文章
8 条回复
切换为时间正序
请发表友善的回复…
发表回复
knowledge_Is_Life 2008-04-30
  • 打赏
  • 举报
回复
不明LZ在说什么
thecorr 2007-11-20
  • 打赏
  • 举报
回复
学习。。
pptor 2007-11-20
  • 打赏
  • 举报
回复
学习
Wolf_FienD 2007-11-19
  • 打赏
  • 举报
回复
DING DING
quentinliu 2007-11-19
  • 打赏
  • 举报
回复
jf
飞哥 2007-11-19
  • 打赏
  • 举报
回复
up
ouyh12345 2007-11-19
  • 打赏
  • 举报
回复
学习
nisus_lee 2007-11-19
  • 打赏
  • 举报
回复
通常一个应用程序将不会直接分配一个DWORD功能数组,但通常是通过D3DX在运行时编译汇编语言或者更高级shader语言为这个DWORD功能数组。

绘制Primitives

一旦顶点数据被定义,并且顶点处理被配置,应用程序可以调用一个draw 方法进行渲染。draw方法很多,包括DrawPrimitiveUp,DrawIndexedPrimitiveUp,DrawPrimitive,DrawIndexedPrimitive,DrawTriPatch,DrawRectPatch等等。

HRESULT DrawPrimitiveUp(D3DPRIMITIVETYPE kind, UINT primitive_count, const void * vertex_data, UINT vertex_stride); 这个方法绘制类型为kind的非索引的顶点数据,它的内部处理是先将输入的数据拷贝到一个新的vertex buffer, 然后绘制里面的primitive,最后在释放这个vertex buffer。创建,释放以及拷贝数据将是非常昂贵的操作,这个方法只能被用于测试或者渲染primitive 比较少的场景。

HRESULT DrawIndexedPrimitiveUp 与DrawPrimitiveUp类似,只是它是绘制被索引的顶点。

顶点数据流

顶点各个组件可以来自与一条或者多条数据流,这些流数据组合起来就是一个完整的顶点。这些组合起来的顶点只是direct3D pipeline流水线的第一步。这些流数据都有一个编号,并且都有一个相关的vertex buffer和一个strid(顶点组件的大小)。流的编号是连续,以0开始,按照编号顺序被连接。一个流必须包含一个或者数个顶点组件。

分配不同的流到不同的顶点组件,可以让应用程序动态的改变一个模型中顶点组件的值,而不用锁住整个顶点buffer只改变其中某个部分的值。使用多个流,我们可以将顶点里面的静态组件放在一个流,而动态组件放在另外一个流。这允许我们可以在动态组件流的vertex buffer上使用D3DLOCK_DISCARD。可以通过HRESULT SetStreamSource(UINT index, IDirect3DVertexBuffer9 ** value, UINT offset, UINT stride)来设置与一个vertex buffer相关联的流。 stride参数是顶点组件的大小,对于一个FVF vertex buffer ,它应该与FVF一样大,对于非FVF,它应该大于或者等于顶点声明计算出来的大小;offset是离流中第一个顶点的字节偏移量。支持这个偏移,需要在D3DCAPS9::Caps2里面设置D3DDEVCAPS2_STREAMOFFSET。

流的最大数目是通过D3DCAPS9::MaxStream。对于DirectX 8.1 或者以后的版本,这个值一般是1到16。但在DirectX 8.1 之前,这个值是0,暗示它只支持单一流。 MaxStreamStride给出了最大的流组件数据的大小。

流sources提供顶点数据,并且也必须供应索引buffer的索引数据。SetIndices为被索引的primitive指定索引数据。可能存在高达16个顶点数据流,但是只有一个索引buffer。GetIndices返回索引属性值。

随着所有的顶点组件和索引被载入,固定功能的顶点处理被选择,stream source被正确的选择,并且所有必须的状态被设置,就可以分别调用DrawPrimitive和DrawIndexedPrimitive。

HRESULT DrawIndexedPrimitive(D3DPRIMITIVETYPE kind, UINT base_vertex_index, UINT min_index, UINT number_vertices, UINT start_index, UINT primitive_count); min_index 指顶点buffer里面的最小的顶点索引,start_index参数是在索引buffer里面的偏移,base_vertex_index参数是将索引buffer的值都加上这个值,根据所得的结果再去从顶点buffer里面取,这样就允许多组流被打包成一组顶点流,而不需要改变索引值。

设备的Draw primitive方法,允许primitive的批量处理转化成一个单独方法的调用。这是最有效的方法,将primitive传送给设备。这个批量处理考虑了CPU 和设备之前更多的并发:当设备正在光栅化一组primitives的时候,cpu正准备传递下一组。

D3DCAPS9的MaxPrimitiveCount给出了能够被draw 方法使用的最大primitive数目。MaxVertexIndex给出了最大的顶点索引值。如果DevCaps的D3DDEVCAPS_DRAWPRIMTLVERTEX被设置,则设备有drawprimitive的硬件支持。

增强型顶点







Direct3D 提供了两种对基本primitive的增强方法。点Sprites 可以让一个点primitves有一个用纹理化过的屏幕空间。N-Patches 可以提高了三角形的可视性,并且不需要做很多的其他工作。

Point sprites 可以使D3DPT_POINTLIST primitives 被光栅化后不只是占据一个像素的空间。如果D3DCaps9::MaxPointSize大于1,则设备支持点Sprites。Point sprites被渲染成屏幕空间里的纹理化的方块。点的大小可以在每个顶点的vertex buffer 用D3DFVF_POINT_SIZE来指定。

如果point sprites使用是D3DFVF_XYZRHW,point sprites 的大小则是在屏幕空间的,否则这个size 必须要经过插值了的。

point sprites 可以被用于渲染一个symbol的许多实例,把symbol的图片存储在纹理里面,然后把symbol的位置作为point sprites。通过这种point sprites的渲染方式,是直接用顶点渲染的1/4的开销。point sprites渲染方式也可以用于渲染粒子系统,每一个被渲染的粒子都是一个点point sprites。

N-patches

N-patches 提供三角形的tessellation基于顶点和表面法线信息。如果D3DCAPS::DevCaps的D3DDEVCAPS_NPATCHES被设置,则设备支持N-patch。

因为三角形近似光滑平面,N-patches 提供为surface 模型提供额外的detail,而不需要巨大的编程工作或者改变模型。当设备的N-patch mode属性大于1.0的时,N-Patches可以用于三角形。易于原有的三角形顶点和法线信息重建三角形光滑表面的patch,然后再根据N-patch属性细分那个patch,这样就产生了额外的三角形。顶点位置和顶点法线都可以产生对应的patches。新顶点位置patch的代数degree定义在RS Position Degree,新顶点法线patch的degree定义在RS Normal Degree。这两个render state的值都是定义在D3DDEGREETYPE里面。RS position Degree可以是线性的或者立体型的,立体型是它的默认值。RS Normal degree也可以是线性的或者二次的,线性是它的默认值。




Higher order Surfaces(高维曲面)

三角形和矩形patch提供了最高质量参数化定义的primitive,这让应用程序可以指定真正smooth的曲面,而不只是一种真实面的三角形模拟。这里表面被定义为高维,因为在数学里面曲面的方程要比三角形方程次数要高。

高维曲面又称为spline 面,在计算机图形学里面已经有很多算法和研究了。

为了定义一个三角形,我们定义它的每个顶点。定义面的patch,我们则提供控制点集合和一个patch定义,这个定义是来决定怎么从控制点产生曲面。曲面将会从控制点控制的数学方程集合中产生。这些数学方程集合通常称为patch的基本方程。

每一个patch都通过基本函数来构造它的表面以及它的维度。D3DBASISTYPE和D3DORDERTYPE分别指定了它的基本方程和维度。Bezier基本方程的控制点是曲线的两个端点以及它附件的点。B-spline方程的控制点不必在曲线上。插值方程保证所有的控制点在曲线上。

typedef enum _D3DBASISTYPE

{

D3DBASIS_BEZIER = 0,

D3DBASIS_BSPLINE = 1,

D3DBAISS_INTERPOLATE = 2
}

Direct3D处理高维曲面的方法是把面镶嵌到三角形集合,然后在后续的流水线里面作进一步的处理。镶嵌结果将会被缓存,并且在将绑定到一个patch handle,在后续的patch渲染过程中就可以直接使用这个镶嵌三角形。

如果D3DCAPS9:: DevCaps的D3DDEVCAPS_RTPATCHES被设置,这个设备就支持直接用DrawTriPatch和DrawRectPatch。如果D3DDEVCAPS_QUINTICRTPATCHES被设置,它将支持Bezier和B-spline RT patch。如果D3DDEVCAPS_PATCHHANDLEZERO被设置,它将意味着绘制未缓存和缓存的patch将会一样的高效。

三角行和矩形patch都是被镶嵌的,分别使用DrawTriPatch和DrawRectPatch来渲染。一个patch的镶嵌会有大量的计算,所以缓存它是很有意义的。如果handle为空,则会根据patch_info进行镶嵌。

HRESULT DeletePatch(UINT handle);

HRESULT DrawRectPatch(UINT handle, const float * num_segments, const D3DRECTPATCH_INFO * patch_info);

HRESULT DrawTriPatch(UINT handle, const float *num_segments, const D3DTRIPATCH_INFO * patch_info);

number_segments对于三角形是三个float的数组,对于矩形是四个float的数组,这允许更加精确的渲染一个patch的边。

镶嵌结果可以直接通过硬件做渲染。或者按照传统方法三角形化然后渲染。

三角形patches

在DrawTriPatch,patch_info 参数定义了一个三角形patch集合。Direct3D 对于三角形patch支持线性,立方型,二次型bezier模型。patch 的控制点的顶点数据也是存放在顶点buffer里面。

Baiss Degree count

Bezier Linear 3

CUBIC 10

Quintic 21

typedef struct _D3DTRIPATCH_INFO

{

UINT StartVertexOffset; // 当前流里面第一个控制点的顶点索引。

UINT NumVertices;

D3DBASISTYPE Basis; //只能是D3DBASIS_BEZIER

D3DDEGREETYPE Degree;

} D3DTRIPATCH_INFO;


矩形patches

Baiss Degree width height

Bezier Linear 2 2

Cubic 4 4

Quintic 6 6

B-Spline Linear 1 1

Cubic 3 3

Quintic 5 5

Catmull-Rom Cubic 3 3



5,530

社区成员

发帖
与我相关
我的任务
社区描述
C/C++ 模式及实现
社区管理员
  • 模式及实现社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧