HLSL有关的基础知识

来源:互联网 发布:吓人软件大全 编辑:程序博客网 时间:2024/05/01 09:41

 

主要是最近在学习XNA中有关一些效果的东西,发现了官网上的2D的一些FX效果相当好。其代码给出如下链接http://creators.xna.com/en-US/sample/spriteeffects 然后就查了一下HLSL这个范例里面的FX还都不算太难,可以理解。

一个简单的HLSL程序,返回一个材质的各点的颜色。

sampler s0 : register(s0);

float4 main(float2 tex : TEXCOORD0) : COLOR

{

     return tex2D(s0,tex);

}

HLSL,颜色表示。

        使用MPC的custom shader,由于在MPC中需要使用HLSL编写shader,所以首先要了解HLSL里面的颜色表示法,HLSL中,尤其是MPC中,颜色基本都是使 用XRGB表示(X代表保留位),一般将其表示为一个四维矢量,数据类型是float4。比如float4 color;,那么接下来就可以用color.x,color.y,color.z,来分别访问R,G,B三个分量了。

        众所周知,对于24位,或者32位图像来说来说,RGB每个通道都是8位,转化为10进制就是0-255的整数,不过在HLSL中情况不同,0对应的是 float(0.0)而255对应的是float(1.0),所以白色表示不再是(255,255,255)而是float3(1.0,1.0,1.0) 或者float4(1.0,1.0,1.0,1.0)。

程序解释

        第一行定义了一个sampler,将pixel shader寄存器s0与我们定义的sampler s0绑定,在MPC中,寄存器s0就是影片图像所对应的纹理。

       float4 main(float2 tex : TEXCOORD0) : COLOR,它代表的意思是,输出一个float4变量,将这个float4变量作为COLOR(作为render target 0中的颜色,关于render target,后面会解释)。这个函数有一个输入参数,就是作为0号纹理坐标(TEXCOORD0)的float2类型的tex。

       HLSL中的最简单的2D纹理映射函数是tex2D(s,t);两个参数,第一个是一个sampler,它代表的是目标纹理和纹理过滤,寻址方式,第二个 参数是一个float2,也就是纹理坐标uv。它返回一个颜色值float4,这就是:纹理s中,纹理坐标t处的颜色之意。

      在这里就需要说明作为输入的TEXCOORD0的内容了,在MPC中,我们知道原纹理要经过shader处理后写入一个新的同样大小的纹理中,所以事实上 它绘制了一个full rect quad(全区域矩形)并把新的纹理作为该次绘制的输出对象(即绘制的东西就不显示于屏幕上,而绘制到该纹理中)。

练习下看代码:

      变亮20%

sampler s0 : register(s0);

float4 main(float2 tex : TEXCOORD0) : COLOR

{

float4 color0 = tex2D(s0,tex);

return color0+color0*float4(0.2,0.2,0.2,0.0);

}

常用MUL函数

    上面color0*float4(0.2,0.2,0.2,0.0)是对位相乘。即:

    color1 = color0*scale;

    则代表

    color1.x = color0.x*scale.x;

    color1.y = color0.y*scale.y;

    color1.z = color0.z*scale.z;

    color1.w = color0.w*scale.w;

    如果需要得到矢量点积,需要用mul(x,y);

    比如float lum = mul(color0.xyz,float3(0.3,0.59,0.11) );其中color0.xyz表示以color0的xyz元素构成的一个3维向量,lum = color0.x*0.3 + color0.y*0.59 + color0.z*0.11;

读代码练习:

灰度转换shader

sampler s0 : register(s0);

float4 main(float2 tex : TEXCOORD0) : COLOR

{

    float4 color0 = tex2D(s0,tex);

    float lum = mul(color0.xyz,float3(0.3,0.59,0.11) );

    return float4(lum,lum,lum,1.0);

}

HLSL的PS版本的问题

     会用color0+color0*float4(0.2,0.2,0.2,0.0)而不是color0*float4(1.2,1.2,1.2,1.0) 是由于ps版本问题。版本越高,HLSL就越灵活,当然,硬件功能要求也就越强。对于ps1.x来说,常量寄存器不能储存超过1的数值,所以如果用 float4(1.2,1.2,1.2,1.0)运行时会直接当作float4(1.0,1.0,1.0,1.0)处理,也可以说,是HLSL编译器比较 弱智,不能自动转化为color0+color0*float4(0.2,0.2,0.2,0.0)。

     (所以会看到wow为每个ps版本写了不同的代码。)

如何获得相邻像素

       如下图,设原纹理大小为M*N个texel,于是每个texel的uv尺寸为(1.0/M,1.0/N),所以可以通过将tex偏移再tex2D来取得相邻处的texel。

       

 

GPU汇编指令

       add——vec1+vec2——矢量对位相加

       mul——vec1*vec2——矢量对位相乘

      mad——vec1*vec2+vec3——矢量对位乘加

       dp3——vec1.x*vec2.x+vec1.y*vec2.y+vec1.z*vec2.z——矢量点积

       等运算,而标量单元负责的是诸如

       pow——scar1^scar2——标量幂运算

       rcp——1.0/scar1——标量倒数运算

       rsq——1.0/sqrt(scar1)——标量方根倒数

       log——log2(scar1)——2为底的对数

       等等

      而纹理单元,执行的自然是

       texld——纹理采样函数

    

在MPC的HLSL编译目标(compile target)选项中,有意义的包括ps_1_1,ps_1_3,ps_1_4,ps_2_0,ps_3_0。HLSL code的结构复杂度、指令的复杂性、指令数多寡决定了它最低需要的编译目标。

ps_1_1和ps_1_3在实际应用中差别甚微。都属于限制非常大的编译目标,主要限制是:

1: 最大指令数目限制严重,不能超过8条算术指令(尽管其实可以不超过3维的矢量运算与标量运算并发(co-issue)所以最多是16条算术指令,但是不用 指望HLSL编译器能够强大到如此程度)和4条纹理指令(ps1.1-1.3的纹理指令其实比较复杂,属于组合功能方式,但是在MPC中基本用不上)

2:无复杂运算指令,诸如rcp,rsq,log,pow等全部没有

3:寄存器数值范围很窄,比如float s1 = 1.5 * s2;就会自动变成float s1 = 1.0 * s2;(也就是1.5超出了范围),只能写成float s1 = s2+0.5*s2;才能正确编译

4:无dependent texture read。而tex基本可以认为无法操作

5:分支什么的就基本不用指望了。除了像if(s1>0.0){s2 = 1.0;}else{s2 = 0.0;}这种极其简单的。

ps_1_1级别的硬件,比较流行的是Nvidia Geforce 3

ps_1_3级别的硬件,比较流行的是Nvidia Geforce 4Ti

ps_1_4比ps_1_3适应性广一些包括

1:最大指令数限制有所松动,算术指令翻倍,纹理指令翻2倍。

2:支持一定限度的dependent texture read(1级),tex可以进行一定程度的操作

ps_1_4级别的硬件,比较流行的是ATI Radeon8500,9000-9200

事实上,由于ps1.x属于定点数结构,所以有一些移位指令可以使用,比如mul_x2,add_x2代表将结果相加后左移一位,相当于乘二。这也是为何第六节的第四个程序有ps1.x版本的原因。不过说到底,ps1.x不是为HLSL准备的。

ps_2_0是HLSL诞生时的默认编译对象

1:支持绝大多数的HLSL函数。

2:64条算术指令,32条纹理指令,最多4级的dependent texture read

3:全部浮点流水线,数据范围精度增强。

4:尽管ps_2_0没有硬件支持的动态分支,不过可以实现一定程度的分支,只不过每个分支都要跑一遍。也就是效率不会很高

ps_2_0级别的硬件包括Nvidia GeforceFx系列(ps_2_a)ATI Radeon 9500及以上X700以下(ps_2_0)X700,X800,X850系列(ps_2_b)

ps_3_0是当前PC上的最高pixel shader版本

1:比ps_2_0增加了一些指令

2:硬件支持分支,循环

3:最大指令数大大增加,不再有纹理算术指令数量分配的限制

4:dependent texture read级数没有限制

ps_3_0级别硬件包括Nvidia Geforce 6系列,7系列,ATI Radeon X1xxx系列

所以,编写HLSL请考虑自身硬件的功能等级。

以下是一些简单的提升性能的考虑

1:如果可以,使用ps1.x编译对象能获得最佳性能,如果该HLSL code不能使用ps1.x编译对象,那么请使用可用的最高ps版本的编译对象,如Geforce6系列就请使用ps_3_0编译对象。

2:在许可的情况下尽量减少使用复杂运算指令,诸如pow,sin,cos,asin,acos,sqrt等。

3:对于Nvidia Geforce Fx和6系列,推荐将float(FP32)替换为half(FP16)以提高性能。

4:对于向量来说,选择需要的最小维数,比如能用float3(half3)就不要用float4(half4),能用标量就不要用矢量

5:动态分支不要太多,除非你用的是X1800XT……