一個(gè)渲染效果一般由以下部分組成:一個(gè)頂點(diǎn)和/或像素著色器,一個(gè)需要設(shè)置的設(shè)備狀態(tài)列表,一個(gè)或更多的渲染通道(rendering
passes)。此外,有一個(gè)能在不同級(jí)別的圖形硬件上渲染效果的可靠機(jī)制通常是值得的(也就是說,有不同的可用的效果版本執(zhí)行同樣的效果或盡可能嘗試執(zhí)行同樣的效果)。顯然,所有這些必要的任務(wù)組合在一起成為一個(gè)效果。因此,一個(gè)合理的做法(步聚)是,設(shè)法將這些任務(wù)封裝到一個(gè)單元中。
Direct3D效果構(gòu)架提供了這樣一個(gè)機(jī)制:將渲染效果的任務(wù)封裝到一個(gè)效果文件。在效果文件中實(shí)現(xiàn)效果有兩方面優(yōu)勢(shì)。其一,它允許我們不必重編譯應(yīng)用程序就能改變一個(gè)效果的執(zhí)行。這是一種更新效果的過程,不管是修正一個(gè)bug,一些簡(jiǎn)單的加強(qiáng),或者利用最新的3D硬件特性。第二,它將所有的效果組成部分封裝到一個(gè)文件。
19.1
手法與通道(Techniques
and Passes)
一個(gè)效果文件由一個(gè)或多個(gè)手法組成。一個(gè)手法是用一個(gè)特殊的方法渲染一些特效。所以換句話說,一個(gè)效果文件提供了渲染相同特效的一個(gè)或多個(gè)不同的通道。為什么同樣的效果需要幾個(gè)不同實(shí)現(xiàn)呢?是的,一些硬件可能不支持一個(gè)效果的一種特定實(shí)現(xiàn)。因此,必需在不同硬件上實(shí)現(xiàn)相同效果的不同版本。
注意:例我們可能實(shí)現(xiàn)一種效果的兩個(gè)版本,一種用著色器實(shí)現(xiàn)而一種用固定管線實(shí)現(xiàn)。這樣,那些有著色器(shader)支持的顯卡用戶能夠利用著色器實(shí)現(xiàn),而那些不支持著色器的用戶仍然可以使用固定管線實(shí)現(xiàn)。
可以在一個(gè)效果文件中實(shí)現(xiàn)所有版本的效果,這讓我們更完整的封裝了所有的效果,也是效果框架的目標(biāo)之一
封裝(encapsulation)。
每種手法包括一次或多次渲染通道(passes)。一個(gè)渲染通道(rendering
pass)在特定通道(pass)中封裝了設(shè)備狀態(tài)、采樣器、和/或用于渲染幾何體的著色器。
注意:一個(gè)效果不僅限于可編程管線使用。例如,它可以使用固定功能管線控制設(shè)備狀態(tài),比如燈光、材質(zhì)以及紋理。
使用多個(gè)通道(multiple passes)的理由是,因?yàn)閷?duì)每種特效,是通過使用不同的設(shè)備狀態(tài)、著色器等等,對(duì)同樣的幾何體進(jìn)行多次渲染來完成的。舉例來說,我們不得不在每幀里用不同的設(shè)備狀態(tài)、多次渲染相同的幾何體,以達(dá)到反射效果。
下面這個(gè)例子,是一個(gè)用兩種手法實(shí)現(xiàn)的效果文件的框架,第一種手法包括一次傳遞而每二種手法包括兩次傳遞:
//
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內(nèi)置對(duì)象(
More HLSL Intrinsic
Objects)
這是一些在HLSL中額外的內(nèi)建對(duì)象類型。
19.2.1
紋理對(duì)象
HLSL內(nèi)建紋理類型描述了一個(gè)IDirect3DTexture9對(duì)象。通過使用紋理對(duì)象我們可以直接地在效果文件中對(duì)特定的采樣器階段結(jié)合紋理。紋理對(duì)象有下面的可以訪問的數(shù)據(jù)成員:
type—紋理類型
(例如:2D, 3D)
format—紋理的像素格式
width—紋理的寬度(單位像素)
height—紋理的高度(單位像素)
depth—紋理的深度(如果是3D紋理,單位像素)
注意:迄今為止我們僅僅使用紋理來存儲(chǔ)圖形數(shù)據(jù),但當(dāng)你學(xué)到更高級(jí)的技術(shù),你會(huì)發(fā)現(xiàn)紋理可用來保存任意表格信息。換句話說,紋理僅是數(shù)據(jù)表,不是必須包含圖形數(shù)據(jù)。例如,在碰撞映射(bump
mapping)時(shí)我們用到一種叫做法線圖的東東(normal map),就是一種在每個(gè)點(diǎn)上包括了法向量的紋理。
19.2.2
采樣器對(duì)象與采樣器狀態(tài)
效果框架定義了新的關(guān)鍵字:sampler_state。使用sampler_state關(guān)鍵字,我們能初始化一個(gè)采樣器對(duì)象(即,直接在效果文件中設(shè)置采樣器對(duì)象的紋理和狀態(tài))。下面的例子說明了這點(diǎn):
Texture Tex;
sampler SO = sampler_state
{
Texture = (Tex);
//
紋理
//
采樣器狀態(tài)
MinFilter = LINEAR;
MagFilter = LINEAR;
MipFilter = LINEAR;
};
|
這里我們給采樣器S0的texture成員關(guān)聯(lián)了紋理
Tex,并給狀態(tài)成員設(shè)置了采樣狀態(tài)。我們直接明了的在效果文件中設(shè)置所有信息。
19.2.3
頂點(diǎn)與像素著色器對(duì)象(Vertex
and Pixel Shader Objects)
vertexshader
和
pixelshader是HLSL的內(nèi)建類型,分別表示
頂點(diǎn)著色器和
像素著色器。它們?cè)谛Ч募斜硎咎囟旤c(diǎn)和/或像素著色器,用于一個(gè)特定的
渲染通道(pass)。vertexshader和/或pixelshader類型在應(yīng)用程序中用ID3DXEffect::SetVertexShader和ID3DXEffect::SetPixelShader函數(shù)分別設(shè)置。
例如,在效果文件中,Effect是一個(gè)有效的ID3DXEffect對(duì)象,VS是一個(gè)有效的IDirect3DVertexShader9對(duì)象,以及VSHandle是一個(gè)D3DXHANDLE(是vertexshader
對(duì)象的引用)。然后,我們可以通過如下寫法初始化VSHandle所引用的頂點(diǎn)著色器:
Effect->SetVertexShader(VSHandle, VS);
|
當(dāng)在應(yīng)用程序中設(shè)置效果文件中的變量時(shí),多數(shù)時(shí)候我們使用SetVertexShader
和
SetPixelShader。
做為選擇,我們可以直接在效果文件中寫頂點(diǎn)和/或像素著色器。當(dāng)使用一種特定的編譯語法時(shí),我們可以設(shè)置一個(gè)著色器變量。下面的例子展示了如何初始化一個(gè)pixelshader類型的變量ps。
//
定義入口函數(shù)
OUTPUT
Main(INPUT input){...}
//
編譯入口函數(shù)
pixelshader ps = compile ps_2_0 Main();
|
在pixelshader關(guān)鍵字之后的特定的版本名,接下來是著色器入口函數(shù)。注意,當(dāng)用這種方式(style)初始化一個(gè)頂點(diǎn)或像素著色器對(duì)象時(shí),入口函數(shù)必須定義在效果文件中。
或者更簡(jiǎn)潔的:
pass P0
{
//
設(shè)置這個(gè)傳遞的頂點(diǎn)著色器,為入口函數(shù)"
Main()"的頂點(diǎn)著色器
vertexshader = compile vs_2_0 Main();
...
}
|
注意:你能用這樣的語法來初始化一個(gè)vertexshader
和
pixelshader
類型:
vertexshader vs = asm { /*assembly
instructions go here */ };
pixelshader ps = asm { /*assembly instructions
go here */ };
|
如果你用匯編語言來寫著色器,你就用這種語法。
19.2.4
字符串
最后,這是一個(gè)字符串對(duì)象,它的用法是這樣地:
string
filename = "texName.bmp";
|
盡管沒有任何HLSL的內(nèi)建函數(shù)支持字符串類型,但它可以在應(yīng)用程序中讀取。這樣,我們能進(jìn)一步封裝效果使用的數(shù)據(jù)文件,比如紋理文件名和X文件字。
19.2.5
注解 (Annotations)
除我們已經(jīng)描述過的語義符之外,注解可以用在變量上。注解在HLSL中是不使用的,但是它們可以被應(yīng)用程序通過效果框架訪問。它們僅僅服務(wù)于一個(gè)綁定
“note”的變量,這樣應(yīng)用程序就能夠訪問這個(gè)變量了。為注解加入了<annotation>語法。下面一行舉例說明:
texture tex0 < string name = "tiger.bmp"; >;
|
在這個(gè)例子中的注解是<string
name = "tiger. bmp";>。它關(guān)聯(lián)了一個(gè)字符串到變量tex0,即保存紋理數(shù)據(jù)的文件名。很明顯,用相應(yīng)的文件名注解一個(gè)紋理是有益的。
注解可以使用下面函數(shù)被重新得到:
D3DXHANDLE ID3DXEffect::GetAnnotationByName(
D3DXHANDLE hObject,
LPCSTR pName
);
|
pName是我們要操作的注解的名字,而hObject是注解所在的父塊句柄,如一個(gè)technique、pass或者結(jié)構(gòu)塊。一旦我們有了一個(gè)注解的句柄,我們就能通過應(yīng)用ID3DXEffect::GetParameterDesc得到有關(guān)它的信息。查看DirectX
SDK文檔以得到更多詳細(xì)的內(nèi)容。
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
效果文件的設(shè)備狀態(tài)(
Device States in an
Effect File)
通常,為了正確執(zhí)行一個(gè)效果,我們必須設(shè)置設(shè)備的狀態(tài),比如渲染狀態(tài)、紋理狀態(tài)、材質(zhì)、燈光和紋理。將全部效果封裝進(jìn)一個(gè)文件使它有支持全部效果的能力,效果框架允許我們?cè)谛Ч募性O(shè)置設(shè)備狀態(tài)。設(shè)備狀態(tài)在渲染的通道部分(pass
block)里設(shè)置,語法看起來象這樣:
考慮FillMode狀態(tài)。值與D3DFILLMODE一樣,但沒有D3DFILL_前綴。如果我們?cè)赟DK文檔中查找D3DFILLMODE,我們找到值:D3DFILL_POINT,
D3DFILL_WIREFRAME, and
D3DFILL_SOLID。因而,對(duì)于效果文件我們省略了前綴,并獲得下列狀態(tài)FillMode的有效值:POINT,
WIREFRAME,
和
SOLID。例如,你可以在效果文件中這么寫-:
FillMode = WIREFRAME;
FillMode = POINT;
FillMode = SOLID;
|
19.4
創(chuàng)建效果
效果用ID3DXEffect接口表示,我們用下面的D3DX函數(shù)創(chuàng)建它:
HRESULT D3DXCreateEffectFromFile(
LPDIRECT3DDEVICE9 pDevice,
LPCSTR pSrcFile,
CONST D3DXMACRO* pDefines,
LPD3DXINCLUDE pInclude,
DWORD Flags,
LPD3DXEFFECTPOOL pPool,
LPD3DXEFFECT* ppEffect,
LPD3DXBUFFER *ppCompilationErrors
);
|
pDevice—被創(chuàng)建的ID3DXEffect對(duì)象所關(guān)聯(lián)的設(shè)備
pSrcFile—我們要編譯的包括效果源代碼的文本文件的名字(效果文件名)
pDefines—這個(gè)參數(shù)是可選的,這里指定為null
pInclude—ID3DXInclude接口指針。這個(gè)接口被設(shè)計(jì)成由應(yīng)用程序執(zhí)行,因而我們可以替換默認(rèn)行為。通常,默認(rèn)行為就挺好,我們可以指定null忽略這個(gè)參數(shù)。
Flags—編譯效果文件中的shader的選項(xiàng)標(biāo)志,指定0為沒有標(biāo)志。有效選項(xiàng)為:
D3DXSHADER_DEBUG—指示編譯器寫入調(diào)試信息
D3DXSHADER_SKIPVALIDATION—指示編譯器不做任何代碼檢測(cè)。這只在你正在用到一個(gè)已知正常工作的shader時(shí)使用。
D3DXSHADER_SKIPOPTIMIZATION—指示編譯器不執(zhí)行任何優(yōu)化。實(shí)際上這只用于調(diào)試時(shí),當(dāng)你不想讓編譯器對(duì)代碼做任何更改時(shí)。
pPool—可選的ID3DXEffectPool接口指針,用于指定效果參數(shù)如何共享其它的效果實(shí)例。這里指定null,表示我們不在參數(shù)與效果文件之間共享。
ppEffect—返回一個(gè)ID3DXEffect接口指針,表示被創(chuàng)建的效果。
ppCompilationErrors—返回一個(gè)包含錯(cuò)誤代碼字符串和消息的ID3DXBuffer指針。
這是一個(gè)調(diào)用D3DXCreateEffectFromFile的例子:
//
修建效果
ID3DXEffect* Effect = 0;
ID3DXBuffer* errorBuffer = 0;
hr =
D3DXCreateEffectFromFile(
Device, //
關(guān)聯(lián)的設(shè)備
"effect.txt", //
效果源文件
0, // no preprocessor
definitions
0, // no ID3DXInclude interface
D3DXSHADER DEBUG, // 編譯標(biāo)記
0, //
不共享參數(shù)
&Effect, //
返回創(chuàng)建效果的指針
&errorBuffer); //
返回的錯(cuò)誤信息
//
輸出錯(cuò)誤信息
if(
errorBuffer )
{
::MessageBox(0, (char*)errorBuffer->GetBufferPointer(), 0, 0);
d3d::Release<ID3DXBuffer*>(errorBuffer);
}
if
(FAILED(hr))
{
::MessageBox(0, "D3DXCreateEffectFromFile() - FAILED", 0, 0);
return false;
}
|