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

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

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

324 Chapter 18

Note: Since we are using pixel shaders, we will need to remember to change the compile target to a pixel shader target (e.g., ps_2_0) instead of a vertex shader target (e.g., vs_2_0). The compile targets are specified through a parameter of the D3DXCompileShaderFromFile function. See section 16.2 for details.

18.3.2 Creating a Pixel Shader

Once we have the compiled shader code, we can obtain a pointer to an

IDirect3DPixelShader9 interface, which represents a pixel shader,

);

 

 

Y

using the following method:

 

HRESULT IDirect3DDevice9::CreatePixelShader(

 

CONST DWORD *pFunction,

 

 

IDirect3DPixelShader9** ppShader

 

 

 

 

 

 

 

M

 

 

pFunction—Pointer to compiledLshader code

 

 

A

 

 

ppShader—Returns a pointerFto an IDirect3DPixelShader9

 

interface

E

 

 

 

 

For example,Tsuppose the variable shader is an ID3DXBuffer that contains the compiled shader code. Then to obtain an IDirect3DPixelShader9 interface, we would write:

IDirect3DPixelShader9* MultiTexPS = 0; hr = Device->CreatePixelShader(

(DWORD*)shader->GetBufferPointer(), &MultiTexPS);

Note: To reiterate, the D3DXCompileShaderFromFile is the function that would return the compiled shader code (shader).

18.3.3 Setting a Pixel Shader

After we have obtained a pointer to an IDirect3DPixelShader9 interface that represents our pixel shader, we can enable it using the following method:

HRESULT IDirect3DDevice9::SetPixelShader(

IDirect3DPixelShader9* pShader

);

The method takes a single parameter where we pass a pointer to the pixel shader that we wish to enable. To enable the shader we created in section 18.3.2, we would write:

Device->SetPixelShader(MultiTexPS);

Team-Fly®

Introduction to Pixel Shaders 325

18.3.4 Destroying a Pixel Shader

As with all Direct3D interfaces, to clean them up we must call their Release method when we are finished with them. Continuing to use the pixel shader we created in section 18.3.2, we have:

d3d::Release<IDirect3DPixelShader9*>(MultiTexPS);

18.4 HLSL Sampler Objects

Textures are sampled in a pixel shader using the special tex*-related intrinsic functions of HLSL.

Note: Sampling refers to indexing a texel for a pixel based on texture coordinates for the pixel and the sampler states (texture filter states).

See section 16.7 for details on these functions. In general, these functions require us to specify two things:

The (u, v) texture coordinates used to index into the texture

The particular texture we want to index into

The (u, v) texture coordinates are, of course, given as input into the pixel shader. The particular texture that we want to index into is identified in the pixel shader by a special HLSL object called a sampler. We can think of a sampler object as an object that identifies a texture and sampler stage. For example, suppose that we were using three texture stages, which implies we need to be able to refer to each of these stages in the pixel shader. In the pixel shader, we would write:

sampler FirstTex; sampler SecondTex; sampler ThirdTex;

Direct3D will associate each of these sampler objects with a unique texture stage. Then in the application we find out the stage that a sampler object corresponds with and set the appropriate texture and sampler states for that stage. The following code illustrates how the application would set the texture and sampler states for FirstTex:

//Create texture: IDirect3DTexture9* Tex;

D3DXCreateTextureFromFile(Device, "tex.bmp", &Tex);

.

.

.

//Get handle to constant:

FirstTexHandle = MultiTexCT->GetConstantByName(0, "FirstTex");

P a r t I V

326Chapter 18

//Get a description of the constant: D3DXCONSTANT_DESC FirstTexDesc;

UINT count;

MultiTexCT->GetConstantDesc(FirstTexHandle, &FirstTexDesc, &count);

.

.

.

//Set texture/sampler states for the sampler FirstTex. We identify

//the stage FirstTex is associated with from the

//D3DXCONSTANT_DESC::RegisterIndex member:

Device->SetTexture(FirstTexDesc.RegisterIndex,

Tex);

Device->SetSamplerState(FirstTexDesc.RegisterIndex,

D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);

Device->SetSamplerState(FirstTexDesc.RegisterIndex,

D3DSAMP_MINFILTER, D3DTEXF_LINEAR);

Device->SetSamplerState(FirstTexDesc.RegisterIndex,

D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);

Note: Alternatively, instead of using the sampler type, you can use the more specific and strongly typed sampler1D, sampler2D, sampler3D, and samplerCube types. These types are more type safe and ensure that they are only used with the appropriate tex* functions. For example, a sampler2D object can only be used with tex2D* functions. Similarly, a sampler3D object can only be used with tex3D* functions.

18.5 Sample Application:

Multitexturing in a Pixel Shader

The sample application for this chapter demonstrates multitexturing using a pixel shader. The sample will texture a quad based on the “result” in Figure 18.2 by blending together a crate texture, a spotlight texture, and a texture that contains the string “Pixel Shader Sample.”

Figure 18.2: Combining the textures. Let b, s, and t be the colors of corresponding texels from the crate texture, spotlight texture, and text texture, respectively. We define how these colors are combined as c = b s + t, where denotes compo- nent-wise multiplication.

Introduction to Pixel Shaders 327

This sample can be done without using pixel shaders. However, it is easy and straightforward to implement this application, and it allows us to demonstrate how to write, create, and use pixel shaders without getting distracted by the algorithm of some special effect.

Although we are only using three textures at once in this sample, it is worthwhile to go over the number of sampler objects that can be used with each pixel shader version. In other words, how many textures we can use at once depends on the pixel shader version that we use.

Pixel shader versions ps_1_1 to ps_1_3 support up to four texture samples.

Pixel shader version ps_1_4 supports up to six texture samples.

Pixel shader versions ps_2_0 to ps_3_0 support up to 16 texture samples.

The pixel shader for multitexturing with two textures is implemented as follows:

//

//File: ps_multitex.txt

//Desc: Pixel shader that does multitexturing.

//

// Globals

//

sampler BaseTex; sampler SpotLightTex; sampler StringTex;

//

// Structures

//

struct PS_INPUT

 

{

 

float2 base

: TEXCOORD0;

float2 spotlight : TEXCOORD1;

float2 text

: TEXCOORD2;

};

 

struct PS_OUTPUT

{

vector diffuse : COLOR0;

};

P a r t I V

//

// Main

//

328 Chapter 18

PS_OUTPUT Main(PS_INPUT input)

{

//zero out members of output PS_OUTPUT output = (PS_OUTPUT)0;

//sample appropriate textures

vector b = tex2D(BaseTex,

input.base);

vector s = tex2D(SpotLightTex,

input.spotlight);

vector t = tex2D(StringTex,

input.text);

//combine texel colors vector c = b * s + t;

//increase the intensity of the pixel slightly c += 0.1f;

//save the resulting pixel color output.diffuse = c;

return output;

}

First the pixel shader declares three sampler objects, one for each texture that we are blending. Next the input and output structures are defined. Notice that we don’t have any color values input into the pixel shader; this is because we are using the textures exclusively for coloring and lighting; that is, BaseTex holds the color of our surface and SpotLightTex is our light map. The pixel shader outputs a single color value that specifies the color that we have computed for this particular pixel.

The Main function samples the three textures using the tex2D function. That is, it fetches the texel from each texture that is to be mapped to the pixel that we are currently computing based on the specified texture coordinates and sampler object. We then combine the texel colors with the statement c = b * s + t. Next we brighten the overall pixel color a bit by adding 0.1f to each component. Finally, we save the resulting pixel color and return it.

Now that we have looked at the actual pixel shader code, we shift gears and look at the application code. The application has the following relevant global variables:

IDirect3DPixelShader9* MultiTexPS = 0;

ID3DXConstantTable* MultiTexCT

= 0;

IDirect3DVertexBuffer9* QuadVB = 0;

IDirect3DTexture9* BaseTex

= 0;

IDirect3DTexture9* SpotLightTex = 0;

IDirect3DTexture9* StringTex

= 0;

Introduction to Pixel Shaders 329

D3DXHANDLE BaseTexHandle

= 0;

D3DXHANDLE SpotLightTexHandle = 0;

D3DXHANDLE StringTexHandle

= 0;

D3DXCONSTANT_DESC BaseTexDesc;

D3DXCONSTANT_DESC SpotLightTexDesc;

D3DXCONSTANT_DESC StringTexDesc;

The vertex structure for the multitexturing sample is defined as:

struct MultiTexVertex

{

MultiTexVertex(float x, float y, float z, float u0, float v0,

float u1, float v1, float u2, float v2)

{

_x = x; _y = y; _z = z; _u0 = u0; _v0 = v0;

_u1 = u1; _v1 = v1; _u2 = u2, _v2 = v2;

}

float _x, _y, _z; float _u0, _v0; float _u1, _v1; float _u2, _v2;

static const DWORD FVF;

};

const DWORD MultiTexVertex::FVF = D3DFVF_XYZ | D3DFVF_TEX3;

Observe that it contains three sets of texture coordinates. The Setup function performs the following tasks:

Fills the vertex buffer representing the quad

Compiles the pixel shader

Creates the pixel shader

Loads the textures

Sets the projection matrix and disables lighting

Gets handles to the sampler objects

Gets descriptions of the sampler objects

bool Setup()

{

HRESULT hr = 0;

//

// Create quad geometry.

//

P a r t I V

Device->CreateVertexBuffer(

6 * sizeof(MultiTexVertex), D3DUSAGE_WRITEONLY,

330 Chapter 18

MultiTexVertex::FVF, D3DPOOL_MANAGED, &QuadVB,

0);

MultiTexVertex* v = 0;

QuadVB->Lock(0, 0, (void**)&v, 0);

v[0] = MultiTexVertex(-10.0f, -10.0f, 5.0f,

0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f); v[1] = MultiTexVertex(-10.0f, 10.0f, 5.0f,

0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f); v[2] = MultiTexVertex( 10.0f, 10.0f, 5.0f,

1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f);

v[3] = MultiTexVertex(-10.0f, -10.0f, 5.0f,

0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f); v[4] = MultiTexVertex( 10.0f, 10.0f, 5.0f,

1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f); v[5] = MultiTexVertex( 10.0f, -10.0f, 5.0f,

1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f);

QuadVB->Unlock();

//

// Compile shader

//

ID3DXBuffer*

shader

=

0;

ID3DXBuffer*

errorBuffer =

0;

hr = D3DXCompileShaderFromFile( "ps_multitex.txt",

0,

0,

"Main", // entry point function name "ps_1_1",

D3DXSHADER_DEBUG, &shader, &errorBuffer, &MultiTexCT);

// output any error messages if( errorBuffer )

{

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

}

if(FAILED(hr))

{

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

}

//

// Create Pixel Shader

Introduction to Pixel Shaders 331

//

hr = Device->CreatePixelShader( (DWORD*)shader->GetBufferPointer(), &MultiTexPS);

if(FAILED(hr))

{

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

}

d3d::Release<ID3DXBuffer*>(shader);

//

// Load textures.

//

D3DXCreateTextureFromFile(Device, "crate.bmp", &BaseTex);

D3DXCreateTextureFromFile(Device, "spotlight.bmp", &SpotLightTex);

D3DXCreateTextureFromFile(Device, "text.bmp", &StringTex);

//

// Set projection matrix

//

D3DXMATRIX P; D3DXMatrixPerspectiveFovLH(

&P, D3DX_PI * 0.25f,

(float)Width / (float)Height, 1.0f, 1000.0f);

Device->SetTransform(D3DTS_PROJECTION, &P);

//

// Disable lighting.

//

Device->SetRenderState(D3DRS_LIGHTING, false);

//

// Get handles

//

BaseTexHandle

= MultiTexCT->GetConstantByName(0, "BaseTex");

SpotLightTexHandle

= MultiTexCT->GetConstantByName(0, "SpotLightTex");

StringTexHandle

= MultiTexCT->GetConstantByName(0, "StringTex");

//

// Set constant descriptions:

//

UINT count;

MultiTexCT->GetConstantDesc( BaseTexHandle, &BaseTexDesc, &count);

MultiTexCT->GetConstantDesc(

P a r t I V

332 Chapter 18

SpotLightTexHandle,

&SpotLightTexDesc,

&count); MultiTexCT->GetConstantDesc(

StringTexHandle,

&StringTexDesc,

&count);

MultiTexCT->SetDefaults(Device);

return true;

}

The Display function sets the pixel shader, enables the two textures, and sets their corresponding sampler states before rendering the quad.

bool Display(float timeDelta)

{

if( Device )

{

// ...camera update code snipped

//

// Render

//

Device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,

0xffffffff, 1.0f, 0);

Device->BeginScene();

// set the pixel shader Device->SetPixelShader(MultiTexPS); Device->SetFVF(MultiTexVertex::FVF);

Device->SetStreamSource(0, QuadVB, 0, sizeof(MultiTexVertex));

// base tex Device->SetTexture(BaseTexDesc.RegisterIndex, BaseTex); Device->SetSamplerState(BaseTexDesc.RegisterIndex,

D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); Device->SetSamplerState(BaseTexDesc.RegisterIndex,

D3DSAMP_MINFILTER, D3DTEXF_LINEAR); Device->SetSamplerState(BaseTexDesc.RegisterIndex,

D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);

// spotlight tex Device->SetTexture(SpotLightTexDesc.RegisterIndex, SpotLightTex); Device->SetSamplerState(SpotLightTexDesc.RegisterIndex,

D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); Device->SetSamplerState(SpotLightTexDesc.RegisterIndex,

D3DSAMP_MINFILTER, D3DTEXF_LINEAR); Device->SetSamplerState(SpotLightTexDesc.RegisterIndex,

D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);

// string tex

Device->SetTexture( StringTexDesc.RegisterIndex, StringTex); Device->SetSamplerState(StringTexDesc.RegisterIndex,

D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);

Introduction to Pixel Shaders 333

Device->SetSamplerState(StringTexDesc.RegisterIndex,

D3DSAMP_MINFILTER, D3DTEXF_LINEAR);

Device->SetSamplerState(StringTexDesc.RegisterIndex,

D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);

// draw the quad Device->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 2);

Device->EndScene(); Device->Present(0, 0, 0, 0);

}

return true;

}

Of course we must remember to free our allocated interfaces in the Cleanup function:

void Cleanup()

{

d3d::Release<IDirect3DVertexBuffer9*>(QuadVB);

d3d::Release<IDirect3DTexture9*>(BaseTex);

d3d::Release<IDirect3DTexture9*>(SpotLightTex);

d3d::Release<IDirect3DTexture9*>(StringTex);

d3d::Release<IDirect3DPixelShader9*>(MultiTexPS);

d3d::Release<ID3DXConstantTable*>(MultiTexCT);

}

18.6Summary

Pixel shaders replace the multitexturing stage of the fixed function pipeline. Furthermore, pixel shaders give us the ability to modify pixels on an individual basis in any way that we choose and access texture data, thereby empowering us to implement many special effects that could not be achieved in the fixed function pipeline.

Multitexturing is the process of enabling several textures at once and blending them together to produce a desired result. Multitexturing is typically used to implement a complete lighting engine for static geometry.

The HLSL intrinsic sampler objects identify a particular texture/sampler stage. A sampler object is used to refer to a texture/sampler stage from the pixel shader.

P a r t I V