强制类型转换
为了有助于着色器的编写和所产生代码的效率,最好熟悉一下HLSL的强制类型转换机制。强制类型转换常用于扩展或缩减选定的变量以匹配要赋值的变量。例如,在下列例子中,初始化vResult时把float型常量0.0f强制转换为float4型{0.0f , 0.0f , 0.0f , 0.0f }。
float4 vResult = 0.0f;
当把一个高维数据类型如向量或矩阵类型赋值给一个低维数据类型时就会发生类似的强制转换。这些情况下,额外数据都被有效省略。例如,编写下列代码:
float3 vLight;
float fFinal, fColor;
fFinal = vLight * fColor;
这个例子中,只是把float类型的标量fColor与vLight中的第一个成员相乘,从而把vLight强制转换为float类型。fFinal等于vLight.x * fColor。
最好先熟悉一下表4,HLSL的强制类型转换规则:
表6. HLSL的强制类型转换规则
Scalar-to-scalar
一直有效。当布尔型被强制转换为整数或浮点型,false变为0,true变为1。当整数或浮点型被强制转换为布尔型,0变为false,非0变为true。当浮点型被强制转换为整数类型,值被向0舍入,这与C语言的一样截断机制一样。
Scalar-to-vector
一直有效。 该强制转换操作通过复制标量并填充到向量。
Scalar-to-matrix
一直有效。 该强制转换操作通过复制标量并填充到矩阵。
Scalar-to-structure
该强制转换操作通过复制标量并填充到结构体。
Vector-to-scalar
一直有效。 选择向量的第一部分。
Vector-to-vector
目标向量必须不大于资源向量。该强制转换操作是通过保留最左边的值,去掉多余值。这样做的目的是可以把行矩阵,列矩阵和数字结构看成向量。
Vector-to-matrix
向量大小必须与矩阵大小相等。
Vector-to-structure
结构体不大于向量,且结构体各部分均为数字则有效。
Matrix-to-scalar
一直有效。 选择了矩阵的左上部分。
Matrix-to-vector
矩阵大小必须与向量大小相等。
Matrix-to-matrix
目标矩阵在任何一维都不大于源矩阵,该强制转换操作是通过保持左上值,去掉多余值。
Matrix-to-structure
结构体的大小等于矩阵的大小,结构体的所有成员都是数字。
Structure-to-scalar
结构体必须包含至少一个数字型成员
Structure-to-vector
结构体必须至少与向量的大小一样,第一个成员必须是数字,一直到向量的大小。(译者注:即成员数量与向量大小一样)
Structure-to-matrix
结构体必须至少与矩阵的大小一样。第一个成员必须是数字,一直到矩阵的大小。(译者注:即成员数量与矩阵大小一样)
Structure-to-object
结构体至少包含一个对象的成员。该成员的类型必须和对象类型完全相同。
Structure-to-structure
目标结构必须不大于源结构。一个有效的强制转换必定存在于所有相应的源成员与目的成员之间。
结构体
正如上边第一个着色器示例显示的,在HLSL着色器中定义一个结构体常常带来方便。例如,许多着色器编写者在他们的顶点着色器代码中会定义一个输出的结构体,使用该结构体作为他们的顶点着色器主函数的返回类型。(对于像素着色器很少这样做因为大多数像素着色器只有一个float4输出。)一个结构体的例子如下,来自于NPR Metallic着色器,我们将在后边讨论该着色器。(译者注:NPR(non-photo reality),是一种独特的二w维效果)
struct VS_OUTPUT
{
float4 Pos : POSITION;
float3 View : TEXCOORD0;
float3 Normal: TEXCOORD1;
float3 Light1: TEXCOORD2;
float3 Light2: TEXCOORD3;
float3 Light3: TEXCOORD4;
};
在HLSL着色器中也可以声明结构体作为普通使用。同样遵循上边所概括的强制类型转换规则。
取样器
要是想在像素着色器中对于每一个不同的纹理贴图进行取样,必须声明一个取样器。再调用一下前边描述的着色器中的hlsl_rings():
float4 lightWood; // xyz == Light Wood Color
float4 darkWood; // xyz == Dark Wood Color
float ringFreq; // ring frequency
sampler PulseTrainSampler;
float4 hlsl_rings (float4 Pshade : TEXCOORD0) : COLOR
{
float scaledDistFromZAxis = sqrt(dot(Pshade.xy, Pshade.xy)) * ringFreq;
float blendFactor = tex1D (PulseTrainSampler, scaledDistFromZAxis);
return lerp (darkWood, lightWood, blendFactor);
}
在这个着色器中,我们在全局范围声明了一个被称为PulseTrainSampler的取样器并把它作为第一个参数传递给内部函数tex1D()(将在下一部分讨论内部函数)。HLSL取样器 有一个非常直接的映射——在基于取样器概念的API与实际硅(在负责寻址纹理和过滤纹理的3D图形处理器中)之间轮换。在一个着色器中必须为每一个你计划访问的纹理贴图定义一个取样器,不过你可以在一个着色器中多次使用给定的取样器。这种处理方法在图像处理程序中非常普遍(在ShaderX2 - Shader Tips & Tricks的"Advanced Image Processing with DirectX 9 Pixel Shaders"章节有讨论),为了给由着色器代码表示的一个内部过滤器提供数据,输入的图像被以不同的纹理坐标多次取样。例如,下面的着色器使用光栅化引擎(rasterizer)通过一对Sobel滤波器把一个高度贴图(height map)转换为一个法线贴图(normal map)。
sampler InputImage;
float4 main( float2 topLeft : TEXCOORD0, float2 left : TEXCOORD1,
float2 bottomLeft : TEXCOORD2, float2 top : TEXCOORD3,
float2 bottom : TEXCOORD4, float2 topRight : TEXCOORD5,
float2 right : TEXCOORD6, float2 bottomRight : TEXCOORD7): COLOR
{
// Take all eight taps
float4 tl = tex2D (InputImage, topLeft);
float4 l = tex2D (InputImage, left);
float4 bl = tex2D (InputImage, bottomLeft);
float4 t = tex2D (InputImage, top);
float4 b = tex2D (InputImage, bottom);
float4 tr = tex2D (InputImage, topRight);
float4 r = tex2D (InputImage, right);
float4 br = tex2D (InputImage, bottomRight);
// Compute dx using Sobel operator:
//
// -1 0 1
// -2 0 2
// -1 0 1
float dX = -tl.a - 2.0f*l.a - bl.a + tr.a + 2.0f*r.a + br.a;
// Compute dy using Sobel operator:
//
// -1 -2 -1
// 0 0 0
// 1 2 1
float dY = -tl.a - 2.0f*t.a - tr.a + bl.a + 2.0f*b.a + br.a;
// Compute cross-product and renormalize
float4 N = float4(normalize(float3(-dX, -dY, 1)), tl.a);
// Convert signed values from -1..1 to 0..1 range and return
return N * 0.5f + 0.5f;
}
该着色器只使用了一个取样器:InputImage,不过示例中八次调用了内部函数tex2D()。