翻译者 周波 游戏的范例向您演示使用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
阅读推荐
相关文章