Shader Model 3
Using Vertex Texture
頂點紋理白皮書中文版
?
?
翻譯者
周波
zhoubo22@hotmail.com
版權所有
? Philipp Gerasimov
????????????????? Randima (Randy) Fernando
???? Simon Green
?????????? NVIDIA Corporation
?
僅以此文贈與
Rita 19
周
歲生日快樂
?
?
?
Shader Model 3.0:Using Vertex Textures? SM3:
使用頂點紋理
?
?????
隨著
GPU
可編程特性的發展,
Vertex Shader
與
Pixel Shader
的差別越來越大。現在,
Geforce6
系列
gpu
將
Vertex Shader
與
Pixel Shader
之間的通用性特征向前發展了一大步。這篇文章特別介紹了
Shader Model3
的一項技術,
Vertex Shader Fetch
。它允許
Vertex Shader
像
Pixel Shader
一樣從紋理中讀取數據。
?????
在現代圖形處理中,頂點處理的性能表現不是受制于內存帶寬、
cpu
速度,就是受制于
Pixel Shader
的處理能力。但這也意味著你可以實現一個復雜的
Vertex Shader
,提高畫質,而且不會有多大損失。
Vertex Shader
的制作成本比
Pixel Shader
高,所以在最新的
6800
芯片里,
Vertex Shader
的數目要少于
Pixel Shader
。這樣,我們就可以安心地實現一打漂亮的效果,比如流體的模擬等等。
?????
這篇白皮書將同時向您展示如何在
OPENGL
以及
DIRECTX
中實現
Vertex Texture
。最后,我們將用一個游戲的范例向您演示使用
Vertex Texture
的情況
。
?????
Specification
詳解
?
????? DIRECTX
與
OPENGL
中都可以使用
Vertex Texture
。
?
?????? DIRECTX9
?
????? MS DX9SDK
的開發文檔中已經包括了
VERTEX TEXTURE
的詳細說明。
????? Vertex Shader3(
即使用Vertex Shader3編譯器生成的Shader)支持
vertex_fetch
,
4
種紋理樣本。
Vertex Texture
,單從名稱上看就同傳統的
PIXEL TEXTURE
類似,但是同
PIXEL TEXTURE
比起來有一些差別,
?
?????
硬件無法直接支持
Bilinear Trilinear
過濾,但是您可以手動在
Vertex Shader
中實現
?????
反鋸齒,內容同上。
?????
自動
Mipmap LOD
,
無效
?
????? D3DCAPS
成員
MaxVertexShader30InstructionSlots
標識
Vertex Shader3
中代碼的上限行數。
MaxVShaderInstructionsExecuted
標識了
Vertex Shader
的上限代碼行數,包括
Texture Fetch
的數目。
????? DIRECTX9
支持軟件
Vertex Processing
模式下使用
Vertex Texture
,這樣甚至當硬件不支持
Vertex Texture
時也可以運行。
????? 6800
支持使用
D3DFMT_R32F and D3DFMT_A32B32G32R32F
的紋理格式實現
Vertex Texture
。
?
OPENGL
?
?????
頂點紋理查找通過
NV_V_PROGRAM3
擴展實現。詳情請參閱
http://www.nvidia.com/dev_content/nvopenglspecs/GL_NV_vertex_program3.txt
這是標準
ARB vertex program language
的一項
Option
(
操作)。這就意味著你可以調用現有的ARB
API
,載入程序,設置參數。在程序開頭加入以下代碼就可以了:
????? OPTION NV_VERTEX_PROGRAM3
?
在程序里加入
Vertex Texture
?
?????
使用
Vertex Texture
的步驟如下:
??????????
檢查硬件的
Vertex Texture
支持情況
??????????
創建
Vertex Texture
資源
??????????
在
Vertex Shader
中加入需要的代碼
?????
下面具體來看看怎樣在
DIRECTX
以及
OPENGL
中實現。
?????
DIRECTX
?
第一,檢查硬件是否支持,否則將不得不用軟件方式實現。調用
IDirect3D9::CheckDeviceFormat
里的
D3DUSAGE_QUERY_VERTEXTEXTURE
旗標查詢
硬件支持的
Vertex Texture
格式。
Software Vertex Texture
支持所有
Vertex Texture
格式。
?
OPENGL
?
????? OPENGL
里只需要檢查硬件是否支持
NV_VERTEX_PROGRAM3
擴展。
GLUT
庫的
glutExtensionSupported
函數可以完成這項任務。
Vertex Texture
數目的上限用下列代碼獲得
?????
glGetIntegerv(
MAX_VERTEX_TEXTURE_IMAGE_UNITS_ARB, &vtex_units)
?? 6
系列
GPU
最大支持
4
個活動紋理(
Active Texture
)。你可以盡情的在
Vertex Shader
中調用它們,不過要注意Vertex Shader的代碼行數。
??
創建
Vertex Texture
資源
?
?????? DIRECTX9
?
?????
庫中的任何紋理創建函數都可以創建頂點紋理,
IDirect3D9::CreateTexture,
IDirect3D9::CreateCubeTexture, IDirect3D9::CreateVolumeTexture
等等。
?????
當使用
SVP
時,頂點紋理必須創建在
D3DPOOL_SCRATCH
池中。
?
?????? OPENGL
?????
基本紋理調用操作已經包括了
Vertex Texture
的綁定,使用
GL_TEXTURE_2D
。目前只有
GL_LUMINANCE_FLOAT32_ATI
與
GL_RGBA_FLOAT32_ATI
這
2
種格式支持
Vertex Texture
。這些格式都包含了
1
個或
4
個
32bit
浮點數據通道。注意,使用其他的紋理格式,或者使用不支持的過濾方式都可以導致驅動調用
Software Vertex Processing
處理,導致性能下降。
?????
示例代碼如下:
????? GLuint vertex_texture;
glGenTextures(
1, &vertex_texture);
glBindTexture(
GL_TEXTURE_2D, vertex_texture);
glTexParameteri(
GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(
GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,GL_NEAREST_MIPMAP_NEAREST);
glTexImage2D(
GL_TEXTURE_2D, 0, GL_LUMINANCE_FLOAT32_ATI, width, height, 0,GL_LUMINANCE, GL_FLOAT, data);
?????
在
Vertex Shader
里訪問
Vertex Texture
?
?????? DIRECTX9
?
程序調用
IDirect3DDevice9::SetTexture
設置
Vertex Texture
,樣本索引為
D3DVERTEXTEXTURESAMPLER1
到
D3DVERTEXTEXTURESAMPLER3
。在
D3DPOOL_DEFAULT
里創建的
Vertex Texture
同時也可以設置成
PIXEL TEXTURE
。
????? Vertex Shader
里的紋理樣本必須使用
DEL_SAMPLEType
標識。
//
匯編代碼
dcl_texcoord0 v0
dcl_2D s0
texldl
r0, o0, s0
?
// HLSL / Cg
代碼
sampler2D
tex;
vDisplacement = tex2Dlod ( tex, t ); // t.w
包括
MIPMAP LOD
數據
?
OPENGL
?
????? VP
的紋理查找功能通過
TEX,TXB, TXL or TXP
實現,就像在
Fragment Shader
里一樣(或者在其他高等級語言中比如
CG
)
。與
Fragment Shader
的差異是
,
紋理查找功能無法自動計算
LOD
。
????? LOD
的意義是確定紋理在屏幕上縮放的尺寸大小。一般根據紋理坐標象素的改變頻率計算,但這里的麻煩是,
Vertex Texture
由頂點訪問,硬件很難計算
LOD
值。所以你不得不自己在
Vertex Processing
里計算
LOD
。
????? MIPMAP
類似普通的
Pixel Shader
紋理,它可以為
Vertex Texture
在性能與畫質之間折中。早期的圖形處理管線中沒有
Pixel-Level
這一概念,無法計算頂點紋理的
LOD
。如果需要使用LOD我們不得不人工在
Vertex Shader
里計算
mipmap
。
?????
示例代碼如下:
????? #define maxMipLevels 10.0f
Out.HPOS = mul( ModelViewProj, vPos );
float
mipLevel = ( Out.HPOS.z / Out.HPOS.w ) * maxMipLevels;
float
vDisplacement = tex2Dbias( tex, float4( t, mipLevel, mipLevel );
?????
這是根據頂點的深度計算
LOD
的算法,開銷很小,精度能夠讓人滿意。
?????
#define maxMipLevels 10.0f
Out.HPOS = mul( ModelViewProj, vPos );
float
mipLevel = ( Out.HPOS.z / Out.HPOS.w ) * maxMipLevels;
float
mipLevelFloor = floor(mipLevel);
float
mipLevelCeiling = mipLevelFloor + 1;
float
mipLevelFrac = frac(mipLevel);
float
vDisplacementFloor = tex2D( tex, float4( t, mipLevelFloor,mipLevelFloor );
float
vDisplacementCeiling = tex2Dbias(tex,
float4(
t,mipLevelCeiling,mipLevelCeiling );
float
vDisplacement = vDisplacementFloor + vDisplacementCeiling
?
Filter
過濾
????? Vertex Texture
允許紋理過濾,但是要根據硬件的支持情況。
6
系列只支持
NEAREST-NEIGHBOR
過濾模式。你也可以手動在
Vertex Texture
里實現過濾。
?
?????????? Bilinear Filtering
#define textureSize 512.0f
#define texelSize 1.0f / 512.0f
float4 tex2D_bilinear( uniform sampler2D tex, float2 t )
{
float2
f = frac( t.xy * textureSize );
float4 t00 = tex2D( tex, t );
float4 t10 = tex2D( tex, t + float2( texelSize, 0.0f );
float4
tA = lerp( t00, t10, f.x );
float4 t01 = tex2D( tex, t + float2( 0.0f, texelSize ) );
float4 t11 = tex2D( tex, t + float2( texelSize, texelSize ) );
float4
tB = lerp( t01, t11, f.x );
return
lerp( tA, tB, f.y );
}
?????????? Bilinear Filtering With Mipmapping
float4 tex2D_bilinear( uniform sampler2D tex, float4 t )
{
float2
f = frac( t.xy * miplevelSize );
float4 t00 = tex2Dbias( tex, t );
float4 t10 = tex2Dbias( tex, t + float4( texelSize, 0.0f, 0.0f, 0,0f );
float4
tA = lerp( t00, t10, f.x );
float4 t01 = tex2Dbias( tex, t + float4( 0.0f, texelSize, 0.0f, 0.0f ) );
float4 t11 = tex2Dbias( tex, t + float4(texelSize, texelSize, 0.0f, 0.0f));
float4
tB = lerp( t01, t11, f.x );
return
lerp( tA, tB, f.y );
}
?????
如果單純站在性能的角度上考慮上述算法,還是
Bilinear
最好。
Bicubic
、
Trilinear
,以及其他的過濾算法都可以在
Vertex Shader
里實現。其中,
Trilinear
過濾對性能的要求要高一點,因為
Shader
需要從不同等級的
mipmap
里訪問紋理。
?
Performance Tips
性能
?
????? 6800
可以在一秒鐘內生成
6
億多個頂點。當然,這是在
Vertex Shader
沒有任何“負載”的情況下測試的結果。如果使用
Vertex Texture Fetch
后是什么情況呢?我們的數字是每秒鐘生成
3
千
3
百多萬個位移頂點,計算了基本位移,使用
NEAREST
方式過濾。
????? 3
千
3
百多萬個位移頂點,意味著如果以每秒
30
幀的速度繪制畫面,每一幀畫面將有
100
多萬個
Displacement Vertics
位移頂點。這比現在任何一款游戲在一幀畫面里出現的頂點都要多,而且,并不是每個頂點都需要進行位移操作。你可以使用
6
系列
gpu
的動態分支功能,對每個定點是否需要進行位移操作進行預測。比如做一次
dot(V,N)
運算,測試頂點是否靠近陰影,如果遠離陰影就可以避免位移操作。這時,你就可以把節省下來的硬件資源用于處理過濾等效果上。我們推薦,如果你的
Vertex Shader
很復雜,最好在處理過程的早期就對畫面或頂點進行剪裁與剔除。
?????????? // OpenGL example
float4
vClipPos = mul( ModelViewProj, vPos );
float3
bClip = abs( vClipPos.xyz ) < ( vClipPos.www + vClipOffset );
if(
all(bClip) )
{
DoLightingAndDisplacement(
);
}
?????
還有一點非常重要,“頂點紋理不應看作連續的
RAM
。頂點紋理在提取數據時不是真正的連續讀取,而是會產生等待時間。因此使用頂點紋理的最佳方法就是先進行紋理提取,然后進行邏輯算法計算,這樣能在使用紋理提取前避免等待時間。頂點紋理不是用來代替大量的常量的陣列,而是用于減少頂點數據,這樣每個頂點只有少量的頂點紋理需要提取數據。”
——
摘自《
GPU_Programming_Guide_Chinese From NVIDIA
》
?
<Case Study>
?????
目前,一些游戲已經開始使用
Vertex Texture
。比如下面要提到的這款游戲,由
Maddox GAME
開發,
Ubi Software
發行的
Pacific Fighter
。
?????
現代游戲的設計中,飛行模擬類游戲最適合使用
dm
技術。這是因為,這些游戲的場景中包括大量的地形、河流、海洋等。
Dm
可以為這些場景提供更好的效果。讓我們看一下這款使用
Displacement Mapping
的游戲。
?????
IL-2 Sturmovik
系列游戲最近年來比較成功的飛行模擬類游戲,在中國武漢曾經進行過一場國際性比賽。游戲制作人員非常留心游戲業里出現的最新技術,并運用到他們的作品中。比如這款最新的
Pacific Fighter
,完全發揮了
6
系列
gpu
的性能。“
Vertex Shader
里可以訪問紋理是
3D
加速硬件最值得期待的技術之一。”
Yuri Kryachko
,主程序員如是說。
?????
在這款游戲中,海水的繪制非常重要。開發人員采用了
Vertex Texture
,實現了目前游戲領域中最真實的流水效果。在沒有采用
Vertex Texture
之前,開發人員一般使用凹凸貼圖模擬水面,但是與采用
Vertex Texture
和幾何位移算法實現的效果比起來有天壤之別。圖片對比如下。
?????
?????
這款游戲的
WaterShader
非常復雜,超過
140
行,用于用物理的方式計算水面的動畫,以及反射折射效果。每一個頂點的位移都是由多個
dynamic normal maps
(動態向量映射)用幾何方式計算出來的。而且
Shader
從多個紋理中讀取數據進行過濾操作,使畫面更加真實。
????? Yuri Kryacko
說,“當我們在
Vertex Shader
及
Pixel Shader
中同時使用動態分支功能時,性能得到了很大的提高。我們想再優化代碼,使用新的
Shader
,提高整體的畫質,使我們的引擎的真實性達到一個新的高度。”
?
Downloads
下載
?????
想學習關于
Vertex Texture Fetch
更多的東西嗎?從
NVIDIA
的站點上下載范例吧
?????
http://download.nvidia.com/developer/SDK/Individual_Samples/samples.html
http://download.nvidia.com/developer/SDK/Individual_Samples/effects.html