随锐旗下互动传媒:

Shader Model 3 Using Vertex Texture 顶点纹理白皮书中文版

http://www.weaseek.com  2007-07-05 00:00:00  来源:e800开发者频道

翻译者 周波 游戏的范例向您演示使用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和几何位移算法实现的效果比起来有天壤之别。图片对比如下。

ectratio="t">

这款游戏的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

  •  
  •  
[责任编辑:]热门关键词:

相关文章