顶点着器详解(VertexShaders)

顶点着⾊器详解(VertexShaders)
学习了顶点处理,你就知道固定功能流⽔线怎么将顶点从模型空间坐标系统转化到屏幕空间坐标系统。虽然固定功能流⽔线也可以通过设置渲染状态和参数来改变最终输出的结果,但是它的整体功能还是受限。当我们想实现⼀个外来的光照模型,外来的Fog或者点⼤⼩计算⽅式,等等,我们可能就放弃使⽤固定功能流⽔线,转⽽使⽤CPU来实现这些计算。
使⽤vertex shaders,它⽤⼀段⼩程序替换固定功能处理。这段⼩程序的输⼊是模型空间的顶点,输出齐次剪裁空间的顶点,并且还携带⼀些信息,如:per-vertex diffuse 和 specualr,雾,透明度,纹理坐标和点⼤⼩。
这⼀节我们将先讲述vertex shaders的汇编语⾔编程模型。
Vertex Shader Arichitecture
Direct3D对于不同的图形处理器有不同的vertex shaders架构版本。每个版本都有不同数⽬和类型的寄存器和不同的指令集。⼀般情况,⾼版本⼀般是低版本的衍⽣品,提供了更多的指令和更少限制。我们将先看完整的1.1版本,然后讨论各个版本在上⾯的增量。
DirectX 9.0c ⽀持的vertex shader版本包括1.1,2.0,2.x和3.0。这些版本的汇编语⾔的语法标志是:vs_1_
1, vs_2_0, vs_2_x和vs_3_0。在⽼的SDK和⽂档⾥⾯,你也许会看到vs_2_a和vs_2_b,他们已经融合到了vs_2_x版本⾥⾯了。当你安装SDK的时候,vertex shader的特殊版本也将会安装,如vs_2_sw和
vs_3_sw,这两个版本只⽤于软件处理,专门⽤于做模拟和调试之⽤。shader的软件版本实现了2.0和3.0架构所有的功能,并且⼤部分的shader验证将被放开。
shader 版本vs_1_1vs_2_0vs_2_x vs_3_0
指令数⽬128256>=256>=512
所有的架构都共享⼀个公共的执⾏模型。执⾏程序称做shader,它在每个顶点上执⾏⼀次。shader包含⼀个或多个指令,每个指令由⼀个操作码与0个或多个操作数组成的。shader可以访问五组不同的寄存器:顶点数据的input寄存器,渲染参数的const寄存器,⽤于查询const寄存器的地址寄存器,存储临时数据的临时寄存器,采样纹理的采样寄存器,shader输出结果的ouput寄存器。不同类型寄存器的数⽬如下表。
Version a0aL bn cn in on p0rn sn vn
vs_1_1100>=96013012016
vs_2_01116>=2561613012016
vs_2_x1116>=25616131>=12016
vs_3_01116>=2561612132416
每个临时寄存器都存储⼀个四维的向量值,⼤多数指令都是在四维向量上进⾏操作。每个值都是⼀个浮点值,⼀般有6个⼩数数字。指令⼀般是通⽤算术运算,如加,乘和⼀般的向量计算(点积,向量矩阵乘法)。跟⼀般的CPU不⼀样的是,低版本的shader⼀般不⽀持流控制,以便于shader更加简单和容易硬件加速。
input 寄存器
顶点组件通过合适的顶点声明映射到对应的semantics上。semantics使⽤dcl_usage指令与shader的输⼊寄存器关联。输⼊寄存器是只读的,只能⽤作顶点shader指令的数据源。虽然不同的操作数能应⽤到不同的修饰符,每个指令只能引⽤⼀个input regesiter。
const 寄存器和地址寄存器
不随着每个顶点变化的参数可以存放在const 寄存器。所有的shader版本都⽀持浮点const,整数const ,
bool const只能⽤在2.0以上的shader版本。每个指令⼀次只能访问⼀个const 寄存器,但是不同的源操作数可以访问带有修饰符的同⼀个const寄存器。 const寄存器值在shader ⾥⾯⼀般是通过def, defb和defi指令定义的,它也能来⾃于设备,通过⽅法:SetVertexShaderConstantF, SetVertexShaderConstantB 和 SetVertexShaderConstantI。你可以认为通过shader 指令定义的值为local const,⽽通过设备⽅法定义的const为global const。
地址寄存器是⼀个带符号的整数,记录了距离base const寄存器的位置偏移量。const寄存器是只读的,地址寄存器是可写的。当地址寄存器越界,它的值将是(0,0,0,0)。在使⽤地址寄存器之前,必须先初始化它。
shader 1.1 只能使⽤地址寄存器的x组件来作为索引。并且地址寄存器只能被设置成mov指令的⽬的地,当使⽤它的时候它将进⾏四舍五⼊成整数。 shader 2.0以上的版本提供了更加通⽤的⼀种使⽤⽅式。寄存器的四个组件都可以⽤来作为索引,能够同时索引const寄存器的不同的部分。mova指令⽤于设置地址寄存器的值。
output寄存器
output寄存器⽤于存储shader计算的结果。output寄存器是可写的。它⽤来存储顶点的同次剪裁空间的坐标以及每个顶点相关的数据,如颜⾊,纹理坐标信息。3.0之前的shader版本,output寄存器将会分
别命名。位置寄存器oPos, 颜⾊寄存器oD0和oD1,fog 寄存器oFog,点⼤⼩寄存器oPts和纹理坐标寄存器oT0到oT7。每个顶点shader都得写oPos的四个组件。fog系数和点⼤⼩的缩放值将分别取oFog和oPts寄存器的x组件。oFog和oPts将被缩放到【0,1】区间。在shader 3.0版本,output寄存器将会使⽤dcl_usage指令定义。
临时寄存器
顶点shader⾥⾯通常会有⼤量的⼯作。shader 通常会将数据从输⼊寄存器移动到临时寄存器,然后在临时寄存器上执⾏计算,最后把结果写⼊到输出寄存器。其他类型的寄存器在⼀个指令可能只使⽤⼀次,但是临时寄存却有可能使⽤多次。在⼀个指令⾥⾯有可能有3个临时寄存器被读,⼀个被写。任何读取⼀个没有写⼊数据的临时寄存器都会产⽣错误。
循环计数器寄存器
shader 2.0或者更⾼版本使⽤loop和endloop指令来控制流,循环计数寄存器al包含计数器的当前值。在循环体外部,这个值是未定义的。在循环体内部它的值将是固定数组的偏移量。在shader 3.0中,循环计数寄存器将⽤于索引输出寄存器和const数组。
条件寄存器
shader 2.x 或者更⾼版本将提供了条件寄存器,它包含⼀个boo值的四维向量。bool值将⽤于执⾏条件控制流。setp_comp是唯⼀的赋值条件寄存器的指令。条件寄存器bool值⽤来控制if ,callnz,breakp指令。
采样寄存器
shader3.0 采⽤采样寄存器来访问纹理。采样寄存器本⾝使⽤texldl指令来采样纹理。采样寄存器在使⽤前必须使⽤dcl_usage声明。使⽤采样寄存器,顶点shader能够执⾏纹理查询。
寄存器修饰符
每个指令默认情况下操作在源操作数和⽬的操作数的四维向量值上。为了提⾼顶点shader的灵活性,并且使指令数减少,每个操作数可以包含⼀个修饰符来提取某⼏个维度的值。对于顶点shader指令,共有四种修饰符:⽬的操作数写掩码,源操作数multiplex,源操作数negation和绝对值操作数。修饰符的语法如下:
⽬的寄存器写掩码: r.xyzw
源寄存器multiplex: r.[xyzw][xyzw][xyzw][xyzw]
源寄存器negation:  -r
绝对值:  r_abs
逻辑negation:  !r
multiplex修改符允许⼀个四维向量从⼀个源寄存器的四个组件构造得到。⼀个组件可能被组合到⼀个向量的多个组件。
⼀个操作数也能使⽤多个修饰符,多个修饰符也能应⽤到⼀个指令⾥⾯。
Vertex Shader 1.1 架构
shader 1.1架构是最简单的架构,没流控制也没有条件分⽀。最少有96个顶点shader const 寄存器。D3DCAPS9::MaxVertexShaderConst定义最⼤数⽬的const寄存器。constant寄存器在被地址寄存器的x组件索引。
指令⽤于声明,基本运算,矩阵计算,简单⽐较以及基本光照计算。更⾼版本的shader能完全⽀持1.1的指令,只是在某些指令上有些微⼩的变动。Vertex Shader 2.0 架构
2.0架构保留了1.1所有的指令和寄存器,并且增加了很多额外的功能。版本2.0主要的改进增加了是静态流控制。静态条件指令包括subroutine,分⽀和循环指令。在静态流控制⾥⾯,计算分⽀点的条件表
达式指向那些在shader执⾏过程中是const的值。使⽤静态流控制,执⾏固定次数的循环,并且条件执⾏遵循同样的路径使⽤同⼀组constants来绘制primitives。 primitives的不同的batch处理可以通过改变constants来改变它们的⾏为。所有的流控制指令都是成对出现,并且属于⼀个指令block。
提供了新的constant寄存器⽂件来定义了⽤于管理控制流的constants。在控制流⾥⾯,你能写⼀个顶点shader应⽤到不同类型的顶点。定义流的constants可以在两次draw primitives调⽤之间重新更新。
2.0版本或者更⾼版本也增强了地址寄存器的使⽤,提供了新的bool和整数寄存器⽂件。寄存器a0的四个组件都可以⽤来索引浮点数寄存器⽂件。bool和整数寄存器⽂件不可以被索引。地址寄存器的任何⼀个组件都可以⽤作⼀个索引,但是在⼀个指令⾥⾯的所有的源操作数必须⽤同样的组件和base寄存器。
地址寄存器能够使⽤mova指令来赋值,mov指令⽤于向临时寄存器和output寄存器写值。新的算术指令包括:abs,crs,lrp,nrm, pow, sgn和sincos指令。bool constant寄存器⽤于if,else,endif指令的条件分⽀。每个寄存器都有⼀个组件包含⼀个bool值。bool 寄存器⽂件的值能通过defb指令赋值。⾮条件suroutine使⽤call调⽤。subroutine的调⽤对象是lable和ret之间的block。使⽤bool寄存器的条件subroutine使⽤callnz调⽤。整数constant寄存器⽂件⾥⾯每个寄存器都有四个组件,但是第四个组件必须是0。寄存器控制了rep, endrep,loop和endloop循环的执⾏次数。rep使⽤⼀个重复次数定义了⼀
个简单的循环,在循环过程中,不会访问内部计数寄存器。    Loop指令定义了⼀个循环,这个循环通过al 循环计数寄存器控制内部计数器。在循环开始之前,就初始化这个寄存器。每当循环⼀次,它就加1。这个循环计数寄存器也可以像地址寄存器⼀样来索引constant寄存器数组。整数寄存器⽂件的值能通过defi定义或者通过API SetVertexShaderConstantI⽅法定义。
顶点Shader 2.x 架构
顶点2.x引⼊了版本2.0架构的扩展。在版本2.0的基础上增加了条件,静态流控制的深度嵌套和动态流控制指令。D3DCaps9的VS20Caps(它是⼀个
D3DVSSHADERCAPS2_0结构)描述了可选的⽀持情况。2.x可选的⽀持包括predicate寄存器,动态流控制,⼤于12个临时寄存器和静态流控制的深度嵌套。typedef struct _D3DVSHADERCAPS2_0
{
DWORD  Caps;
INT  DynamicFlowControlDepth;
INT NumTemps;
INT StaticFlowControlDepth;
} D3DVSHADERCAPS2_0;
如果Caps的D3DVS20CAPS_PREDICATION为被设置,设备将⽀持predicate寄存器p0和它相关的指令setp_comp,if, callnz和breakp。predicate寄存器是⼀个四维的bool向量,只能通过setp_cmp赋值。
NumpTemps制定了能⽀持的临时寄存器rn的数⽬,⼀般⾄少是12个,它的实际值将在【12,32】之间,D3DVS20_MIN_NUMTEMP和
D3DVS20_MAX_NUMTEMPS指定了最⼤值和最⼩值。
动态流控制的指令包括if_comp和break_comp。如果dynamicFlowControlDepth不是0,它将能⽀持。
顶点Shader 3.0架构
顶点shader 3.0放开了很多限制,产⽣了input和output寄存器⽂件,增加了saturate指令修饰符,并且使⽤新的采样寄存器和相关指令来做纹理采样。临时寄存器的数⽬上升到32个。最⼩的指令slot可以达到512。input 和 output寄存器⽂件可以像浮点const 寄存器⼀样被索引。它允许shader在⼀个循环⾥
⾯访问input寄存器,然后产⽣ouput。output寄存器不⽤指定特定的名字,它就像input寄存器,统⼀命名为on。它可以使⽤dcl_usage指定把output寄存器与⼀个semantics关联。这样就可以将shader的output映射到像素shader的input semantics。
sn采样寄存器与dcl_texture关联。声明之后,就可以使⽤texldl指令从对应的纹理采样。
Shader指令语法
在内部Direct3D使⽤⼀个DWORD数组来encode⼀个shader程序。这个encoding可以被认为是⼀个shader程序的机器语⾔。因为很难直接创建⼀个DWORD 数组程序指令,SDK提供了⼯具把⼀个shader程序⽂本编译成机器语⾔。
shader指令的语法也跟⼤多数CPU汇编语⾔类似,⾸先是操作码,然后是操作数。shader 程序⽂本⾸先被解析成⼀串可解析的符号。空格和注释将会被忽略。跟其它汇编语⾔不同的是,它不必⼀⾏只能允许⼀条指令。⼀⾏可以写多条指令。
每个shader指令是由⼀个操作码和多个操作数组成,并且他们都是⼤⼩写敏感的。通常const寄存器操作数⼀般是c0....。但是,可以通过地址寄存器a0来索引const寄存器,c[16+a0.x] 或者c16[a0.x]。
执⾏模型
顶点shader的执⾏模型是相当简单,每个指令按照它在DWORD⾥⾯的次序执⾏。每个顶点shader的开始都必须放置⼀个vs指令,⽤来定义顶点shader的架构版本。 3.0之前的版本,都必须把值存放在oPos寄存器;3.0版本,output postion semantic关联的寄存器必须要赋值。
顶点软件处理
使⽤软件或者混合处理创建的设备可以在CPU上运⾏顶点shader。顶点软件处理能够执⾏所有的顶点shader版本。
顶点shader 1.1 指令
顶点shader指令分成两组,⼀组是简单指令,⼀组是复杂指令。简单指令只在⼀个slot⾥⾯执⾏,复杂指令需要在多个slot⾥⾯执⾏。1.1⽀持的指令如下:
Instruction Slots Function
add d,s0,s11add
dcl_usage d——declare input register
def d,v0,v1,v2,v3——constant definition
dp3 d,s0,s113D dot product
dp4 d,s0,s114D dot product
dst d,s0,s11distance
exp d,s<=10full-precision exponentiate(指数)
expp d,s1patial-precision exponentiate
frc d,s<=3fractional part(⼩数部分)
dst指数
lit d,s1lighting
log d,s<=10full-precision logarithm(全精度对数)
logp d,s1partial-precision(半精度对数)
m3*2 d,s0 ,s1<=2vector, 3*2 matrix product
m3*3 d,s0,s1<=3vector, 3*3 matrix product
m3*3 d,s0,s1<=4vector, 3*4 matrix product
m4*3 d,s0,s1<=3vector, 4*3 matrix product
m4*4 d,s0,s1<=4vector,4*4 matrix product
mad d,s0,s1,s21multiply accumulate
max d,s0,s11maximum
min d,s0,s11minimum
mov d,s1copy
mul d,s0,s11multiply
nop1no operation
rcp d,s>=1reciprocal(倒数)
rsp  d,s>=1reciparocal square root
sge d,s0,s11>= compare
slt d,s0,s11< compare
sub d,s0,s11sutract
vs _major_minor_——shader version
在详细讨论每个指令之前,我们先看看⼀个简单的shader程序。这个shader 程序把输⼊顶点数据直接写⼊到对应的output寄存器。
vs_1_1
dcl_position v0
dcl_color0  v1
dcl_color1  v2
dcl_fog      v2.w
dcl_texcoord0 v3
dcl_texcoord1 v4
dcl_texcoord2 v5
dcl_texcoord3 v6
mov oPos, v0
mov oDo , v1
mov oD1,  v2.xyz
mov oFog, v2.w
mov oT0,v3
mov oT1,v4
mov oT2,v5
mov oT3,v6
指令声明
 每个顶点shader都必须使⽤vs指令声明它的版本号码,⽽且这个指令必须是这个shader程序的第⼀个指令。在shader通过SetVertexShader绑定到到设备的时候,顶点shader的constants也需要绑定。def 指令可以⽤来定义⼀个四浮点值的constant寄存器。def指令必须出现在版本指令之后,在任何计算指令之前。
def d,v0,v1,v2,v3 ------------->    d<---------(v0,v1,v2,v3)
为了将顶点的input寄存器映射到顶点对应的组件,dcl_usage指令被使⽤。
dcl_positionn s
dcl_blendweightn s
dcl_blendindicesn s
dcl_normaln s
dcl_psizen  s
dcl_texcoordn s
dcl_tangentn s
dcl_binormaln s
dcl_tessfactorn s
dcl_colorn s
dcl_fogn s
dcl_depthn s
dcl_sample s
基本算术指令
mov指令⽤来拷贝数据从源操作数到⽬的操作数。基本的运算执⾏只使⽤add,sub,mul 和mad指令。向量的加减使⽤add和sub指令。
mov d ,s    d<-------s
add d,s0,s1  d<---------(s0x+s1x,s0y+s1y,s0z+s1z,s0w+s1w)
sub d, s0,s1  d<---------(s0x-s1x,s0y-s1y,s0z-s1z,s0w-s1w)
mul d,s0,s1  d<---------(s0x s1x,s0y s1y,s0z s1z,s0w s1w)
mad d,s0,s1,s2  d<---------(s0x s1x + s2x, s0y s1y +  s2y, s0z s1z + s2z,  s0w s1w + s2w)
rcp  d, s  只计算w 组件。
如果sw = 1,  d = (1,1,1,1); 如果sw = 0, d = (⽆穷⼤,⽆穷⼤,⽆穷⼤,⽆穷⼤);否则,d= (1/sw,1/sw,1/sw,1/sw)。
rsp d,s
如果abs(sw) = 1, d = (1,1,1,1);如果abs(sw) = 0,d = (⽆穷⼤,⽆穷⼤,⽆穷⼤,⽆穷⼤);否则 d= (1/squart root(sw), 1/squart root(sw),1/squart
root(sw),1/squart root(sw))
dp3 d, s0,s1
d = (f,f,f,f)  f = s0x s1x + s0y s1y + s0z s1z
dp4 d, s0,s1
d= (f,f,f,f)  f = s0x s1x + s0y s1y + s0z s1z + s0w s1w
min d , s0,s1
d = (min(s0x,s1x),min(s0y,s1y),min(s0z,s1z),min(s0w,s1w))
max d, s0,s1
d = (max(s0x,s1x),max(s0y,s1y),max(s0z,s1z),max(s0w,s1w))
exp d,s
d = (f,f,f,f) f=2为底指数为sw的幂
log  d,s
如果|sw| = 0 ,d = (负⽆穷⼤,负⽆穷⼤,负⽆穷⼤,负⽆穷⼤);否则,d= (f,f,f,f)  f= log2(|sw|)
矩阵指令
m3*2, m3*3,m3*4,m4*4都是向量与矩阵相乘的指令。他们第⼀个操作数是向量,第⼆次操作数是矩阵。矩阵存放在连续的寄存器⾥⾯,并且在同⼀个寄存器⽂件⾥⾯。只有4*4,3*4修改了所有的四个组件,m3*2只修改xy,m3*3和m4*3只计算xyz。
⽐较指令
虽然1.1⾥⾯不可以使⽤分⽀指令,但是执⾏⼀些有限的⽐较也是可能的。如果你想要在diffuse color上再增加⼀个color。既然分⽀计算不允许,你只能写两个shader。⼀个增加颜⾊,⼀个不增加颜⾊。然⽽,你也可以在⼀个shader⾥⾯实现,当你不想增加的时候,另外⼀个颜⾊是0。sge和slt指令让你可以这么多。sge d,s0,s1
d = (s0x >= s1x, s0y>=s1y,s0z >= s1z, s0w >= s1w)。 True的时候组件是1.0, False的时候组件值是0.0。
slt  d,s0,s1

本文发布于:2024-09-21 23:28:28,感谢您对本站的认可!

本文链接:https://www.17tex.com/tex/3/380310.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:寄存器   指令   顶点   版本   地址   组件   循环
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2024 Comsenz Inc.Powered by © 易纺专利技术学习网 豫ICP备2022007602号 豫公网安备41160202000603 站长QQ:729038198 关于我们 投诉建议