Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Introduction to 3D Game Programming with DirectX.9.0 - F. D. Luna

.pdf
Скачиваний:
240
Добавлен:
24.05.2014
Размер:
6.94 Mб
Скачать

274 Chapter 16

...

}

The parameters are inputs into the shader; in this example we are inputting three texture coordinates. The shader returns a single color as output, which is denoted by the : COLOR syntax following the function signature. This definition is equivalent to:

struct INPUT

 

 

 

{

 

 

 

 

 

float2 base : TEXCOORD0;

 

Y

 

float2 spot : TEXCOORD1;

 

 

 

 

 

float2 text : TEXCOORD2;

L

};

 

 

 

 

F

struct OUTPUT

 

 

 

 

{

 

M

 

 

float4 c : COLOR;

 

 

 

 

 

};

 

 

 

 

OUTPUT Main(INPUT input)

 

 

 

{

E

 

 

 

 

 

 

...

T

 

 

 

} A

The body of the entry point function is responsible for computing the

output vertex given the input vertex. The shader in this example simply transforms the input vertex to view space and projection space, sets the vertex color to blue, and returns the resulting vertex. First we instantiate a VS_OUTPUT instance and set all of its members to 0.

VS_OUTPUT output = (VS_OUTPUT)0; // zero out all members

Then our shader transforms the input vertex position by the ViewProjMatrix variable using the mul function, which is a built-in function that can do both vector-matrix multiplication and matrixmatrix multiplication. We save the resulting transformed vector in the position member of the output instance:

// transform and project

output.position = mul(input.position, ViewProjMatrix);

Next we set the diffuse color member of output to Blue:

// set vertex diffuse color to blue

output.diffuse = Blue;

Finally, we return our resulting vertex:

return output;

}

Team-Fly®

Introduction to the High-Level Shading Language 275

16.2 Compiling an HLSL Shader

16.2.1 The Constant Table

Every shader has a constant table that is used to store its variables. The D3DX library provides our application access to a shader’s constant table through the ID3DXConstantTable interface. Via this interface we can set variables in the shader source code from our application’s code.

We now describe an abridged list of the methods that ID3DXConstantTable implements. For a complete list, see the Direct3D documentation.

16.2.1.1 Getting a Handle to a Constant

In order to set a particular variable in a shader from our application code, we need a way to refer to it. We can refer to a variable in the shader from our application with a D3DXHANDLE. The following method returns a D3DXHANDLE to a variable in the shader when given its name:

D3DXHANDLE ID3DXConstantTable::GetConstantByName(

D3DXHANDLE hConstant, // scope of constant

LPCSTR pName

// name of constant

);

hConstant—A D3DXHANDLE that identifies the parent structure in which the variable that we want a handle to lives. For example, if we wanted to get a handle to a single data member of a particular structure instance, we would pass in the handle to the structure instance here. If we are obtaining a handle to a top-level variable, we can pass 0 for this parameter.

pName—The name of the variable in the shader source code that we want to obtain a handle to

For example, if the name of the variable in the shader is ViewProjMatrix and it was a top level parameter, we would write:

// Get a handle to the ViewProjMatrix variable in the shader. D3DXHANDLE h0;

h0 = ConstTable->GetConstantByName(0, "ViewProjMatrix");

16.2.1.2 Setting Constants

Once our application has a D3DXHANDLE that refers to a particular variable in the shader code, we can set that variable from our application using the ID3DXConstantTable::SetXXX methods, where the XXX is replaced by a type name to indicate the type of variable being set. For

P a r t I V

276 Chapter 16

example, if the variable that we wish to set is a vector array, the method name would be SetVectorArray.

The general syntax of the ID3DXConstantTable::SetXXX methods is of the form:

HRESULT ID3DXConstantTable::SetXXX(

LPDIRECT3DDEVICE9 pDevice, D3DXHANDLE hConstant,

XXX value

);

pDevice—Pointer to the device that is associated with the constant table

hConstant—A handle that refers to the variable that we are setting

value—The value that we are setting the variable to, where XXX is replaced with the variable type name we are setting. For some types (bool, int, float), we pass a copy of the value, and for other types (vectors, matrices, structures), we pass a pointer to the value.

When we set arrays, the SetXXX method takes an additional fourth parameter that specifies the number of elements in the array. For example, the method to set an array of 4D vectors is prototyped as:

HRESULT ID3DXConstantTable::SetVectorArray(

LPDIRECT3DDEVICE9 pDevice,

// associated device

D3DXHANDLE hConstant,

// handle to shader variable

CONST D3DXVECTOR4* pVector,

// pointer to array

UINT Count

// number of elements in array

);

 

The following list describes the types that we can set with the ID3DXConstantTable interface. Assume that we have a valid device (Device) and a valid handle to the variable that we are setting (handle).

SetBool—Used to set a Boolean value. Sample call:

bool b = true;

ConstTable->SetBool(Device, handle, b);

SetBoolArray—Used to set a Boolean array. Sample call:

bool b[3] = {true, false, true};

ConstTable->SetBoolArray(Device, handle, b, 3);

SetFloat—Used to set a float. Sample call:

float f = 3.14f;

ConstTable->SetFloat(Device, handle, f);

Introduction to the High-Level Shading Language 277

SetFloatArray—Used to set a float array. Sample call:

float f[2] = {1.0f, 2.0f};

ConstTable->SetFloatArray(Device, handle, f, 2);

SetInt—Used to set an integer. Sample call:

int x = 4;

ConstTable->SetInt(Device, handle, x);

SetIntArray—Used to set an integer array. Sample call:

int x[4] = {1, 2, 3, 4};

ConstTable->SetIntArray(Device, handle, x, 4);

SetMatrix—Used to set a 4 4 matrix. Sample call:

D3DXMATRIX M(…);

ConstTable->SetMatrix(Device, handle, &M);

SetMatrixArray—Used to set a 4 4 matrix array. Sample call:

D3DXMATRIX M[4];

// ...Initialize matrices

ConstTable->SetMatrixArray(Device, handle, M, 4);

SetMatrixPointerArray—Used to set an array of 4 4 matrix pointers. Sample call:

D3DXMATRIX* M[4];

// ...Allocate and initialize matrix pointers

ConstTable->SetMatrixPointerArray(Device, handle, M, 4);

SetMatrixTranspose—Used to set a transposed 4 4 matrix. Sample call:

D3DXMATRIX M(…);

D3DXMatrixTranspose(&M, &M);

ConstTable->SetMatrixTranspose(Device, handle, &M);

SetMatrixTransposeArray—Used to set an array of 4 4 transposed matrices. Sample call:

D3DXMATRIX M[4];

// ...Initialize matrices and transpose them.

ConstTable->SetMatrixTransposeArray(Device, handle, M, 4);

SetMatrixTransposePointerArray—Used to set an array of pointers to 4 4 transposed matrices. Sample call:

D3DXMATRIX* M[4];

// ...Allocate,initialize matrix pointers and transpose them.

P a r t I V

ConstTable->SetMatrixTransposePointerArray(Device, handle, M, 4);

278Chapter 16

SetVector—Used to set a variable of type D3DXVECTOR4. Sample call:

D3DXVECTOR4 v(1.0f, 2.0f, 3.0f, 4.0f);

ConstTable->SetVector(Device, handle, &v);

SetVectorArray—Used to set a variable that is a vector array. Sample call:

D3DXVECTOR4 v[3];

// ...Initialize vectors

ConstTable->SetVectorArray(Device, handle, v, 3);

SetValue—Used to set an arbitrarily sized type, such as a structure. In the sample call, we use SetValue to set a D3DXMATRIX:

D3DXMATRIX M(…);

ConstTable->SetValue(Device, handle, (void*)&M, sizeof(M));

16.2.1.3 Setting the Constant Default Values

This next method simply sets the constants to their default values, which are the values they are initialized with when they are declared. This method should be called once during application setup.

HRESULT ID3DXConstantTable::SetDefaults( LPDIRECT3DDEVICE9 pDevice

);

pDevice—Pointer to the device that is associated with the constant table

16.2.2 Compiling an HLSL Shader

We can compile a shader, which we have saved to a text file, using the following function:

HRESULT D3DXCompileShaderFromFile(

LPCSTR

pSrcFile,

CONST D3DXMACRO*

pDefines,

LPD3DXINCLUDE

pInclude,

LPCSTR

pFunctionName,

LPCSTR

pTarget,

DWORD

Flags,

LPD3DXBUFFER*

ppShader,

LPD3DXBUFFER*

ppErrorMsgs,

LPD3DXCONSTANTTABLE* ppConstantTable

);

Introduction to the High-Level Shading Language 279

pSrcFile—Name of the text file that contains the shader source code that we want to compile

pDefines—This parameter is optional, and we specify null for it in this book.

pInclude—Pointer to an ID3DXInclude interface. This interface is designed to be implemented by the application so that we can override default include behavior. In general, the default behavior is fine and we can ignore this parameter by specifying null.

pFunctionName—A string specifying the name of the entry point function. For example, if the shader’s entry point function were called Main, we would pass “Main” for this parameter.

pTarget—A string specifying the shader version to compile the HLSL source code to. Valid vertex shader versions are: vs_1_1, vs_2_0, vs_2_sw. Valid pixel shader versions are: ps_1_1, ps_1_2, ps_1_3, ps_1_4, ps_2_0, ps_2_sw. For example, if we wanted to compile our vertex shader to version 2.0, we would pass vs_2_0 for this parameter.

Remark: The ability to compile to different shader versions is one of the major benefits of using HLSL over assembly language. With HLSL we can almost instantly port a shader to a different version by simply recompiling to the desired target. Using assembly, we would have to port the code by hand.

Flags—Optional compiling flags; specify 0 for no flags. Valid options are:

D3DXSHADER_DEBUG—Instructs the compiler to write debug information

D3DXSHADER_SKIPVALIDATION—Instructs the compiler not to do any code validation. This should only be used when you are using a shader that is known to work.

D3DXSHADER_SKIPOPTIMIZATION—Instructs the compiler

 

not to perform any code optimization. In practice this would

 

only be used in debugging, where you would not want the com-

 

piler to alter the code in any way.

 

 

ppShader—Returns a pointer to an ID3DXBuffer that contains

V

the compiled shader code. This compiled shader code is then used

t I

as a parameter to another function to actually create the ver-

ar

tex/pixel shader.

P

 

ppErrorMsgs—Returns a pointer to an ID3DXBuffer that con-

 

tains a string of error codes and messages

 

280Chapter 16

ppConstantTable—Returns a pointer to an ID3DXConstantTable that contains the constant table data for this shader

Here is an example call of D3DXCompileShaderFromFile:

//

// Compile shader

//

ID3DXConstantTable* TransformConstantTable = 0;

ID3DXBuffer* shader = 0;

ID3DXBuffer* errorBuffer = 0;

hr = D3DXCompileShaderFromFile(

"transform.txt",

// shader filename

0,

 

0,

 

"Main",

// entry point function name

"vs_2_0",

// shader version to compile to

D3DXSHADER_DEBUG,

// debug compile

&shader,

 

&errorBuffer,

 

&TransformConstantTable);

// output any error messages if( errorBuffer )

{

::MessageBox(0, (char*)errorBuffer->GetBufferPointer(), 0, 0); d3d::Release<ID3DXBuffer*>(errorBuffer);

}

if(FAILED(hr))

{

::MessageBox(0, "D3DXCreateEffectFromFile() - FAILED", 0, 0); return false;

}

16.3 Variable Types

Note: In addition to the types that are described in the following sections, HLSL also has some built-in object types (e.g., texture object). However, since these object types are primarily used only in the effects framework, we defer a discussion of them until Chapter 19.

16.3.1 Scalar Types

HLSL supports the following scalar types:

bool—True or false value. Note that HLSL provides the true and false keywords.

int—32-bit signed integer

half—16-bit floating-point number

Introduction to the High-Level Shading Language 281

float—32-bit floating-point number

double—64-bit floating-point number

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

16.3.2 Vector Types

HLSL has the following built-in vector types:

vector—A 4D vector where each component is of type float

vector<T, n>—An n-dimensional vector, where each component is of scalar type T. The dimension n must be between 1 and 4. Here is an example of a 2D double vector:

vector<double, 2> vec2;

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 exactly the 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.

Alternatively, we can use these other predefined types that represent a 2D, 3D, and 4D vector, respectively:

float2 vec2; float3 vec3; float4 vec4;

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 = (ux, uy, uy, uw). The most immediate solution would be to individually copy each component of u over to v as necessary. However, HLSL provides a special syntax for doing these kinds of out-of-order copies called swizzles:

vector u = {1.0f, 2.0f, 3.0f, 4.0f}; vector v = {0.0f, 0.0f, 5.0f, 6.0f};

P a r t I V

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

282 Chapter 16

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

vector u = {1.0f, 2.0f, 3.0f, 4.0f}; vector v = {0.0f, 0.0f, 5.0f, 6.0f};

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

16.3.3 Matrix Types

HLSL has the following built-in matrix types:

matrix—A 4 4 matrix, where each entry is of type float

matrix<T, m, n>—An m n matrix, where each entry is of sca-

lar type T. The matrix dimensions m and n must be between 1 and 4. Here is an example of an 2 2 integer matrix:

matrix<int, 2, 2> m2x2;

Alternatively, we can define an m n matrix, where m and n are between 1 and 4, using the following syntax:

floatmxn matmxn;

Examples:

float2x2 mat2x2; float3x3 mat3x3;

float4x4 mat4x4; float2x4 mat2x4;

Note: The types need not be only float—we can use other types. For instance we can use integers and write:

int2x2 i2x2; int2x2 i3x3; int2x2 i2x4;

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:

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;

Introduction to the High-Level Shading Language 283

Zero-based:

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 refer to the ith row vector in a matrix M, we would write:

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

Note: We can initialize variables in HLSL using the following two types of syntax:

vector u = {0.6f, 0.3f, 1.0f, 1.0f};

vector v = {1.0f, 5.0f, 0.2f, 1.0f};

Or, equivalently, using a constructor style syntax:

vector u = vector(0.6f, 0.3f, 1.0f, 1.0f); vector v = vector(1.0f, 5.0f, 0.2f, 1.0f);

Some other examples:

float2x2 f2x2 = float2x2(1.0f, 2.0f, 3.0f, 4.0f); int2x2 m = {1, 2, 3, 4};

int n = int(5); int a = {5};

float3 x = float3(0, 0, 0);

16.3.4 Arrays

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

float M[4][4]; half p[4]; vector v[12];

16.3.5 Structures

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

struct MyStruct

{

matrix T; vector n;

float f; int x; bool b;

};

P a r t I V