fx HLSL shader program through the technique pointer. The first step in this function is to set our input layout to active in the input assembler. This lets the ...
Tutorial4:Buffers,Shaders,andHLSL
ThistutorialwillbetheintroductiontowritingvertexandpixelshadersinDirectX10.
ItwillalsobetheintroductiontousingvertexandindexbuffersinDirectX10.
Thesearethemostfundamentalconceptsthatyouneedtounderstandandutilizetorender3Dgraphics.
VertexBuffers
Thefirstconcepttounderstandisvertexbuffers.Toillustratethisconceptletustaketheexampleofa3Dmodelofasphere:
The3Dspheremodelisactuallycomposedofhundredsoftriangles:
Eachofthetrianglesinthespheremodelhasthreepointstoit,wecalleachpointavertex.
Soforustorenderthespheremodelweneedtoputalltheverticesthatformthesphereintoaspecialdataarraythatwecallavertexbuffer.
OnceallthepointsofthespheremodelareinthevertexbufferwecanthensendthevertexbuffertotheGPUsothatitcanrenderthemodel.
IndexBuffers
Indexbuffersarerelatedtovertexbuffers.Theirpurposeistorecordthelocationofeachvertexthatisinthevertexbuffer.
TheGPUthenusestheindexbuffertoquicklyfindspecificverticesinthevertexbuffer.
Theconceptofanindexbufferissimilartotheconceptusinganindexinabook,ithelpsfindthetopicyouarelookingforatamuchhigherspeed.
TheDirectXSDKdocumentationsaysthatusingindexbufferscanalsoincreasethepossibilityofcachingthevertexdatainfasterlocationsinvideomemory.
Soitishighlyadvisedtousetheseforperformancereasonsaswell.
VertexShaders
Vertexshadersaresmallprogramsthatarewrittenmainlyfortransformingtheverticesfromthevertexbufferinto3Dspace.
Thereareothercalculationsthatcanbedonesuchascalculatingnormalsforeachvertex.
ThevertexshaderprogramwillbecalledbytheGPUforeachvertexitneedstoprocess.
Forexamplea5,000polygonmodelwillrunyourvertexshaderprogram15,000timeseachframejusttodrawthatsinglemodel.
Soifyoulockyourgraphicsprogramto60fpsitwillcallyourvertexshader900,000timesasecondtodrawjust5,000triangles.
Asyoucantellwritingefficientvertexshadersisimportant.
PixelShaders
Pixelshadersaresmallprogramsthatarewrittenfordoingthecoloringofthepolygonsthatwedraw.
TheyarerunbytheGPUforeveryvisiblepixelthatwillbedrawntothescreen.
Coloring,texturing,lighting,andmostothereffectsyouplantodotoyourpolygonfacesarehandledbythepixelshaderprogram.
PixelshadersmustbeefficientlywrittenduetothenumberoftimestheywillbecalledbytheGPU.
HLSL
HLSListhelanguageweuseinDirectX10tocodethesesmallvertexandpixelshaderprograms.
ThesyntaxisprettymuchidenticaltotheClanguagewithsomepre-definedtypes.
HLSLprogramfilesarecomposedofglobalvariables,typedefines,vertexshaders,pixelshaders,andgeometryshaders.
AsthisisthefirstHLSLtutorialwewilldoaverysimpleHLSLprogramusingDirectX10togetstarted.
UpdatedFramework
Theframeworkhasbeenupdatedforthistutorial.UnderGraphicsClasswehaveaddedthreenewclassescalledCameraClass,ModelClass,andColorShaderClass.
CameraClasswilltakecareofourviewmatrixwetalkedaboutpreviously.Itwillhandlethelocationofthecameraintheworldandpassit
toshaderswhentheyneedtodrawandfigureoutwherewearelookingatthescenefrom.
TheModelClasswillhandlethegeometryofour3Dmodels,inthistutorialthe3Dmodelwilljustbeasingletriangleforsimplicityreasons.
AndfinallyColorShaderClasswillberesponsibleforrenderingthemodeltothescreeninvokingourHLSLshader.
WewillbeginthetutorialcodebylookingattheHLSLshaderprogramfirst.
Color.fx
Thiswillbeourfirstshaderprogram.
Shadersaresmallprogramsthatdotheactualrenderingofmodels.
ThisshaderiswritteninHLSLandstoredinasourcefilecalledcolor.fx.
Iplacedthefilewiththe.cppand.hfilesintheenginefornow.
ThepurposeofthisshaderisjusttodrawcoloredtrianglesasIamkeepingthingssimpleaspossibleinthisfirstHLSLtutorial.
Hereisthecode:
////////////////////////////////////////////////////////////////////////////////
//Filename:color.fx
////////////////////////////////////////////////////////////////////////////////
Globalvariablesinsidetheshaderallowustohaveexternalaccesstomodifytheinternalshadervariables.
ForexamplewiththesethreematriceswewillsetthemintheGraphicsClassandthenpassthemintothisshader
sotheycanbeusedbytheColorVertexShaderbelow.
Youcanusemanytypesofvariablessuchasintorfloatandthensetthemexternallyfortheshaderprogramtouse.
/////////////
//GLOBALS//
/////////////
matrixworldMatrix;
matrixviewMatrix;
matrixprojectionMatrix;
SimilartoCwecancreateourowntypedefinitions.
Wewillusedifferenttypessuchasfloat4thatareavailabletoHLSLwhichmakeprogrammingshaderseasierandreadable.
Inthisexamplewearecreatingtypesthathavex,y,z,wpositionvectorsandred,green,blue,alphacolors.
ThePOSITION,COLOR,andSV_POSITIONaresemanticsthatconveytotheGPUtheuseofthevariable.
Ihavetocreatetwodifferentstructuresheresincethesemanticsaredifferentforvertexandpixelshaderseventhoughthestructuresarethesameotherwise.
POSITIONworksforvertexshadersandSV_POSITIONworksforpixelshaderswhileCOLORworksforboth.
IfyouwantmorethanoneofthesametypethenyouhavetoaddanumbertotheendsuchasCOLOR0,COLOR1,andsoforth.
//////////////
//TYPEDEFS//
//////////////
structVertexInputType
{
float4position:POSITION;
float4color:COLOR;
};
structPixelInputType
{
float4position:SV_POSITION;
float4color:COLOR;
};
ThevertexshaderiscalledbytheGPUwhenitisprocessingdatafromthevertexbuffersthathavebeensenttoit.
ThisfunctionwhichInamedColorVertexShaderwillbecalledforeverysinglevertexinthevertexbuffer.
Theinputtothevertexshadermustmatchthedataformatinthevertexbufferaswellasthetypedefinitionintheshadersourcefilewhich
inthiscaseisVertexInputType.
Theoutputofthevertexshaderwillbesenttothepixelshader.
InthiscasetheoutputtypeiscalledPixelInputTypewhichisdefinedaboveaswell.
WiththatinmindyouseethatthevertexshadercreatesanoutputvariablethatisofthePixelInputTypetype.
Itthentakesthepositionoftheinputvertexandmultipliesitbytheworld,view,andthenprojectionmatrices.
Thiswillplacethevertexinthecorrectlocationforrenderingin3Dspaceaccordingtoourview.
Afterthattheoutputvariabletakesacopyoftheinputcolorandthenreturnstheoutputwhichwillbeusedasinputtothepixelshader.
AlsonotethatIdosettheWvalueoftheinputpositionto1.0otherwiseitisundefinedsinceweonlyreadinaXYZvectorforposition.
////////////////////////////////////////////////////////////////////////////////
//VertexShader
////////////////////////////////////////////////////////////////////////////////
PixelInputTypeColorVertexShader(VertexInputTypeinput)
{
PixelInputTypeoutput;
//Changethepositionvectortobe4unitsforpropermatrixcalculations.
input.position.w=1.0f;
//Calculatethepositionofthevertexagainsttheworld,view,andprojectionmatrices.
output.position=mul(input.position,worldMatrix);
output.position=mul(output.position,viewMatrix);
output.position=mul(output.position,projectionMatrix);
//Storetheinputcolorforthepixelshadertouse.
output.color=input.color;
returnoutput;
}
Thepixelshaderdrawseachpixelonthepolygonsthatwillberenderedtothescreen.
InthispixelshaderitusesPixelInputTypeasinputandreturnsafloat4asoutputwhichrepresentsthefinalpixelcolor.
Inthispixelshaderwejusttellittocolorthepixelthesameastheinputvalueofthecolor.
Notethatthepixelshadergetsitsinputfromthevertexshaderoutput.
////////////////////////////////////////////////////////////////////////////////
//PixelShader
////////////////////////////////////////////////////////////////////////////////
float4ColorPixelShader(PixelInputTypeinput):SV_Target
{
returninput.color;
}
Thetechniqueistheactual"shader".
Thisiswhatisusedtorenderourpolygonsandcallthevertexandpixelshaders,youcanconsideritasthemain()functionoftheHLSLshader.
Youcansetituptodomultiplepassesandcallallsortsofdifferentpixelandvertexshaderstocreateadesiredeffect.
Inthisexamplewewillonlyuseasinglepassandonlycallthevertexandpixelshaderwedefinedearlier.
Wewon'tbecallingageometryshadereitherasitisn'trequiredforourpurposesrightnow.
Onethingtonoteisthatwesetourvertexshadertoversion4.0byusingthevs_4_0asthefirstargumentinSetVertexShader.
ThisgivesusaccesstotheDirectX10HLSLfunctionalitythatcorrespondstovertexshader4.0.
Aswellwealsosetthepixelshadertoversion4.0usingps_4_0intheSetPixelShaderfunction.
Youcansetthistobeoldervertexandpixelshaderversionsbutwewanttoworkwiththelatestandgreatest.
////////////////////////////////////////////////////////////////////////////////
//Technique
////////////////////////////////////////////////////////////////////////////////
technique10ColorTechnique
{
passpass0
{
SetVertexShader(CompileShader(vs_4_0,ColorVertexShader()));
SetPixelShader(CompileShader(ps_4_0,ColorPixelShader()));
SetGeometryShader(NULL);
}
}
Modelclass.h
AsstatedpreviouslytheModelClassisresponsibleforencapsulatingthegeometryfor3Dmodels.
Inthistutorialwewillmanuallysetupthedataforasinglegreentriangle.
Wewillalsocreateavertexandindexbufferforthetrianglesothatitcanberendered.
////////////////////////////////////////////////////////////////////////////////
//Filename:modelclass.h
////////////////////////////////////////////////////////////////////////////////
#ifndef_MODELCLASS_H_
#define_MODELCLASS_H_
//////////////
//INCLUDES//
//////////////
#include
#include
////////////////////////////////////////////////////////////////////////////////
//Classname:ModelClass
////////////////////////////////////////////////////////////////////////////////
classModelClass
{
private:
HereisthedefinitionofourvertextypethatwillbeusedwiththevertexbufferinthisModelClass.
AlsotakenotethatthistypedefmustmatchthelayoutintheColorShaderClassthatwillbelookedatlaterinthetutorial.
structVertexType
{
D3DXVECTOR3position;
D3DXVECTOR4color;
};
public:
ModelClass();
ModelClass(constModelClass&);
~ModelClass();
Thefunctionsherehandleinitializingandshutdownofthemodel'svertexandindexbuffers.
TheRenderfunctionputsthemodelgeometryonthevideocardtoprepareitfordrawingbythecolorshader.
boolInitialize(ID3D10Device*);
voidShutdown();
voidRender(ID3D10Device*);
intGetIndexCount();
private:
boolInitializeBuffers(ID3D10Device*);
voidShutdownBuffers();
voidRenderBuffers(ID3D10Device*);
TheprivatevariablesintheModelClassarethevertexandindexbufferaswellastwointegerstokeeptrackofthesizeofeachbuffer.
NotethatallDirectX10buffersgenerallyusethegenericID3D10Buffertypeandaremoreclearlyidentifiedbyabufferdescriptionwhentheyarefirstcreated.
private:
ID3D10Buffer*m_vertexBuffer,*m_indexBuffer;
intm_vertexCount,m_indexCount;
};
#endif
Modelclass.cpp
////////////////////////////////////////////////////////////////////////////////
//Filename:modelclass.cpp
////////////////////////////////////////////////////////////////////////////////
#include"modelclass.h"
Theclassconstructorinitializesthevertexandindexbufferpointerstonull.
ModelClass::ModelClass()
{
m_vertexBuffer=0;
m_indexBuffer=0;
}
ModelClass::ModelClass(constModelClass&other)
{
}
ModelClass::~ModelClass()
{
}
TheInitializefunctionwillcalltheinitializationfunctionsforthevertexandindexbuffers.
boolModelClass::Initialize(ID3D10Device*device)
{
boolresult;
//Initializethevertexandindexbufferthatholdthegeometryforthetriangle.
result=InitializeBuffers(device);
if(!result)
{
returnfalse;
}
returntrue;
}
TheShutdownfunctionwillcalltheshutdownfunctionsforthevertexandindexbuffers.
voidModelClass::Shutdown()
{
//Releasethevertexandindexbuffers.
ShutdownBuffers();
return;
}
RenderiscalledfromtheGraphicsClass::Renderfunction.
ThisfunctioncallsRenderBufferstoputthevertexandindexbuffersonthegraphicspipelinesothecolorshaderwillbeabletorenderthem.
voidModelClass::Render(ID3D10Device*device)
{
//Putthevertexandindexbuffersonthegraphicspipelinetopreparethemfordrawing.
RenderBuffers(device);
return;
}
GetIndexCountreturnsthenumberofindexesinthemodel.
Thecolorshaderwillneedthisinformationtodrawthismodel.
intModelClass::GetIndexCount()
{
returnm_indexCount;
}
TheInitializeBuffersfunctioniswherewehandlecreatingthevertexandindexbuffers.
Usuallyyouwouldreadinamodelandcreatethebuffersfromthatdatafile.
Forthistutorialwewilljustsetthethreepointsinthevertexandindexbuffermanuallysinceitisonlyasingletriangle.
boolModelClass::InitializeBuffers(ID3D10Device*device)
{
VertexType*vertices;
unsignedlong*indices;
D3D10_BUFFER_DESCvertexBufferDesc,indexBufferDesc;
D3D10_SUBRESOURCE_DATAvertexData,indexData;
HRESULTresult;
Firstcreatetwotemporaryarraystoholdthevertexandindexdatathatwewilluselatertopopulatethefinalbufferswith.
//Setthenumberofverticesinthevertexarray.
m_vertexCount=3;
//Setthenumberofindicesintheindexarray.
m_indexCount=3;
//Createthevertexarray.
vertices=newVertexType[m_vertexCount];
if(!vertices)
{
returnfalse;
}
//Createtheindexarray.
indices=newunsignedlong[m_indexCount];
if(!indices)
{
returnfalse;
}
Nowfillboththevertexandindexarraywiththethreepointsofthetriangleaswellastheindextoeachofthepoints.
PleasenotethatIcreatethepointsintheclockwiseorderofdrawingthem.
Ifyoudothiscounterclockwiseitwillthinkthetriangleisfacingtheoppositedirectionandnotdrawitduetobackfaceculling.
AlwaysrememberthattheorderinwhichyousendyourverticestotheGPUisveryimportant!
Thecolorissethereaswellsinceitispartofthevertexdescription.
Isetthecolortogreen.
//Loadthevertexarraywithdata.
vertices[0].position=D3DXVECTOR3(-1.0f,-1.0f,0.0f);//Bottomleft.
vertices[0].color=D3DXVECTOR4(0.0f,1.0f,0.0f,1.0f);
vertices[1].position=D3DXVECTOR3(0.0f,1.0f,0.0f);//Topmiddle.
vertices[1].color=D3DXVECTOR4(0.0f,1.0f,0.0f,1.0f);
vertices[2].position=D3DXVECTOR3(1.0f,-1.0f,0.0f);//Bottomright.
vertices[2].color=D3DXVECTOR4(0.0f,1.0f,0.0f,1.0f);
//Loadtheindexarraywithdata.
indices[0]=0;//Bottomleft.
indices[1]=1;//Topmiddle.
indices[2]=2;//Bottomright.
Withthevertexarrayandindexarrayfilledoutwecannowusethosetocreatethevertexbufferandindexbuffer.
Creatingbothbuffersisdoneinthesamefashion.
Firstfilloutadescriptionofthebuffer.
InthedescriptiontheByteWidth(sizeofthebuffer)andtheBindFlags(typeofbuffer)arewhatyouneedtoensurearefilledoutcorrectly.
Afterthedescriptionisfilledoutyouneedtoalsofilloutasubresourcepointerwhichwillpointtoeitheryourvertexorindexarrayyoupreviouslycreated.
WiththedescriptionandsubresourcepointeryoucancallCreateBufferusingtheD3Ddeviceanditwillreturnapointertoyournewbuffer.
//Setupthedescriptionofthevertexbuffer.
vertexBufferDesc.Usage=D3D10_USAGE_DEFAULT;
vertexBufferDesc.ByteWidth=sizeof(VertexType)*m_vertexCount;
vertexBufferDesc.BindFlags=D3D10_BIND_VERTEX_BUFFER;
vertexBufferDesc.CPUAccessFlags=0;
vertexBufferDesc.MiscFlags=0;
//Givethesubresourcestructureapointertothevertexdata.
vertexData.pSysMem=vertices;
//Nowfinallycreatethevertexbuffer.
result=device->CreateBuffer(&vertexBufferDesc,&vertexData,&m_vertexBuffer);
if(FAILED(result))
{
returnfalse;
}
//Setupthedescriptionoftheindexbuffer.
indexBufferDesc.Usage=D3D10_USAGE_DEFAULT;
indexBufferDesc.ByteWidth=sizeof(unsignedlong)*m_indexCount;
indexBufferDesc.BindFlags=D3D10_BIND_INDEX_BUFFER;
indexBufferDesc.CPUAccessFlags=0;
indexBufferDesc.MiscFlags=0;
//Givethesubresourcestructureapointertotheindexdata.
indexData.pSysMem=indices;
//Createtheindexbuffer.
result=device->CreateBuffer(&indexBufferDesc,&indexData,&m_indexBuffer);
if(FAILED(result))
{
returnfalse;
}
Afterthevertexbufferandindexbufferhavebeencreatedyoucandeletethevertexandindexarraysastheyarenolonger
neededsincethedatawascopiedintothebuffers.
//Releasethearraysnowthatthevertexandindexbuffershavebeencreatedandloaded.
delete[]vertices;
vertices=0;
delete[]indices;
indices=0;
returntrue;
}
TheShutdownBuffersfunctionjustreleasesthevertexbufferandindexbufferthatwerecreatedintheInitializeBuffersfunction.
voidModelClass::ShutdownBuffers()
{
//Releasetheindexbuffer.
if(m_indexBuffer)
{
m_indexBuffer->Release();
m_indexBuffer=0;
}
//Releasethevertexbuffer.
if(m_vertexBuffer)
{
m_vertexBuffer->Release();
m_vertexBuffer=0;
}
return;
}
RenderBuffersiscalledfromtheRenderfunction.
ThepurposeofthisfunctionistosetthevertexbufferandindexbufferasactiveontheinputassemblerintheGPU.
OncetheGPUhasanactivevertexbufferitcanthenusetheHLSLshaderwewrotetorenderthatbuffer.
Thisfunctionalsodefineshowthosebuffersshouldbedrawnsuchastriangles,lines,fans,andsoforth.
InthistutorialwesetthevertexbufferandindexbufferasactiveontheinputassemblerandtelltheGPUthatthe
buffersshouldbedrawnastrianglesusingtheIASetPrimitiveTopologyDirectXfunction.
voidModelClass::RenderBuffers(ID3D10Device*device)
{
unsignedintstride;
unsignedintoffset;
//Setvertexbufferstrideandoffset.
stride=sizeof(VertexType);
offset=0;
//Setthevertexbuffertoactiveintheinputassemblersoitcanberendered.
device->IASetVertexBuffers(0,1,&m_vertexBuffer,&stride,&offset);
//Settheindexbuffertoactiveintheinputassemblersoitcanberendered.
device->IASetIndexBuffer(m_indexBuffer,DXGI_FORMAT_R32_UINT,0);
//Setthetypeofprimitivethatshouldberenderedfromthisvertexbuffer,inthiscasetriangles.
device->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
return;
}
Colorshaderclass.h
TheColorShaderClassiswhatwewillusetoinvokeourHLSLshaderfordrawingthe3DmodelsthatareontheGPU.
////////////////////////////////////////////////////////////////////////////////
//Filename:colorshaderclass.h
////////////////////////////////////////////////////////////////////////////////
#ifndef_COLORSHADERCLASS_H_
#define_COLORSHADERCLASS_H_
//////////////
//INCLUDES//
//////////////
#include
#include
#include
usingnamespacestd;
////////////////////////////////////////////////////////////////////////////////
//Classname:ColorShaderClass
////////////////////////////////////////////////////////////////////////////////
classColorShaderClass
{
public:
ColorShaderClass();
ColorShaderClass(constColorShaderClass&);
~ColorShaderClass();
Thefunctionsherehandleinitializingandshutdownoftheshader.
Therenderfunctionsetstheshaderparametersandthendrawsthepreparedmodelverticesusingtheshader.
boolInitialize(ID3D10Device*,HWND);
voidShutdown();
voidRender(ID3D10Device*,int,D3DXMATRIX,D3DXMATRIX,D3DXMATRIX);
private:
boolInitializeShader(ID3D10Device*,HWND,WCHAR*);
voidShutdownShader();
voidOutputShaderErrorMessage(ID3D10Blob*,HWND,WCHAR*);
voidSetShaderParameters(D3DXMATRIX,D3DXMATRIX,D3DXMATRIX);
voidRenderShader(ID3D10Device*,int);
private:
ID3D10Effect*m_effect;
ID3D10EffectTechnique*m_technique;
ID3D10InputLayout*m_layout;
ID3D10EffectMatrixVariable*m_worldMatrixPtr;
ID3D10EffectMatrixVariable*m_viewMatrixPtr;
ID3D10EffectMatrixVariable*m_projectionMatrixPtr;
};
#endif
Colorshaderclass.cpp
////////////////////////////////////////////////////////////////////////////////
//Filename:colorshaderclass.cpp
////////////////////////////////////////////////////////////////////////////////
#include"colorshaderclass.h"
Asusualtheclassconstructorinitializesalltheprivatepointersintheclasstonull.
ColorShaderClass::ColorShaderClass()
{
m_effect=0;
m_technique=0;
m_layout=0;
m_worldMatrixPtr=0;
m_viewMatrixPtr=0;
m_projectionMatrixPtr=0;
}
ColorShaderClass::ColorShaderClass(constColorShaderClass&other)
{
}
ColorShaderClass::~ColorShaderClass()
{
}
TheInitializefunctionwillcalltheinitializationfunctionfortheshader.WepassinthenameoftheHLSLshaderfile,
inthistutorialitisnamedcolor.fx.
boolColorShaderClass::Initialize(ID3D10Device*device,HWNDhwnd)
{
boolresult;
//Initializetheshaderthatwillbeusedtodrawthetriangle.
result=InitializeShader(device,hwnd,L"../Engine/color.fx");
if(!result)
{
returnfalse;
}
returntrue;
}
TheShutdownfunctionwillcalltheshutdownoftheshader.
voidColorShaderClass::Shutdown()
{
//Shutdowntheshadereffect.
ShutdownShader();
return;
}
RenderwillfirstsettheparametersinsidetheshaderusingtheSetShaderParametersfunction.
OncetheparametersaresetitthencallsRenderShadertodrawthegreentriangleusingtheHLSLshader.
voidColorShaderClass::Render(ID3D10Device*device,intindexCount,D3DXMATRIXworldMatrix,D3DXMATRIXviewMatrix,D3DXMATRIXprojectionMatrix)
{
//Settheshaderparametersthatitwilluseforrendering.
SetShaderParameters(worldMatrix,viewMatrix,projectionMatrix);
//Nowrenderthepreparedbufferswiththeshader.
RenderShader(device,indexCount);
return;
}
NowwewillstartwithoneofthemoreimportantfunctionstothistutorialwhichiscalledInitializeShader.
ThisfunctioniswhatactuallyloadstheshaderfileandmakesitusabletoDirectXandtheGPU.
YouwillalsoseethesetupofthelayoutandhowthevertexbufferdataisgoingtolookonthegraphicspipelineintheGPU.
ThelayoutwillneedthematchtheVertexTypeinthemodelclass.hfileaswellastheonedefinedinthecolor.fxfile.
boolColorShaderClass::InitializeShader(ID3D10Device*device,HWNDhwnd,WCHAR*filename)
{
HRESULTresult;
ID3D10Blob*errorMessage;
D3D10_INPUT_ELEMENT_DESCpolygonLayout[2];
unsignedintnumElements;
D3D10_PASS_DESCpassDesc;
//Initializetheerrormessage.
errorMessage=0;
Hereiswherewecompiletheshaderprogramintoaneffect.
Wegiveitthenameoftheshaderfile,theshaderversion(4.0inDirectX10),andtheeffecttocompiletheshaderinto.
IfitfailscompilingtheshaderitwillputanerrormessageinsidetheerrorMessagestringwhichwesendtoanotherfunctiontowriteouttheerror.
IfitstillfailsandthereisnoerrorMessagestringthenitmeansitcouldnotfindtheshaderfileinwhichcasewepopupadialogboxsayingso.
//Loadtheshaderinfromthefile.
result=D3DX10CreateEffectFromFile(filename,NULL,NULL,"fx_4_0",D3D10_SHADER_ENABLE_STRICTNESS,0,
device,NULL,NULL,&m_effect,&errorMessage,NULL);
if(FAILED(result))
{
//Iftheshaderfailedtocompileitshouldhavewritensomethingtotheerrormessage.
if(errorMessage)
{
OutputShaderErrorMessage(errorMessage,hwnd,filename);
}
//Iftherewasnothingintheerrormessagethenitsimplycouldnotfindtheshaderfileitself.
else
{
MessageBox(hwnd,filename,L"MissingShaderFile",MB_OK);
}
returnfalse;
}
Oncetheshadercodehassuccessfullycompiledintoaneffectwethenusethateffecttogetthetechniqueinsidetheshader.
Wewillusethetechniquetodrawwiththeshaderfromthispointforward.
//Getapointertothetechniqueinsidetheshader.
m_technique=m_effect->GetTechniqueByName("ColorTechnique");
if(!m_technique)
{
returnfalse;
}
Thenextstepistocreatethelayoutofthevertexdatathatwillbeprocessedbytheshader.
Asthisshaderusesapositionandcolorvectorweneedtocreatebothinthelayoutspecifyingthesizeofboth.
Thesemanticnameisthefirstthingtofilloutinthelayout,thisallowstheshadertodeterminetheusageofthiselementofthelayout.
AswehavetwodifferentelementsweusePOSITIONforthefirstoneandCOLORforthesecond.
ThenextimportantpartofthelayoutistheFormat.
ForthepositionvectorweuseDXGI_FORMAT_R32G32B32_FLOATandforthecolorweuseDXGI_FORMAT_R32G32B32A32_FLOAT.
ThefinalthingyouneedtopayattentiontoistheAlignedByteOffsetwhichindicateshowthedataisspacedinthebuffer.
Forthislayoutwearetellingitthefirst12bytesarepositionandthenext16byteswillbecolor,AlignedByteOffsetshowswhereeachelementbegins.
YoucanuseD3D10_APPEND_ALIGNED_ELEMENTinsteadofplacingyourownvaluesinAlignedByteOffsetanditwillfigureoutthespacingforyou.
TheothersettingsI'vemadedefaultfornowastheyarenotneededinthistutorial.
//Nowsetupthelayoutofthedatathatgoesintotheshader.
//ThissetupneedstomatchtheVertexTypestuctureintheModelClassandintheshader.
polygonLayout[0].SemanticName="POSITION";
polygonLayout[0].SemanticIndex=0;
polygonLayout[0].Format=DXGI_FORMAT_R32G32B32_FLOAT;
polygonLayout[0].InputSlot=0;
polygonLayout[0].AlignedByteOffset=0;
polygonLayout[0].InputSlotClass=D3D10_INPUT_PER_VERTEX_DATA;
polygonLayout[0].InstanceDataStepRate=0;
polygonLayout[1].SemanticName="COLOR";
polygonLayout[1].SemanticIndex=0;
polygonLayout[1].Format=DXGI_FORMAT_R32G32B32A32_FLOAT;
polygonLayout[1].InputSlot=0;
polygonLayout[1].AlignedByteOffset=D3D10_APPEND_ALIGNED_ELEMENT;
polygonLayout[1].InputSlotClass=D3D10_INPUT_PER_VERTEX_DATA;
polygonLayout[1].InstanceDataStepRate=0;
OncethelayoutdescriptionhasbeensetupwecangetthesizeofitandthencreatetheinputlayoutusingtheD3Ddevice.
//Getacountoftheelementsinthelayout.
numElements=sizeof(polygonLayout)/sizeof(polygonLayout[0]);
//Getthedescriptionofthefirstpassdescribedintheshadertechnique.
m_technique->GetPassByIndex(0)->GetDesc(&passDesc);
//Createtheinputlayout.
result=device->CreateInputLayout(polygonLayout,numElements,passDesc.pIAInputSignature,passDesc.IAInputSignatureSize,
&m_layout);
if(FAILED(result))
{
returnfalse;
}
Wewillalsograbpointerstotheglobalmatricesthatareinsidetheshaderfile.
ThiswaywhenwesetamatrixfromtheGraphicsClassinsidetheshadereasilybyjustusingthesepointers.
//Getpointerstothethreematricesinsidetheshadersowecanupdatethemfromthisclass.
m_worldMatrixPtr=m_effect->GetVariableByName("worldMatrix")->AsMatrix();
m_viewMatrixPtr=m_effect->GetVariableByName("viewMatrix")->AsMatrix();
m_projectionMatrixPtr=m_effect->GetVariableByName("projectionMatrix")->AsMatrix();
returntrue;
}
ShutdownShaderreleasestheinterfacesandpointersthatweresetupintheInitializeShaderfunction.
voidColorShaderClass::ShutdownShader()
{
//Releasethepointerstothematricesinsidetheshader.
m_worldMatrixPtr=0;
m_viewMatrixPtr=0;
m_projectionMatrixPtr=0;
//Releasethepointertotheshaderlayout.
if(m_layout)
{
m_layout->Release();
m_layout=0;
}
//Releasethepointertotheshadertechnique.
m_technique=0;
//Releasethepointertotheshader.
if(m_effect)
{
m_effect->Release();
m_effect=0;
}
return;
}
TheOutputShaderErrorMessagewritesouterrormessagesthataregeneratingwhencompilingeithervertexshadersorpixelshaders.
voidColorShaderClass::OutputShaderErrorMessage(ID3D10Blob*errorMessage,HWNDhwnd,WCHAR*shaderFilename)
{
char*compileErrors;
unsignedlongbufferSize,i;
ofstreamfout;
//Getapointertotheerrormessagetextbuffer.
compileErrors=(char*)(errorMessage->GetBufferPointer());
//Getthelengthofthemessage.
bufferSize=errorMessage->GetBufferSize();
//Openafiletowritetheerrormessageto.
fout.open("shader-error.txt");
//Writeouttheerrormessage.
for(i=0;iRelease();
errorMessage=0;
//Popamessageuponthescreentonotifytheusertocheckthetextfileforcompileerrors.
MessageBox(hwnd,L"Errorcompilingshader.Checkshader-error.txtformessage.",shaderFilename,MB_OK);
return;
}
TheSetShaderVariablesfunctionexiststomakesettingtheglobalvariablesintheshadereasier.
ThematricesusedinthisfunctionarecreatedinsidetheGraphicsClass,afterwhichthisfunctioniscalledtosendthemfromthere
intotheshaderduringtheRenderfunctioncall.
voidColorShaderClass::SetShaderParameters(D3DXMATRIXworldMatrix,D3DXMATRIXviewMatrix,D3DXMATRIXprojectionMatrix)
{
//Settheworldmatrixvariableinsidetheshader.
m_worldMatrixPtr->SetMatrix((float*)&worldMatrix);
//Settheviewmatrixvariableinsidetheshader.
m_viewMatrixPtr->SetMatrix((float*)&viewMatrix);
//Settheprojectionmatrixvariableinsidetheshader.
m_projectionMatrixPtr->SetMatrix((float*)&projectionMatrix);
return;
}
RenderShaderisthesecondfunctioncalledfromtheColorShaderClass::Renderfunction.
SetShaderParametersiscalledbeforethistoensuretheshaderparametersaresetupcorrectlybeforeinvokingtheHLSLprogram.
RenderShaderwillinvokethecolor.fxHLSLshaderprogramthroughthetechniquepointer.
Thefirststepinthisfunctionistosetourinputlayouttoactiveintheinputassembler.
ThisletstheGPUknowtheformatofthedatainthevertexbuffer.
Thesecondstepistogetthedescriptionofthetechniquefromtheshader.
ThetechniqueiswhattellstheGPUwhichpixelandvertexshaderfunctionstocalltodrawthevertexbuffer.
InthiscasewearegettingthedescriptionofColorTechniquefromourcolor.fxfile.
OncewehavethetechniquewethenloopthrougheachpassinthetechniqueandrenderthetrianglebycallingtheDrawIndexedDirectXfunctionusingtheD3Ddevice.
Notethattheshaderonlyhasasinglepasscurrently(pass0)butIhavesetuptherenderfunctiontohandlemultiplepassesforfuturetutorials.
Oncethisfunctioniscalleditwillrenderthegreentriangle.
voidColorShaderClass::RenderShader(ID3D10Device*device,intindexCount)
{
D3D10_TECHNIQUE_DESCtechniqueDesc;
unsignedinti;
//Settheinputlayout.
device->IASetInputLayout(m_layout);
//Getthedescriptionstructureofthetechniquefrominsidetheshadersoitcanbeusedforrendering.
m_technique->GetDesc(&techniqueDesc);
//Gothrougheachpassinthetechnique(shouldbejustonecurrently)andrenderthetriangles.
for(i=0;iGetPassByIndex(i)->Apply(0);
device->DrawIndexed(indexCount,0,0);
}
return;
}
Cameraclass.h
WehaveexaminedhowtocodeaHLSLshader,howtosetupvertexandindexbuffers,andhowtoinvoketheHLSL
shadertodrawthosebuffersusingtheColorShaderClass.
Theonethingwearemissinghoweveristheviewpointtodrawthemfrom.
ForthiswewillrequireacameraclasstoletDirectX10knowfromwhereandalsohowweareviewingthescene.
Thecameraclasswillkeeptrackofwherethecameraisanditscurrentrotation.
ItwillusethepositionandrotationinformationtogenerateaviewmatrixwhichwillbepassedintotheHLSLshaderforrendering.
////////////////////////////////////////////////////////////////////////////////
//Filename:cameraclass.h
////////////////////////////////////////////////////////////////////////////////
#ifndef_CAMERACLASS_H_
#define_CAMERACLASS_H_
//////////////
//INCLUDES//
//////////////
#include
////////////////////////////////////////////////////////////////////////////////
//Classname:CameraClass
////////////////////////////////////////////////////////////////////////////////
classCameraClass
{
public:
CameraClass();
CameraClass(constCameraClass&);
~CameraClass();
voidSetPosition(float,float,float);
voidSetRotation(float,float,float);
D3DXVECTOR3GetPosition();
D3DXVECTOR3GetRotation();
voidRender();
voidGetViewMatrix(D3DXMATRIX&);
private:
floatm_positionX,m_positionY,m_positionZ;
floatm_rotationX,m_rotationY,m_rotationZ;
D3DXMATRIXm_viewMatrix;
};
#endif
TheCameraClassheaderisquitesimplewithjustfourfunctionsthatwillbeused.
TheSetPositionandSetRotationfunctionswillbeusedtosetthepositionandrotationofthecameraobject.
Renderwillbeusedtocreatetheviewmatrixbasedonthepositionandrotationofthecamera.
AndfinallyGetViewMatrixwillbeusedtoretrievetheviewmatrixfromthecameraobjectsothattheshaderscanuseitforrendering.
Cameraclass.cpp
////////////////////////////////////////////////////////////////////////////////
//Filename:cameraclass.cpp
////////////////////////////////////////////////////////////////////////////////
#include"cameraclass.h"
Theclassconstructorwillinitializethepositionandrotationofthecameratobeattheoriginofthescene.
CameraClass::CameraClass()
{
m_positionX=0.0f;
m_positionY=0.0f;
m_positionZ=0.0f;
m_rotationX=0.0f;
m_rotationY=0.0f;
m_rotationZ=0.0f;
}
CameraClass::CameraClass(constCameraClass&other)
{
}
CameraClass::~CameraClass()
{
}
TheSetPositionandSetRotationfunctionsareusedforsettingupthepositionandrotationofthecamera.
voidCameraClass::SetPosition(floatx,floaty,floatz)
{
m_positionX=x;
m_positionY=y;
m_positionZ=z;
return;
}
voidCameraClass::SetRotation(floatx,floaty,floatz)
{
m_rotationX=x;
m_rotationY=y;
m_rotationZ=z;
return;
}
TheGetPositionandGetRotationfunctionsreturnthelocationandrotationofthecameratocallingfunctions.
D3DXVECTOR3CameraClass::GetPosition()
{
returnD3DXVECTOR3(m_positionX,m_positionY,m_positionZ);
}
D3DXVECTOR3CameraClass::GetRotation()
{
returnD3DXVECTOR3(m_rotationX,m_rotationY,m_rotationZ);
}
TheRenderfunctionusesthepositionandrotationofthecameratobuildandupdatetheviewmatrix.
Wefirstsetupourvariablesforup,position,rotation,andsoforth.
Thenattheoriginofthescenewefirstrotatethecamerabasedonthex,y,andzrotationofthecamera.
Onceitisproperlyrotatedwhenthentranslatethecameratothepositionin3Dspace.
Withthecorrectvaluesintheposition,lookAt,andupwecanthenusetheD3DXMatrixLookAtLHfunctiontocreatetheviewmatrixtorepresent
thecurrentcamerarotationandtranslation.
voidCameraClass::Render()
{
D3DXVECTOR3up,position,lookAt;
floatyaw,pitch,roll;
D3DXMATRIXrotationMatrix;
//Setupthevectorthatpointsupwards.
up.x=0.0f;
up.y=1.0f;
up.z=0.0f;
//Setupthepositionofthecameraintheworld.
position.x=m_positionX;
position.y=m_positionY;
position.z=m_positionZ;
//Setupwherethecameraislookingbydefault.
lookAt.x=0.0f;
lookAt.y=0.0f;
lookAt.z=1.0f;
//Settheyaw(Yaxis),pitch(Xaxis),androll(Zaxis)rotationsinradians.
pitch=m_rotationX*0.0174532925f;
yaw=m_rotationY*0.0174532925f;
roll=m_rotationZ*0.0174532925f;
//Createtherotationmatrixfromtheyaw,pitch,androllvalues.
D3DXMatrixRotationYawPitchRoll(&rotationMatrix,yaw,pitch,roll);
//TransformthelookAtandupvectorbytherotationmatrixsotheviewiscorrectlyrotatedattheorigin.
D3DXVec3TransformCoord(&lookAt,&lookAt,&rotationMatrix);
D3DXVec3TransformCoord(&up,&up,&rotationMatrix);
//Translatetherotatedcamerapositiontothelocationoftheviewer.
lookAt=position+lookAt;
//Finallycreatetheviewmatrixfromthethreeupdatedvectors.
D3DXMatrixLookAtLH(&m_viewMatrix,&position,&lookAt,&up);
return;
}
AftertheRenderfunctionhasbeencalledtocreatetheviewmatrixwecanprovidetheupdateviewmatrixtocallingfunctionsusingthisGetViewMatrixfunction.
TheviewmatrixwillbeoneofthethreemainmatricesusedintheHLSLshader.
voidCameraClass::GetViewMatrix(D3DXMATRIX&viewMatrix)
{
viewMatrix=m_viewMatrix;
return;
}
Graphicsclass.h
GraphicsClassnowhasthethreenewclassesaddedtoit.
CameraClass,ModelClass,andColorShaderClasshaveheadersaddedhereaswellasprivatemembervariables.
RememberthatGraphicsClassisthemainclassthatisusedtorenderthescenebyinvokingalltheneededclassobjectsfortheproject.
////////////////////////////////////////////////////////////////////////////////
//Filename:graphicsclass.h
////////////////////////////////////////////////////////////////////////////////
#ifndef_GRAPHICSCLASS_H_
#define_GRAPHICSCLASS_H_
///////////////////////
//MYCLASSINCLUDES//
///////////////////////
#include"d3dclass.h"
#include"cameraclass.h"
#include"modelclass.h"
#include"colorshaderclass.h"
/////////////
//GLOBALS//
/////////////
constboolFULL_SCREEN=true;
constboolVSYNC_ENABLED=true;
constfloatSCREEN_DEPTH=1000.0f;
constfloatSCREEN_NEAR=0.1f;
////////////////////////////////////////////////////////////////////////////////
//Classname:GraphicsClass
////////////////////////////////////////////////////////////////////////////////
classGraphicsClass
{
public:
GraphicsClass();
GraphicsClass(constGraphicsClass&);
~GraphicsClass();
boolInitialize(int,int,HWND);
voidShutdown();
boolFrame();
private:
boolRender();
private:
D3DClass*m_D3D;
CameraClass*m_Camera;
ModelClass*m_Model;
ColorShaderClass*m_ColorShader;
};
#endif
Graphicsclass.cpp
////////////////////////////////////////////////////////////////////////////////
//Filename:graphicsclass.cpp
////////////////////////////////////////////////////////////////////////////////
#include"graphicsclass.h"
ThefirstchangetoGraphicsClassisinitializingthecamera,model,andcolorshaderobjectsintheclassconstructortonull.
GraphicsClass::GraphicsClass()
{
m_D3D=0;
m_Camera=0;
m_Model=0;
m_ColorShader=0;
}
TheInitializefunctionhasalsobeenupdatedtocreateandinitializethethreenewobjects.
boolGraphicsClass::Initialize(intscreenWidth,intscreenHeight,HWNDhwnd)
{
boolresult;
//CreatetheDirect3Dobject.
m_D3D=newD3DClass;
if(!m_D3D)
{
returnfalse;
}
//InitializetheDirect3Dobject.
result=m_D3D->Initialize(screenWidth,screenHeight,VSYNC_ENABLED,hwnd,FULL_SCREEN,SCREEN_DEPTH,SCREEN_NEAR);
if(!result)
{
MessageBox(hwnd,L"CouldnotinitializeDirect3D.",L"Error",MB_OK);
returnfalse;
}
//Createthecameraobject.
m_Camera=newCameraClass;
if(!m_Camera)
{
returnfalse;
}
//Settheinitialpositionofthecamera.
m_Camera->SetPosition(0.0f,0.0f,-10.0f);
//Createthemodelobject.
m_Model=newModelClass;
if(!m_Model)
{
returnfalse;
}
//Initializethemodelobject.
result=m_Model->Initialize(m_D3D->GetDevice());
if(!result)
{
MessageBox(hwnd,L"Couldnotinitializethemodelobject.",L"Error",MB_OK);
returnfalse;
}
//Createthecolorshaderobject.
m_ColorShader=newColorShaderClass;
if(!m_ColorShader)
{
returnfalse;
}
//Initializethecolorshaderobject.
result=m_ColorShader->Initialize(m_D3D->GetDevice(),hwnd);
if(!result)
{
MessageBox(hwnd,L"Couldnotinitializethecolorshaderobject.",L"Error",MB_OK);
returnfalse;
}
returntrue;
}
Shutdownisalsoupdatedtoshutdownandreleasethethreenewobjects.
voidGraphicsClass::Shutdown()
{
//Releasethecolorshaderobject.
if(m_ColorShader)
{
m_ColorShader->Shutdown();
deletem_ColorShader;
m_ColorShader=0;
}
//Releasethemodelobject.
if(m_Model)
{
m_Model->Shutdown();
deletem_Model;
m_Model=0;
}
//Releasethecameraobject.
if(m_Camera)
{
deletem_Camera;
m_Camera=0;
}
//ReleasetheDirect3Dobject.
if(m_D3D)
{
m_D3D->Shutdown();
deletem_D3D;
m_D3D=0;
}
return;
}
TheFramefunctionhasremainedthesameastheprevioustutorial.
boolGraphicsClass::Frame()
{
boolresult;
//Renderthegraphicsscene.
result=Render();
if(!result)
{
returnfalse;
}
returntrue;
}
AsyouwouldexpecttheRenderfunctionhadthemostchangestoit.
Itstillbeginswithclearingthesceneexceptthatitisclearedtoblack.
AfterthatitcallstheRenderfunctionforthecameraobjecttocreateaviewmatrixbasedonthecamera'slocationthatwassetintheInitializefunction.
Oncetheviewmatrixiscreatedwegetacopyofitfromthecameraclass.
WealsogetcopiesoftheworldandprojectionmatrixfromtheD3DClassobject.
WethencalltheModelClass::Renderfunctiontoputthegreentrianglemodelgeometryonthegraphicspipeline.
Withtheverticesnowpreparedwecallthecolorshadertodrawtheverticesusingthemodelinformationandthethreematricesforpositioningeachvertex.
Thegreentriangleisnowdrawntothebackbuffer.
WiththatthesceneiscompleteandwecallEndScenetodisplayittothescreen.
boolGraphicsClass::Render()
{
D3DXMATRIXviewMatrix,projectionMatrix,worldMatrix;
//Clearthebufferstobeginthescene.
m_D3D->BeginScene(0.0f,0.0f,0.0f,1.0f);
//Generatetheviewmatrixbasedonthecamera'sposition.
m_Camera->Render();
//Gettheworld,view,andprojectionmatricesfromthecameraandd3dobjects.
m_Camera->GetViewMatrix(viewMatrix);
m_D3D->GetWorldMatrix(worldMatrix);
m_D3D->GetProjectionMatrix(projectionMatrix);
//Putthemodelvertexandindexbuffersonthegraphicspipelinetopreparethemfordrawing.
m_Model->Render(m_D3D->GetDevice());
//Renderthemodelusingthecolorshader.
m_ColorShader->Render(m_D3D->GetDevice(),m_Model->GetIndexCount(),worldMatrix,viewMatrix,projectionMatrix);
//Presenttherenderedscenetothescreen.
m_D3D->EndScene();
returntrue;
}
Summary
Soinsummaryyoushouldhavelearnedthebasicsabouthowvertexandindexbufferswork.
YoushouldhavealsolearnedthebasicsofvertexandpixelshadersandhowtowritethemusingHLSL.
Andfinallyyoushouldunderstandhowwe'veincorporatedthesenewconceptsintoourframeworktoproduceagreentrianglethatrenderstothescreen.
IalsowanttomentionthatIrealizethecodeisfairlylongjusttodrawasingletriangleanditcouldhaveallbeenstuckinsideasinglemain()function.
HoweverIdiditthiswaywithaproperframeworksothatthecomingtutorialsrequireveryfewchangesinthecodetodofarmorecomplexgraphics.
ToDoExercises
1.Compileandrunthetutorial.Ensureitdrawsagreentriangletothescreen.Pressescapetoquitonceitdoes.
2.Changethecolorofthetriangletored.
3.Changethetriangletoasquare.
4.Movethecameraback10moreunits.
5.Changethepixelshadertooutputthecolorhalfasbright.(hugehint:multiplysomethinginColorPixelShaderby0.5f)
SourceCode
VisualStudio2010Project:dx10tut04.zip
SourceOnly:dx10src04.zip
ExecutableOnly:dx10exe04.zip
BacktoTutorialIndex