高级D3D应用

高级D3D应用

09_01_高级阴影发生器语言入门(Introduction to the High-Level Shading Language

Overview

在这一节中我们描述High-Level Shading Language (HLSL),那个我们用于在接下来的三节中用于编程顶点和像素阴影发生器的东东。暂时的,顶点和像素shader是我们写出的小的特定的程序,在图形卡的GPU(图形存取单元graphics processing unit)执行,这就代替了一部分固定功能管道。通过用我们自己的shader程序代替一部分固定功能管道,我们获得了在图形效果上能达到的巨大灵活性。我们不再受限于预先确定的“固定”操作。

为了写出shader程序,我们需要一种把它们写入的语言。在DirectX 8.x中,shaders被用一种低级shader汇编语言(low-level shader assembly language)写入。幸运的,我们不用再用汇编语言写shaders了,在DirectX 9中支持一种High-Level Shading Language我们用于写shaders。用HLSL代替汇编语言写shader就象用高级语言,如C++代替汇编语言写应用程序一样有利,即:

·         增加生产力用高级语言写程序比用低级语言更快更容易。我们可以分配更多时间聚焦于运算法则而不是编码。

·         改良可读性用高级语言编程易于阅读,这意味着用高级语言写的程序易于调试与维护。

·         编译器,常常而不是从不,比手写汇编码产生更有效率的汇编码。

·         HLSL编译器,我们能编译我们的代码到任何可用shade版本。使用汇编语言,我们就要不得不转换到不同的目标版本。

HLSL也有非常近似于CC++的语法,因而具有非常短的学习曲线。

最后,如果你的物理卡不支持顶点和像素shader,你可能要切换到REF设备以执行shader例程。使用REF设备意味着shader例程运行非常慢,但它们仍会显示正确的结果,以允许我们校验我们的代码是正确的。

 

提示 

顶点shader能被软件顶点处理在软件中模仿D3DCREATE_SOFTWARE_VERTEX-PROCESSING.

Objectives

·         学习如何写和编译一个HLSL shader程序

·         学习如何从应用程序传递数据到shader程序

·         熟悉HLSL的功能的语法、定义及构建

一、 写一个HLSL Shader(Writing an HLSL Shader)

我们能够在我们的应用程序的源文件中用一个长的字符串直接写HLSL shader代码。然而,更便利和模块化的方法是从应用程序代码中分离shader代码。基于这个原因,我们用记事本写我们的shaders并保存它们为ASCII文本文件。然后我们用D3DXCompileShaderFromFile函数编译我们的shaders

作为入门,下面的代码是一个简单的用HLSL写出的被保存为文本文件的用记事本产生的名字叫Transform.txt vertex shader。完整工程可以在例程中找到。这个vertex shader用一个视图和放射结合的距阵转换顶点,并设置顶点的漫反射颜色成分为兰色。

 

Note 

这个例子用一个vertex shader做为例子,但不用担心这样做vertex shader不被支持,它们在下一节解释。现在,目的是让你自己熟悉HLDL程序的语法和格式。

/
//
// File: transform.txt
//
// Author: Frank D. Luna (C) All Rights Reserved
//
// System: AMD Athlon 1800+ XP, 512 DDR, Geforce 3, Windows XP,
//         MSVC++ 7.0
//
// Desc: Vertex shader that transforms a vertex by the view and
//       projection transformation, and sets the vertex color to blue.
//
/
 
//
// Globals
//
 
// Global variable to store a combined view and projection
// transformation matrix.  We initialize this variable
// from the application.
matrix ViewProjMatrix;
 
// Initialize a global blue color vector.
vector Blue = {0.0f, 0.0f, 1.0f, 1.0f};
 
//
// Structures
//
// Input structure describes the vertex that is input
// into the shader.  Here the input vertex contains
// a position component only.
struct VS_INPUT
{
     vector position  : POSITION;
};
// Output structure describes the vertex that is
// output from the shader. Here the output
// vertex contains a position and color component.
struct VS_OUTPUT
{
     vector position : POSITION;
     vector diffuse  : COLOR;
};
 
//
// Main Entry Point, observe the main function
// receives a copy of the input vertex through
// its parameter and returns a copy of the output
// vertex it computes.
//
 
VS_OUTPUT Main(VS_INPUT input)
{
     // zero out members of output
     VS_OUTPUT output = (VS_OUTPUT)0;
 
     // transform to view space and project
     output.position  = mul(input.position, ViewProjMatrix);
 
     // set vertex diffuse color to blue
     output.diffuse = Blue;
 
     //Output the projected and colored vertex.
     return output;
}

1.1全局( Globals)

首先我们例示两个全局变量:

matrix ViewProjMatrix;
vector Blue = {0.0f, 0.0f, 1.0f, 1.0f};

第一个变量,ViewProjMatrix,,是一个距阵类型,是一个在HLSL中建立的4 X 4距阵类型。这个变量存贮一个由视图和放射组合的距阵,因而它描述两种转换。通过这种方式我们只要做一次向量距阵乘法而不是两次。注意在shader源代码中我们哪儿都没初始化这一变量。那是因为我们在应用程序代码中设置它而不是在shader中。从应用程序到shader程序的通讯是个频繁的操作,我们在2.1节中解释。

第二个变量,Blue,是用vector类型建立的,它是一个4D向量。我们简单的初始化它的组成成分为兰色,按RGBA颜色向量处理它。

1.2 输入输出Structures (Input and Output Structures)

全局变量声明后,我们定义两个特殊结构体,我们叫它们为input and output structures。对于vertex shaders,这些结构体分别定义我们shader输入输出的顶点数据。

struct VS_INPUT
{
     vector position : POSITION;
};
 
struct VS_OUTPUT
{
     vector position : POSITION;
     vector diffuse  : COLOR;
};
 

 

Note 

The input and output structures for pixel shaders define pixel data.

本例中,我们输入到我们的vertex shader的顶点只包含一个位置成员。我们输出的顶点包含一个位置成员和一个颜色成员。

独特的冒号表示一种语义(semantic),它用于指示变量的用法。这与顶点结构的灵活顶点格式(FVF)相似。举例来说,在VS_INPUT中,我们有成员:

vector position : POSITION;

符号" : POSITION"是说vector position是用于描述输入顶点的位置的。做为另一个例子,在VS_OUTPUT中我们有:

vector diffuse  : COLOR;

这里 ": COLOR" 是说vector diffuse 是用于描述输出顶点的颜色的。对于更多的可用的标识符我们将在下两节谈到。

 

Note 

从低级观点来看,这种语义的语法关联到一个shader中的与硬件寄存器有关的变量。也就是说,input变量关联到输入寄存器,output变量关联到输出寄存器。举例来说,VS_INPUT的位置成员连接顶点输入位置寄存器。同样的,diffuse连接到一个特殊的顶点输出颜色寄存器。

1.3 入口点函数(Entry Point Function

就象在C++程序中一样,每一个HLSL程序有一个入口点。在我们的示例shader,我们把入口点函数叫做Main,然而,这个名字不是强制性的。shader的入口点函数名字可以为任何有效的函数名,它常常传递输入顶点到我们的shader。入口点函数也会返回一个输出结构的实例,它常常用于输出经我们的shader处理过的顶点。

VS_OUTPUT Main(VS_INPUT input)
{
 

 

Note 

实际上,不是强制使用inputoutput结构。举例来说,你可能有时会看到与下面相似的语法,独特的pixel shaders:

float4 Main(in float2 base : TEXCOORD0,
            in float2 spot : TEXCOORD1,
            in float2 text : TEXCOORD2) : COLOR
{
...
}

参数被输入shader,在这个例子中我们输入三个纹理坐标。shader返回一个单一颜色做为输出,它被在函数后面的: COLOR语法所指定。这种定义方法等同于:

struct INPUT
{
     float2 base : TEXCOORD0;
     float2 spot : TEXCOORD1;
     float2 text : TEXCOORD2;
};
 
struct OUTPUT
{
     float4 c : COLOR;
};
OUTPUT Main(INPUT input)
{
...
}

入口点函数的函数体负责通过输入顶点计算输出顶点。本例中的shader简单的转换输入顶点到视图空间和放射空间,设置顶点颜色为兰色,返回结果顶点。首先我们实例化一个VS_OUTPUT实例并设置它的所有成员为0

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

    然后我们的shaderViewProjMatrix变量使用mul函数转换输入顶点位置,mul是一个内置(built-in)函数,既能做vector-matrix乘法又能做matrix-matrix乘法。我们保存转换后的vector结果到输出实例的位置成员:

// transform and project
output.position = mul(input.position, ViewProjMatrix);

接下来,我们设置输出的漫反射颜色为蓝色:

// set vertex diffuse color to blue
output.diffuse = Blue;

最后,我们返回结果顶点:

return output;
}

 

二、编译一个HLSL Shader(Compiling an HLSL Shader)

2.1 系数表(The Constant Table

每一个shader都有一个系数的表用于存贮它的变量。D3DX库提供我们的应用程序通过ID3DXConstantTable界面操作shader的系数表的权力。通过这一界面我们能从我们的应用程序代码设置shader代码中的变量。

我们现在描述一个ID3DXConstantTable工具的简化的函数表,完整表,请看Direct3D文档。

2.1.1 获得系数的句柄(Getting a Handle to a Constant

    为了从我们的应用程序设置一个在一个shader中的一个特定变量。我们能在我们的应用程序中用D3DXHANDLE提取一个shader中的变量。下面的方法当给出一个名字时返回一个到shader中的变量的D3DXHANDLE

D3DXHANDLE ID3DXConstantTable::GetConstantByName(
     D3DXHANDLE hConstant, // scope of constant
     LPCSTR pName          // name of constant
);

·         hConstant一个 D3DXHANDLE识别我们要操作的变量所存活的父结构体。举例来说,如果我们想得到一个特定结构实例中的单一数据成员的句柄,我们将在此传递这个结构实例句柄。如果获得一个顶级变量的句柄,我们可以传递0到这个参数。

·         pName我们要获得的在shader源码中的变量名。

举个例子,如果shader中的变量名为ViewProjMatrix,并且它是一个顶级参数,我们将这样写:

// Get a handle to the ViewProjMatrix variable in the shader.
D3DXHANDLE h0;
h0 = ConstTable->GetConstantByName(0, "ViewProjMatrix");
2.1.2设置系数(Setting Constants

一旦我们的应用程序有了一个引用一个shader代码中特定变量的D3DXHANDLE,我们就能通过ID3DXConstantTable::SetXXX方法来在我们的应用程序中设置这个变量,这里XXX用一个类型名代替以指示要设置的变量类型。例如,如果我们要设置的变量是一个vector数组,这个方法名字就为SetVectorArray

    ID3DXConstantTable::SetXXX方法的通用语法是如下形态:

HRESULT ID3DXConstantTable::SetXXX(
     LPDIRECT3DDEVICE9 pDevice,
     D3DXHANDLE hConstant,
     XXX value
);

·         pDevice指向系数表所关联的设备的指针

·         hConstant一个句柄,引用我们正在设置的变量

·         value我们正在设置的变量的值,这里的XXX将被替换为我们正在设置的变量类型名。对于一些类型(bool, int, float),我们传递一个值的拷贝,对于其它类型(vectors, matrices, structures),我们传递一个指向值的指针

当我们设置数组时,SetXXX方法具有第四个参数指定数组中的元素数量。举个例子,这个方法以设置一个4D向量数组的原形为:

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
);

下面的列表描述我们能用ID3DXConstantTable界面设置的类型。假设我们有一个正确的设备(Device)和一个正确的我们正在设置的变量句柄(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);

·         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.
 
ConstTable->SetMatrixTransposePointerArray(Device, handle, M, 4);

·         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));
2.1.3 设置系数的默认值(Setting the Constant Default Values

下一个方法简单的设置系数为它们的默认值,也就是当它们被声明时被初始化的值。在整个应用程序setup期间这个方法将被调用一次。

HRESULT ID3DXConstantTable::SetDefaults(
     LPDIRECT3DDEVICE9 pDevice
);

·         pDevice指向系数表所关联的设备的指针

2.2编译一个HLSL ShaderCompiling an HLSL Shader

我们能使用下面的函数编译一个我们保存为文本文件的shader:

HRESULT D3DXCompileShaderFromFile(
     LPCSTR               pSrcFile,
     CONST D3DXMACRO*     pDefines,
     LPD3DXINCLUDE        pInclude,
     LPCSTR               pFunctionName,
     LPCSTR               pTarget,
     DWORD                Flags,
     LPD3DXBUFFER*        ppShader,
     LPD3DXBUFFER*        ppErrorMsgs,
     LPD3DXCONSTANTTABLE* ppConstantTable
);
 

·         pSrcFile包含我们要编译的shader源码的文本文件名

·         pDefines这个参数是可选的,我们一般指定null

·         pInclude指向ID3DXInclude界面的指针。这个界面设计为被应用程序执行,因而我们能不考虑默认包含行为。通常,默认行为是好的,因此我们能用指定null忽略这一参数。

·         pFunctionName指定函数入口点的名字的字符串。例如,如果shader的入口点函数叫做Main,我们传递“Main”到这个参数。

·         pTarget一个指定被编译的HLSL源代码shader版本的字符串。有效的vertex shader版本为:vs_1_1, vs_2_0, vs_2_sw。有效的pixel shader版本为:ps_1_1, ps_1_2, ps_1_3, ps_1_4, ps_2_0, ps_2_sw。例如,如果我们想要编译我们的vertex shaderversion 2.0,我们就传递version 2.0到这一参数。

 

Remark: 

编译为不同shader版本是用HLSL代替汇编语言的好处之一。 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:

o        D3DXSHADER_DEBUG提示编译器写入错误信息。

o        D3DXSHADER_SKIPVALIDATION通知编译器不进行任何代码确认。它只在你已知一个shader能工作时运用。

o        D3DXSHADER_SKIPOPTIMIZATION通知编译器不进行任何代码优化。实际上只在调试时才这么用,当你不想让编译器对代码做任何改变时。

·         ppShader返回一个指向ID3DXBuffer的指针,以包含编译后的shader代码。这一编译后的shader代码接着被其它函数做为参数使用,以事实上创建vertex/pixel shader

·         ppErrorMsgs返回一个指向ID3DXBuffer的指针,以包含错误代码和消息的字符串。

·         ppConstantTable返回一个指向ID3DXConstantTable的指针,以包含这个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(errorBuffer);
}
 
if (FAILED (hr))
{
     ::MessageBox(0, "D3DXCreateEffectFromFile() - FAILED", 0, 0);
     return false;
}

三、变量类型(Variable Types

 

Note 

除了下面章节要讲述的类型外,HLSL还有一些内建的对象类型(比如,纹理对象)。然而,因为这些对象类型主要要是在效果框架(effects framework)中应用,我们在以后章节中讨论它们。

3.1 数量类型(Scalar Types

HLSL supports the following scalar types:

·         bool—True or false value.注意HLSL提供truefalse关键字。

·         int—32-bit signed integer

·         half—16-bit floating-point number

·         float—32-bit floating-point number

·         double—64-bit floating-point number

 

Note 

一些平台可能不支持int,halfdouble,如果是这样,这些类型用float仿效。

16.3.2 Vector Types

HLSL 下面的内置 vector 类型:

·         vector—4D向量,每个成员为float型。

·         vector—n维向量,每个成员类型为T。维数n必须为14之间。这是一个2D double vector例子:

vector vec2;

我们可以用下面的语法来访问一个vector的成员,例如,要设置一个vector vec成员i,我们可以这么写:

vec[i] = 2.0f;

    另外,我们能像访问结构的成员一样访问一个vector vec的成员,用定义好的成员名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;

名字r,g,b,a分别与名字x,y,z,w指相同的成员。当用vector描述颜色时,RGBA是非常合适的,因为它加强了vector描述颜色这一实际情况。

    替代方案,我们可以用这些另处的预定义类型来描述2D3D4D vector,分别是:

float2 vec2;
float3 vec3;
float4 vec4;

考虑到vector u = (ux, uy, uz, uw)并假设我们想拷贝u的成员到vv v = (ux, uy, uy, uw)。最直接的方案可能是分别拷贝u的每一个成员到v。然而,HLSL提供了一个特殊的语法以执行这些按顺序拷贝叫做swizzles:

vector u = {l.0f, 2.0f, 3.0f, 4.0f};
vector v = {0.0f, 0.0f, 5.0f, 6.0f};
 
v = u.xyyw; // v = {1.0f, 2.0f, 2.0f, 4.0f}

当拷贝vector时,我们不用所有成员。例如,我们能只拷贝xy成员,就像下面代码片断这样:

vector u = {1.0f, 2.0f, 3.0f, 4.0f};
vector v = {0.0f, 0.0f, 5.0f, 6.0f};
 
v.xy = u; // v = {l.0f, 2.0f, 5.0f, 6.0f}

3.3 距阵类型(Matrix Types

HLSL拥有如下内置距阵类型:

·         matrix每一个条目类型为float的一个4 X 4 距阵。

·         matrix—An m × n matrix, where each entry is of scalar 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 m2x2;

作为选择,我们可以用如下语法定义一个mn14之间的m X n距阵:

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;

我们可以使用一个双下标语法来存取一个条目到一个距阵中。例如:

M[i] [j] = value;

更进一步,我们可以引用距阵M的条目来存取结构体的成员。下面的条目名是定义好了的:

基于1的:

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;
 

基于0的:

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;

有时我们想引用一个距阵中的特定行向量。我们可以使用一个单独数组下标语法做到这点。例如,为了引用一个距阵M的第i行向量,我们可以这么写:

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;
};
MyStruct s; // instantiate
s.f = 5.0f; // member access

3.6 typedef关键字(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 using the following syntax:

typedef vector point;

Then instead of writing:

vector myPoint;

... we can just write:

point myPoint;

Here are two more examples that show how to use the typedef keyword with a constant type and an array:

typedef const float CFLOAT;
typedef float point2[2];

3.7 变量前缀(Variable Prefixes

The following keywords can prefix a variable declaration:

·         static如果一个全局变量拥有static前缀,那就意味着它不会被暴露在shader外。换句话说,它是shader的局部的。如果一个局部变量拥有static前缀,它就和在C++中的局部static变量有相同的行为。

     static int x = 5;

·         uniform(相同的)如果一个变量有uniform关键字的前缀,就意味着此变量在shader外被初始化,通过C++应用程序实例化,而输入到shader中。

·          extern如果一个变量有extern前缀,就意味着此变量可以在shader外被访问,通过C++程序实例化。只有全局变量能被冠以extern前缀。非static的全局变量默认为extern的。

·         shared当一个变量用shared关键字做前缀,它就是由效果框架(effects framework)提供,这一变量将在很多效果中共享。只有全局变量能用shared关键字做前缀。

·         volatile当一个变量用volatile关键字做前缀,将暗示效果框架(effects framework)此变量将要常常改变。只有全局变量能用volatile做前缀。

·         const—HLSL中的const的前缀与C++中的意思相同。

     const float pi = 3.14f;
 
四、关键字、语句和强制转换( Keywords, Statements, and Casting)

4.1 Keywords

作为参考,这是HLSL定义的关键字表:

asm

bool

compile

const

decl

do

double

else

extern

false

float

for

half

if

in

inline

inout

int

matrix

out

pass

pixelshader

return

sampler

shared

static

string

struct

technique

texture

true

typedef

uniform

vector

vertexshader

void

volatile

while

 

 

 

 

接下来显示的标识符是保留的没用到的,但是将来可能成为关键字:

auto

break

case

catch

char

class

const_cast

continue

default

delete

dynamic cast

enum

explicit

friend

goto

long

mutable

namespace

new

operator

private

protected

public

register

reinterpret_cast

short

signed

sizeof

static_cast

switch

template

this

throw

try

typename

union

unsigned

using

virtual

 

 

 

4.2基本程序流程( Basic Program Flow

HLSL支持很多熟悉的C++语句,选择、循环和普通的程序语句。这些语句的语法严格类似C++

The Return statement:

return (表达式);

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 );

4.3 强制类型转换(Casting)

HLSL支持一种非常灵活的强制转换方案。HLSL中的强制转换语法与C语言相同。例如,为了强制转换一个float到一个matrix,我们写为:

float f = 5.0f;
matrix m = (matrix)f;

For the examples in this book, you will be able to deduce the meaning of the cast from the syntax. However, if you want more detailed information on the supported casts, in the DirectX SDK documentation, under the Contents tab, see DirectX Graphics/Reference/Shader Reference/High Level Shading Language/Type.

五、操作符( 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++. The following table lists the HLSL operators:

[]

 

< =

> =

! =

= =

!

&&

 

?:

+

+ =

-

- =

*

*=

/

/=

%

%=

+ +

--

=

()

'

 

 

 

虽然操作符的行为非常接近于C++,也有一些不同。首先,取模%操作符用于整型和浮点两种类型。为了应用取模操作符,左值和右值两者都必须具有相同的符号(比如,两边都必须为正的或负的)。

    其次,很多HLSL操作符是以每一成员为基础工作的。这是因为实际上vectorsmatrices是内建于语言的,而且这些类型包括不同的成员。当操作符工作于成员级,操作符象vector/matrix相加,vector/matrix相减及vector/matrix相等测试,能够如我们用于数字类型一样的应用。看下面的例子。

 

Note 

操作行为如同数字(也就是说,C++通常方式)。

vector u = {1.0f, 0.0f, -3.0f, 1.0f};
vector v = {-4.0f, 2.0f, 1.0f, 0.0f};
 
// adds corresponding components
vector 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 component-wise:

vector u = {1.0f, 0.0f, -3.0f, 1.0f};
vector v = {-4.0f, 2.0f, 1.0f, 0.0f};
 
// multiply corresponding components
vector sum = u * v; // product = (-4.0f, 0.0f, -3.0f, 0.0f)

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:

vector u = { 1.0f, 0.0f, -3.0f, 1.0f};
vector v = {-4.0f, 0.0f, 1.0f, 1.0f};
 
vector b = (u == v); // b = (false, true, false, true)

最后,我们用二元操作符的变量提升来结束:

·         For binary operations, if the left side and right side differ in dimension, 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.

·         For binary operations, if the left side and right 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.

六、用户定义函数( User-Defined Functions)

HLSL中的函数有下面属性:

·         函数具有熟悉的C++语法。

·         参数通常按值传递。

·         不支持递归(Recursion) 。

·         函数通常是 inline.

此外,HLSL增加了一些额外的关键字用于函数。例如,考虑下面函数在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;
}

除了关键字in,out,inout外这个函数与C++函数一样。

·         in指定实参(我们传给一个参数的特殊变量)将在函数开始前拷贝到参数。不必显式指定参数为in,因为一个参数默认为in。例如,下面是等价的:

float square(in float x)
{
     return x * x;
}

没有显式指定in:

float square(float x)
{
     return x * x;
}

·         out指定参数在函数返回时拷贝到实参。这对于用参数返回值是有好处的。out关键字是必须的,因为HLSL不允许我们传参考或传指针。我们注意到如果参数被标为out,实参在函数开始前不被拷贝。换句话说,一个out参数只能用于输出数据它不能被用于输入。

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.

·         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.

七、 内置函数(Built-in Functions

HLSL has a rich set of built-in functions that are useful for 3D graphics. The following table is an abridged(删减的) list of them. In the next two chapters we will get practice using some of these functions. For now, just get familiar with them.

 

Note 

For further reference, the complete list of the built-in HLSL functions can be found in the DirectX documentation, under the Contents tab, then DirectX Graphics/Reference/Shader Reference/High Level Shader Language/Intrinsic Functions.

 

Function

Description

abs(x)

Returns |x|.

ceil(x)

Returns the smallest integer x.

clamp(x, a, b)

Clamps x to the range [a, b] and returns the result.

cos(x)

Returns the cosine of x, where x is in radians.

cross(u, v)

Returns u×v.

degrees(x)

Converts x from radians to degrees.

determinant(M)

Returns the determinant det(M).

distance(u, v)

Returns the distance ||v - u|| between the points u and v.

dot(u, v)

Returns u · v.

floor(x)

Returns the greatest integer x.

length(v)

Returns ||v||.

lerp(u, v, t)

Linearly interpolates between u and v based on the parameter t [0, 1 ].

log(x)

Returns ln(x).

log10(x)

Returns log10(x).

log2(x)

Returns log2(x).

max(x, y)

Returns x if x y, else returns y.

min(x, y)

Returns x if x, x y else returns y.

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.

normalize(v)

Returns v/v.

pow(b, n)

Returns bn.

radians(x)

Converts x from degrees to radians.

reflect(v, n)

Computes the reflection vector given the incident vector v and the surface normal n.

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.

rsqrt(x)

Returns

saturate(x)

Returns clamp(x, 0.0, 1.0).

sin(x)

Returns the sine of x, where x is in radians.

sincos(in x, out s, out c)

Returns the sine and cosine of x, where x is in radians.

sqrt(x)

Returns .

tan(x)

Returns the tangent of x, where x is in radians.

transpose(M)

Returns the transpose MT.

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, and 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:

floats 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)).

The following examples show how some of these intrinsic functions might be called:

float x = sin(1.0f);       // sine of 1.0f radian.
float y = sqrt(4.0f);      // square root of 4.
 
vector u = {1.0f, 2.0f, -3.0f, 0.0f};
vector v = {3.0f, -1.0f, 0.0f, 2.0f};
float  s = dot(u, v);      // compute dot product of u and v.
 
float3 i = {1.0f, 0.0f, 0.0f};
float3 j = {0.0f, 1.0f, 0.0f};
float3 k = cross(i, j);    // compute cross product of i and j.
 
matrix M = {1.0f, 2.0f, 3.0f, 4.0f};
matrix T = transpose(M); // compute transpose

附注:如何调试一个shader程序:

1、在VS.NET中点“调试àDirect3Dàstart with Direct3D debug”启动调试。

2、可以在shader中设置断点。

3、可以在断点停了后点“调试à窗体à反汇编”看到反汇编码。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值