First we look at the project result: A picture that looks “grey”.
Below is the original Picture.
So let’s see how to do it. First you had to know some knowledge about “DirectX Texture”. If you don’t, you can see the article “DirectX 9.0 C++ tutorial Texture ” Firstly.
Today’s topic is HLSL and Pixel Shader, it’s a some kind difficult topic.
HLSL is an abbreviation for “High-LevelShading Language”. We will use HLSL to write a small Pixel Shade program.Shortly, vertex and pixel shaders are small custom programs we write, executedon the graphic card’s GPU, that replace a portion of the “fixed function pipeline”. By replacing a section of the fixed function pipeline with our own custom shader program, we obtain a huge amount of flexibility in the graphical effects that we can achieve. We are no longer limited to predefined “fixed” operations.
HLSL’s grammar is similar to C or C++; we can learn it very quickly.
We can write the code to our HLSL shaders directly into our application source files as a long character string. However,it is more convenient and modular to separate the shader code from the application code. For this reason, we write our shaders in Notepad and save them as regular ASCII text files. Then we use the D3DXComplileShaderFromFile function to compile our shaders.
This sample uses a pixel shader as an example.
//
// globals
//
sampler BaseTex;
//
// input structures
//
struct PS_INPUT
{
float2 base : TEXCOORD;
};
//
// output structures
//
struct PS_OUTPUT
{
vector color : COLOR;
};
//
// Main Function
//
PS_OUTPUT Main(PS_INPUT input)
{
// zero out members of output
PS_OUTPUT output = (PS_OUTPUT)0;
vector b = tex2D(BaseTex, input.base);
// The core code, calculate the texel colors, so the picture will be look like "black and white"
vector c = 0.3 * b.x + 0.59 * b.y + 0.11 * b.z ;
output.color = c;
return output;
}
First we instantiate a global variable:
sampler BaseTex;
After the global variable is declared, we define two special structures, which we call the input and output structures.For pixel shaders, these structures define the texture data that our shader inputs and outputs, respectively.
struct PS_INPUT
{
float2 base : TEXCOORD;
};
struct PS_OUTPUT
{
vector color : COLOR;
};
In this sample, the texture coordinate that we input into our pixel shader contains only a texture coordinate component.The texture coordinate that our shader outputs contains a color component.
The syntax “: COLOR” says that the vector color is used to describe the color of the output vertex.
As with a C++ program, every HLSL program has an entry point. In our sample shader, we call our entry point function Main; however, that name is not mandatory. The shader’s entry point function name can be any valid function name. The entry point function must have an input structure parameter, which is used to pass the input texture coordinate intoour shader. The entry point function must also return an output structure instance, which is used to output the manipulated vertex from our shader.
Every shader has a constant table that is used to store its variable. The D3DX library providers 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.
D3DXHANDLE ID3DXConstantTable::GetConstantByName(
D3DXHANDLE hConstant, // scope of constant
LPCSTR pName // name of constant
);
For example, if the name of the variable in the shader is “BaseTex” and it was a top level parameter, we could write:
D3DXHANDLE h0;
H0 = ConstTable->GetConstantByName(0, “BaseTex”);
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 theXXX is replaced by a type name to indicate the type of variable being set. Forexample, if the variable that we wish to set is a Sampler State, the method name would be SetSampleState.
Theg eneral syntax of the ID3DXConstantTable::SetXXX methods is of the form:HRESULT ID3DXConstantTable::SetXXX(
LPDIRECT3DDEVICE9 pDevice,
D3DXHANDLE hConstant,
XXX value
);
For example:
LPDIRECT3DDEVICE9 g_pd3dDevice;
D3DXCONSTANT_DESC BaseTexDesc;
g_pd3dDevice->SetSamplerState(BaseTexDesc.RegisterIndex,D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
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
);
Here is an example call of D3DXCompileShaderFromFile:
//
// Compile shader
//
ID3DXConstantTable* TransformConstantTable = 0;
ID3DXBuffer* shader = 0;
ID3DXBuffer* errorBuffer = 0;
hr = D3DXCompileShaderFromFile("hlslTexture.txt",
0,
0,
"Main", // entry point function name
"ps_1_1", // HLSL shader name
D3DXSHADER_DEBUG,
&shader, // containing the created shader
&errorBuffer, // containing a listing of errors and warnings
&TextCT); // used to access shader constants
// 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;
}
Two things are input into a pixel shader:colors and texture coordinates. Both are per pixel. A per pixel texture coordinate is simply the (u,v) coordinates that specify the texel in the texture that is to be mapped to the pixel in question. Direct3D computes both colors and texture coordinates per pixel, from vertex colors and vertex texture coordinates, before entering the pixel shader. The number of colors and texture coordinates input into the pixel shader depends on how many colors and texture coordinates were output by the vertex shader. For example, if a vertex shader outputs two colors and three texture coordinates, then Direct3D will calculate two colors and three texture coordinates per pixel and input them into the pixel shader. We map the input colors and texture coordinates to variables inour pixel shader program using the semantic syntax. Using the previous example,we would write:
struct PS_INPUT
{
vector c0 : COLOR0;
vector c1 : COLOR1;
float2 t0 : TEXCOORD0;
float2 t1 : TEXCOORD1;
float2 t2 : TEXCOORD2;
};
For output, a pixel shader outputs a single computed color value for the pixel:
struct PS_OUTPUT
{
vector finalPixelColor : COLOR0;
};
The following list outlines the steps necessary to create and use a pixel shader.
1. Write and compile the pixel shader.
2. Create an IDirect3DPixelShader9 interface to represent the pixel shader based on the compiled shader code.
3. Enable the pixel shader with the IDirect3DDevice9::SetPixelShader method.
Of course, we have to destroy the pixel shader when we are done with it. The next few subsections go into these steps in more detail.
We compile the shader using the D3DXCompileShaderFromFile function.
Once we have the compiled shader code, we can obtain a pointer to an IDirect3DPixelShader9 interface, which represents apixel shader, using the following method:
HRESULT IDirect3DDevice9::CreatePixelShader(
CONST DWORD *pFunction,
IDirect3DPixelShader9** ppShader
);
For example, suppose 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);
After we have obtained a point 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 point to the pixel shader that we wish to enable. We would write:
Device->SetPixelShader(MultiTexPS);
As with all Direct3D interfaces, to clean them up we must call their Release method when we are finished with them.
//-----------------------------------------------------------------------------
// File: Textures.cpp
// About "Using Pixel Shader"
// Copyright (c) Microsoft Corporation & Waitingfy.com. All rights reserved.
//-----------------------------------------------------------------------------
#include
#include
#include
#include
//-----------------------------------------------------------------------------
// Global variables
//-----------------------------------------------------------------------------
LPDIRECT3D9 g_pD3D = NULL; // Used to create the D3DDevice
LPDIRECT3DDEVICE9 g_pd3dDevice = NULL; // Our rendering device
LPDIRECT3DVERTEXBUFFER9 g_pVB = NULL; // Buffer to hold vertices
LPDIRECT3DTEXTURE9 g_pTexture = NULL; // Our Texture
IDirect3DPixelShader9* TexPS = 0; // Interface to encapsulate the functionality of a pixel shader
ID3DXConstantTable* TextCT = 0; // Used to access the constant table
D3DXHANDLE BaseTexHandle = 0;
D3DXCONSTANT_DESC BaseTexDesc; // A description of a constant in a constant table
// A structure for out custom vertex type. And We added texture coordinates
struct CUSTOMVERTEX
{
float x, y, z;
unsigned long color;
FLOAT tu, tv; // texture coordinates
};
// Compare to "Triangle" Example, Here add D3DFVF_TEX1
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE|D3DFVF_TEX1)
//-----------------------------------------------------------------------------
// Name: InitD3D()
// Desc: Initializes Direct3D
//-----------------------------------------------------------------------------
HRESULT InitD3D( HWND hWnd )
{
// Create the D3D object
if( NULL == ( g_pD3D = Direct3DCreate9( D3D_SDK_VERSION ) ) )
return E_FAIL;
// Set up the structure used to create the D3DDevice. Since we are now
// using more complex geomitry, we will create a device with a zbuffer.
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory( &d3dpp, sizeof(d3dpp) );
d3dpp.Windowed = TRUE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
d3dpp.EnableAutoDepthStencil = TRUE;
d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
// Create the D3DDevice
if( FAILED( g_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
D3DCREATE_SOFTWARE_VERTEXPROCESSING,
&d3dpp, &g_pd3dDevice ) ) )
{
return E_FAIL;
}
// Turn off culling
g_pd3dDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE );
// Turn off D3D lighting
g_pd3dDevice->SetRenderState( D3DRS_LIGHTING, FALSE );
// Turn on the zbuffer
g_pd3dDevice->SetRenderState( D3DRS_ZENABLE, TRUE );
HRESULT hr = 0;
ID3DXBuffer* shader = 0;
ID3DXBuffer* errorBuffer = 0;
// Compile shader from a file
hr = D3DXCompileShaderFromFile("hlslTexture.txt",
0,
0,
"Main", // entry point function name
"ps_1_1", // HLSL shader name
D3DXSHADER_DEBUG,
&shader, // containing the created shader
&errorBuffer, // containing a listing of errors and warnings
&TextCT); // used to access shader constants
if(errorBuffer)
{
::MessageBox(0,(char*)errorBuffer->GetBufferPointer(), 0, 0);
errorBuffer->Release();
}
if(FAILED(hr))
{
::MessageBox(0,"D3DXCompileShaderFromFile() - FAILED", 0, 0);
return false;
}
// creates a pixel shader
hr = g_pd3dDevice->CreatePixelShader((DWORD*)shader->GetBufferPointer(),&TexPS);
if(FAILED(hr))
{
::MessageBox(0,"CreateVertexShader - FAILED", 0, 0);
return false;
}
shader->Release();
// gets a constant
BaseTexHandle = TextCT->GetConstantByName(0, "BaseTex");
// set constant descriptions
UINT count;
TextCT->GetConstantDesc(BaseTexHandle, &BaseTexDesc, &count);
return S_OK;
}
//-----------------------------------------------------------------------------
// Name: InitGeometry()
// Desc: Create the Textures and vertex buffers
//-----------------------------------------------------------------------------
HRESULT InitGeometry()
{
HRESULT hr = 0;
// create texture
hr = D3DXCreateTextureFromFile( g_pd3dDevice, "objective-c.png", &g_pTexture );
if(FAILED(hr))
{
MessageBox(NULL, "Could not find objective-c.png", "Textures.exe", MB_OK);
return E_FAIL;
}
// Initialize the vertices including texture coordinate
CUSTOMVERTEX Vertices[] =
{
{ -1.0f, -1.0f, 0.0f, D3DCOLOR_XRGB(255,255,255),0,1 }, // x, y, z, rhw, color,tu,tv
{ -1.0f, 1.0f, 0.0f, D3DCOLOR_XRGB(255,255,255), 0,0},
{ 1.0f, -1.0f, 0.0f, D3DCOLOR_XRGB(255,255,255), 1,1},
{ 1.0f, 1.0f, 0.0f, D3DCOLOR_XRGB(255,255,255), 1,0},
};
// Create the vertex buffer
hr = g_pd3dDevice->CreateVertexBuffer(sizeof(Vertices),
D3DUSAGE_WRITEONLY,D3DFVF_CUSTOMVERTEX,
D3DPOOL_MANAGED,&g_pVB,0);
if(hr)
{
return E_FAIL;
}
// Filled with data from the custom vertices
VOID* pVertices;
if( FAILED( g_pVB->Lock( 0, sizeof(Vertices), (void**)&pVertices, 0 ) ) )
return E_FAIL;
memcpy( pVertices, Vertices, sizeof(Vertices) );
g_pVB->Unlock();
return S_OK;
}
//-----------------------------------------------------------------------------
// Name: Cleanup()
// Desc: Released all previously initialized objects
//-----------------------------------------------------------------------------
VOID Cleanup()
{
if( g_pTexture != NULL )
g_pTexture->Release();
if( g_pVB != NULL )
g_pVB->Release();
if( g_pd3dDevice != NULL )
g_pd3dDevice->Release();
if( g_pD3D != NULL )
g_pD3D->Release();
if(TexPS != NULL )
TexPS->Release();
if(TextCT != NULL)
TextCT->Release();
}
//-----------------------------------------------------------------------------
// Name: SetupMatrices()
// Desc: Sets up the world, view, and projection transform matrices.
//-----------------------------------------------------------------------------
VOID SetupMatrices()
{
// Rotate by the Y axis
D3DXMATRIXA16 matWorld;
D3DXMatrixIdentity( &matWorld );
//remove the comments below to rotate the picture
//D3DXMatrixRotationY( &matWorld, timeGetTime()/1000.0f );
g_pd3dDevice->SetTransform( D3DTS_WORLD, &matWorld );
// Set View Transformation Matrix. Including some carame information
D3DXVECTOR3 vEyePt( 0.0f, 0.0f,-2.5f );
D3DXVECTOR3 vLookatPt( 0.0f, 0.0f, 0.0f );
D3DXVECTOR3 vUpVec( 0.0f, 1.0f, 0.0f );
D3DXMATRIXA16 matView;
D3DXMatrixLookAtLH( &matView, &vEyePt, &vLookatPt, &vUpVec );
g_pd3dDevice->SetTransform( D3DTS_VIEW, &matView );
// Set Transformation Matrix
D3DXMATRIXA16 matProj;
D3DXMatrixPerspectiveFovLH( &matProj, D3DX_PI/4, 1.0f, 1.0f, 100.0f );
g_pd3dDevice->SetTransform( D3DTS_PROJECTION, &matProj );
}
//-----------------------------------------------------------------------------
// Name: Render()
// Desc: Draws the scene
//-----------------------------------------------------------------------------
VOID Render()
{
// Clear the backbuffer and the zbuffer, Set color to blue
g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER,
D3DCOLOR_XRGB(0,0,255), 1.0f, 0 );
// Begin the scene
if( SUCCEEDED( g_pd3dDevice->BeginScene() ) )
{
// Setup the world, view, and projection matrices
SetupMatrices();
g_pd3dDevice->SetPixelShader(TexPS);
// Setting the texture to use
g_pd3dDevice->SetTexture(BaseTexDesc.RegisterIndex, g_pTexture);
g_pd3dDevice->SetSamplerState(BaseTexDesc.RegisterIndex, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
g_pd3dDevice->SetSamplerState(BaseTexDesc.RegisterIndex, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
g_pd3dDevice->SetSamplerState(BaseTexDesc.RegisterIndex, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
// Render the vertex buffer contents
g_pd3dDevice->SetStreamSource( 0, g_pVB, 0, sizeof(CUSTOMVERTEX) );
g_pd3dDevice->SetFVF( D3DFVF_CUSTOMVERTEX );
// A rectangle made of two triangles
g_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLESTRIP, 0, 2);
// End the scene
g_pd3dDevice->EndScene();
}
// Present the backbuffer contents to the dispaly
g_pd3dDevice->Present( NULL, NULL, NULL, NULL );
}
//-----------------------------------------------------------------------------
// Name: MsgProc()
// Desc: The window's message handler
//-----------------------------------------------------------------------------
LRESULT WINAPI MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
switch( msg )
{
case WM_DESTROY:
Cleanup();
PostQuitMessage( 0 );
return 0;
}
return DefWindowProc( hWnd, msg, wParam, lParam );
}
//-----------------------------------------------------------------------------
// Name: WinMain()
// Desc: The application's entry point
//-----------------------------------------------------------------------------
INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR, INT )
{
// Register the window class
WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc, 0L, 0L,
GetModuleHandle(NULL), NULL, NULL, NULL, NULL,
"D3D Tutorial", NULL };
RegisterClassEx( &wc );
// Create the application's window
HWND hWnd = CreateWindow( "D3D Tutorial", "D3D Tutorial 05: Textures",
WS_OVERLAPPEDWINDOW, 100, 100, 300, 300,
GetDesktopWindow(), NULL, wc.hInstance, NULL );
// Initialize Direct3D
if( SUCCEEDED( InitD3D( hWnd ) ) )
{
// Create the scene geometry
if( SUCCEEDED( InitGeometry() ) )
{
// Show the window
ShowWindow( hWnd, SW_SHOWDEFAULT );
UpdateWindow( hWnd );
// Enter the message loop
MSG msg;
ZeroMemory( &msg, sizeof(msg) );
while( msg.message!=WM_QUIT )
{
if( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
else
Render();
}
}
}
UnregisterClass( "D3D Tutorial", wc.hInstance );
return 0;
}
References:
1. <Introduction to 3D Game Programming with DirectX 9.0> Chapter 16:Introduction to the High-Level Shading Language,Chapter 18:Introduction to Pixel Shaders
2. DirectX 9.0 SDK's Tutorial 5: Using Texture Maps
3. HLSL初步知识讲解 http://download.csdn.net/detail/chengscga/3948392
Project Download:
HLSL+Pixel+Shader (www.Waitingfy.com Download)
http://www.waitingfy.com/?p=387