Appendix B

High-Level Shading Language Reference

B.1 Variable Types

B.1.1 Scalar Types

Image    bool: True or false value. Note that the HLSL provides the true and false keywords like in C++.

Image    int: 32-bit signed integer.

Image    half: 16-bit floating-point number.

Image    float: 32-bit floating-point number.

Image    double: 64-bit floating-point number.

 

Note:    Some platforms might not support int, half, and double. If this is the case these types will be emulated using float.

B.1.2 Vector Types

Image    float2: 2D vector, where the components are of type float.

Image    float3: 3D vector, where the components are of type float.

Image    float4: 4D vector, where the components are of type float.

 

Note:    You can create vectors where the components are of a type other than float, such as int2, half3, and bool4.

We can initialize a vector using array-like syntax or constructor-like syntax:

float3 v = {1.0f, 2.0f, 3.0f};

float2 w = float2(x, y);

float4 u = float4(w, 3.0f, 4.0f); // u = (w.x, w.y, 3.0f, 4.0f)

We can access a component of a vector using an array subscript syntax. For example, to set the ith component of a vector vec, we would write:

vec[i] = 2.0f;

In addition, we can access the components of a vector vec, as we would access the members of a structure, using the defined component names x, y, z, w, r, g, b, and a.

vec.x = vec.r = 1.0f;

vec.y = vec.g = 2.0f;

vec.z = vec.b = 3.0f;

vec.w = vec.a = 4.0f;

The names r, g, b, and a refer to the exact same component as the names x, y, z, and w, respectively. When using vectors to represent colors, the RGBA notation is more desirable since it reinforces the fact that the vector is representing a color.

B.1.2.1 Swizzles

Consider the vector u =(ux, uy, uz, uw) and suppose we want to copy the components of u to a vector v such that v = (uw, uy, uy, ux). The most immediate solution would be to individually copy each component of u over to v as necessary. However, the HLSL provides a special syntax for doing these kinds of out-of-order copies called swizzles:

float4 u = {1.0f, 2.0f, 3.0f, 4.0f};

float4 v = {0.0f, 0.0f, 5.0f, 6.0f};

v = u.wyyx; // v = {4.0f, 2.0f, 2.0f, 1.0f}

Another example:

float4 u = {1.0f, 2.0f, 3.0f, 4.0f};

float4 v = {0.0f, 0.0f, 5.0f, 6.0f};

v = u.wzyx; // v = {4.0f, 3.0f, 2.0f, 1.0f}

When copying vectors, we do not have to copy every component. For example, we can copy only the x- and y-components, as this code snippet illustrates:

float4 u = {1.0f, 2.0f, 3.0f, 4.0f};

float4 v = {0.0f, 0.0f, 5.0f, 6.0f};

v.xy = u; // v = {1.0f, 2.0f, 5.0f, 6.0f}

B.1.3 Matrix Types

We can define an m × n matrix, where m and n are from 1 to 4, using the following syntax:

floatmxn matmxn;

Examples:

Image    float2×2: 2 × 2 matrix, where the entries are of type float.

Image    float3×3: 3 × 3 matrix, where the entries are of type float.

Image    float4×4: 4 × 4 matrix, where the entries are of type float.

Image    float3×4: 3 × 4 matrix, where the entries are of type float.

 

Note:    You can create matrices where the components are of a type other than float, such as int2×2, half3×3, and bool4×4.

We can access an entry in a matrix using a double array subscript syntax. For example, to set the ijth entry of a matrix M we would write:

M[i][j] = value;

In addition, we can refer to the entries of a matrix M as we would access the members of a structure. The following entry names are defined:

One-based indexing:

M._11 = M._12 = M._13 = M._14 = 0.0f;

M._21 = M._22 = M._23 = M._24 = 0.0f;

M._31 = M._32 = M._33 = M._34 = 0.0f;

M._41 = M._42 = M._43 = M._44 = 0.0f;

Zero-based indexing:

M._m00 = M._m01 = M._m02 = M._m03 = 0.0f;

M._m10 = M._m11 = M._m12 = M._m13 = 0.0f;

M._m20 = M._m21 = M._m22 = M._m23 = 0.0f;

M._m30 = M._m31 = M._m32 = M._m33 = 0.0f;

Sometimes we want to refer to a particular row vector in a matrix. We can do so using a single array subscript syntax. For example, to extract the ith row vector in a 3×3 matrix M, we would write:

float3 ithRow = M[i]; // get the ith row vector in M

In this next example, we insert three vectors into the first, second, and third rows of a matrix:

float3 N = normalize(pIn.normalW);

float3 T = normalize(pIn.tangentW - dot(pIn.tangentW, N)*N);

float3 B = cross(N, T);

float3×3 TBN;

TBN[0] = T;   // sets row 1

TBN[1] = B;   // sets row 2

TBN[2] = N;   // sets row 3

We can also construct a matrix from vectors:

float3 N = normalize(pIn.normalW);

float3 T = normalize(pIn.tangentW - dot(pIn.tangentW, N)*N);

float3 B = cross(N, T);

float3×3 TBN = float3×3(T, B, N);

 

Note:    Instead of using float4 and float4×4 to represent 4D vectors and matrices, you can equivalently use the vector and matrix type:

vector u = {1.0f, 2.0f, 3.0f, 4.0f};
matrix M; // 4×4 matrix

B.1.4 Arrays

We can declare an array of a particular type using familiar C++ syntax, for example:

float M[4][4];

half p[4];

float3 v[12]; // 12 3D vectors

B.1.5 Structures

Structures are defined exactly as they are in C++. However, structures in the HLSL cannot have member functions. Here is an example of a structure in the HLSL:

struct SurfaceInfo

{

float3 pos;

     float3 normal;

     float4 diffuse;

     float4 spec;

};

SurfaceInfo v;

litColor + = v.diffuse;

dot(lightVec, v.normal);

float specPower = max(v.spec.a, 1.0f);

B.1.6 The typedef Keyword

The HLSL typedef keyword functions exactly the same as it does in C++. For example, we can give the name point to the type vector<float, 3> using the following syntax:

typedef float3 point;

Then instead of writing:

float3 myPoint;

We can just write:

point myPoint;

Here is another example showing how to use the typedef keyword with the HLSL const keyword (which works the same in C++):

typedef const float CFLOAT;

B.1.7 Variable Prefixes

The following keywords can prefix a variable declaration.

Image    static: Essentially the opposite of extern; this means that the shader variable will not be exposed to the C++ application.

static float3 v = {1.0f, 2.0f, 3.0f};

Image    uniform: This means that the variable does not change per vertex/pixel — it is constant for all vertices/pixels until we change it at the C++ application level. Uniform variables are initialized from outside the shader program (e.g., by the C++ application).

Image    extern: This means that the C++ application can see the variable (i.e., the variable can be accessed outside the shader file by the C++ application code). Global variables in a shader program are, by default, uniform and extern.

Image    shared: This is for sharing variables across multiple effect (.fx) files.

Image    volatile: Hints to the effect framework that the variable will be modified often. Only global variables can be prefixed with the volatile keyword.

Image    const: The const keyword in the HLSL has the same meaning it has in C++. That is, if a variable is prefixed with the const keyword, then that variable is constant and cannot be changed.

const float pi = 3.14f;

B.1.8 Casting

The HLSL supports a very flexible casting scheme. The casting syntax in the HLSL is the same as in the C programming language. For example, to cast a float to a matrix we write:

float f = 5.0f;

float4×4 m = (float4×4)f; // copy f into each entry of m.

What this scalar-matrix cast does is copy the scalar into each entry of the matrix.

Consider the following example:

float3 n = float3(…);

float3 v = 2.0f*n - 1.0f;

The 2.0f*n is just scalar-vector multiplication, which is well defined. However, to make this a vector equation, the scalar 1.0f is augmented to the vector (1.0f, 1.0f, 1.0f). So the above statement is like:

float3 v = 2.0f*n – float3(1.0f, 1.0f, 1.0f);

For the examples in this book, you will be able to deduce the meaning of the cast from the syntax. For a complete list of casting rules, search the SDK documentation index for “Casting and Conversion.”

B.2 Keywords and Operators

B.2.1 Keywords

For reference, here is a list of the keywords the HLSL defines:

Image

This next set of keywords displays identifiers that are reserved and unused, but may become keywords in the future:

Image

B.2.2 Operators

HLSL supports many familiar C++ operators. With a few exceptions noted below, they are used exactly the same way as they are in C++. These are the HLSL operators:

Image

Although the operators’ behavior is very similar to C++, there are some differences. First of all, the modulus operator (%) works on both integer and floating-point types. And, in order to use the modulus operator, both the left-hand-side value and right-hand-side value must have the same sign (e.g., both sides must be positive or both sides must be negative).

Secondly, observe that many of the HLSL operations work on a per-component basis. This is due to the fact that vectors and matrices are built into the language and these types consist of several components. By having the operations work on a component level, operations such as vector-matrix addition, vector-matrix subtraction, and vector/matrix equality tests can be done using the same operators we use for scalar types. See the following examples.

 

Note:    The operators behave as expected for scalars, that is, in the usual C++ way.

float4 u = {1.0f, 0.0f, -3.0f, 1.0f};

float4 v = {-4.0f, 2.0f, 1.0f, 0.0f};

// adds corresponding components

float4 sum = u + v; // sum = (-3.0f, 2.0f, -2.0f, 1.0f)

Incrementing a vector increments each component:

// before increment: sum = (-3.0f, 2.0f, -2.0f, 1.0f)

sum++; // after increment: sum = (-2.0f, 3.0f, -1.0f, 2.0f)

Multiplying vectors componentwise:

float4 u = {1.0f, 0.0f, -3.0f, 1.0f};

float4 v = {-4.0f, 2.0f, 1.0f, 0.0f};

// multiply corresponding components

float4 product = u * v; // product = (-4.0f, 0.0f, -3.0f, 0.0f)

 

Warning:    If you have two matrices:

float4×4 A;
float4×4 B;

The syntax A*B does componentwise multiplication, not matrix multiplication. You need to use the mul function for matrix multiplication.

Comparison operators are also done per component and return a vector or matrix where each component is of type bool. The resulting “bool” vector contains the results of each compared component. For example:

float4 u = { 1.0f, 0.0f, -3.0f, 1.0f};

float4 v = {-4.0f, 0.0f, 1.0f, 1.0f};

float4 b = (u == v); // b = (false, true, false, true)

Finally, we conclude by discussing variable promotions with binary operations:

Image    For binary operations, if the left-hand side and right-hand side differ in dimension, then the side with the smaller dimension is promoted (cast) to have the same dimension as the side with the larger dimension. For example, if x is of type float and y is of type float3, in the expression (x + y), the variable x is promoted to float3 and the expression evaluates to a value of type float3. The promotion is done using the defined cast. In this case we are casting scalar-to-vector; therefore, after x is promoted to float3, x = (x, x, x) as the scalar-to-vector cast defines. Note that the promotion is not defined if the cast is not defined. For example, we can’t promote float2 to float3 because there exists no such defined cast.

Image    For binary operations, if the left-hand side and right-hand side differ in type, then the side with the lower type resolution is promoted (cast) to have the same type as the side with the higher type resolution. For example, if x is of type int and y is of type half, in the expression (x + y), the variable x is promoted to a half and the expression evaluates to a value of type half.

B.3 Program Flow

The HLSL supports many familiar C++ statements for selection, repetition, and general program flow. The syntax of these statements is exactly like C++.

The return statement:

return (expression);

The if and if…else statements:

if( condition )

{

    statement(s);

}

if( condition )

{

    statement(s);

}

else

{

    statement(s);

}

The for statement:

for(initial; condition; increment)

{

    statement(s);

}

The while statement:

while( condition )

{

      statement(s);

}

The do…while statement:

do

{

      statement(s);

}

while( condition );

B.4 Functions

B.4.1 User-Defined Functions

Functions in the HLSL have the following properties:

Image    Functions use a familiar C++ syntax.

Image    Parameters are always passed by value.

Image    Recursion is not supported.

Image    Functions are always inlined.

Furthermore, the HLSL adds some extra keywords that can be used with functions. For example, consider the following function written in the HLSL:

bool foo(in const bool b, // input bool

            out int r1,        // output int

            inout float r2)   // input/output float

{

      if( b ) // test input value

      {

           r1 = 5; // output a value through r1

      }

      else

      {

           r1 = 1; // output a value through r1

      }

      // since r2 is inout we can use it as an input

      // value and also output a value through it

      r2 = r2 * r2 * r2;

      return true;

}

The function is almost identical to a C++ function except for the in, out, and inout keywords.

Image    in: Specifies that the argument (particular variable we pass into a parameter) should be copied to the parameter before the function begins. It is not necessary to explicitly specify a parameter as in because a parameter is in by default. For example, the following are equivalent:

float square(in float x)

{

     return x * x;

}

And without explicitly specifying in:

float square(float x)

{

     return x * x;

}

Image    out: Specifies that the parameter should be copied to the argument when the function returns. This is useful for returning values through parameters. The out keyword is necessary because the HLSL doesn’t allow us to pass by reference or to pass a pointer. We note that if a parameter is marked as out the argument is not copied to the parameter before the function begins. In other words, an out parameter can only be used to output data — it can’t be used for input.

void square(in float x, out float y)

{

      y = x * x;

}

Here we input the number to be squared through x and return the square of x through the parameter y.

Image    inout: Shortcut that denotes a parameter as both in and out. Specify inout if you wish to use a parameter for both input and output.

void square(inout float x)

{

      x = x * x;

}

Here we input the number to be squared through x and also return the square of x through x.

B.4.2 Built-in Functions

The HLSL has a rich set of built in functions that are useful for 3D graphics. The following is an abridged list:

Image    abs(x) — Returns |x|.

Image    ceil(x) — Returns the smallest integer ≥ x.

Image    clamp(x, a, b) — Clamps x to the range [a, b] and returns the result.

Image    clip(x) — This function can only be called in a pixel shader; it discards the current pixel from further processing if x < 0.

Image    cos(x) — Returns the cosine of x, where x is in radians.

Image    cross(u, v) — Returns u × v.

Image    degrees(x) — Converts x from radians to degrees.

Image    determinant(M) — Returns the determinant of a matrix.

Image    distance(u, v) — Returns the distance ||vu|| between the points u and v.

Image    dot(u, v) — Returns u · v.

Image    floor(x) — Returns the greatest integer ≤ x.

Image    frac(x) — This function returns the fractional part of a floating-point number (i.e., the mantissa). For example, if x = (235.52, 696.32), then frac(x) = (0.52, 0.32).

Image    length(v) — Returns ||v||.

Image    lerp(u, v, t) — Linearly interpolates between u and v based on the parameter t ∈[0, 1].

Image    log(x) — Returns ln(x).

Image    log10(x) — Returns log10(x).

Image    log2(x) — Returns log2(x).

Image    max(x, y) — Returns x if xy, else returns y.

Image    min(x, y) — Returns x if xy, else returns y.

Image    mul(M, N) — Returns the matrix product MN. Note that the matrix product MN must be defined. If M is a vector, it is treated as a row vector so that the vector-matrix product is defined. Likewise, if N is a vector, it is treated as a column vector so that the matrix-vector product is defined.

Image    normalize(v) — Returns v||v||.

Image    pow(b, n) — Returns bn.

Image    radians(x) — Converts x from degrees to radians.

Image    saturate(x) — Returns clamp(x, 0.0, 1.0).

Image    sin(x) — Returns the sine of x, where x is in radians.

Image    sincos(in x, out s, out c) — Returns the sine and cosine of x, where x is in radians.

Image    sqrt(x) — Returns Image.

Image    reflect(v, n) — Computes the reflection vector given the incident vector v and the surface normal n.

Image    refract(v, n, eta) — Computes the refraction vector given the incident vector v, the surface normal n, and the ratio of the two indices of refraction of the two materials eta. Look up Snell’s law in a physics book or on the Internet for information on refraction.

Image    rsqrt(x) — Returns Image.

Image    tan(x) — Returns the tangent of x, where x is in radians.

Image    transpose(M) — Returns the transpose MT.

Image    Texture2D::Sample(S, texC) — Returns a color from a 2D texture map based on the SamplerState object S (recall a sampler state specifies texture filters and texture address modes), and 2D texture coordinates texC.

Image    Texture2D::SampleLevel(S, texC, LOD) — Returns a color from a 2D texture map based on the SamplerState object S (recall a sampler state specifies texture filters and texture address modes), and 2D texture coordinates texC. This function differs from Texture2D::Sample in that the third parameter specifies the mipmap level to use. For example, we would specify 0 to access the topmost mipmap LOD. Use this function to manually specify the mipmap level you want to sample.

Image    TextureCube::Sample(S, v) — Returns a color from a cube map based on the SamplerState object S (recall a sampler specifies texture filters and texture address modes) and 3D lookup vector v.

 

Note:    Most of the functions are overloaded to work with all the built-in types for which the function makes sense. For instance, abs makes sense for all scalar types and so is overloaded for all of them. As another example, the cross product cross only makes sense for 3D vectors, so it is only overloaded for 3D vectors of any type (e.g., 3D vectors of ints, floats, doubles, etc.). On the other hand, linear interpolation, lerp, makes sense for scalars, 2D, 3D, and 4D vectors, and therefore is overloaded for all types.

 

Note:    If you pass in a non-scalar type into a “scalar” function, that is, a function that traditionally operates on scalars (e.g., cos(x)), the function will act per component. For example, if you write:

float3 v = float3(0.0f, 0.0f, 0.0f);

v = cos(v);

Then the function will act per component: v = (cos(x), cos(y), cos(z)).

 

Note:    For further reference, the complete list of the built-in HLSL functions can be found in the DirectX documentation. Search the index for “HLSL Intrinsic Functions.”

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.173.199