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