一个渲染效果一般由以下部分组成:一个顶点和/或像素着色器,一个需要设置的设备状态列表,一个或更多的渲染通道(rendering
passes)。此外,有一个能在不同级别的图形硬件上渲染效果的可靠机制通常是值得的(也就是说,有不同的可用的效果版本执行同样的效果或尽可能尝试执行同样的效果)。显然,所有这些必要的任务组合在一起成为一个效果。因此,一个合理的做法(步聚)是,设法将这些任务封装到一个单元中。
Direct3D效果构架提供了这样一个机制:将渲染效果的任务封装到一个效果文件。在效果文件中实现效果有两方面优势。其一,它允许我们不必重编译应用程序就能改变一个效果的执行。这是一种更新效果的过程,不管是修正一个bug,一些简单的加强,或者利用最新的3D硬件特性。第二,它将所有的效果组成部分封装到一个文件。
19.1
手法与通道(Techniques
and Passes)
一个效果文件由一个或多个手法组成。一个手法是用一个特殊的方法渲染一些特效。所以换句话说,一个效果文件提供了渲染相同特效的一个或多个不同的通道。为什么同样的效果需要几个不同实现呢?是的,一些硬件可能不支持一个效果的一种特定实现。因此,必需在不同硬件上实现相同效果的不同版本。
注意:例我们可能实现一种效果的两个版本,一种用着色器实现而一种用固定管线实现。这样,那些有着色器(shader)支持的显卡用户能够利用着色器实现,而那些不支持着色器的用户仍然可以使用固定管线实现。
可以在一个效果文件中实现所有版本的效果,这让我们更完整的封装了所有的效果,也是效果框架的目标之一
封装(encapsulation)。
每种手法包括一次或多次渲染通道(passes)。一个渲染通道(rendering
pass)在特定通道(pass)中封装了设备状态、采样器、和/或用于渲染几何体的着色器。
注意:一个效果不仅限于可编程管线使用。例如,它可以使用固定功能管线控制设备状态,比如灯光、材质以及纹理。
使用多个通道(multiple passes)的理由是,因为对每种特效,是通过使用不同的设备状态、着色器等等,对同样的几何体进行多次渲染来完成的。举例来说,我们不得不在每帧里用不同的设备状态、多次渲染相同的几何体,以达到反射效果。
下面这个例子,是一个用两种手法实现的效果文件的框架,第一种手法包括一次传递而每二种手法包括两次传递:
//
effect.txt
...
technique T0
{
// first and only pass for this technique
pass P0
{
...[specify pass device states, shaders, samplers, etc.]
}
}
technique T1
{
// first pass
pass P0
{
...[specify pass device states, shaders, samplers, etc.]
}
// second pass
pass P1
{
...[specify pass device states, shaders, samplers, etc.]
}
}
|
19.2
更多HLSL内置对象(
More HLSL Intrinsic
Objects)
这是一些在HLSL中额外的内建对象类型。
19.2.1
纹理对象
HLSL内建纹理类型描述了一个IDirect3DTexture9对象。通过使用纹理对象我们可以直接地在效果文件中对特定的采样器阶段结合纹理。纹理对象有下面的可以访问的数据成员:
type—纹理类型
(例如:2D, 3D)
format—纹理的像素格式
width—纹理的宽度(单位像素)
height—纹理的高度(单位像素)
depth—纹理的深度(如果是3D纹理,单位像素)
注意:迄今为止我们仅仅使用纹理来存储图形数据,但当你学到更高级的技术,你会发现纹理可用来保存任意表格信息。换句话说,纹理仅是数据表,不是必须包含图形数据。例如,在碰撞映射(bump
mapping)时我们用到一种叫做法线图的东东(normal map),就是一种在每个点上包括了法向量的纹理。
19.2.2
采样器对象与采样器状态
效果框架定义了新的关键字:sampler_state。使用sampler_state关键字,我们能初始化一个采样器对象(即,直接在效果文件中设置采样器对象的纹理和状态)。下面的例子说明了这点:
Texture Tex;
sampler SO = sampler_state
{
Texture = (Tex);
//
纹理
//
采样器状态
MinFilter = LINEAR;
MagFilter = LINEAR;
MipFilter = LINEAR;
};
|
这里我们给采样器S0的texture成员关联了纹理
Tex,并给状态成员设置了采样状态。我们直接明了的在效果文件中设置所有信息。
19.2.3
顶点与像素着色器对象(Vertex
and Pixel Shader Objects)
vertexshader
和
pixelshader是HLSL的内建类型,分别表示
顶点着色器和
像素着色器。它们在效果文件中表示特定顶点和/或像素着色器,用于一个特定的
渲染通道(pass)。vertexshader和/或pixelshader类型在应用程序中用ID3DXEffect::SetVertexShader和ID3DXEffect::SetPixelShader函数分别设置。
例如,在效果文件中,Effect是一个有效的ID3DXEffect对象,VS是一个有效的IDirect3DVertexShader9对象,以及VSHandle是一个D3DXHANDLE(是vertexshader
对象的引用)。然后,我们可以通过如下写法初始化VSHandle所引用的顶点着色器:
Effect->SetVertexShader(VSHandle, VS);
|
当在应用程序中设置效果文件中的变量时,多数时候我们使用SetVertexShader
和
SetPixelShader。
做为选择,我们可以直接在效果文件中写顶点和/或像素着色器。当使用一种特定的编译语法时,我们可以设置一个着色器变量。下面的例子展示了如何初始化一个pixelshader类型的变量ps。
//
定义入口函数
OUTPUT
Main(INPUT input){...}
//
编译入口函数
pixelshader ps = compile ps_2_0 Main();
|
在pixelshader关键字之后的特定的版本名,接下来是着色器入口函数。注意,当用这种方式(style)初始化一个顶点或像素着色器对象时,入口函数必须定义在效果文件中。
或者更简洁的:
pass P0
{
//
设置这个传递的顶点着色器,为入口函数"
Main()"的顶点着色器
vertexshader = compile vs_2_0 Main();
...
}
|
注意:你能用这样的语法来初始化一个vertexshader
和
pixelshader
类型:
vertexshader vs = asm { /*assembly
instructions go here */ };
pixelshader ps = asm { /*assembly instructions
go here */ };
|
如果你用汇编语言来写着色器,你就用这种语法。
19.2.4
字符串
最后,这是一个字符串对象,它的用法是这样地:
string
filename = "texName.bmp";
|
尽管没有任何HLSL的内建函数支持字符串类型,但它可以在应用程序中读取。这样,我们能进一步封装效果使用的数据文件,比如纹理文件名和X文件字。
19.2.5
注解 (Annotations)
除我们已经描述过的语义符之外,注解可以用在变量上。注解在HLSL中是不使用的,但是它们可以被应用程序通过效果框架访问。它们仅仅服务于一个绑定
“note”的变量,这样应用程序就能够访问这个变量了。为注解加入了<annotation>语法。下面一行举例说明:
texture tex0 < string name = "tiger.bmp"; >;
|
在这个例子中的注解是<string
name = "tiger. bmp";>。它关联了一个字符串到变量tex0,即保存纹理数据的文件名。很明显,用相应的文件名注解一个纹理是有益的。
注解可以使用下面函数被重新得到:
D3DXHANDLE ID3DXEffect::GetAnnotationByName(
D3DXHANDLE hObject,
LPCSTR pName
);
|
pName是我们要操作的注解的名字,而hObject是注解所在的父块句柄,如一个technique、pass或者结构块。一旦我们有了一个注解的句柄,我们就能通过应用ID3DXEffect::GetParameterDesc得到有关它的信息。查看DirectX
SDK文档以得到更多详细的内容。
Gets the handle of an annotation by looking up its
name.
D3DXHANDLE GetAnnotationByName(
D3DXHANDLE hObject,
LPCSTR pName
);
Parameters
- hObject
- [in] Handle of a technique, pass, or top-level
parameter.
- pName
- [in] String containing the annotation name.
Return Values
Returns the handle of the specified annotation, or NULL
if the name was not found. See Handles.
ID3DXBaseEffect::GetParameterDesc
Gets a parameter or annotation description.
HRESULT GetParameterDesc(
D3DXHANDLE hParameter,
D3DXPARAMETER_DESC* pDesc
);
Parameters
- hParameter
- [in] Parameter or annotation handle.
- pDesc
- [out] Returns a description of the specified
parameter or annotation.
Return Values
If the method succeeds, the return value is S_OK. If
the method fails, the return value can be D3DERR_INVALIDCALL.
D3DXPARAMETER_DESC
Describes a parameter used for an effect object.
typedef struct D3DXPARAMETER_DESC {
LPCSTR Name;
LPCSTR Semantic;
D3DXPARAMETER_CLASS Class;
D3DXPARAMETER_TYPE Type;
UINT Rows;
UINT Columns;
UINT Elements;
UINT Annotations;
UINT StructMembers;
DWORD Flags;
UINT Bytes;
} D3DXPARAMETER_DESC, *LPD3DXPARAMETER_DESC;
Members
- Name
- Name of the parameter.
- Semantic
- Semantic meaning, also called the usage.
- Class
- Parameter class. Set this to one of the values in
D3DXPARAMETER_CLASS.
- Type
- Parameter type. Set this to one of the values in
D3DXPARAMETER_TYPE.
- Rows
- Number of rows in the array.
- Columns
- Number of columns in the array.
- Elements
- Number of elements in the array.
- Annotations
- Number of annotations.
- StructMembers
- Number of structure members.
- Flags
- Parameter attributes. See Effect Constants.
- Bytes
- The size of the parameter, in bytes.
-
19.3
效果文件的设备状态(
Device States in an
Effect File)
通常,为了正确执行一个效果,我们必须设置设备的状态,比如渲染状态、纹理状态、材质、灯光和纹理。将全部效果封装进一个文件使它有支持全部效果的能力,效果框架允许我们在效果文件中设置设备状态。设备状态在渲染的通道部分(pass
block)里设置,语法看起来象这样:
考虑FillMode状态。值与D3DFILLMODE一样,但没有D3DFILL_前缀。如果我们在SDK文档中查找D3DFILLMODE,我们找到值:D3DFILL_POINT,
D3DFILL_WIREFRAME, and
D3DFILL_SOLID。因而,对于效果文件我们省略了前缀,并获得下列状态FillMode的有效值:POINT,
WIREFRAME,
和
SOLID。例如,你可以在效果文件中这么写-:
FillMode = WIREFRAME;
FillMode = POINT;
FillMode = SOLID;
|
效果用ID3DXEffect接口表示,我们用下面的D3DX函数创建它:
HRESULT D3DXCreateEffectFromFile(
LPDIRECT3DDEVICE9 pDevice,
LPCSTR pSrcFile,
CONST D3DXMACRO* pDefines,
LPD3DXINCLUDE pInclude,
DWORD Flags,
LPD3DXEFFECTPOOL pPool,
LPD3DXEFFECT* ppEffect,
LPD3DXBUFFER *ppCompilationErrors
);
|
pDevice—被创建的ID3DXEffect对象所关联的设备
pSrcFile—我们要编译的包括效果源代码的文本文件的名字(效果文件名)
pDefines—这个参数是可选的,这里指定为null
pInclude—ID3DXInclude接口指针。这个接口被设计成由应用程序执行,因而我们可以替换默认行为。通常,默认行为就挺好,我们可以指定null忽略这个参数。
Flags—编译效果文件中的shader的选项标志,指定0为没有标志。有效选项为:
D3DXSHADER_DEBUG—指示编译器写入调试信息
D3DXSHADER_SKIPVALIDATION—指示编译器不做任何代码检测。这只在你正在用到一个已知正常工作的shader时使用。
D3DXSHADER_SKIPOPTIMIZATION—指示编译器不执行任何优化。实际上这只用于调试时,当你不想让编译器对代码做任何更改时。
pPool—可选的ID3DXEffectPool接口指针,用于指定效果参数如何共享其它的效果实例。这里指定null,表示我们不在参数与效果文件之间共享。
ppEffect—返回一个ID3DXEffect接口指针,表示被创建的效果。
ppCompilationErrors—返回一个包含错误代码字符串和消息的ID3DXBuffer指针。
这是一个调用D3DXCreateEffectFromFile的例子:
//
修建效果
ID3DXEffect* Effect = 0;
ID3DXBuffer* errorBuffer = 0;
hr =
D3DXCreateEffectFromFile(
Device, //
关联的设备
"effect.txt", //
效果源文件
0, // no preprocessor
definitions
0, // no ID3DXInclude interface
D3DXSHADER DEBUG, // 编译标记
0, //
不共享参数
&Effect, //
返回创建效果的指针
&errorBuffer); //
返回的错误信息
//
输出错误信息
if(
errorBuffer )
{
::MessageBox(0, (char*)errorBuffer->GetBufferPointer(), 0, 0);
d3d::Release<ID3DXBuffer*>(errorBuffer);
}
if
(FAILED(hr))
{
::MessageBox(0, "D3DXCreateEffectFromFile() - FAILED", 0, 0);
return false;
}
|