Difference between revisions of "GAM670/DPS905 Weekly Schedule 20121"
(→To Do) |
(→This Week) |
||
(108 intermediate revisions by 2 users not shown) | |||
Line 7: | Line 7: | ||
* Assignment Discussion | * Assignment Discussion | ||
* Suggested Enhancements | * Suggested Enhancements | ||
− | * | + | * Review of the Base Code |
− | ** | + | ** Definition of a Framework |
− | * | + | *** Modularity through stable interfaces |
− | ** | + | *** Re-usability through generic components |
− | ** | + | *** Extensibility through hook methods |
− | * | + | *** Inversion of control - determines which application methods to invoke in response to external events |
− | ** | + | ** Framework Architecture |
− | * | + | *** Modelling Layer |
− | ** | + | *** API Translation Layer |
− | * | + | ** Notable Features of the Base Code |
− | ** | + | ** camera, sound, and light are also derived from the Frame class |
− | ** | + | ** textures attach at the object level |
− | + | ** texture connection is uncoupled from drawing of the graphics primitives | |
− | ** | + | ** reference frames are relative |
− | ** | ||
− | |||
− | ** camera, sound, and light | ||
− | ** textures | ||
− | ** texture connection uncoupled from drawing | ||
− | ** | ||
− | |||
** very simple collision detection | ** very simple collision detection | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
=== To Do === | === To Do === | ||
Line 54: | Line 27: | ||
# add your name to the student list | # add your name to the student list | ||
# create a team page that includes the semester number 20121 | # create a team page that includes the semester number 20121 | ||
− | # | + | #* describe the game that you intend to develop |
− | # | + | #* list the topics of interest to your team in developing its game |
− | # | + | #* list the other topics of interest |
<!-- | <!-- | ||
# Port Game Design over to Update Base Code--> | # Port Game Design over to Update Base Code--> | ||
=== Resources === | === Resources === | ||
− | + | ||
== Week 2 - Jan 16 == | == Week 2 - Jan 16 == | ||
Line 67: | Line 40: | ||
=== This Week === | === This Week === | ||
+ | * Relative Reference Frames | ||
+ | ** Recursive calls | ||
+ | **: Vector Frame::position() | ||
+ | **: Matrix Frame::rotation() | ||
+ | **: Matrix Frame::world() | ||
+ | ** Detaching from and attaching to a parent frame | ||
+ | **: Frame::attachTo() | ||
+ | * Geometry | ||
+ | ** Plane | ||
+ | **: normal + constant - examples | ||
+ | **: equation of a plane: dot(n, x) + D = 0 | ||
+ | **: positive side of a plane dot(n, x) + D > 0 | ||
* Collision Detection | * Collision Detection | ||
*: types of colliders | *: types of colliders | ||
Line 73: | Line 58: | ||
*:: axis-aligned bounding boxes | *:: axis-aligned bounding boxes | ||
*:: oriented bounding boxes | *:: oriented bounding boxes | ||
+ | |||
+ | === To Do === | ||
+ | * Research the feature that you are going to add and prepare a plan of action | ||
+ | * Prepare a team page for your team so that repos can be ordered | ||
+ | * Add a section to your team page to track your project and solicit commentary | ||
+ | |||
+ | === Resources === | ||
+ | |||
+ | == Week 3 - Jan 23 == | ||
+ | === This Week === | ||
+ | * Collision Detection (cont'd) | ||
+ | *: Shape | ||
+ | *:: Shape : Frame | ||
+ | *:: Shape::setRadius() | ||
+ | *:: Shape::getRadius() | ||
+ | *:: Shape::setRadius(float r); | ||
+ | *:: Shape::setRadius(float x, float y, float z); | ||
+ | *:: Shape::getRadius() const { return radius; } | ||
+ | *:: Shape::setPlane(Vector n, float d); | ||
+ | *:: Shape::setAxisAligned(Vector min, Vector max); | ||
* Comprehensive Camerawork | * Comprehensive Camerawork | ||
*: rotation about an axis | *: rotation about an axis | ||
*: order of rotation matters | *: order of rotation matters | ||
*: Euler angles | *: Euler angles | ||
− | *: | + | *:: [http://www.youtube.com/watch?v=UpSMNYTVqQI&feature=related 3-2-1 angles] |
− | *:: quaternions | + | *: gimbal lock |
− | *:: geometric algebra (more abstract) | + | *:: [http://www.youtube.com/watch?v=rrUCBOlJdt4&feature=related StephenSeefeld.net] |
− | * | + | *: complex numbers |
− | *: | + | *:: solution of cubic equations 16th century |
− | *: | + | *:: two-dimensional representation |
− | * | + | *:: [http://en.wikipedia.org/wiki/Complex_number#Matrix_representation_of_complex_numbers matrix representation] |
− | *: | + | *: quaternions |
− | *: | + | *:: extension of complex numbers |
+ | *:: four-dimensional representation | ||
+ | *:: [http://en.wikipedia.org/wiki/Quaternion#Matrix_representations matrix representation] | ||
+ | *: geometric algebra (more abstract) | ||
+ | *:: [http://staff.science.uva.nl/~leo/cinderella/line1.html Dorst's site] | ||
+ | *:: [http://sinai.apphy.u-fukui.ac.jp/gcj/software/GAcindy-1.4/GAcindy.htm Hitzer's site] | ||
+ | * Visibility Determination | ||
+ | **: test a point for presence within a set of planes | ||
+ | **: normal calculations - general rotation matrix - vector and angle | ||
+ | ** ViewFrustum | ||
+ | **: parameter - view * projection | ||
+ | **: 6 planes | ||
+ | **:: near and far planes | ||
+ | **:: left and right planes | ||
+ | **:: top and bottom planes | ||
+ | **: coding | ||
+ | **:: constructor | ||
+ | **:: ViewFrustum::contains() | ||
+ | ** Finite Size of Objects | ||
+ | **: Expansion of the View Frustum | ||
* Index Buffers | * Index Buffers | ||
*: amount of storage needed for vertex data | *: amount of storage needed for vertex data | ||
Line 91: | Line 115: | ||
*: indexing | *: indexing | ||
*: indexed primitives | *: indexed primitives | ||
+ | |||
+ | === To Do === | ||
+ | === Resources === | ||
+ | * [http://en.wikipedia.org/wiki/Complex_number Wikipedia on Complex Numbers] | ||
+ | * [http://en.wikipedia.org/wiki/Quaternion Wikipedia on Quaternions] | ||
+ | * [http://en.wikipedia.org/wiki/Quaternions_and_spatial_rotation Wikipedia on quaternions and Spatial Rotations] | ||
+ | * [http://mathworld.wolfram.com/Quaternion.html Wolfram on Quaternions] | ||
+ | * [http://www.cprogramming.com/tutorial/3d/quaternions.html CProgramming.com on Quaternions] | ||
+ | * [http://www.ogre3d.org/tikiwiki/Quaternion+and+Rotation+Primer Ogre intro on Quaternions] | ||
+ | * collision sample | ||
+ | * indexBuffering sample | ||
+ | |||
+ | |||
+ | == Week 4 - Jan 30 == | ||
+ | === This Week === | ||
* Meshes | * Meshes | ||
+ | *: What is a mesh? | ||
+ | *:: vertex list -> vertex buffer | ||
+ | *:: index list -> index buffer | ||
+ | *:: attribute list -> subset to which primitives belong | ||
+ | *:: pyramid sample | ||
*: Stock Objects | *: Stock Objects | ||
*:: Sphere | *:: Sphere | ||
+ | *::: slices and partitions | ||
*:: Cylinder | *:: Cylinder | ||
*:: Torus | *:: Torus | ||
*:: Utah Teapot | *:: Utah Teapot | ||
− | *: | + | *:: APIGraphic.h and .cpp code |
− | *: | + | *: Custom Mesh |
*:: Create a Mesh | *:: Create a Mesh | ||
− | *:: DrawSubset | + | *:: APIGraphic.h code |
− | *:: FVF | + | <syntaxhighlight lang="cpp"> |
+ | template <class T = Vertex, class I = Index> | ||
+ | class APICustomMesh : public iAPIGraphic, public APIBase { | ||
+ | |||
+ | unsigned nSubsets; // number of subsets | ||
+ | unsigned nPrimitives; // number of primitives | ||
+ | unsigned* attribute; // points to mesh's attribute list | ||
+ | I* index; // points to mesh's array of indices | ||
+ | unsigned iIndex; // number of indices currently saved | ||
+ | unsigned nIndices; // number of indices in the mesh | ||
+ | T* vertex; // points to mesh's array of vertices | ||
+ | unsigned iVertex; // number of vertices currently saved | ||
+ | unsigned nVertices; // number of vertices in the mesh | ||
+ | LPD3DXMESH apiMesh; // set of vertices, indices | ||
+ | |||
+ | protected: | ||
+ | virtual ~APICustomMesh(); | ||
+ | void setup(); | ||
+ | |||
+ | public: | ||
+ | APICustomMesh(unsigned*, int, int, int, int); | ||
+ | APICustomMesh(const APICustomMesh& v); | ||
+ | APICustomMesh& operator=(const APICustomMesh& v); | ||
+ | APICustomMesh* clone() const { return new APICustomMesh(*this); } | ||
+ | unsigned add(const T& v); | ||
+ | void add(unsigned); | ||
+ | void draw(unsigned); | ||
+ | void suspend(); | ||
+ | void release() { suspend(); } | ||
+ | void Delete() const { delete this; } | ||
+ | }; | ||
+ | </syntaxhighlight> | ||
+ | *:: APIGraphic.cpp - APIGraphic | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | template <class T, class I> | ||
+ | APICustomMesh<T, I>::APICustomMesh(unsigned* a, int np, int ni, int nv, int ns) | ||
+ | : nSubsets(ns), nPrimitives(np), nIndices(ni), nVertices(nv), iIndex(0), | ||
+ | iVertex(0) { | ||
+ | |||
+ | attribute = new unsigned[nPrimitives]; | ||
+ | for (unsigned i = 0; i < nPrimitives; i++) | ||
+ | attribute[i] = a[i]; | ||
+ | |||
+ | index = new I[nIndices]; | ||
+ | vertex = new T[nVertices]; | ||
+ | |||
+ | apiMesh = nullptr; | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | *:: APIGraphic.cpp - add() | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | template <class T, class I> | ||
+ | unsigned APICustomMesh<T, I>::add(const T& v) { | ||
+ | |||
+ | unsigned i = iVertex; | ||
+ | |||
+ | if (vertex && iVertex < nVertices) | ||
+ | vertex[iVertex++] = v; | ||
+ | |||
+ | return i; | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | template <class T, class I> | ||
+ | void APICustomMesh<T, I>::add(unsigned v) { | ||
+ | |||
+ | if (index && iIndex < nIndices) | ||
+ | index[iIndex++] = v; | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | *:: APIGraphic.cpp - setup() | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | template <class T, class I> | ||
+ | void APICustomMesh<T, I>::setup() { | ||
+ | |||
+ | T *pv; | ||
+ | I *pi; | ||
+ | DWORD *pa; | ||
+ | nIndices = iIndex; | ||
+ | nVertices = iVertex; | ||
+ | |||
+ | // create an empty mesh and lock its buffers | ||
+ | if (FAILED(D3DXCreateMesh(nPrimitives, nVertices, 0, | ||
+ | APIVertexDeclaration<T>::format(), d3dd, &apiMesh))) { | ||
+ | error(L"APIMesh::14 Couldn\'t create the empty mesh"); | ||
+ | apiMesh = nullptr; | ||
+ | } | ||
+ | else if (FAILED(apiMesh->LockVertexBuffer(0, (void**)&pv))) { | ||
+ | error(L"APIMesh::15 Couldn\'t lock vertex buffer"); | ||
+ | release(); | ||
+ | } | ||
+ | else if (FAILED(apiMesh->LockIndexBuffer(0, (void**)&pi))) { | ||
+ | error(L"APIMesh::16 Couldn\'t lock index buffer"); | ||
+ | release(); | ||
+ | } | ||
+ | else if (FAILED(apiMesh->LockAttributeBuffer(0, &pa))) { | ||
+ | error(L"APIMesh::17 Couldn\'t lock attribute buffer"); | ||
+ | release(); | ||
+ | } | ||
+ | else { | ||
+ | // populate the newly created Vertex Buffer | ||
+ | for (unsigned i = 0; i < nVertices; i++) | ||
+ | vertex[i].populate((void**)&pv); | ||
+ | apiMesh->UnlockVertexBuffer(); | ||
+ | // populate the newly created Index Buffer | ||
+ | for (unsigned i = 0; i < nIndices; i++) | ||
+ | pi[i] = index[i]; | ||
+ | apiMesh->UnlockIndexBuffer(); | ||
+ | // Populate the newly created Attribute Buffer | ||
+ | for (unsigned i = 0; i < nPrimitives; i++) | ||
+ | pa[i] = attribute[i]; | ||
+ | apiMesh->UnlockAttributeBuffer(); | ||
+ | } | ||
+ | }</syntaxhighlight> | ||
+ | *:: APIGraphic.cpp - DrawSubset() | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | template <class T, class I> | ||
+ | void APICustomMesh<T, I>::draw(unsigned iSubset) { | ||
+ | |||
+ | // if mesh doesn't exist, set it up first | ||
+ | if (!apiMesh) setup(); | ||
+ | |||
+ | if (apiMesh) | ||
+ | apiMesh->DrawSubset(iSubset); | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | *::: DrawIndexedPrimitive parameters | ||
+ | *:: APIGraphic.cpp - suspend() | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | template <class T, class I> | ||
+ | void APICustomMesh<T, I>::suspend() { | ||
+ | |||
+ | // release the interface to the mesh | ||
+ | if (apiMesh) { | ||
+ | apiMesh->Release(); | ||
+ | apiMesh = nullptr; | ||
+ | } | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | *:: APIGraphic.cpp - ~APIGraphic | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | template <class T, class I> | ||
+ | APICustomMesh<T, I>::~APICustomMesh() { | ||
+ | |||
+ | release(); | ||
+ | if (attribute) | ||
+ | delete [] attribute; | ||
+ | if (index) | ||
+ | delete [] index; | ||
+ | if (vertex) | ||
+ | delete [] vertex; | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | *: X File | ||
+ | *:: Create Mesh from File | ||
+ | * SkyBox | ||
+ | *: definition of a skybox | ||
+ | *:: attachment to camera | ||
+ | *:: inverted coordinates | ||
+ | *: skybox textures | ||
+ | *: Graphic.cpp code | ||
+ | *: more complicated forms - skydome | ||
+ | * Billboards | ||
+ | *: definition, purpose of a billboard | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | void Billboard::render(unsigned) { | ||
+ | |||
+ | Vector h, u, r, p = position(); | ||
+ | Camera* camera = *(Camera**)(Camera::getCurrent()); | ||
+ | Vector cameraPosition = camera->position(); | ||
+ | Vector cameraHeading = ::normal(camera->orientation('z')); | ||
+ | Vector cameraUp = ::normal(camera->orientation('y')); | ||
+ | switch (type) { | ||
+ | // ... see below | ||
+ | } | ||
+ | Matrix rot(r.x, r.y, r.z, 0, | ||
+ | u.x, u.y, u.z, 0, | ||
+ | h.x, h.y, h.z, 0, | ||
+ | 0, 0, 0, 1); | ||
+ | orient(rot); | ||
+ | |||
+ | Object::render(0); | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | *: types of billboards | ||
+ | *:: screen-aligned - useful for annotation text, lens flares | ||
+ | *::: normal is opposite to camera heading | ||
+ | *::: up is camera->up | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | case SCREEN: | ||
+ | h = cameraHeading; // fixed | ||
+ | u = cameraUp; // up is fixed | ||
+ | r = cross(u, h); | ||
+ | break; | ||
+ | </syntaxhighlight> | ||
+ | *:: axial - useful for cylindrical symmetry - trees (textured object does not face straight on) | ||
+ | *::: up is fixed | ||
+ | *::: normal faces the viewer as much as possible | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | case AXIAL: | ||
+ | h = ::normal(position() - cameraPosition); // heading is open to change | ||
+ | u = Vector(0, 1, 0); // up axis is fixed | ||
+ | r = cross(u, h); | ||
+ | h = cross(u, r); | ||
+ | break; | ||
+ | </syntaxhighlight> | ||
+ | *:: view_plane - no distortion - useful for | ||
+ | *::: normal is fixed (opposite to camera heading) | ||
+ | *::: up is open to change | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | case VIEW_PLANE: | ||
+ | h = cameraHeading; // heading is fixed | ||
+ | u = Vector(0, 1, 0); // up is open to change | ||
+ | r = cross(u, h); | ||
+ | u = cross(h, r); | ||
+ | break; | ||
+ | </syntaxhighlight> | ||
+ | *:: viewpoint - simulates distortion due to perspective projection - useful for clouds | ||
+ | *::: normal is fixed (difference between viewpoint position and camera heading) | ||
+ | *::: up is open to change | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | case VIEWPOINT: | ||
+ | h = ::normal(position() - cameraPosition); // heading is fixed | ||
+ | u = Vector(0, 1, 0); // up is open to change | ||
+ | r = cross(u, h); | ||
+ | u = cross(h, r); | ||
+ | break; | ||
+ | </syntaxhighlight> | ||
+ | *: Object.h and Object.cpp code | ||
+ | * Texture Filtering | ||
+ | *: mip maps | ||
+ | *:: multum in parvo | ||
+ | *:: texture creation | ||
+ | *:: APITexture::SetSamplerState() | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | // mipmap filtering | ||
+ | if (flags & TEX_MIPMAP) | ||
+ | d3dd->SetSamplerState(i, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR); | ||
+ | else | ||
+ | d3dd->SetSamplerState(i, D3DSAMP_MIPFILTER, D3DTEXF_NONE); | ||
+ | </syntaxhighlight> | ||
+ | * DirectX Errors | ||
+ | *: DirectX Utilities - Lookup Tool | ||
+ | *: APIDisplay::restore() example | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | bool APIDisplay::restore() { | ||
+ | |||
+ | bool rc = false; | ||
+ | |||
+ | if (d3dd) { | ||
+ | HRESULT hr; | ||
+ | hr = d3dd->TestCooperativeLevel(); | ||
+ | if (hr == D3DERR_DEVICENOTRESET) | ||
+ | // reset the APIDisplay device | ||
+ | rc = d3dd->Reset(&d3dpp) == D3D_OK; | ||
+ | else if (hr == S_OK) | ||
+ | rc = true; | ||
+ | } | ||
+ | if (rc) { | ||
+ | // reacquire sprite manager references to video memory | ||
+ | if (manager) | ||
+ | manager->OnResetDevice(); | ||
+ | } | ||
+ | |||
+ | // complete the restoration | ||
+ | if (rc) { | ||
+ | setupLighting(); | ||
+ | setupBlending(); | ||
+ | } | ||
+ | |||
+ | return rc; | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | === To Do === | ||
+ | === Resources === | ||
+ | * meshes sample | ||
+ | * [http://rbwhitaker.wikidot.com/skyboxes-1 Rob Whitaker on Skyboxes] | ||
+ | * [http://web.cs.wpi.edu/~emmanuel/courses/cs563/S05/talks/suman_wk8_IBR.pdf Image Based Rendering - Suman Nadella on Billboarding and Imposters] | ||
+ | * [http://www.digitalrune.com/Products/GameEngine/Particles.aspx DigitalRune] | ||
+ | * [http://en.wikipedia.org/wiki/Mipmap Wikipedia on Texture Filtering] | ||
+ | * [http://www.directxtutorial.com/Tutorial9/B-Direct3DBasics/dx9B7.aspx D3D 9 tutorial on modeling with an Index list] | ||
+ | |||
+ | == Week 5 - Feb 6 == | ||
+ | === This Week === | ||
+ | * Vertex Declarations | ||
+ | *: [http://msdn.microsoft.com/en-us/library/windows/desktop/bb172559%28v=vs.85%29.aspx FVF Codes] | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | template <class T = Vertex> | ||
+ | class APIVertexDeclaration { | ||
+ | |||
+ | static D3DVERTEXELEMENT9 fmt[MAXD3DDECLLENGTH + 1]; | ||
+ | static unsigned vertexSize; | ||
+ | |||
+ | public: | ||
+ | static D3DVERTEXELEMENT9* format() { return fmt; } | ||
+ | static unsigned size() { return vertexSize; } | ||
+ | }; | ||
+ | </syntaxhighlight> | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | template <> | ||
+ | D3DVERTEXELEMENT9 APIVertexDeclaration<Vertex>::fmt[MAXD3DDECLLENGTH + 1] | ||
+ | = { | ||
+ | { 0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, | ||
+ | D3DDECLUSAGE_POSITION, 0}, | ||
+ | { 0, 12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, | ||
+ | D3DDECLUSAGE_NORMAL, 0}, | ||
+ | { 0, 24, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, | ||
+ | D3DDECLUSAGE_TEXCOORD, 0}, | ||
+ | D3DDECL_END()}; | ||
+ | |||
+ | template<> | ||
+ | unsigned APIVertexDeclaration<Vertex>::vertexSize = 32; | ||
+ | </syntaxhighlight> | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | |||
+ | template <> | ||
+ | D3DVERTEXELEMENT9 APIVertexDeclaration<LitVertex>::fmt[MAXD3DDECLLENGTH + 1] | ||
+ | = { | ||
+ | { 0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, | ||
+ | D3DDECLUSAGE_POSITION, 0}, | ||
+ | { 0, 12, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, | ||
+ | D3DDECLUSAGE_COLOR, 0}, | ||
+ | D3DDECL_END()}; | ||
+ | |||
+ | template <> | ||
+ | unsigned APIVertexDeclaration<LitVertex>::vertexSize = 16; | ||
+ | </syntaxhighlight> | ||
+ | :: [http://msdn.microsoft.com/en-us/library/windows/desktop/bb172630%28v=vs.85%29.aspx D3DVERTEXELEMENT9 struct] | ||
+ | * The Pipeline | ||
+ | *: [http://en.wikipedia.org/wiki/Instruction_pipeline What is a pipeline] | ||
+ | *: | ||
+ | * The GPU | ||
+ | *: [http://en.wikipedia.org/wiki/Graphics_processing_unit What is a GPU] | ||
+ | ** nVidia | ||
+ | **: [http://en.wikipedia.org/wiki/Geforce GeForce] | ||
+ | **: [http://en.wikipedia.org/wiki/NVIDIA_Quadro Quadro] | ||
+ | **: [http://en.wikipedia.org/wiki/Nvidia_Tesla Tesla] | ||
+ | **: [http://en.wikipedia.org/wiki/Comparison_of_Nvidia_graphics_processing_units Comparison] | ||
+ | ** AMD (previously ATI) | ||
+ | **: [http://en.wikipedia.org/wiki/Radeon_R520 Radeon 520] | ||
+ | **: [http://en.wikipedia.org/wiki/Comparison_of_AMD_graphics_processing_units Comparison] | ||
+ | * Shader Languages | ||
+ | *: [http://en.wikipedia.org/wiki/Shader what is a shader] | ||
+ | *: dedicated shaders | ||
+ | *: [http://en.wikipedia.org/wiki/Unified_shader_model unified shaders] | ||
+ | *: how does a shader work | ||
+ | ** Languages | ||
+ | **: [http://en.wikipedia.org/wiki/HLSL HLSL - Microsoft] | ||
+ | **: [http://en.wikipedia.org/wiki/Cg_%28programming_language%29 Cg - nVidia] | ||
+ | **: [http://en.wikipedia.org/wiki/GLSL GLSL - OpenGL (Khronos)] | ||
+ | * Vertex Shaders | ||
+ | * Pixel Shaders | ||
+ | * effect of skybox and point light on frame rate | ||
=== To Do === | === To Do === | ||
− | |||
− | |||
+ | == Week 6 - Feb 13 == | ||
+ | === This Week === | ||
+ | ==== Vertex Shader Programming ==== | ||
+ | * Host | ||
+ | : APIPlatformSettings.h - Vertex Shader Identification - select between fixed function and programmable pipelines here | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | // shader file data | ||
+ | #define VERTEX_SHADER_FILE L"vertexShader.hlsl" | ||
+ | #define VERTEX_SHADER_ENTRY_POINT "vertexShader" | ||
+ | </syntaxhighlight> | ||
+ | : APIBase.h - pointers into the shader - APIBase is the base class for the graphics API classes | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | static IDirect3DVertexShader9* vertexShader; // vertex shader | ||
+ | static ID3DXConstantTable* uniformVS; // for vertex shader | ||
+ | </syntaxhighlight> | ||
+ | : APIBase.cpp - initialize the shader pointers | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | IDirect3DVertexShader9* APIBase::vertexShader = nullptr; // vertex shader | ||
+ | ID3DXConstantTable* APIBase::uniformVS = nullptr; // for vertex shader | ||
+ | </syntaxhighlight> | ||
+ | : APIDisplay.h - keeps track of the current projection matrix | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | Matrix projection; // projection transformation | ||
+ | </syntaxhighlight> | ||
+ | : APIDisplay.cpp - setup() - checks the shader version | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | // points to compiled shader code | ||
+ | LPD3DXBUFFER compiledCodeVS = nullptr; | ||
+ | |||
+ | // check for minimum vertex shader version required | ||
+ | if (caps.VertexShaderVersion < D3DVS_VERSION(2,0)) | ||
+ | error(L"APIDisplay::09 Device does not support vertex shader 2_0"); | ||
+ | </syntaxhighlight> | ||
+ | compiles the shader hlsl code, retrieves address of constant memory and vertex shader | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | // compile the vertex shader source code | ||
+ | else if (FAILED(D3DXCompileShaderFromFile(VERTEX_SHADER_FILE, NULL, | ||
+ | NULL, VERTEX_SHADER_ENTRY_POINT, D3DXGetVertexShaderProfile(d3dd), | ||
+ | D3DXSHADER_DEBUG | D3DXSHADER_SKIPOPTIMIZATION, | ||
+ | &compiledCodeVS, NULL, &uniformVS))) { | ||
+ | release(); | ||
+ | error(L"APIDisplay::13 Unable to compile vertex shader"); | ||
+ | } | ||
+ | // create the vertex shader object | ||
+ | else if (FAILED(d3dd->CreateVertexShader( | ||
+ | (DWORD*)compiledCodeVS->GetBufferPointer(), &vertexShader))) { | ||
+ | compiledCodeVS->Release(); | ||
+ | release(); | ||
+ | error(L"APIDisplay::14 Unable to create vertex shader object"); | ||
+ | } | ||
+ | else { | ||
+ | compiledCodeVS->Release(); | ||
+ | </syntaxhighlight> | ||
+ | sets the current vertex shader | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | d3dd->SetVertexShader(vertexShader); | ||
+ | </syntaxhighlight> | ||
+ | : APIDisplay.cpp - setProjection() - stores the projection matrix | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | this->projection = *((Matrix*)projection); | ||
+ | </syntaxhighlight> | ||
+ | : APIDisplay.cpp - beginDrawFrame() - copies the view matrix and the camera heading to constant memory | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | Matrix& v = *((Matrix*)view); | ||
+ | Matrix viewProjection = v * projection; | ||
+ | uniformVS->SetMatrix(d3dd, "viewProjection", | ||
+ | (D3DXMATRIX*)&viewProjection); | ||
+ | Vector heading(v.m13, v.m23, v.m33); | ||
+ | // Required for specular lighting calculations | ||
+ | uniformVS->SetFloatArray(d3dd, "heading", (FLOAT*)&heading, 3); | ||
+ | </syntaxhighlight> | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | Colour colour(red, green, blue); | ||
+ | uniformVS->SetFloatArray(d3dd, "ambient", (FLOAT*)&colour, 4); | ||
+ | uniformVS->SetInt(d3dd, "noLights", 4); | ||
+ | </syntaxhighlight> | ||
+ | : APIDisplay.cpp - set() - copies the lighting state to constant memory | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | uniformVS->SetBool(d3dd, "lighting", b); | ||
+ | </syntaxhighlight> | ||
+ | : APIDisplay.cpp - setWorld() - copies the world matrix to constant memory | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | uniformVS->SetMatrix(d3dd, "world", (D3DXMATRIX*)world); | ||
+ | </syntaxhighlight> | ||
+ | : APIDisplay.cpp - setReflectivity() - copies the reflectivity to constant memory | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | uniformVS->SetFloatArray(d3dd, "material.ambient", (FLOAT*)&r.ambient, 4); | ||
+ | uniformVS->SetFloatArray(d3dd, "material.diffuse", (FLOAT*)&r.diffuse, 4); | ||
+ | uniformVS->SetFloatArray(d3dd, "material.specular", (FLOAT*)&r.specular, 4); | ||
+ | uniformVS->SetFloat (d3dd, "material.power", (FLOAT)r.power); | ||
+ | </syntaxhighlight> | ||
+ | : APIDisplay.cpp - release() - releases constant memory and the vertex shader | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | // release the shader COM objects | ||
+ | if (uniformVS) { | ||
+ | uniformVS->Release(); | ||
+ | uniformVS = nullptr; | ||
+ | } | ||
+ | if (vertexShader) { | ||
+ | vertexShader->Release(); | ||
+ | vertexShader = nullptr; | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | : APILight.cpp - setup() - copies the light properties to constant memory | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | // Populate the vertex shader constant table | ||
+ | // | ||
+ | // Light descriptors within the vertex shader | ||
+ | char typ[] = "light[0].type"; | ||
+ | char amb[] = "light[0].ambient"; | ||
+ | char dif[] = "light[0].diffuse"; | ||
+ | char spe[] = "light[0].specular"; | ||
+ | char pos[] = "light[0].position"; | ||
+ | char dir[] = "light[0].direction"; | ||
+ | char spt[] = "light[0].spot"; | ||
+ | char att[] = "light[0].attenuation"; | ||
+ | char ran[] = "light[0].range"; | ||
+ | // | ||
+ | // Reset index in light descriptor | ||
+ | typ[6] = index + '0'; | ||
+ | amb[6] = index + '0'; | ||
+ | dif[6] = index + '0'; | ||
+ | spe[6] = index + '0'; | ||
+ | pos[6] = index + '0'; | ||
+ | dir[6] = index + '0'; | ||
+ | spt[6] = index + '0'; | ||
+ | att[6] = index + '0'; | ||
+ | ran[6] = index + '0'; | ||
+ | // Populate the vertex shader constant table | ||
+ | Vector attenuation(attenuation0, attenuation1, attenuation2); | ||
+ | Vector spot(cosf(phi/2), cosf(theta/2), falloff); | ||
+ | Vector zero; | ||
+ | uniformVS->SetInt(d3dd, typ, type); | ||
+ | uniformVS->SetFloatArray(d3dd, amb, (FLOAT*)&ambient, 4); | ||
+ | uniformVS->SetFloatArray(d3dd, dif, (FLOAT*)&diffuse, 4); | ||
+ | uniformVS->SetFloatArray(d3dd, spe, (FLOAT*)&specular, 4); | ||
+ | uniformVS->SetFloatArray(d3dd, pos, (FLOAT*)&zero, 3); | ||
+ | uniformVS->SetFloatArray(d3dd, dir, (FLOAT*)&zero, 3); | ||
+ | uniformVS->SetFloatArray(d3dd, att, (FLOAT*)&attenuation, 3); | ||
+ | uniformVS->SetFloatArray(d3dd, spt, (FLOAT*)&spot, 3); | ||
+ | uniformVS->SetFloat(d3dd, ran, range); | ||
+ | rc = true; | ||
+ | </syntaxhighlight> | ||
+ | : APILight.cpp - turnOn() - repositions the light and turns it on | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | char constantLightOn[] = "lightOn[0]"; | ||
+ | constantLightOn[8] = index + '0'; | ||
+ | char pos[] = "light[0].position"; | ||
+ | char dir[] = "light[0].direction"; | ||
+ | pos[6] = index + '0'; | ||
+ | dir[6] = index + '0'; | ||
+ | uniformVS->SetFloatArray(d3dd, pos, (FLOAT*)&p, 3); | ||
+ | uniformVS->SetFloatArray(d3dd, dir, (FLOAT*)&o, 3); | ||
+ | uniformVS->SetBool(d3dd, constantLightOn, true); | ||
+ | </syntaxhighlight> | ||
+ | : APILight.cpp - update() - repositions the light | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | char constantLightOn[] = "lightOn[0]"; | ||
+ | constantLightOn[8] = index + '0'; | ||
+ | char pos[] = "light[0].position"; | ||
+ | char dir[] = "light[0].direction"; | ||
+ | pos[6] = index + '0'; | ||
+ | dir[6] = index + '0'; | ||
+ | uniformVS->SetFloatArray(d3dd, pos, (FLOAT*)&p, 3); | ||
+ | uniformVS->SetFloatArray(d3dd, dir, (FLOAT*)&o, 3); | ||
+ | uniformVS->SetBool(d3dd, constantLightOn, true); | ||
+ | </syntaxhighlight> | ||
+ | : APILight.cpp - turnOff() - turns off the light | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | char constantLightOn[] = "lightOn[0]"; | ||
+ | constantLightOn[8] = index + '0'; | ||
+ | uniformVS->SetBool(d3dd, constantLightOn, false); | ||
+ | </syntaxhighlight> | ||
+ | : APIGraphic.h - class APIXMesh - processes an X file mesh | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | D3DXCOLOR* ambient; | ||
+ | D3DXCOLOR* diffuse; | ||
+ | D3DXCOLOR* specular; | ||
+ | FLOAT* power; | ||
+ | </syntaxhighlight> | ||
+ | : APIGraphic.cpp - APIXMesh() - constructor | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | ambient = nullptr; | ||
+ | diffuse = nullptr; | ||
+ | specular = nullptr; | ||
+ | power = nullptr; | ||
+ | </syntaxhighlight> | ||
+ | : APIGraphic.cpp - APIXMesh() - copy constructor | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | ambient = nullptr; | ||
+ | diffuse = nullptr; | ||
+ | specular = nullptr; | ||
+ | power = nullptr; | ||
+ | </syntaxhighlight> | ||
+ | : APIGraphic.cpp - operator=() = assignment operator | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | if (ambient) | ||
+ | delete [] ambient; | ||
+ | if (diffuse) | ||
+ | delete [] diffuse; | ||
+ | if (specular) | ||
+ | delete [] specular; | ||
+ | if (power) | ||
+ | delete [] power; | ||
+ | ambient = new D3DXCOLOR[src.nSubsets]; | ||
+ | diffuse = new D3DXCOLOR[src.nSubsets]; | ||
+ | specular = new D3DXCOLOR[src.nSubsets]; | ||
+ | power = new FLOAT[src.nSubsets]; | ||
+ | </syntaxhighlight> | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | for (unsigned i = 0; i < nSubsets; i++) { | ||
+ | ambient[i] = src.ambient[i]; | ||
+ | diffuse[i] = src.diffuse[i]; | ||
+ | specular[i] = src.specular[i]; | ||
+ | power[i] = src.power[i]; | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | : APIGraphic.cpp - setup() - | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | ambient = new D3DXCOLOR[nSubsets]; | ||
+ | diffuse = new D3DXCOLOR[nSubsets]; | ||
+ | specular = new D3DXCOLOR[nSubsets]; | ||
+ | power = new FLOAT[nSubsets]; | ||
+ | </syntaxhighlight> | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | ambient[i].r = matl[i].MatD3D.Diffuse.r * 0.7f; | ||
+ | ambient[i].g = matl[i].MatD3D.Diffuse.g * 0.7f; | ||
+ | ambient[i].b = matl[i].MatD3D.Diffuse.b * 0.7f; | ||
+ | ambient[i].a = matl[i].MatD3D.Diffuse.a; | ||
+ | diffuse[i] = matl[i].MatD3D.Diffuse; // reflected from lights | ||
+ | specular[i] = matl[i].MatD3D.Specular; // shine from lights | ||
+ | power[i] = matl[i].MatD3D.Power; // 0 if it shouldn't be shiny | ||
+ | </syntaxhighlight> | ||
+ | : APIGraphic.cpp - draw() | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | uniformVS->SetFloatArray(d3dd, "material.ambient", (FLOAT*)&ambient[i], 4); | ||
+ | uniformVS->SetFloatArray(d3dd, "material.diffuse", (FLOAT*)&diffuse[i], 4); | ||
+ | uniformVS->SetFloatArray(d3dd, "material.specular", (FLOAT*)&specular[i], 4); | ||
+ | uniformVS->SetFloat(d3dd, "material.power", (FLOAT)power[i]); | ||
+ | </syntaxhighlight> | ||
+ | : APIGraphic.cpp - suspend() | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | if (ambient) | ||
+ | delete [] ambient; | ||
+ | if (diffuse) | ||
+ | delete [] diffuse; | ||
+ | if (specular) | ||
+ | delete [] specular; | ||
+ | if (power) | ||
+ | delete [] power; | ||
+ | </syntaxhighlight> | ||
+ | * Device | ||
+ | : vertexShader.hlsl - Constant Memory | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | #define MLIGHTS 4 | ||
+ | #define POINT_LIGHT 0 | ||
+ | #define SPOT_LIGHT 1 | ||
+ | #define DIRECTIONAL_LIGHT 2 | ||
+ | |||
+ | // Types | ||
+ | // | ||
+ | // Light holds the data for a single light in world space | ||
+ | // | ||
+ | struct Light { | ||
+ | int type; // POINT_LIGHT, SPOT_LIGHT, DIRECTIONAL_LIGHT | ||
+ | float4 ambient; | ||
+ | float4 diffuse; | ||
+ | float4 specular; | ||
+ | float3 direction; // in world space | ||
+ | float3 position; // in world space | ||
+ | float3 attenuation; // .xyz for 1.0f/ (.x + .y * d + .z * d * d) | ||
+ | float3 spot; // .x = cos(phi/2), .y = cos(theta/2), .z = falloff | ||
+ | float range; // where attenuation becomes 0 | ||
+ | }; | ||
+ | |||
+ | // Material holds the reflectivity properties of the material | ||
+ | // | ||
+ | struct Material { | ||
+ | float4 ambient; | ||
+ | float4 diffuse; | ||
+ | float4 specular; | ||
+ | float power; | ||
+ | }; | ||
+ | |||
+ | // RawVertex holds the untransformed data for a single vertex | ||
+ | // | ||
+ | struct RawVertex { | ||
+ | |||
+ | float3 position : POSITION; // position in local space | ||
+ | float3 normal : NORMAL; // normal in local space | ||
+ | float2 texCoord : TEXCOORD; // texture coordinates | ||
+ | }; | ||
+ | |||
+ | // TransformedVertex holds the transformed data for a single vertex | ||
+ | // | ||
+ | struct TransformedVertex { | ||
+ | |||
+ | float4 position : POSITION; // position in homogeneous clip space | ||
+ | float4 colour : COLOR; // colour of the lit vertex | ||
+ | float2 texCoord0 : TEXCOORD0; // texture coordinates - stage 0 | ||
+ | float2 texCoord1 : TEXCOORD1; // texture coordinates - stage 1 | ||
+ | }; | ||
+ | |||
+ | // Uniform Data (constant for a stream of vertices) | ||
+ | // | ||
+ | // Lighting | ||
+ | float4 ambient; // global ambient light - always on | ||
+ | Light light[MLIGHTS]; // static lights | ||
+ | bool lightOn[MLIGHTS]; // switches for static lights | ||
+ | Material material; // material reflectivity | ||
+ | // Geometry | ||
+ | float4x4 viewProjection; // view * projection transformation | ||
+ | float4x4 world; // world transformation | ||
+ | float3 heading; // camera heading for specular calcs | ||
+ | // Lit Vertex | ||
+ | bool litVertex; // omit lighting calculations - already lit | ||
+ | |||
+ | </syntaxhighlight> | ||
+ | : vertexShader.hlsl - vertexShader() | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | // vertexShader receives a raw vertex and returns the transformed vertex | ||
+ | // | ||
+ | TransformedVertex vertexShader(RawVertex raw) { | ||
+ | |||
+ | TransformedVertex transformed; | ||
+ | float4 worldPosition; // world position of the vertex | ||
+ | float3 worldNormal; // vertex normal in world space | ||
+ | |||
+ | // Transform the vertex to homogeneous clip coordinates | ||
+ | // | ||
+ | // A more efficient algorithm would accept the world*view*projection | ||
+ | // tranformation as one uniform matrix and avoid the 2-stage product | ||
+ | // This will require a bit of restructuring of the application code. | ||
+ | // | ||
+ | worldPosition = mul(float4(raw.position, 1.0), world); // local to world | ||
+ | transformed.position = mul(worldPosition, viewProjection); //... to clip | ||
+ | |||
+ | // not working | ||
+ | if (litVertex) { | ||
+ | transformed.colour.r = raw.normal.r; | ||
+ | transformed.colour.g = raw.normal.g; | ||
+ | transformed.colour.b = raw.normal.b; | ||
+ | transformed.colour.a = 1.0f; | ||
+ | } | ||
+ | else { | ||
+ | |||
+ | // Transform the vertex normal to world space. Only the rotation-scaling | ||
+ | // part of the world transformation is used. Since the world | ||
+ | // transformation may contain scaling, the result of this multiplication | ||
+ | // needs to be normalized. | ||
+ | // | ||
+ | worldNormal = mul(raw.normal, (float3x3)world); | ||
+ | worldNormal = normalize(worldNormal); | ||
+ | |||
+ | // Determine the colour of the vertex from each light in turn | ||
+ | // | ||
+ | // Use the cosine of the angle between the worldNormal and the direction | ||
+ | // of the light to determine the amount of reflected light. The cosine | ||
+ | // is given by the dot product, if both vectors have been normalized. If | ||
+ | // the cosine is less than 0, the angle is greater than 90 degrees and | ||
+ | // no light is reflected. Use saturate() to implement this condition. | ||
+ | // | ||
+ | // A more efficient algorithm would supply the light direction already | ||
+ | // converted to the local space of the vertex (by using the inverse of | ||
+ | // the world transformation). | ||
+ | // | ||
+ | |||
+ | float diffuseFactor, reflectFactor, distance; | ||
+ | float attenuationFactor, spotFactor, rho; | ||
+ | float3 lightDirection, camera = normalize(heading); | ||
+ | float3 ambientLight = ambient.xyz; | ||
+ | float3 diffuseLight = (float3)0; | ||
+ | float3 specularLight = (float3)0; | ||
+ | |||
+ | for (int i = 0; i < MLIGHTS; i++) { | ||
+ | if (lightOn[i]) { | ||
+ | lightDirection = - normalize((light[i].type == POINT_LIGHT)? | ||
+ | float3(worldPosition.x, worldPosition.y, worldPosition.z) - | ||
+ | light[i].position : light[i].direction); | ||
+ | diffuseFactor = saturate(dot(worldNormal, lightDirection)); | ||
+ | reflectFactor = saturate(dot(normalize(2 * diffuseFactor * | ||
+ | worldNormal - lightDirection), camera)); | ||
+ | attenuationFactor = 1.0f; | ||
+ | spotFactor = 1.0f; | ||
+ | |||
+ | if (light[i].type == POINT_LIGHT || | ||
+ | light[i].type == SPOT_LIGHT) { | ||
+ | // detail calcs for attenuationFactor and spotFactor | ||
+ | distance = length((float3)worldPosition - light[i].position); | ||
+ | if (distance < light[i].range) { | ||
+ | attenuationFactor = light[i].attenuation.x + | ||
+ | light[i].attenuation.y * distance + | ||
+ | light[i].attenuation.z * distance * distance; | ||
+ | attenuationFactor = 1.0f / attenuationFactor; | ||
+ | if (light[i].type == SPOT_LIGHT) { | ||
+ | rho = saturate(dot(normalize(light[i].position - | ||
+ | float3(worldPosition.x, worldPosition.y, | ||
+ | worldPosition.z)), | ||
+ | normalize(-light[i].direction))); | ||
+ | if (rho <= light[i].spot.x) | ||
+ | spotFactor = 0.0f; | ||
+ | else if (rho <= light[i].spot.y) | ||
+ | spotFactor = pow( | ||
+ | abs((rho - light[i].spot.x)/ | ||
+ | (light[i].spot.y - light[i].spot.x)), | ||
+ | light[i].spot.z); | ||
+ | } | ||
+ | } | ||
+ | else | ||
+ | attenuationFactor = 0.0f; | ||
+ | } | ||
+ | |||
+ | // accumulate ambient, diffuse, and specular elements of light | ||
+ | // | ||
+ | ambientLight += attenuationFactor * spotFactor * | ||
+ | light[i].ambient.xyz; | ||
+ | diffuseLight += attenuationFactor * spotFactor * diffuseFactor * | ||
+ | light[i].diffuse.xyz; | ||
+ | specularLight += attenuationFactor * spotFactor * | ||
+ | light[i].specular.xyz * pow(reflectFactor, material.power); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // apply material reflectivity to each accumulated element of light | ||
+ | // to obtain the colour of the lit vertex | ||
+ | // | ||
+ | transformed.colour.xyz = | ||
+ | saturate(material.ambient.xyz * ambientLight) + | ||
+ | saturate(material.diffuse.xyz * diffuseLight) + | ||
+ | saturate(material.specular.xyz * specularLight); | ||
+ | |||
+ | // pass the diffuse alpha along as the alpha component | ||
+ | // | ||
+ | transformed.colour.w = material.diffuse.w; | ||
+ | } | ||
+ | |||
+ | // pass the texture coordinates along unaltered | ||
+ | // | ||
+ | transformed.texCoord0 = raw.texCoord; | ||
+ | transformed.texCoord1 = raw.texCoord; | ||
+ | |||
+ | return transformed; | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | ==== Fragment Shader ==== | ||
+ | * Host | ||
+ | : APIPlatformSettings.h | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | #define FRAGMENT_SHADER_FILE L"fragmentShader.hlsl" | ||
+ | #define FRAGMENT_SHADER_ENTRY_POINT "fragmentShader" | ||
+ | </syntaxhighlight> | ||
+ | : APIBase.h | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | // Fragment Shader Support | ||
+ | static IDirect3DPixelShader9* fragmentShader; // fragment shader | ||
+ | static ID3DXConstantTable* uniformFS; // for fragment shader | ||
+ | </syntaxhighlight> | ||
+ | : APIBase.cpp | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | IDirect3DPixelShader9* APIBase::fragmentShader = nullptr; // fragment shader | ||
+ | ID3DXConstantTable* APIBase::uniformFS = nullptr; // for fragment shader | ||
+ | </syntaxhighlight> | ||
+ | : APIDisplay.cpp - setup() | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | LPD3DXBUFFER compiledCodeFS = nullptr; | ||
+ | |||
+ | // checks for minimum pixel shader version required | ||
+ | else if (caps.PixelShaderVersion < D3DPS_VERSION(3,0)) | ||
+ | error(L"Display::10 Device does not support pixel shader 3_0"); | ||
+ | </syntaxhighlight> | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | // compile the fragment shader source code | ||
+ | else if (FAILED(D3DXCompileShaderFromFile(FRAGMENT_SHADER_FILE, NULL, | ||
+ | NULL, FRAGMENT_SHADER_ENTRY_POINT, D3DXGetPixelShaderProfile(d3dd), 0, | ||
+ | &compiledCodeFS, NULL, &uniformFS))) { | ||
+ | release(); | ||
+ | error(L"APIDisplay::15 Unable to compile the fragment shader code"); | ||
+ | } | ||
+ | // create the pixel shader object | ||
+ | else if (FAILED(d3dd->CreatePixelShader( | ||
+ | (DWORD*)compiledCodeFS->GetBufferPointer(), &fragmentShader))) { | ||
+ | compiledCodeFS->Release(); | ||
+ | release(); | ||
+ | error(L"APIDisplay::16 Unable to create fragment shader object"); | ||
+ | } | ||
+ | else { | ||
+ | compiledCodeFS->Release(); | ||
+ | </syntaxhighlight> | ||
+ | :: [http://msdn.microsoft.com/en-us/library/windows/desktop/bb172732%28v=vs.85%29.aspx D3DXCompileShaderFromFile()] | ||
+ | :: [http://msdn.microsoft.com/en-us/library/windows/desktop/bb172870%28v=vs.85%29.aspx D3DXGetPixelShaderProfile()] | ||
+ | :: [http://msdn.microsoft.com/en-us/library/windows/desktop/bb174450%28v=vs.85%29.aspx IDirect3DDevice9::CreatePixelShader()] | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | d3dd->SetPixelShader(fragmentShader); | ||
+ | </syntaxhighlight> | ||
+ | :: [http://msdn.microsoft.com/en-us/library/windows/desktop/bb174450%28v=vs.85%29.aspx IDirect3DDevice9::SetPixelShader()] | ||
+ | : APIDisplay.cpp - beginDrawFrame() | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | Colour colour(red, green, blue); | ||
+ | uniformFS->SetFloatArray(d3dd, "ambient", (FLOAT*)&colour, 4); | ||
+ | uniformFS->SetInt(d3dd, "noLights", 4); | ||
+ | </syntaxhighlight> | ||
+ | :: [http://msdn.microsoft.com/en-us/library/windows/desktop/bb205775%28v=vs.85%29.aspx ID3DXConstantTable::SetFloatArray()] | ||
+ | :: [http://msdn.microsoft.com/en-us/library/windows/desktop/bb205776%28v=vs.85%29.aspx ID3DXConstantTable::SetInt()] | ||
+ | : APIDisplay.cpp - set() | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | uniformFS->SetBool(d3dd, "lighting", b); | ||
+ | </syntaxhighlight> | ||
+ | :: [http://msdn.microsoft.com/en-us/library/windows/desktop/bb205771%28v=vs.85%29.aspx ID3DXConstantTable::SetBool()] | ||
+ | : APIDisplay.cpp - setReflectivity() | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | uniformFS->SetFloatArray(d3dd, "material.ambient", (FLOAT*)&r.ambient, 4); | ||
+ | uniformFS->SetFloatArray(d3dd, "material.diffuse", (FLOAT*)&r.diffuse, 4); | ||
+ | uniformFS->SetFloatArray(d3dd, "material.specular", (FLOAT*)&r.specular, 4); | ||
+ | uniformFS->SetFloat (d3dd, "material.power", (FLOAT)r.power); | ||
+ | </syntaxhighlight> | ||
+ | : APIDisplay.cpp - release() | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | } | ||
+ | if (uniformFS) { | ||
+ | uniformFS->Release(); | ||
+ | uniformFS = nullptr; | ||
+ | } | ||
+ | if (fragmentShader) { | ||
+ | fragmentShader->Release(); | ||
+ | fragmentShader = nullptr; | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | : APIGraphic.cpp - APIXMesh::draw() | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | uniformFS->SetFloatArray(d3dd, "material.ambient", (FLOAT*)&ambient[i], 4); | ||
+ | uniformFS->SetFloatArray(d3dd, "material.diffuse", (FLOAT*)&diffuse[i], 4); | ||
+ | uniformFS->SetFloatArray(d3dd, "material.specular", (FLOAT*)&specular[i], 4); | ||
+ | uniformFS->SetFloat(d3dd, "material.power", (FLOAT)power[i]); | ||
+ | </syntaxhighlight> | ||
+ | :: [http://msdn.microsoft.com/en-us/library/windows/desktop/bb205774%28v=vs.85%29.aspx ID3DXConstantTable::SetFloat()] | ||
+ | : APILight.cpp - setup() | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | uniformFS->SetInt(d3dd, typ, type); | ||
+ | uniformFS->SetFloatArray(d3dd, amb, (FLOAT*)&ambient, 4); | ||
+ | uniformFS->SetFloatArray(d3dd, dif, (FLOAT*)&diffuse, 4); | ||
+ | uniformFS->SetFloatArray(d3dd, spe, (FLOAT*)&specular, 4); | ||
+ | uniformFS->SetFloatArray(d3dd, pos, (FLOAT*)&zero, 3); | ||
+ | uniformFS->SetFloatArray(d3dd, dir, (FLOAT*)&zero, 3); | ||
+ | uniformFS->SetFloatArray(d3dd, att, (FLOAT*)&attenuation, 3); | ||
+ | uniformFS->SetFloatArray(d3dd, spt, (FLOAT*)&spot, 3); | ||
+ | uniformFS->SetFloat(d3dd, ran, range); | ||
+ | </syntaxhighlight> | ||
+ | : APILight.cpp - turnOn() | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | uniformFS->SetFloatArray(d3dd, pos, (FLOAT*)&p, 3); | ||
+ | uniformFS->SetFloatArray(d3dd, dir, (FLOAT*)&o, 3); | ||
+ | uniformFS->SetBool(d3dd, constantLightOn, true); | ||
+ | </syntaxhighlight> | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | uniformFS->SetFloatArray(d3dd, pos, (FLOAT*)&p, 3); | ||
+ | uniformFS->SetFloatArray(d3dd, dir, (FLOAT*)&o, 3); | ||
+ | uniformFS->SetBool(d3dd, constantLightOn, true); | ||
+ | </syntaxhighlight> | ||
+ | : APILight.cpp - update() | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | uniformFS->SetFloatArray(d3dd, pos, (FLOAT*)&p, 3); | ||
+ | uniformFS->SetFloatArray(d3dd, dir, (FLOAT*)&o, 3); | ||
+ | uniformFS->SetBool(d3dd, constantLightOn, true); | ||
+ | </syntaxhighlight> | ||
+ | : APILight.cpp - turnOff() | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | uniformFS->SetBool(d3dd, constantLightOn, false); | ||
+ | </syntaxhighlight> | ||
+ | : APITexture.cpp - attach() | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | char str[] = "texOn"; | ||
+ | uniformFS->SetBool(d3dd, str, true); | ||
+ | </syntaxhighlight> | ||
+ | : APITexture.cpp - detach() | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | char str[] = "texOn"; | ||
+ | uniformFS->SetBool(d3dd, str, false); | ||
+ | </syntaxhighlight> | ||
+ | * Device | ||
+ | : vertexShader.hlsl - Constant Memory | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | // Types | ||
+ | // | ||
+ | // RawVertex holds the original data for a vertex in the stream | ||
+ | // | ||
+ | struct RawVertex { | ||
+ | |||
+ | float3 position : POSITION; // position in local space | ||
+ | float3 normal : NORMAL; // normal in local space | ||
+ | float2 texCoord : TEXCOORD0; // texture coordinates | ||
+ | }; | ||
+ | |||
+ | // TransformedVertex holds the transformed data for the vertex | ||
+ | // | ||
+ | struct TransformedVertex { | ||
+ | |||
+ | float4 position : POSITION; // position in homogeneous clip space | ||
+ | float2 texCoord : TEXCOORD; // texture coordinates | ||
+ | float3 worldPos : TEXCOORD1; // position in world space | ||
+ | float3 worldNor : TEXCOORD2; // lighting normal in world space | ||
+ | float3 toViewer : TEXCOORD3; // direction to viewer in world space | ||
+ | }; | ||
+ | |||
+ | // Uniform Data (constant for the stream of vertices) | ||
+ | // | ||
+ | // Geometry | ||
+ | float4x4 viewProjection; // view * projection transformation | ||
+ | float4x4 world; // world transformation | ||
+ | float3 viewPoint; // camera viewpoint for specular calcs | ||
+ | // Lit Vertex | ||
+ | bool litVertex; // omit lighting calculations - already lit | ||
+ | |||
+ | </syntaxhighlight> | ||
+ | : vertexShader.hlsl - vertexShader() | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | // vertexShader receives a raw data for a vertex and transforms that data | ||
+ | // | ||
+ | TransformedVertex vertexShader(RawVertex raw) { | ||
+ | |||
+ | TransformedVertex transformed; // result returned by this function | ||
+ | float4 worldPosition; // world position of the vertex | ||
+ | float3 worldNormal; // vertex normal in world space | ||
+ | |||
+ | // Transform the vertex to homogeneous clip coordinates | ||
+ | // | ||
+ | // A more efficient algorithm would accept the world*view*projection | ||
+ | // tranformation as one uniform matrix and avoid the 2-stage product | ||
+ | // This will require a bit of restructuring of the application code. | ||
+ | // | ||
+ | worldPosition = mul(float4(raw.position, 1.0), world); // local to world | ||
+ | transformed.position = mul(worldPosition, viewProjection); //... to clip | ||
+ | transformed.worldPos = worldPosition.xyz; | ||
+ | |||
+ | // Transform the vertex normal to world space. Only the rotation-scaling | ||
+ | // part of the world transformation is used. Since the world | ||
+ | // transformation may contain scaling, the result of this multiplication | ||
+ | // needs to be normalized. | ||
+ | // | ||
+ | worldNormal = mul(raw.normal, (float3x3)world); | ||
+ | worldNormal = normalize(worldNormal); | ||
+ | transformed.worldNor = worldNormal; | ||
+ | |||
+ | // Determine the direction from the camera's viewpoint to this vertex for | ||
+ | // subsequent lighting calculations | ||
+ | // | ||
+ | transformed.toViewer = normalize(viewPoint - worldPosition.xyz); | ||
+ | |||
+ | // pass the texture coordinates along unaltered | ||
+ | // | ||
+ | transformed.texCoord = raw.texCoord; | ||
+ | |||
+ | return transformed; | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | : fragmentShader.hlsl - Constant Memory | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | #define MLIGHTS 4 | ||
+ | #define MTEXTURES 2 | ||
+ | #define POINT_LIGHT 0 | ||
+ | #define SPOT_LIGHT 1 | ||
+ | #define DIRECTIONAL_LIGHT 2 | ||
+ | |||
+ | // Types | ||
+ | // | ||
+ | // Light holds the data for a single static light in world space | ||
+ | // | ||
+ | struct Light { | ||
+ | int type; // POINT_LIGHT, SPOT_LIGHT, DIRECTIONAL_LIGHT | ||
+ | float4 ambient; | ||
+ | float4 diffuse; | ||
+ | float4 specular; | ||
+ | float3 direction; // in world space | ||
+ | float3 position; // in world space | ||
+ | float3 attenuation; // .xyz for 1.0f/ (.x + .y * d + .z * d * d) | ||
+ | float3 spot; // .x = cos(phi/2), .y = cos(theta/2), .z = falloff | ||
+ | float range; // where attenuation becomes 0 | ||
+ | }; | ||
+ | |||
+ | // Material holds the reflectivity properties of the material | ||
+ | // | ||
+ | struct Material { | ||
+ | float4 ambient; | ||
+ | float4 diffuse; | ||
+ | float4 specular; | ||
+ | float power; | ||
+ | }; | ||
+ | |||
+ | // RawPixel holds the data for a single fragment of the stream | ||
+ | // | ||
+ | struct RawPixel { | ||
+ | float2 texcoord : TEXCOORD0; // texture coordinate at this fragment | ||
+ | float3 position : TEXCOORD1; // fragment position in world space | ||
+ | float3 normal : TEXCOORD2; // lighting normal in world space | ||
+ | float3 toViewer : TEXCOORD3; // direction to viewer in world space | ||
+ | }; | ||
+ | |||
+ | // Uniform Data (constant for a stream of fragments) | ||
+ | // | ||
+ | float4 ambient; // global ambient light - always on | ||
+ | int noLights; // no of active lights | ||
+ | Light light[MLIGHTS]; // static lights | ||
+ | bool lightOn[MLIGHTS]; // light switch | ||
+ | Material material; // material reflectivity | ||
+ | bool lighting; // lighting calculations on? | ||
+ | |||
+ | bool texOn; // texture switch | ||
+ | sampler2D tex; // set by the application | ||
+ | |||
+ | </syntaxhighlight> | ||
+ | : fragmentShader.hlsl - fragmentShader() | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | // The fragment shader receives raw fragment data and returns a pixel colour | ||
+ | // | ||
+ | float4 fragmentShader(RawPixel raw) : COLOR { | ||
+ | |||
+ | float4 colour; // result returned by this function | ||
+ | float3 normal; // normal to the fragment | ||
+ | float3 toViewer; // from fragment to the camera | ||
+ | float3 toLightSource; // from fragment to current light source | ||
+ | |||
+ | // lighting contribution accumulators | ||
+ | float3 ambientLight = ambient.xyz; | ||
+ | float3 diffuseLight = (float3)0; | ||
+ | float3 specularLight = (float3)0; | ||
+ | // lighting calculation factors | ||
+ | float diffuseFactor, reflectFactor, distance; | ||
+ | float attenuationFactor, spotFactor, rho; | ||
+ | |||
+ | // normalize the fragment data | ||
+ | normal = normalize(raw.normal); | ||
+ | toViewer = normalize(raw.toViewer); | ||
+ | |||
+ | // perform calculations for each light in turn | ||
+ | for (int i = 0; i < noLights && i < MLIGHTS; i++) { | ||
+ | if (lightOn[i]) { | ||
+ | float diffuseFactor, reflectFactor, factor; | ||
+ | // diffuse and reflection factors | ||
+ | toLightSource = normalize((light[i].type == POINT_LIGHT)? | ||
+ | light[i].position - raw.position : - light[i].direction); | ||
+ | diffuseFactor = saturate(dot(normal, toLightSource)); | ||
+ | reflectFactor = saturate(dot(normalize(2 * diffuseFactor * | ||
+ | normal - toLightSource), toViewer)); | ||
+ | |||
+ | attenuationFactor = 1.0f; | ||
+ | spotFactor = 1.0f; | ||
+ | |||
+ | if (light[i].type == POINT_LIGHT || | ||
+ | light[i].type == SPOT_LIGHT) { | ||
+ | // detail calcs for attenuationFactor and spotFactor | ||
+ | distance = length(raw.position - light[i].position); | ||
+ | if (distance < light[i].range) { | ||
+ | attenuationFactor = light[i].attenuation.x + | ||
+ | light[i].attenuation.y * distance + | ||
+ | light[i].attenuation.z * distance * distance; | ||
+ | attenuationFactor = 1.0f / attenuationFactor; | ||
+ | if (light[i].type == SPOT_LIGHT) { | ||
+ | rho = saturate(dot(normalize(light[i].position - | ||
+ | float3(raw.position.x, raw.position.y, | ||
+ | raw.position.z)), | ||
+ | normalize(-light[i].direction))); | ||
+ | if (rho <= light[i].spot.x) | ||
+ | spotFactor = 0.0f; | ||
+ | else if (rho <= light[i].spot.y) | ||
+ | spotFactor = pow(abs( | ||
+ | (rho - light[i].spot.x)/ | ||
+ | (light[i].spot.y - light[i].spot.x)), | ||
+ | light[i].spot.z); | ||
+ | } | ||
+ | } | ||
+ | else | ||
+ | attenuationFactor = 0.0f; | ||
+ | } | ||
+ | |||
+ | // accumulate ambient, diffuse, and specular elements of light | ||
+ | // | ||
+ | ambientLight += attenuationFactor * spotFactor * | ||
+ | light[i].ambient.xyz; | ||
+ | diffuseLight += attenuationFactor * spotFactor * diffuseFactor * | ||
+ | light[i].diffuse.xyz; | ||
+ | specularLight += attenuationFactor * spotFactor * | ||
+ | light[i].specular.xyz * pow(reflectFactor, material.power); | ||
+ | } | ||
+ | |||
+ | // apply material reflectivity to each accumulated element of light | ||
+ | // to obtain the colour of the lit fragment | ||
+ | // | ||
+ | colour.xyz = | ||
+ | saturate(material.ambient.xyz * ambientLight) + | ||
+ | saturate(material.diffuse.xyz * diffuseLight) + | ||
+ | saturate(material.specular.xyz * specularLight); | ||
+ | colour.w = material.diffuse.w; | ||
+ | } | ||
+ | |||
+ | // apply texture | ||
+ | // | ||
+ | if (texOn) | ||
+ | colour *= tex2D(tex, raw.texcoord); | ||
+ | |||
+ | return colour; | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | === To Do === | ||
+ | * reorganize framework code so that vertex shader receives product of world, view, and projection matrices | ||
+ | ** store viewProjection matrix as an instance variable in APIDisplay | ||
+ | ** pre-multiply viewProjection by world to obtain composite matrix to pass to vertex shader | ||
+ | ** add composite matrix to the constant table in the vertex shader | ||
+ | * reorganize framework code to minimize duplication of heading normalization | ||
+ | ** perform normalization of heading in APIDisplay::beginDrawFrame() | ||
+ | |||
+ | === Resources === | ||
+ | |||
+ | == Week 7 - Feb 20 == | ||
+ | === This Week === | ||
+ | * Effects Framework | ||
+ | ** techniques | ||
+ | *: passes | ||
+ | * Source Code | ||
+ | : APIBase.h | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | static ID3DXEffect* effect; // points to effects framework | ||
+ | </syntaxhighlight> | ||
+ | : APIBase.cpp | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | ID3DXEffect* APIBase::effect = nullptr; // effects framework | ||
+ | </syntaxhighlight> | ||
+ | : APIDisplay.h | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | // Effects Framework handles | ||
+ | D3DXHANDLE viewProjection; // view * projection | ||
+ | D3DXHANDLE viewPoint; // camera viewpoint | ||
+ | D3DXHANDLE ambient; // global ambient color | ||
+ | D3DXHANDLE ambientHandle; // current ambient reflectivity | ||
+ | D3DXHANDLE diffuseHandle; // current diffuse reflectivity | ||
+ | D3DXHANDLE specularHandle; // current specular reflectivity | ||
+ | D3DXHANDLE powerHandle; // current shininess coefficient | ||
+ | D3DXHANDLE worldHandle; // current world transformation | ||
+ | </syntaxhighlight> | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | void beginEffect(const char*, unsigned&); | ||
+ | void beginPass(unsigned); | ||
+ | void endPass(); | ||
+ | void endEffect(); | ||
+ | </syntaxhighlight> | ||
+ | : APIDisplay.cpp - APIDisplay() | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | viewProjection = nullptr; | ||
+ | viewPoint = nullptr; | ||
+ | ambient = nullptr; | ||
+ | ambientHandle = nullptr; | ||
+ | diffuseHandle = nullptr; | ||
+ | specularHandle = nullptr; | ||
+ | powerHandle = nullptr; | ||
+ | worldHandle = nullptr; | ||
+ | </syntaxhighlight> | ||
+ | : APIDisplay.cpp - setup() | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | LPD3DXBUFFER errorBuffer = NULL; | ||
+ | </syntaxhighlight> | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | // create the effects framework | ||
+ | else if (FAILED(D3DXCreateEffectFromFile(d3dd, EFFECT_FILE, 0, | ||
+ | 0, D3DXSHADER_DEBUG, 0, &effect, &errorBuffer))) { | ||
+ | release(); | ||
+ | error(L"APIDisplay::17 Unable to create the effects framework"); | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | :: [http://msdn.microsoft.com/en-us/library/windows/desktop/bb172768%28v=vs.85%29.aspx D3DXCreateEffectFromFile()] | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | if (errorBuffer) errorBuffer->Release(); | ||
+ | viewProjection = effect->GetParameterByName(0, "viewProjection"); | ||
+ | viewPoint = effect->GetParameterByName(0, "viewPoint"); | ||
+ | ambient = effect->GetParameterByName(0, "ambient"); | ||
+ | worldHandle = effect->GetParameterByName(0, "world"); | ||
+ | ambientHandle = effect->GetParameterByName(0, "material.ambient"); | ||
+ | diffuseHandle = effect->GetParameterByName(0, "material.diffuse"); | ||
+ | specularHandle = effect->GetParameterByName(0, "material.specular"); | ||
+ | powerHandle = effect->GetParameterByName(0, "material.power"); | ||
+ | </syntaxhighlight> | ||
+ | :: [http://msdn.microsoft.com/en-us/library/windows/desktop/bb205696%28v=vs.85%29.aspx GetParameterByName()] | ||
+ | : APIDisplay.cpp - beginDrawFrame() | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | Matrix& v = *((Matrix*)view); | ||
+ | Matrix viewprojection = v * projection; | ||
+ | Vector heading(v.m13, v.m23, v.m33); | ||
+ | effect->SetMatrix(viewProjection, (D3DXMATRIX*)&viewprojection); | ||
+ | effect->SetVector(viewPoint, (D3DXVECTOR4*)&heading); | ||
+ | </syntaxhighlight> | ||
+ | :: [http://msdn.microsoft.com/en-us/library/windows/desktop/bb205720%28v=vs.85%29.aspx SetMatrix()] | ||
+ | :: [http://msdn.microsoft.com/en-us/library/windows/desktop/bb205729%28v=vs.85%29.aspx SetVector()] | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | Colour colour(red, green, blue); | ||
+ | effect->SetVector(ambient, (D3DXVECTOR4*)&colour); | ||
+ | </syntaxhighlight> | ||
+ | : APIDisplay.cpp - beginEffect() | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | void APIDisplay::beginEffect(const char* technique, unsigned& nPasses) { | ||
+ | |||
+ | D3DXHANDLE techniqueHandle = effect->GetTechniqueByName(technique); | ||
+ | effect->SetTechnique(techniqueHandle); | ||
+ | effect->Begin(&nPasses, 0); | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | :: [http://msdn.microsoft.com/en-us/library/windows/desktop/bb205706%28v=vs.85%29.aspx GetTechniqueByName()] | ||
+ | :: [http://msdn.microsoft.com/en-us/library/windows/desktop/bb205834%28v=vs.85%29.aspx SetTechnique()] | ||
+ | :: [http://msdn.microsoft.com/en-us/library/windows/desktop/bb205815%28v=vs.85%29.aspx Begin()] | ||
+ | : APIDisplay.cpp - beginPass() | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | void APIDisplay::beginPass(unsigned i) { | ||
+ | |||
+ | effect->BeginPass(i); | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | :: [http://msdn.microsoft.com/en-us/library/windows/desktop/bb205817%28v=vs.85%29.aspx BeginPass()] | ||
+ | : APIDisplay.cpp - endPass() | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | void APIDisplay::endPass() { | ||
+ | |||
+ | effect->EndPass(); | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | :: [http://msdn.microsoft.com/en-us/library/windows/desktop/bb205823%28v=vs.85%29.aspx EndPass()] | ||
+ | : APIDisplay.cpp - endEffect() | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | void APIDisplay::endEffect() { | ||
+ | |||
+ | effect->End(); | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | :: [http://msdn.microsoft.com/en-us/library/windows/desktop/bb205821%28v=vs.85%29.aspx End()] | ||
+ | : APIDisplay.cpp - setWorld() | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | effect->SetMatrix(worldHandle, (D3DXMATRIX*)world); | ||
+ | </syntaxhighlight> | ||
+ | : APIDisplay.cpp - setReflectivity() | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | effect->SetVector(ambientHandle, (D3DXVECTOR4*)&r.ambient); | ||
+ | effect->SetVector(diffuseHandle, (D3DXVECTOR4*)&r.diffuse); | ||
+ | effect->SetVector(specularHandle, (D3DXVECTOR4*)&r.specular); | ||
+ | effect->SetFloat(powerHandle, r.power); | ||
+ | effect->CommitChanges(); | ||
+ | </syntaxhighlight> | ||
+ | :: [http://msdn.microsoft.com/en-us/library/windows/desktop/bb205819%28v=vs.85%29.aspx CommitChanges()] | ||
+ | : APIDisplay.cpp - release() | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | if (effect) { | ||
+ | effect->Release(); | ||
+ | effect = NULL; | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | : effects.fx - Constant Memory | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | #define MLIGHTS 4 | ||
+ | #define POINT_LIGHT 0 | ||
+ | #define SPOT_LIGHT 1 | ||
+ | #define DIRECTIONAL_LIGHT 2 | ||
+ | |||
+ | // Types | ||
+ | // | ||
+ | // Light holds the data for a single static light in world space | ||
+ | // | ||
+ | struct Light { | ||
+ | int type; // POINT_LIGHT, SPOT_LIGHT, DIRECTIONAL_LIGHT | ||
+ | float3 ambient; | ||
+ | float3 diffuse; | ||
+ | float3 specular; | ||
+ | float3 direction; // in world space | ||
+ | float3 position; // in world space | ||
+ | float3 attenuation; // .xyz for 1.0f/ (.x + .y * d + .z * d * d) | ||
+ | float3 spot; // .x = cos(phi/2), .y = cos(theta/2), .z = falloff | ||
+ | float range; // where attenuation becomes 0 | ||
+ | }; | ||
+ | |||
+ | // Material holds the reflectivity properties of the material | ||
+ | // | ||
+ | struct Material { | ||
+ | float4 ambient; | ||
+ | float4 diffuse; | ||
+ | float4 specular; | ||
+ | float power; | ||
+ | }; | ||
+ | |||
+ | // RawPixel holds the data for a single fragment of the stream | ||
+ | // | ||
+ | struct RawPixel { | ||
+ | float2 texcoord : TEXCOORD0; // texture coordinate at this fragment | ||
+ | float3 position : TEXCOORD1; // fragment position in world space | ||
+ | float3 normal : TEXCOORD2; // lighting normal in world space | ||
+ | float3 toViewer : TEXCOORD3; // direction to viewer in world space | ||
+ | }; | ||
+ | |||
+ | // Uniform Data (constant for a stream of fragments) | ||
+ | // | ||
+ | float4 ambient; // global ambient light - always on | ||
+ | int noLights; // no of static lights | ||
+ | Light light[MLIGHTS]; // static lights | ||
+ | bool lightOn[MLIGHTS]; // light switch | ||
+ | Material material; // material reflectivity | ||
+ | |||
+ | bool texOn; // texture switch | ||
+ | sampler2D tex; // set by the application | ||
+ | |||
+ | // Types | ||
+ | // | ||
+ | // RawVertex holds the original data for a vertex in the stream | ||
+ | // | ||
+ | struct RawVertex { | ||
+ | |||
+ | float3 position : POSITION; // position in local space | ||
+ | float3 normal : NORMAL; // normal in local space | ||
+ | float2 texCoord : TEXCOORD0; // texture coordinates | ||
+ | }; | ||
+ | |||
+ | // TransformedVertex holds the transformed data for the vertex | ||
+ | // | ||
+ | struct TransformedVertex { | ||
+ | |||
+ | float4 position : POSITION; // position in homogeneous clip space | ||
+ | float2 texCoord : TEXCOORD; // texture coordinates | ||
+ | float3 worldPos : TEXCOORD1; // position in world space | ||
+ | float3 worldNor : TEXCOORD2; // lighting normal in world space | ||
+ | float3 toViewer : TEXCOORD3; // direction to viewer in world space | ||
+ | }; | ||
+ | |||
+ | // Uniform Data (constant for the stream of vertices) | ||
+ | // | ||
+ | // Geometry | ||
+ | float4x4 viewProjection; // view * projection transformation | ||
+ | float4x4 world; // world transformation | ||
+ | float4 viewPoint; // camera viewpoint for specular calcs | ||
+ | // Geometry | ||
+ | float3 heading; // camera heading for specular calcs | ||
+ | // Lit Vertex | ||
+ | bool litVertex; // omit lighting calculations - already lit | ||
+ | |||
+ | </syntaxhighlight> | ||
+ | : effects.fx - VertexShader | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | // vertexShader receives a raw data for a vertex and transforms that data | ||
+ | // | ||
+ | TransformedVertex vertexShader(RawVertex raw) { | ||
+ | |||
+ | TransformedVertex transformed; // result returned by this function | ||
+ | float4 worldPosition; // world position of the vertex | ||
+ | float3 worldNormal; // vertex normal in world space | ||
+ | |||
+ | // Transform the vertex to homogeneous clip coordinates | ||
+ | // | ||
+ | // A more efficient algorithm would accept the world*view*projection | ||
+ | // tranformation as one uniform matrix and avoid the 2-stage product | ||
+ | // This will require a bit of restructuring of the application code. | ||
+ | // | ||
+ | worldPosition = mul(float4(raw.position, 1.0), world); // local to world | ||
+ | transformed.position = mul(worldPosition, viewProjection); //... to clip | ||
+ | transformed.worldPos = worldPosition.xyz; | ||
+ | |||
+ | // Transform the vertex normal to world space. Only the rotation-scaling | ||
+ | // part of the world transformation is used. Since the world | ||
+ | // transformation may contain scaling, the result of this multiplication | ||
+ | // needs to be normalized. | ||
+ | // | ||
+ | worldNormal = mul(raw.normal, (float3x3)world); | ||
+ | worldNormal = normalize(worldNormal); | ||
+ | transformed.worldNor = worldNormal; | ||
+ | |||
+ | // Determine the direction from the camera's viewpoint to this vertex for | ||
+ | // subsequent lighting calculations | ||
+ | // | ||
+ | transformed.toViewer = normalize(viewPoint - worldPosition.xyz); | ||
+ | |||
+ | // pass the texture coordinates along unaltered | ||
+ | // | ||
+ | transformed.texCoord = raw.texCoord; | ||
+ | |||
+ | return transformed; | ||
+ | } | ||
+ | |||
+ | </syntaxhighlight> | ||
+ | : effects.fx - FragmentShader | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | // The fragment shader receives raw fragment data and returns a pixel colour | ||
+ | // | ||
+ | float4 fragmentShader(RawPixel raw) : COLOR { | ||
+ | |||
+ | float4 colour; // result returned by this function | ||
+ | float3 normal; // normal to the fragment | ||
+ | float3 toViewer; // from fragment to the camera | ||
+ | float3 toLightSource; // from fragment to current light source | ||
+ | |||
+ | // lighting contribution accumulators | ||
+ | float3 ambientLight = ambient.xyz; | ||
+ | float3 diffuseLight = (float3)0; | ||
+ | float3 specularLight = (float3)0; | ||
+ | // lighting calculation factors | ||
+ | float diffuseFactor, reflectFactor, distance; | ||
+ | float attenuationFactor, spotFactor, rho; | ||
+ | |||
+ | // normalize the fragment data | ||
+ | normal = normalize(raw.normal); | ||
+ | toViewer = normalize(raw.toViewer); | ||
+ | |||
+ | // perform calculations for each light in turn | ||
+ | for (int i = 0; i < noLights && i < MLIGHTS; i++) { | ||
+ | if (lightOn[i]) { | ||
+ | float diffuseFactor, reflectFactor, factor; | ||
+ | // diffuse and reflection factors | ||
+ | toLightSource = normalize((light[i].type == POINT_LIGHT)? | ||
+ | light[i].position - raw.position : - light[i].direction); | ||
+ | diffuseFactor = saturate(dot(normal, toLightSource)); | ||
+ | reflectFactor = saturate(dot(normalize(2 * diffuseFactor * | ||
+ | normal - toLightSource), toViewer)); | ||
+ | |||
+ | attenuationFactor = 1.0f; | ||
+ | spotFactor = 1.0f; | ||
+ | |||
+ | if (light[i].type == POINT_LIGHT || | ||
+ | light[i].type == SPOT_LIGHT) { | ||
+ | // detail calcs for attenuationFactor and spotFactor | ||
+ | distance = length(raw.position - light[i].position); | ||
+ | if (distance < light[i].range) { | ||
+ | attenuationFactor = light[i].attenuation.x + | ||
+ | light[i].attenuation.y * distance + | ||
+ | light[i].attenuation.z * distance * distance; | ||
+ | attenuationFactor = 1.0f / attenuationFactor; | ||
+ | if (light[i].type == SPOT_LIGHT) { | ||
+ | rho = saturate(dot(normalize(light[i].position - | ||
+ | float3(raw.position.x, raw.position.y, | ||
+ | raw.position.z)), | ||
+ | normalize(-light[i].direction))); | ||
+ | if (rho <= light[i].spot.x) | ||
+ | spotFactor = 0.0f; | ||
+ | else if (rho <= light[i].spot.y) | ||
+ | spotFactor = pow(abs( | ||
+ | (rho - light[i].spot.x)/ | ||
+ | (light[i].spot.y - light[i].spot.x)), | ||
+ | light[i].spot.z); | ||
+ | } | ||
+ | } | ||
+ | else | ||
+ | attenuationFactor = 0.0f; | ||
+ | } | ||
+ | |||
+ | // accumulate ambient, diffuse, and specular elements of light | ||
+ | // | ||
+ | ambientLight += attenuationFactor * spotFactor * | ||
+ | light[i].ambient.xyz; | ||
+ | diffuseLight += attenuationFactor * spotFactor * diffuseFactor * | ||
+ | light[i].diffuse.xyz; | ||
+ | specularLight += attenuationFactor * spotFactor * | ||
+ | light[i].specular.xyz * pow(reflectFactor, material.power); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // apply material reflectivity to each accumulated element of light | ||
+ | // to obtain the colour of the lit fragment | ||
+ | // | ||
+ | colour.xyz = | ||
+ | saturate(material.ambient.xyz * ambientLight) + | ||
+ | saturate(material.diffuse.xyz * diffuseLight) + | ||
+ | saturate(material.specular.xyz * specularLight); | ||
+ | colour.w = material.diffuse.w; | ||
+ | |||
+ | // apply texture | ||
+ | // | ||
+ | if (texOn) | ||
+ | colour *= tex2D(tex, raw.texcoord); | ||
+ | |||
+ | return colour; | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | : effects.fx - technique opaque | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | technique opaque { | ||
+ | |||
+ | pass { | ||
+ | VertexShader = compile vs_3_0 vertexShader(); | ||
+ | PixelShader = compile ps_3_0 fragmentShader(); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | </syntaxhighlight> | ||
+ | : effects.fx - technique translucent | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | technique translucent { | ||
+ | |||
+ | pass { | ||
+ | VertexShader = compile vs_3_0 vertexShader(); | ||
+ | PixelShader = compile ps_3_0 fragmentShader(); | ||
+ | } | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | </syntaxhighlight> | ||
+ | === To Do === | ||
=== Resources === | === Resources === | ||
− | == Week | + | == Week 8 - Mar 4 == |
=== This Week === | === This Week === | ||
+ | * Frank Luna's notes for DirectX10 (page 306) | ||
+ | :[http://books.google.ca/books?id=AyACb-t8E-MC&pg=PA306&lpg=PA306#v=onepage&q&f=false Environment Maps](Google books) | ||
+ | :[http://libcat.senecac.on.ca/vwebv/holdingsInfo?searchId=19588&recCount=50&recPointer=2&bibId=261220 Environment Maps](Seneca ELibraray) | ||
+ | * Design.cpp | ||
+ | : Design::initialize() - create the skybox object | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | // initialize initializes the general display design coordinator, creates the | ||
+ | // primitive sets, textures, objects, lights, sounds, cameras, and text items | ||
+ | // | ||
+ | void Design::initialize() { | ||
+ | |||
+ | // ... | ||
+ | |||
+ | // create textures | ||
+ | iTexture* sunset = CreateCubeTexture(L"Islands.dds"); | ||
+ | |||
+ | // ... | ||
+ | |||
+ | iObject* skybox = CreateSkybox(); | ||
+ | skybox->rotatex(-1.5708f); | ||
+ | skybox->attach(sunset); | ||
+ | setSkybox(skybox); | ||
+ | |||
+ | // ... | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | * Coordinator.cpp | ||
+ | : Coordinator::render() - using different techniques for different objects | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | void Coordinator::render() { | ||
+ | |||
+ | // adjust framecount and fps | ||
+ | if (now - lastReset <= unitsPerSec) | ||
+ | framecount++; | ||
+ | else { | ||
+ | // recalculate the frame rate | ||
+ | fps = framecount * unitsPerSec / (now - lastReset); | ||
+ | framecount = 0; | ||
+ | lastReset = now; | ||
+ | if (timerText) { | ||
+ | wchar_t str[MAX_DESC + 1]; | ||
+ | sprintf(str, fps, L" fps"); | ||
+ | timerText->set(str); | ||
+ | } | ||
+ | } | ||
+ | // update the user input devices | ||
+ | userInput->update(); | ||
+ | Coordinator::update(); | ||
+ | // update the model | ||
+ | update(); | ||
+ | // update the audio | ||
+ | audio->setVolume(volume); | ||
+ | audio->setFrequencyRatio(frequency); | ||
+ | audio->update(Camera::getView()); | ||
+ | |||
+ | // start rendering | ||
+ | display->beginDrawFrame(Camera::getView()); | ||
+ | display->setAmbientLight(ambient.r, ambient.g, ambient.b); | ||
+ | unsigned nPasses; | ||
+ | // render all of the opaque unlit objects | ||
+ | display->beginEffect("opaque", nPasses); | ||
+ | for (unsigned i = 0; i < nPasses; i++) { | ||
+ | display->beginPass(i); | ||
+ | render(OPAQUE_OBJECT); | ||
+ | display->endPass(); | ||
+ | } | ||
+ | display->endEffect(); | ||
+ | // render all of the translucent unlit objects | ||
+ | display->beginEffect("translucent", nPasses); | ||
+ | for (unsigned i = 0; i < nPasses; i++) { | ||
+ | display->beginPass(i); | ||
+ | render(TRANSLUCENT_OBJECT); | ||
+ | display->endPass(); | ||
+ | } | ||
+ | display->endEffect(); | ||
+ | // render all of the lit objects | ||
+ | display->beginEffect("litObjects", nPasses); | ||
+ | for (unsigned i = 0; i < nPasses; i++) { | ||
+ | display->beginPass(i); | ||
+ | render(LIT_OBJECT); | ||
+ | display->endPass(); | ||
+ | } | ||
+ | display->endEffect(); | ||
+ | // render the skybox | ||
+ | display->beginEffect("skybox", nPasses); | ||
+ | if (background && !skybox) { | ||
+ | Rectf fullScreen(0, 0, 1, 1); | ||
+ | display->beginDrawHUD(0); | ||
+ | background->render(fullScreen, true); | ||
+ | display->endDrawHUD(); | ||
+ | } | ||
+ | else if (skybox) { | ||
+ | for (unsigned i = 0; i < nPasses; i++) { | ||
+ | display->beginPass(i); | ||
+ | render(SKYBOX); | ||
+ | display->endPass(); | ||
+ | } | ||
+ | } | ||
+ | display->endEffect(); | ||
+ | display->set(ALPHA_BLEND, false); | ||
+ | display->beginDrawHUD(HUD_ALPHA); | ||
+ | render(ALL_HUDS); | ||
+ | display->endDrawHUD(); | ||
+ | display->endDrawFrame(); | ||
+ | render(ALL_SOUNDS); | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | : Coordinator::render(iObject*) - render a single object one subset at a time | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | void Coordinator::render(iObject* object) { | ||
+ | |||
+ | display->setWorld(&object->world()); | ||
+ | unsigned nSubsets = object->noSubsets(); | ||
+ | for (unsigned i = 0; i < nSubsets; i++) { | ||
+ | iTexture* texture = object->getTexture(i); | ||
+ | if (texture) texture->attach(); | ||
+ | display->setReflectivity(object->getReflectivity(i)); | ||
+ | object->render(i); | ||
+ | if (texture) texture->detach(); | ||
+ | } | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | * Skybox class | ||
+ | : iObject interface - CreateSkybox declaration | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | class iObject : public Shape, public Base { | ||
+ | public: | ||
+ | // initialization | ||
+ | virtual void attach(iTexture* t) = 0; | ||
+ | virtual void attach(iTexture** t) = 0; | ||
+ | // execution | ||
+ | virtual unsigned noSubsets() const = 0; | ||
+ | virtual void render(unsigned) = 0; | ||
+ | virtual void setTextureFilter(unsigned) = 0; | ||
+ | virtual iTexture* getTexture(unsigned) const = 0; | ||
+ | virtual const void* getReflectivity(unsigned) const = 0; | ||
+ | virtual bool belongsTo(Category category) const = 0; | ||
+ | }; | ||
+ | |||
+ | iObject* CreateObject(iGraphic*, const Reflectivity* = nullptr, unsigned = 1u); | ||
+ | iObject* CreateBillboard(BillboardType, iGraphic*, | ||
+ | const Reflectivity* = nullptr); | ||
+ | iObject* CreateSkybox(); | ||
+ | |||
+ | iObject* Clone(const iObject*); | ||
+ | </syntaxhighlight> | ||
+ | : Skybox class - derived from Object | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | //-------------------------------- Skybox ------------------------------------- | ||
+ | // | ||
+ | // A Skybox is an inverted Object that translates with the viewpoint | ||
+ | // | ||
+ | class Skybox : public Object { | ||
+ | |||
+ | public: | ||
+ | Skybox(); | ||
+ | void render(unsigned); | ||
+ | }; | ||
+ | </syntaxhighlight> | ||
+ | : CreateSkybox | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | iObject* CreateSkybox() { | ||
+ | |||
+ | return new Skybox(); | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | : Skybox::Skybox - SKYBOX category, 2 x 2 x 2 cube | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | Skybox::Skybox() : Object(SKYBOX, CreateIBox(-1, -1, -1 * MODEL_Z_AXIS, 1, 1, | ||
+ | 1 * MODEL_Z_AXIS)) { | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | : Skybox::render(unsigned) - move skybox centroid to current camera position | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | void Skybox::render(unsigned) { | ||
+ | |||
+ | Camera* camera = *(Camera**)(Camera::getCurrent()); | ||
+ | Vector disp = camera->position() - position(); | ||
+ | translate(disp.x, disp.y, disp.z); | ||
+ | |||
+ | Object::render(0); | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | * Texture class | ||
+ | : iTexture interface - CreateCubeTexture declaration | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | class iTexture : public Base { | ||
+ | public: | ||
+ | virtual void attach() const = 0; | ||
+ | virtual void setFilter(unsigned) const = 0; | ||
+ | virtual void detach() = 0; | ||
+ | virtual void render(const Rectf&, bool = false) = 0; | ||
+ | }; | ||
+ | |||
+ | iTexture* CreateTexture(const wchar_t* file, unsigned filter = 0); | ||
+ | iTexture* CreateCubeTexture(const wchar_t* file, unsigned filter = 0); | ||
+ | |||
+ | iTexture* Clone(const iTexture*); | ||
+ | </syntaxhighlight> | ||
+ | : Texture class - add the cube parameter to constructor | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | class Texture : public iTexture { | ||
+ | |||
+ | iAPITexture* apiTexture; // points to the api texture | ||
+ | |||
+ | Texture(const Texture&); | ||
+ | virtual ~Texture(); | ||
+ | |||
+ | public: | ||
+ | Texture(const wchar_t* file, unsigned filter = 0, bool cube = false); | ||
+ | Texture& operator=(const Texture&); | ||
+ | void* clone() const { return new Texture(*this); } | ||
+ | // execution | ||
+ | void attach() const; | ||
+ | void setFilter(unsigned) const; | ||
+ | void detach(); | ||
+ | void render(const Rectf&, bool); | ||
+ | // termination | ||
+ | void suspend(); | ||
+ | void release(); | ||
+ | };</syntaxhighlight> | ||
+ | : CreateCubeTexture - call constructor with true flag for cube | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | iTexture* CreateCubeTexture(const wchar_t* file, unsigned filter) { | ||
+ | |||
+ | return new Texture(file, filter, true); | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | : Texture::Texture() - create APICubeTexture() | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | Texture::Texture(const wchar_t* file, unsigned filter, bool cube) { | ||
+ | |||
+ | coordinator->add(this); | ||
+ | |||
+ | wchar_t* fileWithPath = nullptr; | ||
+ | if (file) { | ||
+ | // add the directory to create the relative filename | ||
+ | int len = strlen(file) + strlen(TEXTURE_DIRECTORY) + 1; | ||
+ | fileWithPath = new wchar_t[len + 1]; | ||
+ | ::nameWithDir(fileWithPath, TEXTURE_DIRECTORY, file, len); | ||
+ | } | ||
+ | |||
+ | // apiTexture on the graphics device | ||
+ | if (cube) | ||
+ | apiTexture = CreateAPICubeTexture(fileWithPath); | ||
+ | else | ||
+ | apiTexture = CreateAPITexture(fileWithPath, filter); | ||
+ | |||
+ | if (fileWithPath) delete [] fileWithPath; | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | : iAPITexture interface - add CreateAPICubeTexture declaration | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | class iAPITexture { | ||
+ | public: | ||
+ | virtual iAPITexture* clone() const = 0; | ||
+ | // execution | ||
+ | virtual void attach() = 0; | ||
+ | virtual void setFilter(unsigned flags) = 0; | ||
+ | virtual void detach() = 0; | ||
+ | virtual void render(const Rectf&, unsigned char, bool = false) = 0; | ||
+ | // termination | ||
+ | virtual void suspend() = 0; | ||
+ | virtual void release() = 0; | ||
+ | virtual void Delete() const = 0; | ||
+ | }; | ||
+ | |||
+ | iAPITexture* CreateAPITexture(const wchar_t* file, unsigned filter); | ||
+ | iAPITexture* CreateAPICubeTexture(const wchar_t* file); | ||
+ | </syntaxhighlight> | ||
+ | * APITexture.h | ||
+ | : APICubeTexture class definition | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | class APICubeTexture : public iAPITexture, public APIBase { | ||
+ | |||
+ | wchar_t* file; // points to file with texture image | ||
+ | unsigned filter; // default texture filtering flags | ||
+ | |||
+ | IDirect3DCubeTexture9* tex; // interface to texture COM object | ||
+ | |||
+ | virtual ~APICubeTexture(); | ||
+ | |||
+ | void setup(); | ||
+ | |||
+ | public: | ||
+ | APICubeTexture(const wchar_t* file); | ||
+ | APICubeTexture(const APICubeTexture&); | ||
+ | iAPITexture& operator=(const APICubeTexture&); | ||
+ | iAPITexture* clone() const { return new APICubeTexture(*this); } | ||
+ | // execution | ||
+ | void attach(); | ||
+ | void setFilter(unsigned filter) {} | ||
+ | void detach(); | ||
+ | void render(const Rectf&, unsigned char, bool) {} | ||
+ | // suspension | ||
+ | void suspend(); | ||
+ | // termination | ||
+ | void release(); | ||
+ | void Delete() const { delete this; } | ||
+ | }; | ||
+ | </syntaxhighlight> | ||
+ | :: [http://msdn.microsoft.com/en-us/library/windows/desktop/bb174329%28v=vs.85%29.aspx IDirect3DCubeTexture9 interface] | ||
+ | : APICubeTexture class implementation | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | //-------------------------------- APICubeTexture ----------------------------- | ||
+ | // | ||
+ | // The APICubeTexture class implements a texture at the API level | ||
+ | // | ||
+ | iAPITexture* CreateAPICubeTexture(const wchar_t* file) { | ||
+ | |||
+ | return new APICubeTexture(file); | ||
+ | } | ||
+ | |||
+ | // constructor initializes the texture identifier | ||
+ | // | ||
+ | APICubeTexture::APICubeTexture(const wchar_t* file) { | ||
+ | |||
+ | if (file) { | ||
+ | int len = strlen(file); | ||
+ | this->file = new wchar_t[len + 1]; | ||
+ | strcpy(this->file, file, len); | ||
+ | } | ||
+ | else | ||
+ | this->file = nullptr; | ||
+ | |||
+ | tex = nullptr; | ||
+ | } | ||
+ | |||
+ | APICubeTexture::APICubeTexture(const APICubeTexture& src) { | ||
+ | |||
+ | file = nullptr; | ||
+ | tex = nullptr; | ||
+ | *this = src; | ||
+ | } | ||
+ | |||
+ | iAPITexture& APICubeTexture::operator=(const APICubeTexture& src) { | ||
+ | |||
+ | if (this != &src) { | ||
+ | if (file) | ||
+ | delete [] file; | ||
+ | if (src.file) { | ||
+ | int len = strlen(src.file); | ||
+ | file = new wchar_t[len + 1]; | ||
+ | strcpy(file, src.file, len); | ||
+ | } | ||
+ | else | ||
+ | file = nullptr; | ||
+ | suspend(); | ||
+ | tex = nullptr; | ||
+ | } | ||
+ | |||
+ | return *this; | ||
+ | } | ||
+ | |||
+ | // setup creates the api texture from the texture file | ||
+ | // | ||
+ | void APICubeTexture::setup() { | ||
+ | |||
+ | // create a texture COM object from the texture file | ||
+ | // | ||
+ | HRESULT hr; | ||
+ | if (file && FAILED(hr = D3DXCreateCubeTextureFromFileEx(d3dd, file, | ||
+ | 0, D3DX_DEFAULT, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, D3DX_DEFAULT, | ||
+ | D3DX_DEFAULT, 0, nullptr, nullptr, &tex))) { | ||
+ | error(L"APICubeTexture::11 Failed to create texture COM object from file"); | ||
+ | tex = nullptr; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // attach attaches the api texture to sampling stage i | ||
+ | // | ||
+ | void APICubeTexture::attach() { | ||
+ | |||
+ | if (!tex) setup(); | ||
+ | |||
+ | if (tex) | ||
+ | d3dd->SetTexture(0, tex); | ||
+ | } | ||
+ | |||
+ | // detach detaches the api texture from sampling stage 0 | ||
+ | // | ||
+ | void APICubeTexture::detach() { | ||
+ | |||
+ | if (tex) | ||
+ | d3dd->SetTexture(0, nullptr); | ||
+ | } | ||
+ | |||
+ | // suspend releases the api texture | ||
+ | // | ||
+ | void APICubeTexture::suspend() { | ||
+ | |||
+ | // release the Interface to the texture COM object | ||
+ | if (tex) { | ||
+ | tex->Release(); | ||
+ | tex = nullptr; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // releases suspends the api texture | ||
+ | // | ||
+ | void APICubeTexture::release() { | ||
+ | |||
+ | suspend(); | ||
+ | } | ||
+ | |||
+ | // destructor releases the api texture | ||
+ | // | ||
+ | APICubeTexture::~APICubeTexture() { | ||
+ | |||
+ | release(); | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | :: [http://msdn.microsoft.com/en-us/library/windows/desktop/bb172757%28v=vs.85%29.aspx D3DXCreateCubeTextureFromFileEx] | ||
+ | * effects.fx | ||
+ | : vertex shader outpu structure | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | //-------------------------------- Skybox ------------------------------------- | ||
+ | // | ||
+ | struct FS_Skybox { | ||
+ | float4 pos : POSITION; | ||
+ | float3 tex : TEXCOORD0; | ||
+ | }; | ||
+ | </syntaxhighlight> | ||
+ | : vertex shader | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | FS_Skybox skyboxVertexShader(float3 pos : POSITION) { | ||
+ | FS_Skybox output = (FS_Skybox) 0; | ||
+ | output.pos = mul(float4(pos, 0), world); // Note the 0, this so the skybox rotates, but without translation | ||
+ | output.pos = mul(output.pos, viewProjection).xyww; // The z coordinate is replaced by w, so that the point is always projected at infinity | ||
+ | output.tex = pos.xzy; // Note that y and z are switched to match the texture coordinate system | ||
+ | return output; | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | : skybox sampler state | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | texture skyBox; | ||
+ | samplerCUBE skySampler = sampler_state { | ||
+ | texture = <skyBox>; | ||
+ | MagFilter = LINEAR; | ||
+ | Minfilter = LINEAR; | ||
+ | Mipfilter = LINEAR; | ||
+ | AddressU = MIRROR; | ||
+ | AddressV = MIRROR; | ||
+ | AddressW = MIRROR; | ||
+ | }; | ||
+ | </syntaxhighlight> | ||
+ | : fragment shader | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | float4 skyboxFragmentShader(FS_Skybox input) : COLOR0 { | ||
+ | return texCUBE(skySampler, input.tex); | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | : skybox technique | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | technique skybox { | ||
+ | |||
+ | pass { | ||
+ | AlphaBlendEnable = false; | ||
+ | ZENABLE = true; | ||
+ | ZWRITEENABLE = false; // By not storing the skybox's z-buffer value, it enables objects behind the skybox to be drawn, giving a realist look (e.g. an airplane in the distance) | ||
+ | CullMode = None; | ||
+ | VertexShader = compile vs_3_0 skyboxVertexShader(); | ||
+ | PixelShader = compile ps_3_0 skyboxFragmentShader(); | ||
+ | } | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
=== To Do === | === To Do === | ||
=== Resources === | === Resources === | ||
− | * [http:// | + | * [http://rbwhitaker.wikidot.com/skyboxes-1 RB Whitaker's notes on skyboxes] |
+ | * [http://knol.google.com/k/3d-skybox# Koen Samyn's knol] | ||
+ | * [http://www.gameengineer.net/samples-graphics.html Game Engineer's demo] | ||
+ | * [http://www.planetside.co.uk/terragen Terragen Texture Tool] | ||
+ | * DirectX Utility - DirectX Texture Tool | ||
+ | |||
− | == Week | + | == Week 9 - Mar 11 == |
=== This Week === | === This Week === | ||
* [http://msdn.microsoft.com/en-us/library/bb147178%28v=VS.85%29.aspx Mathematics of Lighting] | * [http://msdn.microsoft.com/en-us/library/bb147178%28v=VS.85%29.aspx Mathematics of Lighting] | ||
− | |||
* Lighting in Vertex Shaders | * Lighting in Vertex Shaders | ||
** Notation | ** Notation | ||
Line 174: | Line 2,236: | ||
*** [http://msdn.microsoft.com/en-us/library/bb509617%28v=vs.85%29.aspx length()] - length of a vector | *** [http://msdn.microsoft.com/en-us/library/bb509617%28v=vs.85%29.aspx length()] - length of a vector | ||
*** [http://msdn.microsoft.com/en-us/library/bb509645%28v=vs.85%29.aspx saturate()] - clamp scalar, vector, or matrix to [0, 1] | *** [http://msdn.microsoft.com/en-us/library/bb509645%28v=vs.85%29.aspx saturate()] - clamp scalar, vector, or matrix to [0, 1] | ||
+ | * effects.fx - for unlit vertices and fragments | ||
+ | : Uniform data | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | #define MLIGHTS 4 | ||
+ | #define POINT_LIGHT 0 | ||
+ | #define SPOT_LIGHT 1 | ||
+ | #define DIRECTIONAL_LIGHT 2 | ||
+ | |||
+ | // Types | ||
+ | // | ||
+ | // Light holds the data for a single static light in world space | ||
+ | // | ||
+ | struct Light { | ||
+ | int type; // POINT_LIGHT, SPOT_LIGHT, DIRECTIONAL_LIGHT | ||
+ | float3 ambient; | ||
+ | float3 diffuse; | ||
+ | float3 specular; | ||
+ | float3 direction; // in world space | ||
+ | float3 position; // in world space | ||
+ | float3 attenuation; // .xyz for 1.0f/ (.x + .y * d + .z * d * d) | ||
+ | float3 spot; // .x = cos(phi/2), .y = cos(theta/2), .z = falloff | ||
+ | float range; // where attenuation becomes 0 | ||
+ | }; | ||
+ | |||
+ | // Material holds the reflectivity properties of the material | ||
+ | // | ||
+ | struct Material { | ||
+ | float4 ambient; | ||
+ | float4 diffuse; | ||
+ | float4 specular; | ||
+ | float power; | ||
+ | }; | ||
+ | |||
+ | // Uniform Data (constant for the stream of vertices) | ||
+ | // | ||
+ | // Geometry | ||
+ | float4x4 viewProjection; // view * projection transformation | ||
+ | float4x4 world; // world transformation | ||
+ | float4 viewPoint; // camera viewpoint for specular calcs | ||
+ | float3 heading; // camera heading for specular calcs | ||
+ | int noLights; // no of static lights | ||
+ | Light light[MLIGHTS]; // static lights | ||
+ | bool lightOn[MLIGHTS]; // light switch | ||
+ | |||
+ | // Uniform Data (constant for a stream of fragments) | ||
+ | // | ||
+ | float4 ambient; // global ambient light - always on | ||
+ | Material material; // material reflectivity | ||
+ | |||
+ | bool texOn; // texture switch | ||
+ | texture texMap; | ||
+ | sampler2D tex = sampler_state { | ||
+ | texture = <texMap>; | ||
+ | Filter = MIN_MAG_MIP_LINEAR; | ||
+ | AddressU = MIRROR; | ||
+ | AddressV = MIRROR; | ||
+ | }; | ||
+ | </syntaxhighlight> | ||
+ | : Varying Data - simple unlit objects | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | //-------------------------------- Simple Unlit Objects ----------------------- | ||
+ | // | ||
+ | // UnlitInput holds the original data for a vertex in the stream | ||
+ | // | ||
+ | struct UnlitVSInput { | ||
+ | |||
+ | float3 position : POSITION; // position in local space | ||
+ | float3 normal : NORMAL; // normal in local space | ||
+ | float2 texCoord : TEXCOORD0; // texture coordinates | ||
+ | }; | ||
+ | |||
+ | // UnlitOutput holds the transformed data for the vertex | ||
+ | // | ||
+ | struct UnlitVSOutput { | ||
+ | |||
+ | float4 position : POSITION; // position in homogeneous clip space | ||
+ | float2 texCoord : TEXCOORD; // texture coordinates | ||
+ | float3 normal : TEXCOORD1; // lighting normal in world space | ||
+ | float3 toViewer : TEXCOORD2; // direction to viewer in world space | ||
+ | float3 toLight[MLIGHTS] : TEXCOORD3; // light vector | ||
+ | }; | ||
+ | |||
+ | // UnlitFSInput holds the input data for a single fragment of the stream | ||
+ | // | ||
+ | struct UnlitFSInput { | ||
+ | float2 texCoord : TEXCOORD0; // texture coordinate at this fragment | ||
+ | float3 normal : TEXCOORD1; // lighting normal in world space | ||
+ | float3 toViewer : TEXCOORD2; // direction to viewer in world space | ||
+ | float3 toLight[MLIGHTS] : TEXCOORD3; // direction from fragment to light i | ||
+ | }; | ||
+ | </syntaxhighlight> | ||
+ | : Vertex Shader | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | // unlitVShader transforms vertex and lighting data for simple unlit objects | ||
+ | // | ||
+ | UnlitVSOutput unlitVShader(UnlitVSInput input) { | ||
+ | |||
+ | UnlitVSOutput output; // result returned by this function | ||
+ | |||
+ | // Transform the vertex coordinates to homogeneous clip coordinates | ||
+ | // | ||
+ | // A more efficient algorithm would accept the world*view*projection | ||
+ | // tranformation as one uniform matrix and avoid the 2-stage product | ||
+ | // This will require a bit of restructuring of the application code. | ||
+ | // | ||
+ | output.position = mul(float4(input.position, 1.0), world); // local to world | ||
+ | output.position = mul(output.position, viewProjection); //... to clip | ||
+ | |||
+ | // Determine the vector from this vertex to the viewer | ||
+ | output.toViewer = viewPoint.xyz - input.position; | ||
+ | |||
+ | // Determine the vector from this vertex to light source i in local space | ||
+ | // (assumes that light position and direction are specified in local space) | ||
+ | for (int i = 0; i < noLights; i++) | ||
+ | if (light[i].type == DIRECTIONAL_LIGHT) | ||
+ | output.toLight[i] = - light[i].direction; | ||
+ | else | ||
+ | output.toLight[i] = light[i].position - input.position; | ||
+ | |||
+ | // pass the normal and texture coordinates along unaltered | ||
+ | // | ||
+ | output.normal = input.normal; | ||
+ | output.texCoord = input.texCoord; | ||
+ | |||
+ | return output; | ||
+ | } | ||
+ | |||
+ | </syntaxhighlight> | ||
+ | : Fragment Shader | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | // This fragment shader processes lighting for noLights lights in object space | ||
+ | // and returns a pixel colour | ||
+ | // | ||
+ | float4 unlitFShader(UnlitFSInput input) : COLOR { | ||
+ | |||
+ | float4 output; // result returned by this function | ||
+ | float3 normal; // normal to the fragment | ||
+ | float3 toViewer; // from fragment to the camera | ||
+ | float3 toLightSource; // from fragment to current light source | ||
+ | |||
+ | // lighting contribution accumulators | ||
+ | float3 ambientLight = ambient.xyz; | ||
+ | float3 diffuseLight = (float3)0; | ||
+ | float3 specularLight = (float3)0; | ||
+ | // lighting calculation factors | ||
+ | float diffuseFactor, reflectFactor, distance, factor; | ||
+ | float attenuationFactor, spotFactor, rho; | ||
+ | |||
+ | // normalize the fragment input | ||
+ | normal = normalize(input.normal); | ||
+ | toViewer = normalize(input.toViewer); | ||
+ | |||
+ | // perform calculations for each light in turn | ||
+ | for (int i = 0; i < noLights && i < MLIGHTS; i++) { | ||
+ | if (lightOn[i]) { | ||
+ | // diffuse and reflection factors | ||
+ | toLightSource = normalize(input.toLight[i]); | ||
+ | diffuseFactor = saturate(dot(normal, toLightSource)); | ||
+ | reflectFactor = saturate(dot(normalize(2 * diffuseFactor * | ||
+ | normal - toLightSource), toViewer)); | ||
+ | |||
+ | // attenuation | ||
+ | if (light[i].type != DIRECTIONAL_LIGHT) { | ||
+ | distance = length(input.toLight[i]); | ||
+ | if (distance < light[i].range) { | ||
+ | attenuationFactor = light[i].attenuation.x + | ||
+ | light[i].attenuation.y * distance + | ||
+ | light[i].attenuation.z * distance * distance; | ||
+ | attenuationFactor = 1.0f / attenuationFactor; | ||
+ | } | ||
+ | else | ||
+ | attenuationFactor = 0.0f; | ||
+ | } | ||
+ | else | ||
+ | attenuationFactor = 1.0f; | ||
+ | |||
+ | // spot | ||
+ | if (light[i].type == SPOT_LIGHT) { | ||
+ | rho = saturate(dot(toLightSource, | ||
+ | normalize(-light[i].direction))); | ||
+ | if (rho <= light[i].spot.x) | ||
+ | spotFactor = 0.0f; | ||
+ | else if (rho <= light[i].spot.y) | ||
+ | spotFactor = pow(abs( | ||
+ | (rho - light[i].spot.x)/ | ||
+ | (light[i].spot.y - light[i].spot.x)), | ||
+ | light[i].spot.z); | ||
+ | else | ||
+ | spotFactor = 1.0f; | ||
+ | } | ||
+ | else | ||
+ | spotFactor = 1.0; | ||
+ | |||
+ | factor = attenuationFactor * spotFactor; | ||
+ | |||
+ | // accumulate ambient, diffuse, and specular elements of light i | ||
+ | ambientLight += factor * light[i].ambient.xyz; | ||
+ | diffuseLight += factor * diffuseFactor * light[i].diffuse.xyz; | ||
+ | specularLight += factor * light[i].specular.xyz * | ||
+ | pow(reflectFactor, material.power); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // apply material reflectivity to each accumulated element of light | ||
+ | // to obtain the colour of the lit fragment | ||
+ | // | ||
+ | output.xyz = | ||
+ | saturate(material.ambient.xyz * ambientLight) + | ||
+ | saturate(material.diffuse.xyz * diffuseLight) + | ||
+ | saturate(material.specular.xyz * specularLight); | ||
+ | output.w = material.diffuse.w; | ||
+ | |||
+ | // sample the texture | ||
+ | // | ||
+ | if (texOn) | ||
+ | output *= tex2D(tex, input.texCoord); | ||
+ | |||
+ | return output; | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | : techniques - opaque and translucent | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | technique opaque { | ||
+ | |||
+ | pass { | ||
+ | AlphaBlendEnable = false; | ||
+ | VertexShader = compile vs_3_0 unlitVShader(); | ||
+ | PixelShader = compile ps_3_0 unlitFShader(); | ||
+ | } | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | <syntaxhighlight lang="cpp"> | ||
+ | technique translucent { | ||
+ | |||
+ | pass { | ||
+ | AlphaBlendEnable = true; | ||
+ | VertexShader = compile vs_3_0 unlitVShader(); | ||
+ | PixelShader = compile ps_3_0 unlitFShader(); | ||
+ | } | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
=== To Do === | === To Do === | ||
− | + | === Resources === | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | == Week | + | == Week 10 - Mar 18 == |
=== This Week === | === This Week === | ||
+ | * Texturing - Identification | ||
+ | : connection between Design.cpp and effects.fx | ||
+ | ** iTexture.h | ||
+ | :: add texture and texture state identifier | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | iTexture* CreateTexture(const wchar_t* file, const char* str, | ||
+ | const char* isOn); | ||
+ | </syntaxhighlight> | ||
+ | ** Texture.h | ||
+ | : | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | Texture(const wchar_t*, const char*, const char*); | ||
+ | </syntaxhighlight> | ||
+ | ** Texture.cpp | ||
+ | : | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | // constructor initializes the texture identifier | ||
+ | // | ||
+ | Texture::Texture(const wchar_t* file, const char* str, | ||
+ | const char* isOn) : filter(0u) { | ||
+ | |||
+ | if (file) { | ||
+ | int len = strlen(file); | ||
+ | this->file = new wchar_t[len + 1]; | ||
+ | strcpy(this->file, file, len); | ||
+ | } | ||
+ | else | ||
+ | this->file = nullptr; | ||
+ | |||
+ | tex = nullptr; | ||
+ | #if PIPELINE == FIXED_FUNCTION | ||
+ | #elif PIPELINE == PROGRAMMABLE | ||
+ | #elif PIPELINE == PROGRAMMABLE_EFFECT | ||
+ | textureHandle = effect->GetParameterByName(0, str); | ||
+ | textureonHandle = effect->GetParameterByName(0, isOn); | ||
+ | #endif | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | : | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | effect->SetTexture(textureHandle, tex); | ||
+ | effect->SetBool(textureonHandle, true); | ||
+ | </syntaxhighlight> | ||
+ | ** iAPITexture.h | ||
+ | :: add texture and texture state identifier | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | iAPITexture* CreateAPITexture(const wchar_t* file, const char* str, | ||
+ | const char* isOn); | ||
+ | </syntaxhighlight> | ||
+ | ** APITexture.h | ||
+ | : | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | D3DXHANDLE textureHandle; // points to texture | ||
+ | D3DXHANDLE textureonHandle; // points to texture on status | ||
+ | public: | ||
+ | APITexture(const wchar_t*, const char*, const char*); | ||
+ | </syntaxhighlight> | ||
+ | ** APITexture.cpp | ||
+ | : | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | // constructor initializes the texture identifier | ||
+ | // | ||
+ | APITexture::APITexture(const wchar_t* file, const char* str, | ||
+ | const char* isOn, AddressMode m) : filter(0u), mode(m) { | ||
+ | |||
+ | if (file) { | ||
+ | int len = strlen(file); | ||
+ | this->file = new wchar_t[len + 1]; | ||
+ | strcpy(this->file, file, len); | ||
+ | } | ||
+ | else | ||
+ | this->file = nullptr; | ||
+ | |||
+ | tex = nullptr; | ||
+ | target = nullptr; | ||
+ | #if PIPELINE == FIXED_FUNCTION | ||
+ | #elif PIPELINE == PROGRAMMABLE | ||
+ | #elif PIPELINE == PROGRAMMABLE_EFFECT | ||
+ | textureHandle = effect->GetParameterByName(0, str); | ||
+ | textureonHandle = effect->GetParameterByName(0, isOn); | ||
+ | #endif | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | : | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | effect->SetTexture(textureHandle, tex); | ||
+ | effect->SetBool(textureonHandle, true); | ||
+ | </syntaxhighlight> | ||
+ | * Texturing - Tiling | ||
+ | : [http://msdn.microsoft.com/en-us/library/windows/desktop/bb206245%28v=vs.85%29.aspx Texture Coordinates] | ||
+ | ** iGraphic.h | ||
+ | :: add u v parameters, both of which default to 1 | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | iGraphic* CreateBox(float, float, float, float, float, float, | ||
+ | float = 1, float = 1); | ||
+ | iGraphic* CreateIBox(float, float, float, float, float, float, | ||
+ | float = 1, float = 1); | ||
+ | iGraphic* CreateRectangleList(float, float, float, float, | ||
+ | float = 1, float = 1); | ||
+ | iGraphic* CreateIRectangleList(float, float, float, float, | ||
+ | float = 1, float = 1); | ||
+ | iGraphic* CreateMeshBox(float, float, float, float, float, | ||
+ | float, float = 1, float = 1); | ||
+ | </syntaxhighlight> | ||
+ | ** Graphic.cpp | ||
+ | :: extend add functions to include u v parameters | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | //-------------------------------- Graphic Structures ------------------------- | ||
+ | // | ||
+ | // prototypes for add() function used by the Create...() functions | ||
+ | void add(VertexList<Vertex>*, const Vector&, const Vector&, const Vector&, | ||
+ | const Vector&, const Vector&, float = 1, float = 1); | ||
+ | void add(IndexedList<Vertex>*, const Vector&, const Vector&, const Vector&, | ||
+ | const Vector&, const Vector&, float = 1, float = 1); | ||
+ | void add(CustomMesh<Vertex>*, const Vector&, const Vector&, const Vector&, | ||
+ | const Vector&, const Vector&, float = 1, float = 1); | ||
+ | |||
+ | // CreateBox builds a triangle vertex list for a brick-like box from two | ||
+ | // extreme points one face at a time with all faces having the same attributes | ||
+ | // | ||
+ | iGraphic* CreateBox(float minx, float miny, float minz, float maxx, | ||
+ | float maxy, float maxz, float u, float v) { | ||
+ | |||
+ | VertexList<Vertex>* vertexList = | ||
+ | (VertexList<Vertex>*)CreateVertexList<Vertex>(TRIANGLE_LIST, 12); | ||
+ | |||
+ | float x = (minx + maxx) / 2; | ||
+ | float y = (miny + maxy) / 2; | ||
+ | float z = (minz + maxz) / 2; | ||
+ | minx -= x; | ||
+ | miny -= y; | ||
+ | minz -= z; | ||
+ | maxx -= x; | ||
+ | maxy -= y; | ||
+ | maxz -= z; | ||
+ | // bounding sphere | ||
+ | float max; | ||
+ | max = maxx > maxy ? maxx : maxy; | ||
+ | max = maxz > max ? maxz : max; | ||
+ | vertexList->setRadius(1.73205f * max); | ||
+ | // locate centroid at origin | ||
+ | Vector p1 = Vector(minx, miny, minz), | ||
+ | p2 = Vector(minx, maxy, minz), | ||
+ | p3 = Vector(maxx, maxy, minz), | ||
+ | p4 = Vector(maxx, miny, minz), | ||
+ | p5 = Vector(minx, miny, maxz), | ||
+ | p6 = Vector(minx, maxy, maxz), | ||
+ | p7 = Vector(maxx, maxy, maxz), | ||
+ | p8 = Vector(maxx, miny, maxz); | ||
+ | add(vertexList, p1, p2, p3, p4, Vector(0, 0, -1), u, v); // front | ||
+ | add(vertexList, p4, p3, p7, p8, Vector(1, 0, 0), u, v); // right | ||
+ | add(vertexList, p8, p7, p6, p5, Vector(0, 0, 1), u, v); // back | ||
+ | add(vertexList, p6, p2, p1, p5, Vector(-1, 0, 0), u, v); // left | ||
+ | add(vertexList, p1, p4, p8, p5, Vector(0, -1, 0), u, v); // bottom | ||
+ | add(vertexList, p2, p6, p7, p3, Vector(0, 1, 0), u, v); // top | ||
+ | |||
+ | return vertexList; | ||
+ | } | ||
+ | |||
+ | iGraphic* CreateIBox(float minx, float miny, float minz, float maxx, | ||
+ | float maxy, float maxz, float u, float v) { | ||
+ | |||
+ | IndexedList<Vertex>* indexedList = (IndexedList<Vertex>*) | ||
+ | CreateIndexedList<Vertex, unsigned short>(TRIANGLE_LIST, 12); | ||
+ | |||
+ | float x = (minx + maxx) / 2; | ||
+ | float y = (miny + maxy) / 2; | ||
+ | float z = (minz + maxz) / 2; | ||
+ | minx -= x; | ||
+ | miny -= y; | ||
+ | minz -= z; | ||
+ | maxx -= x; | ||
+ | maxy -= y; | ||
+ | maxz -= z; | ||
+ | // bounding sphere | ||
+ | float max; | ||
+ | max = maxx > maxy ? maxx : maxy; | ||
+ | max = maxz > max ? maxz : max; | ||
+ | indexedList->setRadius(1.73205f * max); | ||
+ | // locate centroid at origin | ||
+ | Vector p1 = Vector(minx, miny, minz), | ||
+ | p2 = Vector(minx, maxy, minz), | ||
+ | p3 = Vector(maxx, maxy, minz), | ||
+ | p4 = Vector(maxx, miny, minz), | ||
+ | p5 = Vector(minx, miny, maxz), | ||
+ | p6 = Vector(minx, maxy, maxz), | ||
+ | p7 = Vector(maxx, maxy, maxz), | ||
+ | p8 = Vector(maxx, miny, maxz); | ||
+ | add(indexedList, p1, p2, p3, p4, Vector(0, 0, -1), u, v); // front | ||
+ | add(indexedList, p4, p3, p7, p8, Vector(1, 0, 0), u, v); // right | ||
+ | add(indexedList, p8, p7, p6, p5, Vector(0, 0, 1), u, v); // back | ||
+ | add(indexedList, p6, p2, p1, p5, Vector(-1, 0, 0), u, v); // left | ||
+ | add(indexedList, p1, p4, p8, p5, Vector(0, -1, 0), u, v); // bottom | ||
+ | add(indexedList, p2, p6, p7, p3, Vector(0, 1, 0), u, v); // top | ||
+ | |||
+ | return indexedList; | ||
+ | } | ||
+ | |||
+ | // CreateRectangleList builds a triangle list in the x-y plane from its two | ||
+ | // extreme points | ||
+ | // | ||
+ | iGraphic* CreateRectangleList(float minx, float miny, float maxx, float maxy, | ||
+ | float u, float v) { | ||
+ | |||
+ | VertexList<Vertex>* vertexList = | ||
+ | (VertexList<Vertex>*)CreateVertexList<Vertex>(TRIANGLE_LIST, 2); | ||
+ | |||
+ | float x = (minx + maxx) / 2, y = (miny + maxy) / 2; | ||
+ | minx -= x; | ||
+ | miny -= y; | ||
+ | maxx -= x; | ||
+ | maxy -= y; | ||
+ | // bounding sphere | ||
+ | float max; | ||
+ | max = maxx > maxy ? maxx : maxy; | ||
+ | vertexList->setRadius(1.73205f * max); | ||
+ | // locate centroid at origin | ||
+ | Vector p1 = Vector(minx, miny, 0), | ||
+ | p2 = Vector(minx, maxy, 0), | ||
+ | p3 = Vector(maxx, maxy, 0), | ||
+ | p4 = Vector(maxx, miny, 0); | ||
+ | add(vertexList, p1, p2, p3, p4, Vector(0, 0, -1), u, v); | ||
+ | |||
+ | return vertexList; | ||
+ | } | ||
+ | |||
+ | // CreateIRectangleList builds an indexed triangle list in the x-y plane from | ||
+ | // its two extreme points | ||
+ | // | ||
+ | iGraphic* CreateIRectangleList(float minx, float miny, float maxx, float maxy, | ||
+ | float u, float v) { | ||
+ | |||
+ | IndexedList<Vertex>* indexedList = (IndexedList<Vertex>*) | ||
+ | CreateIndexedList<Vertex, unsigned short>(TRIANGLE_LIST, 2); | ||
+ | |||
+ | float x = (minx + maxx) / 2, y = (miny + maxy) / 2; | ||
+ | minx -= x; | ||
+ | miny -= y; | ||
+ | maxx -= x; | ||
+ | maxy -= y; | ||
+ | // bounding sphere | ||
+ | float max; | ||
+ | max = maxx > maxy ? maxx : maxy; | ||
+ | indexedList->setRadius(1.73205f * max); | ||
+ | // locate centroid at origin | ||
+ | Vector p1 = Vector(minx, miny, 0), | ||
+ | p2 = Vector(minx, maxy, 0), | ||
+ | p3 = Vector(maxx, maxy, 0), | ||
+ | p4 = Vector(maxx, miny, 0); | ||
+ | add(indexedList, p1, p2, p3, p4, Vector(0, 0, -1), u, v); | ||
+ | |||
+ | return indexedList; | ||
+ | } | ||
+ | |||
+ | // CreateIRectangleList builds an indexed triangle list in the x-y plane from | ||
+ | // its two extreme points | ||
+ | // | ||
+ | iGraphic* CreateBRectangleList(float minx, float miny, float maxx, float maxy, | ||
+ | float u, float v) { | ||
+ | |||
+ | IndexedList<BumpVertex>* indexedList = (IndexedList<BumpVertex>*) | ||
+ | CreateIndexedList<BumpVertex, unsigned short>(TRIANGLE_LIST, 2); | ||
+ | |||
+ | float x = (minx + maxx) / 2, y = (miny + maxy) / 2; | ||
+ | minx -= x; | ||
+ | miny -= y; | ||
+ | maxx -= x; | ||
+ | maxy -= y; | ||
+ | // bounding sphere | ||
+ | float max; | ||
+ | max = maxx > maxy ? maxx : maxy; | ||
+ | indexedList->setRadius(1.73205f * max); | ||
+ | // locate centroid at origin | ||
+ | Vector p1 = Vector(minx, miny, 0), | ||
+ | p2 = Vector(minx, maxy, 0), | ||
+ | p3 = Vector(maxx, maxy, 0), | ||
+ | p4 = Vector(maxx, miny, 0); | ||
+ | add(indexedList, p1, p2, p3, p4, Vector(0, 0, -1), u, v); | ||
+ | |||
+ | return indexedList; | ||
+ | } | ||
+ | |||
+ | // CreateMeshBox builds a mesh for a brick-like box from two extreme points one | ||
+ | // face at a time with each faces having distinct attributes | ||
+ | // | ||
+ | iGraphic* CreateMeshBox(float minx, float miny, float minz, float maxx, | ||
+ | float maxy, float maxz, float u, float v) { | ||
+ | |||
+ | unsigned attribute[] = {0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5}; | ||
+ | |||
+ | CustomMesh<>* mesh = (CustomMesh<>*) | ||
+ | CreateCustomMesh<Vertex, unsigned short>(attribute, 12, 36, 24, 6); | ||
+ | |||
+ | float x = (minx + maxx) / 2, y = (miny + maxy) / 2, z = (minz + maxz) / 2; | ||
+ | minx -= x; | ||
+ | miny -= y; | ||
+ | minz -= z; | ||
+ | maxx -= x; | ||
+ | maxy -= y; | ||
+ | maxz -= z; | ||
+ | // bounding sphere | ||
+ | float max; | ||
+ | max = maxx > maxy ? maxx : maxy; | ||
+ | max = maxz > max ? maxz : max; | ||
+ | mesh->setRadius(1.73205f * max); | ||
+ | // one face at a time | ||
+ | Vector p1 = Vector(minx, miny, minz), | ||
+ | p2 = Vector(minx, maxy, minz), | ||
+ | p3 = Vector(maxx, maxy, minz), | ||
+ | p4 = Vector(maxx, miny, minz), | ||
+ | p5 = Vector(minx, miny, maxz), | ||
+ | p6 = Vector(minx, maxy, maxz), | ||
+ | p7 = Vector(maxx, maxy, maxz), | ||
+ | p8 = Vector(maxx, miny, maxz); | ||
+ | add(mesh, p1, p2, p3, p4, Vector(0, 0, -1), u, v); // front | ||
+ | add(mesh, p4, p3, p7, p8, Vector(1, 0, 0), u, v); // right | ||
+ | add(mesh, p8, p7, p6, p5, Vector(0, 0, 1), u, v); // back | ||
+ | add(mesh, p6, p2, p1, p5, Vector(-1, 0, 0), u, v); // left | ||
+ | add(mesh, p1, p4, p8, p5, Vector(0, -1, 0), u, v); // bottom | ||
+ | add(mesh, p2, p6, p7, p3, Vector(0, 1, 0), u, v); // top | ||
+ | |||
+ | return mesh; | ||
+ | } | ||
+ | |||
+ | void add(VertexList<Vertex>* vertexList, const Vector& p1, const Vector& p2, | ||
+ | const Vector& p3, const Vector& p4, const Vector& n, float u, float v) { | ||
+ | |||
+ | vertexList->add(Vertex(p1, n, 0, v)); | ||
+ | vertexList->add(Vertex(p2, n, 0, 0)); | ||
+ | vertexList->add(Vertex(p3, n, u, 0)); | ||
+ | vertexList->add(Vertex(p1, n, 0, v)); | ||
+ | vertexList->add(Vertex(p3, n, u, 0)); | ||
+ | vertexList->add(Vertex(p4, n, u, v)); | ||
+ | } | ||
+ | |||
+ | void add(IndexedList<Vertex>* indexedList, const Vector& p1, const Vector& p2, | ||
+ | const Vector& p3, const Vector& p4, const Vector& n, float u, float v) { | ||
+ | |||
+ | unsigned v1 = indexedList->add(Vertex(p1, n, 0, v)); | ||
+ | unsigned v2 = indexedList->add(Vertex(p2, n, 0, 0)); | ||
+ | unsigned v3 = indexedList->add(Vertex(p3, n, u, 0)); | ||
+ | unsigned v4 = indexedList->add(Vertex(p4, n, u, v)); | ||
+ | indexedList->add(v1); | ||
+ | indexedList->add(v2); | ||
+ | indexedList->add(v3); | ||
+ | indexedList->add(v1); | ||
+ | indexedList->add(v3); | ||
+ | indexedList->add(v4); | ||
+ | } | ||
+ | |||
+ | void add(CustomMesh<Vertex>* mesh, const Vector& p1, const Vector& p2, | ||
+ | const Vector& p3, const Vector& p4, const Vector& n, float u, float v) { | ||
+ | |||
+ | unsigned v1 = mesh->add(Vertex(p1, n, 0, v)); | ||
+ | unsigned v2 = mesh->add(Vertex(p2, n, 0, 0)); | ||
+ | unsigned v3 = mesh->add(Vertex(p3, n, u, 0)); | ||
+ | unsigned v4 = mesh->add(Vertex(p4, n, u, v)); | ||
+ | mesh->add(v1); | ||
+ | mesh->add(v2); | ||
+ | mesh->add(v3); | ||
+ | mesh->add(v1); | ||
+ | mesh->add(v3); | ||
+ | mesh->add(v4); | ||
+ | } | ||
+ | |||
+ | </syntaxhighlight> | ||
+ | * Texturing - Address Modes | ||
+ | : [http://msdn.microsoft.com/en-us/library/windows/desktop/bb206239%28v=vs.85%29.aspx Direct3D9 Addressing Modes] | ||
+ | ** APITexture.h | ||
+ | : | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | D3DXHANDLE textureMode; // points to texture mode | ||
+ | </syntaxhighlight> | ||
+ | ** APITexture.cpp | ||
+ | : | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | effect->SetInt(textureMode, (int)mode); | ||
+ | </syntaxhighlight> | ||
+ | ** effects.fx - uniform variables | ||
+ | :: | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | // Addressing Modes | ||
+ | #define TEX_WRAP 0 | ||
+ | #define TEX_CLAMP 1 | ||
+ | #define TEX_MIRROR 2 | ||
+ | |||
+ | //... | ||
+ | |||
+ | bool tex_1_On; // texture switch | ||
+ | int tex_mode; // addressing mode | ||
+ | texture tex_1; | ||
+ | |||
+ | sampler2D tex_1_ww = sampler_state { | ||
+ | texture = <tex_1>; | ||
+ | Filter = MIN_MAG_MIP_LINEAR; | ||
+ | AddressU = WRAP; | ||
+ | AddressV = WRAP; | ||
+ | }; | ||
+ | |||
+ | sampler2D tex_1_mm = sampler_state { | ||
+ | texture = <tex_1>; | ||
+ | Filter = MIN_MAG_MIP_LINEAR; | ||
+ | AddressU = MIRROR; | ||
+ | AddressV = MIRROR; | ||
+ | }; | ||
+ | |||
+ | sampler2D tex_1_cc = sampler_state { | ||
+ | texture = <tex_1>; | ||
+ | Filter = MIN_MAG_MIP_LINEAR; | ||
+ | AddressU = CLAMP; | ||
+ | AddressV = CLAMP; | ||
+ | }; | ||
+ | |||
+ | </syntaxhighlight> | ||
+ | ** effects.fx - fragment shader | ||
+ | : | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | // sample the texture | ||
+ | // | ||
+ | if (tex_1_On) { | ||
+ | if (tex_mode == TEX_CLAMP) | ||
+ | output *= tex2D(tex_1_cc, input.texCoord); | ||
+ | else if (tex_mode == TEX_WRAP) | ||
+ | output *= tex2D(tex_1_ww, input.texCoord); | ||
+ | else if (tex_mode == TEX_MIRROR) | ||
+ | output *= tex2D(tex_1_mm, input.texCoord); | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | * Texturing - Multi-Texturing | ||
+ | ** effects.fx - uniform variables | ||
+ | : | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | bool tex_2_On; // texture switch | ||
+ | texture tex_2; | ||
+ | |||
+ | sampler2D tex_2_ww = sampler_state { | ||
+ | texture = <tex_2>; | ||
+ | Filter = MIN_MAG_MIP_LINEAR; | ||
+ | AddressU = WRAP; | ||
+ | AddressV = WRAP; | ||
+ | }; | ||
+ | |||
+ | sampler2D tex_2_mm = sampler_state { | ||
+ | texture = <tex_2>; | ||
+ | Filter = MIN_MAG_MIP_LINEAR; | ||
+ | AddressU = MIRROR; | ||
+ | AddressV = MIRROR; | ||
+ | }; | ||
+ | |||
+ | sampler2D tex_2_cc = sampler_state { | ||
+ | texture = <tex_2>; | ||
+ | Filter = MIN_MAG_MIP_LINEAR; | ||
+ | AddressU = CLAMP; | ||
+ | AddressV = CLAMP; | ||
+ | }; | ||
+ | |||
+ | </syntaxhighlight> | ||
+ | ** effects.fx - fragment shader | ||
+ | : | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | if (tex_2_On) { | ||
+ | if (tex_mode == TEX_CLAMP) | ||
+ | output *= tex2D(tex_2_cc, input.texCoord); | ||
+ | else if (tex_mode == TEX_WRAP) | ||
+ | output *= tex2D(tex_2_ww, input.texCoord); | ||
+ | else if (tex_mode == TEX_MIRROR) | ||
+ | output *= tex2D(tex_2_mm, input.texCoord); | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
=== To Do === | === To Do === | ||
=== Resources === | === Resources === | ||
− | == Week | + | <!-- |
+ | == Week 11 - Mar 25 == | ||
=== This Week === | === This Week === | ||
+ | |||
+ | <syntaxhighlight lang="cpp"> | ||
+ | </syntaxhighlight> | ||
+ | |||
=== To Do === | === To Do === | ||
=== Resources === | === Resources === | ||
+ | --> | ||
+ | <!-- | ||
+ | == Week 12 - Apr 1 == | ||
+ | === This Week === | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | </syntaxhighlight> | ||
− | |||
− | |||
=== To Do === | === To Do === | ||
=== Resources === | === Resources === | ||
--> | --> |
Latest revision as of 08:00, 21 March 2012
GAM670/DPS905 | Weekly Schedule | Student List | Project Requirements | Teams and their Projects | Student Resources
Contents
GAM670/DPS905 -- Weekly Schedule 20121
Week 1 - Jan 8
This Week
- Assignment Discussion
- Suggested Enhancements
- Review of the Base Code
- Definition of a Framework
- Modularity through stable interfaces
- Re-usability through generic components
- Extensibility through hook methods
- Inversion of control - determines which application methods to invoke in response to external events
- Framework Architecture
- Modelling Layer
- API Translation Layer
- Notable Features of the Base Code
- camera, sound, and light are also derived from the Frame class
- textures attach at the object level
- texture connection is uncoupled from drawing of the graphics primitives
- reference frames are relative
- very simple collision detection
- Definition of a Framework
To Do
- add your name to the student list
- create a team page that includes the semester number 20121
- describe the game that you intend to develop
- list the topics of interest to your team in developing its game
- list the other topics of interest
Resources
Week 2 - Jan 16
This Week
- Relative Reference Frames
- Recursive calls
- Vector Frame::position()
- Matrix Frame::rotation()
- Matrix Frame::world()
- Detaching from and attaching to a parent frame
- Frame::attachTo()
- Recursive calls
- Geometry
- Plane
- normal + constant - examples
- equation of a plane: dot(n, x) + D = 0
- positive side of a plane dot(n, x) + D > 0
- Plane
- Collision Detection
- types of colliders
- spheres
- planes
- axis-aligned bounding boxes
- oriented bounding boxes
- types of colliders
To Do
- Research the feature that you are going to add and prepare a plan of action
- Prepare a team page for your team so that repos can be ordered
- Add a section to your team page to track your project and solicit commentary
Resources
Week 3 - Jan 23
This Week
- Collision Detection (cont'd)
- Shape
- Shape : Frame
- Shape::setRadius()
- Shape::getRadius()
- Shape::setRadius(float r);
- Shape::setRadius(float x, float y, float z);
- Shape::getRadius() const { return radius; }
- Shape::setPlane(Vector n, float d);
- Shape::setAxisAligned(Vector min, Vector max);
- Shape
- Comprehensive Camerawork
- rotation about an axis
- order of rotation matters
- Euler angles
- gimbal lock
- complex numbers
- solution of cubic equations 16th century
- two-dimensional representation
- matrix representation
- quaternions
- extension of complex numbers
- four-dimensional representation
- matrix representation
- geometric algebra (more abstract)
- Visibility Determination
- test a point for presence within a set of planes
- normal calculations - general rotation matrix - vector and angle
- ViewFrustum
- parameter - view * projection
- 6 planes
- near and far planes
- left and right planes
- top and bottom planes
- coding
- constructor
- ViewFrustum::contains()
- Finite Size of Objects
- Expansion of the View Frustum
- Index Buffers
- amount of storage needed for vertex data
- duplication of vertex data
- indexing
- indexed primitives
To Do
Resources
- Wikipedia on Complex Numbers
- Wikipedia on Quaternions
- Wikipedia on quaternions and Spatial Rotations
- Wolfram on Quaternions
- CProgramming.com on Quaternions
- Ogre intro on Quaternions
- collision sample
- indexBuffering sample
Week 4 - Jan 30
This Week
- Meshes
- What is a mesh?
- vertex list -> vertex buffer
- index list -> index buffer
- attribute list -> subset to which primitives belong
- pyramid sample
- Stock Objects
- Sphere
- slices and partitions
- Cylinder
- Torus
- Utah Teapot
- APIGraphic.h and .cpp code
- Sphere
- Custom Mesh
- Create a Mesh
- APIGraphic.h code
- What is a mesh?
template <class T = Vertex, class I = Index>
class APICustomMesh : public iAPIGraphic, public APIBase {
unsigned nSubsets; // number of subsets
unsigned nPrimitives; // number of primitives
unsigned* attribute; // points to mesh's attribute list
I* index; // points to mesh's array of indices
unsigned iIndex; // number of indices currently saved
unsigned nIndices; // number of indices in the mesh
T* vertex; // points to mesh's array of vertices
unsigned iVertex; // number of vertices currently saved
unsigned nVertices; // number of vertices in the mesh
LPD3DXMESH apiMesh; // set of vertices, indices
protected:
virtual ~APICustomMesh();
void setup();
public:
APICustomMesh(unsigned*, int, int, int, int);
APICustomMesh(const APICustomMesh& v);
APICustomMesh& operator=(const APICustomMesh& v);
APICustomMesh* clone() const { return new APICustomMesh(*this); }
unsigned add(const T& v);
void add(unsigned);
void draw(unsigned);
void suspend();
void release() { suspend(); }
void Delete() const { delete this; }
};
- APIGraphic.cpp - APIGraphic
template <class T, class I>
APICustomMesh<T, I>::APICustomMesh(unsigned* a, int np, int ni, int nv, int ns)
: nSubsets(ns), nPrimitives(np), nIndices(ni), nVertices(nv), iIndex(0),
iVertex(0) {
attribute = new unsigned[nPrimitives];
for (unsigned i = 0; i < nPrimitives; i++)
attribute[i] = a[i];
index = new I[nIndices];
vertex = new T[nVertices];
apiMesh = nullptr;
}
- APIGraphic.cpp - add()
template <class T, class I>
unsigned APICustomMesh<T, I>::add(const T& v) {
unsigned i = iVertex;
if (vertex && iVertex < nVertices)
vertex[iVertex++] = v;
return i;
}
template <class T, class I>
void APICustomMesh<T, I>::add(unsigned v) {
if (index && iIndex < nIndices)
index[iIndex++] = v;
}
- APIGraphic.cpp - setup()
template <class T, class I>
void APICustomMesh<T, I>::setup() {
T *pv;
I *pi;
DWORD *pa;
nIndices = iIndex;
nVertices = iVertex;
// create an empty mesh and lock its buffers
if (FAILED(D3DXCreateMesh(nPrimitives, nVertices, 0,
APIVertexDeclaration<T>::format(), d3dd, &apiMesh))) {
error(L"APIMesh::14 Couldn\'t create the empty mesh");
apiMesh = nullptr;
}
else if (FAILED(apiMesh->LockVertexBuffer(0, (void**)&pv))) {
error(L"APIMesh::15 Couldn\'t lock vertex buffer");
release();
}
else if (FAILED(apiMesh->LockIndexBuffer(0, (void**)&pi))) {
error(L"APIMesh::16 Couldn\'t lock index buffer");
release();
}
else if (FAILED(apiMesh->LockAttributeBuffer(0, &pa))) {
error(L"APIMesh::17 Couldn\'t lock attribute buffer");
release();
}
else {
// populate the newly created Vertex Buffer
for (unsigned i = 0; i < nVertices; i++)
vertex[i].populate((void**)&pv);
apiMesh->UnlockVertexBuffer();
// populate the newly created Index Buffer
for (unsigned i = 0; i < nIndices; i++)
pi[i] = index[i];
apiMesh->UnlockIndexBuffer();
// Populate the newly created Attribute Buffer
for (unsigned i = 0; i < nPrimitives; i++)
pa[i] = attribute[i];
apiMesh->UnlockAttributeBuffer();
}
}
- APIGraphic.cpp - DrawSubset()
template <class T, class I>
void APICustomMesh<T, I>::draw(unsigned iSubset) {
// if mesh doesn't exist, set it up first
if (!apiMesh) setup();
if (apiMesh)
apiMesh->DrawSubset(iSubset);
}
- DrawIndexedPrimitive parameters
- APIGraphic.cpp - suspend()
template <class T, class I>
void APICustomMesh<T, I>::suspend() {
// release the interface to the mesh
if (apiMesh) {
apiMesh->Release();
apiMesh = nullptr;
}
}
- APIGraphic.cpp - ~APIGraphic
template <class T, class I>
APICustomMesh<T, I>::~APICustomMesh() {
release();
if (attribute)
delete [] attribute;
if (index)
delete [] index;
if (vertex)
delete [] vertex;
}
- X File
- Create Mesh from File
- X File
- SkyBox
- definition of a skybox
- attachment to camera
- inverted coordinates
- skybox textures
- Graphic.cpp code
- more complicated forms - skydome
- definition of a skybox
- Billboards
- definition, purpose of a billboard
void Billboard::render(unsigned) {
Vector h, u, r, p = position();
Camera* camera = *(Camera**)(Camera::getCurrent());
Vector cameraPosition = camera->position();
Vector cameraHeading = ::normal(camera->orientation('z'));
Vector cameraUp = ::normal(camera->orientation('y'));
switch (type) {
// ... see below
}
Matrix rot(r.x, r.y, r.z, 0,
u.x, u.y, u.z, 0,
h.x, h.y, h.z, 0,
0, 0, 0, 1);
orient(rot);
Object::render(0);
}
- types of billboards
- screen-aligned - useful for annotation text, lens flares
- normal is opposite to camera heading
- up is camera->up
- screen-aligned - useful for annotation text, lens flares
- types of billboards
case SCREEN:
h = cameraHeading; // fixed
u = cameraUp; // up is fixed
r = cross(u, h);
break;
- axial - useful for cylindrical symmetry - trees (textured object does not face straight on)
- up is fixed
- normal faces the viewer as much as possible
- axial - useful for cylindrical symmetry - trees (textured object does not face straight on)
case AXIAL:
h = ::normal(position() - cameraPosition); // heading is open to change
u = Vector(0, 1, 0); // up axis is fixed
r = cross(u, h);
h = cross(u, r);
break;
- view_plane - no distortion - useful for
- normal is fixed (opposite to camera heading)
- up is open to change
- view_plane - no distortion - useful for
case VIEW_PLANE:
h = cameraHeading; // heading is fixed
u = Vector(0, 1, 0); // up is open to change
r = cross(u, h);
u = cross(h, r);
break;
- viewpoint - simulates distortion due to perspective projection - useful for clouds
- normal is fixed (difference between viewpoint position and camera heading)
- up is open to change
- viewpoint - simulates distortion due to perspective projection - useful for clouds
case VIEWPOINT:
h = ::normal(position() - cameraPosition); // heading is fixed
u = Vector(0, 1, 0); // up is open to change
r = cross(u, h);
u = cross(h, r);
break;
- Object.h and Object.cpp code
- Texture Filtering
- mip maps
- multum in parvo
- texture creation
- APITexture::SetSamplerState()
- mip maps
// mipmap filtering
if (flags & TEX_MIPMAP)
d3dd->SetSamplerState(i, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
else
d3dd->SetSamplerState(i, D3DSAMP_MIPFILTER, D3DTEXF_NONE);
- DirectX Errors
- DirectX Utilities - Lookup Tool
- APIDisplay::restore() example
bool APIDisplay::restore() {
bool rc = false;
if (d3dd) {
HRESULT hr;
hr = d3dd->TestCooperativeLevel();
if (hr == D3DERR_DEVICENOTRESET)
// reset the APIDisplay device
rc = d3dd->Reset(&d3dpp) == D3D_OK;
else if (hr == S_OK)
rc = true;
}
if (rc) {
// reacquire sprite manager references to video memory
if (manager)
manager->OnResetDevice();
}
// complete the restoration
if (rc) {
setupLighting();
setupBlending();
}
return rc;
}
To Do
Resources
- meshes sample
- Rob Whitaker on Skyboxes
- Image Based Rendering - Suman Nadella on Billboarding and Imposters
- DigitalRune
- Wikipedia on Texture Filtering
- D3D 9 tutorial on modeling with an Index list
Week 5 - Feb 6
This Week
- Vertex Declarations
template <class T = Vertex>
class APIVertexDeclaration {
static D3DVERTEXELEMENT9 fmt[MAXD3DDECLLENGTH + 1];
static unsigned vertexSize;
public:
static D3DVERTEXELEMENT9* format() { return fmt; }
static unsigned size() { return vertexSize; }
};
template <>
D3DVERTEXELEMENT9 APIVertexDeclaration<Vertex>::fmt[MAXD3DDECLLENGTH + 1]
= {
{ 0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT,
D3DDECLUSAGE_POSITION, 0},
{ 0, 12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT,
D3DDECLUSAGE_NORMAL, 0},
{ 0, 24, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT,
D3DDECLUSAGE_TEXCOORD, 0},
D3DDECL_END()};
template<>
unsigned APIVertexDeclaration<Vertex>::vertexSize = 32;
template <>
D3DVERTEXELEMENT9 APIVertexDeclaration<LitVertex>::fmt[MAXD3DDECLLENGTH + 1]
= {
{ 0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT,
D3DDECLUSAGE_POSITION, 0},
{ 0, 12, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT,
D3DDECLUSAGE_COLOR, 0},
D3DDECL_END()};
template <>
unsigned APIVertexDeclaration<LitVertex>::vertexSize = 16;
- The Pipeline
- The GPU
- nVidia
- AMD (previously ATI)
- Shader Languages
- what is a shader
- dedicated shaders
- unified shaders
- how does a shader work
- Vertex Shaders
- Pixel Shaders
- effect of skybox and point light on frame rate
To Do
Week 6 - Feb 13
This Week
Vertex Shader Programming
- Host
- APIPlatformSettings.h - Vertex Shader Identification - select between fixed function and programmable pipelines here
// shader file data
#define VERTEX_SHADER_FILE L"vertexShader.hlsl"
#define VERTEX_SHADER_ENTRY_POINT "vertexShader"
- APIBase.h - pointers into the shader - APIBase is the base class for the graphics API classes
static IDirect3DVertexShader9* vertexShader; // vertex shader
static ID3DXConstantTable* uniformVS; // for vertex shader
- APIBase.cpp - initialize the shader pointers
IDirect3DVertexShader9* APIBase::vertexShader = nullptr; // vertex shader
ID3DXConstantTable* APIBase::uniformVS = nullptr; // for vertex shader
- APIDisplay.h - keeps track of the current projection matrix
Matrix projection; // projection transformation
- APIDisplay.cpp - setup() - checks the shader version
// points to compiled shader code
LPD3DXBUFFER compiledCodeVS = nullptr;
// check for minimum vertex shader version required
if (caps.VertexShaderVersion < D3DVS_VERSION(2,0))
error(L"APIDisplay::09 Device does not support vertex shader 2_0");
compiles the shader hlsl code, retrieves address of constant memory and vertex shader
// compile the vertex shader source code
else if (FAILED(D3DXCompileShaderFromFile(VERTEX_SHADER_FILE, NULL,
NULL, VERTEX_SHADER_ENTRY_POINT, D3DXGetVertexShaderProfile(d3dd),
D3DXSHADER_DEBUG | D3DXSHADER_SKIPOPTIMIZATION,
&compiledCodeVS, NULL, &uniformVS))) {
release();
error(L"APIDisplay::13 Unable to compile vertex shader");
}
// create the vertex shader object
else if (FAILED(d3dd->CreateVertexShader(
(DWORD*)compiledCodeVS->GetBufferPointer(), &vertexShader))) {
compiledCodeVS->Release();
release();
error(L"APIDisplay::14 Unable to create vertex shader object");
}
else {
compiledCodeVS->Release();
sets the current vertex shader
d3dd->SetVertexShader(vertexShader);
- APIDisplay.cpp - setProjection() - stores the projection matrix
this->projection = *((Matrix*)projection);
- APIDisplay.cpp - beginDrawFrame() - copies the view matrix and the camera heading to constant memory
Matrix& v = *((Matrix*)view);
Matrix viewProjection = v * projection;
uniformVS->SetMatrix(d3dd, "viewProjection",
(D3DXMATRIX*)&viewProjection);
Vector heading(v.m13, v.m23, v.m33);
// Required for specular lighting calculations
uniformVS->SetFloatArray(d3dd, "heading", (FLOAT*)&heading, 3);
Colour colour(red, green, blue);
uniformVS->SetFloatArray(d3dd, "ambient", (FLOAT*)&colour, 4);
uniformVS->SetInt(d3dd, "noLights", 4);
- APIDisplay.cpp - set() - copies the lighting state to constant memory
uniformVS->SetBool(d3dd, "lighting", b);
- APIDisplay.cpp - setWorld() - copies the world matrix to constant memory
uniformVS->SetMatrix(d3dd, "world", (D3DXMATRIX*)world);
- APIDisplay.cpp - setReflectivity() - copies the reflectivity to constant memory
uniformVS->SetFloatArray(d3dd, "material.ambient", (FLOAT*)&r.ambient, 4);
uniformVS->SetFloatArray(d3dd, "material.diffuse", (FLOAT*)&r.diffuse, 4);
uniformVS->SetFloatArray(d3dd, "material.specular", (FLOAT*)&r.specular, 4);
uniformVS->SetFloat (d3dd, "material.power", (FLOAT)r.power);
- APIDisplay.cpp - release() - releases constant memory and the vertex shader
// release the shader COM objects
if (uniformVS) {
uniformVS->Release();
uniformVS = nullptr;
}
if (vertexShader) {
vertexShader->Release();
vertexShader = nullptr;
}
- APILight.cpp - setup() - copies the light properties to constant memory
// Populate the vertex shader constant table
//
// Light descriptors within the vertex shader
char typ[] = "light[0].type";
char amb[] = "light[0].ambient";
char dif[] = "light[0].diffuse";
char spe[] = "light[0].specular";
char pos[] = "light[0].position";
char dir[] = "light[0].direction";
char spt[] = "light[0].spot";
char att[] = "light[0].attenuation";
char ran[] = "light[0].range";
//
// Reset index in light descriptor
typ[6] = index + '0';
amb[6] = index + '0';
dif[6] = index + '0';
spe[6] = index + '0';
pos[6] = index + '0';
dir[6] = index + '0';
spt[6] = index + '0';
att[6] = index + '0';
ran[6] = index + '0';
// Populate the vertex shader constant table
Vector attenuation(attenuation0, attenuation1, attenuation2);
Vector spot(cosf(phi/2), cosf(theta/2), falloff);
Vector zero;
uniformVS->SetInt(d3dd, typ, type);
uniformVS->SetFloatArray(d3dd, amb, (FLOAT*)&ambient, 4);
uniformVS->SetFloatArray(d3dd, dif, (FLOAT*)&diffuse, 4);
uniformVS->SetFloatArray(d3dd, spe, (FLOAT*)&specular, 4);
uniformVS->SetFloatArray(d3dd, pos, (FLOAT*)&zero, 3);
uniformVS->SetFloatArray(d3dd, dir, (FLOAT*)&zero, 3);
uniformVS->SetFloatArray(d3dd, att, (FLOAT*)&attenuation, 3);
uniformVS->SetFloatArray(d3dd, spt, (FLOAT*)&spot, 3);
uniformVS->SetFloat(d3dd, ran, range);
rc = true;
- APILight.cpp - turnOn() - repositions the light and turns it on
char constantLightOn[] = "lightOn[0]";
constantLightOn[8] = index + '0';
char pos[] = "light[0].position";
char dir[] = "light[0].direction";
pos[6] = index + '0';
dir[6] = index + '0';
uniformVS->SetFloatArray(d3dd, pos, (FLOAT*)&p, 3);
uniformVS->SetFloatArray(d3dd, dir, (FLOAT*)&o, 3);
uniformVS->SetBool(d3dd, constantLightOn, true);
- APILight.cpp - update() - repositions the light
char constantLightOn[] = "lightOn[0]";
constantLightOn[8] = index + '0';
char pos[] = "light[0].position";
char dir[] = "light[0].direction";
pos[6] = index + '0';
dir[6] = index + '0';
uniformVS->SetFloatArray(d3dd, pos, (FLOAT*)&p, 3);
uniformVS->SetFloatArray(d3dd, dir, (FLOAT*)&o, 3);
uniformVS->SetBool(d3dd, constantLightOn, true);
- APILight.cpp - turnOff() - turns off the light
char constantLightOn[] = "lightOn[0]";
constantLightOn[8] = index + '0';
uniformVS->SetBool(d3dd, constantLightOn, false);
- APIGraphic.h - class APIXMesh - processes an X file mesh
D3DXCOLOR* ambient;
D3DXCOLOR* diffuse;
D3DXCOLOR* specular;
FLOAT* power;
- APIGraphic.cpp - APIXMesh() - constructor
ambient = nullptr;
diffuse = nullptr;
specular = nullptr;
power = nullptr;
- APIGraphic.cpp - APIXMesh() - copy constructor
ambient = nullptr;
diffuse = nullptr;
specular = nullptr;
power = nullptr;
- APIGraphic.cpp - operator=() = assignment operator
if (ambient)
delete [] ambient;
if (diffuse)
delete [] diffuse;
if (specular)
delete [] specular;
if (power)
delete [] power;
ambient = new D3DXCOLOR[src.nSubsets];
diffuse = new D3DXCOLOR[src.nSubsets];
specular = new D3DXCOLOR[src.nSubsets];
power = new FLOAT[src.nSubsets];
for (unsigned i = 0; i < nSubsets; i++) {
ambient[i] = src.ambient[i];
diffuse[i] = src.diffuse[i];
specular[i] = src.specular[i];
power[i] = src.power[i];
}
- APIGraphic.cpp - setup() -
ambient = new D3DXCOLOR[nSubsets];
diffuse = new D3DXCOLOR[nSubsets];
specular = new D3DXCOLOR[nSubsets];
power = new FLOAT[nSubsets];
ambient[i].r = matl[i].MatD3D.Diffuse.r * 0.7f;
ambient[i].g = matl[i].MatD3D.Diffuse.g * 0.7f;
ambient[i].b = matl[i].MatD3D.Diffuse.b * 0.7f;
ambient[i].a = matl[i].MatD3D.Diffuse.a;
diffuse[i] = matl[i].MatD3D.Diffuse; // reflected from lights
specular[i] = matl[i].MatD3D.Specular; // shine from lights
power[i] = matl[i].MatD3D.Power; // 0 if it shouldn't be shiny
- APIGraphic.cpp - draw()
uniformVS->SetFloatArray(d3dd, "material.ambient", (FLOAT*)&ambient[i], 4);
uniformVS->SetFloatArray(d3dd, "material.diffuse", (FLOAT*)&diffuse[i], 4);
uniformVS->SetFloatArray(d3dd, "material.specular", (FLOAT*)&specular[i], 4);
uniformVS->SetFloat(d3dd, "material.power", (FLOAT)power[i]);
- APIGraphic.cpp - suspend()
if (ambient)
delete [] ambient;
if (diffuse)
delete [] diffuse;
if (specular)
delete [] specular;
if (power)
delete [] power;
- Device
- vertexShader.hlsl - Constant Memory
#define MLIGHTS 4
#define POINT_LIGHT 0
#define SPOT_LIGHT 1
#define DIRECTIONAL_LIGHT 2
// Types
//
// Light holds the data for a single light in world space
//
struct Light {
int type; // POINT_LIGHT, SPOT_LIGHT, DIRECTIONAL_LIGHT
float4 ambient;
float4 diffuse;
float4 specular;
float3 direction; // in world space
float3 position; // in world space
float3 attenuation; // .xyz for 1.0f/ (.x + .y * d + .z * d * d)
float3 spot; // .x = cos(phi/2), .y = cos(theta/2), .z = falloff
float range; // where attenuation becomes 0
};
// Material holds the reflectivity properties of the material
//
struct Material {
float4 ambient;
float4 diffuse;
float4 specular;
float power;
};
// RawVertex holds the untransformed data for a single vertex
//
struct RawVertex {
float3 position : POSITION; // position in local space
float3 normal : NORMAL; // normal in local space
float2 texCoord : TEXCOORD; // texture coordinates
};
// TransformedVertex holds the transformed data for a single vertex
//
struct TransformedVertex {
float4 position : POSITION; // position in homogeneous clip space
float4 colour : COLOR; // colour of the lit vertex
float2 texCoord0 : TEXCOORD0; // texture coordinates - stage 0
float2 texCoord1 : TEXCOORD1; // texture coordinates - stage 1
};
// Uniform Data (constant for a stream of vertices)
//
// Lighting
float4 ambient; // global ambient light - always on
Light light[MLIGHTS]; // static lights
bool lightOn[MLIGHTS]; // switches for static lights
Material material; // material reflectivity
// Geometry
float4x4 viewProjection; // view * projection transformation
float4x4 world; // world transformation
float3 heading; // camera heading for specular calcs
// Lit Vertex
bool litVertex; // omit lighting calculations - already lit
- vertexShader.hlsl - vertexShader()
// vertexShader receives a raw vertex and returns the transformed vertex
//
TransformedVertex vertexShader(RawVertex raw) {
TransformedVertex transformed;
float4 worldPosition; // world position of the vertex
float3 worldNormal; // vertex normal in world space
// Transform the vertex to homogeneous clip coordinates
//
// A more efficient algorithm would accept the world*view*projection
// tranformation as one uniform matrix and avoid the 2-stage product
// This will require a bit of restructuring of the application code.
//
worldPosition = mul(float4(raw.position, 1.0), world); // local to world
transformed.position = mul(worldPosition, viewProjection); //... to clip
// not working
if (litVertex) {
transformed.colour.r = raw.normal.r;
transformed.colour.g = raw.normal.g;
transformed.colour.b = raw.normal.b;
transformed.colour.a = 1.0f;
}
else {
// Transform the vertex normal to world space. Only the rotation-scaling
// part of the world transformation is used. Since the world
// transformation may contain scaling, the result of this multiplication
// needs to be normalized.
//
worldNormal = mul(raw.normal, (float3x3)world);
worldNormal = normalize(worldNormal);
// Determine the colour of the vertex from each light in turn
//
// Use the cosine of the angle between the worldNormal and the direction
// of the light to determine the amount of reflected light. The cosine
// is given by the dot product, if both vectors have been normalized. If
// the cosine is less than 0, the angle is greater than 90 degrees and
// no light is reflected. Use saturate() to implement this condition.
//
// A more efficient algorithm would supply the light direction already
// converted to the local space of the vertex (by using the inverse of
// the world transformation).
//
float diffuseFactor, reflectFactor, distance;
float attenuationFactor, spotFactor, rho;
float3 lightDirection, camera = normalize(heading);
float3 ambientLight = ambient.xyz;
float3 diffuseLight = (float3)0;
float3 specularLight = (float3)0;
for (int i = 0; i < MLIGHTS; i++) {
if (lightOn[i]) {
lightDirection = - normalize((light[i].type == POINT_LIGHT)?
float3(worldPosition.x, worldPosition.y, worldPosition.z) -
light[i].position : light[i].direction);
diffuseFactor = saturate(dot(worldNormal, lightDirection));
reflectFactor = saturate(dot(normalize(2 * diffuseFactor *
worldNormal - lightDirection), camera));
attenuationFactor = 1.0f;
spotFactor = 1.0f;
if (light[i].type == POINT_LIGHT ||
light[i].type == SPOT_LIGHT) {
// detail calcs for attenuationFactor and spotFactor
distance = length((float3)worldPosition - light[i].position);
if (distance < light[i].range) {
attenuationFactor = light[i].attenuation.x +
light[i].attenuation.y * distance +
light[i].attenuation.z * distance * distance;
attenuationFactor = 1.0f / attenuationFactor;
if (light[i].type == SPOT_LIGHT) {
rho = saturate(dot(normalize(light[i].position -
float3(worldPosition.x, worldPosition.y,
worldPosition.z)),
normalize(-light[i].direction)));
if (rho <= light[i].spot.x)
spotFactor = 0.0f;
else if (rho <= light[i].spot.y)
spotFactor = pow(
abs((rho - light[i].spot.x)/
(light[i].spot.y - light[i].spot.x)),
light[i].spot.z);
}
}
else
attenuationFactor = 0.0f;
}
// accumulate ambient, diffuse, and specular elements of light
//
ambientLight += attenuationFactor * spotFactor *
light[i].ambient.xyz;
diffuseLight += attenuationFactor * spotFactor * diffuseFactor *
light[i].diffuse.xyz;
specularLight += attenuationFactor * spotFactor *
light[i].specular.xyz * pow(reflectFactor, material.power);
}
}
// apply material reflectivity to each accumulated element of light
// to obtain the colour of the lit vertex
//
transformed.colour.xyz =
saturate(material.ambient.xyz * ambientLight) +
saturate(material.diffuse.xyz * diffuseLight) +
saturate(material.specular.xyz * specularLight);
// pass the diffuse alpha along as the alpha component
//
transformed.colour.w = material.diffuse.w;
}
// pass the texture coordinates along unaltered
//
transformed.texCoord0 = raw.texCoord;
transformed.texCoord1 = raw.texCoord;
return transformed;
}
Fragment Shader
- Host
- APIPlatformSettings.h
#define FRAGMENT_SHADER_FILE L"fragmentShader.hlsl"
#define FRAGMENT_SHADER_ENTRY_POINT "fragmentShader"
- APIBase.h
// Fragment Shader Support
static IDirect3DPixelShader9* fragmentShader; // fragment shader
static ID3DXConstantTable* uniformFS; // for fragment shader
- APIBase.cpp
IDirect3DPixelShader9* APIBase::fragmentShader = nullptr; // fragment shader
ID3DXConstantTable* APIBase::uniformFS = nullptr; // for fragment shader
- APIDisplay.cpp - setup()
LPD3DXBUFFER compiledCodeFS = nullptr;
// checks for minimum pixel shader version required
else if (caps.PixelShaderVersion < D3DPS_VERSION(3,0))
error(L"Display::10 Device does not support pixel shader 3_0");
// compile the fragment shader source code
else if (FAILED(D3DXCompileShaderFromFile(FRAGMENT_SHADER_FILE, NULL,
NULL, FRAGMENT_SHADER_ENTRY_POINT, D3DXGetPixelShaderProfile(d3dd), 0,
&compiledCodeFS, NULL, &uniformFS))) {
release();
error(L"APIDisplay::15 Unable to compile the fragment shader code");
}
// create the pixel shader object
else if (FAILED(d3dd->CreatePixelShader(
(DWORD*)compiledCodeFS->GetBufferPointer(), &fragmentShader))) {
compiledCodeFS->Release();
release();
error(L"APIDisplay::16 Unable to create fragment shader object");
}
else {
compiledCodeFS->Release();
d3dd->SetPixelShader(fragmentShader);
- APIDisplay.cpp - beginDrawFrame()
Colour colour(red, green, blue);
uniformFS->SetFloatArray(d3dd, "ambient", (FLOAT*)&colour, 4);
uniformFS->SetInt(d3dd, "noLights", 4);
- APIDisplay.cpp - set()
uniformFS->SetBool(d3dd, "lighting", b);
- APIDisplay.cpp - setReflectivity()
uniformFS->SetFloatArray(d3dd, "material.ambient", (FLOAT*)&r.ambient, 4);
uniformFS->SetFloatArray(d3dd, "material.diffuse", (FLOAT*)&r.diffuse, 4);
uniformFS->SetFloatArray(d3dd, "material.specular", (FLOAT*)&r.specular, 4);
uniformFS->SetFloat (d3dd, "material.power", (FLOAT)r.power);
- APIDisplay.cpp - release()
}
if (uniformFS) {
uniformFS->Release();
uniformFS = nullptr;
}
if (fragmentShader) {
fragmentShader->Release();
fragmentShader = nullptr;
}
- APIGraphic.cpp - APIXMesh::draw()
uniformFS->SetFloatArray(d3dd, "material.ambient", (FLOAT*)&ambient[i], 4);
uniformFS->SetFloatArray(d3dd, "material.diffuse", (FLOAT*)&diffuse[i], 4);
uniformFS->SetFloatArray(d3dd, "material.specular", (FLOAT*)&specular[i], 4);
uniformFS->SetFloat(d3dd, "material.power", (FLOAT)power[i]);
- APILight.cpp - setup()
uniformFS->SetInt(d3dd, typ, type);
uniformFS->SetFloatArray(d3dd, amb, (FLOAT*)&ambient, 4);
uniformFS->SetFloatArray(d3dd, dif, (FLOAT*)&diffuse, 4);
uniformFS->SetFloatArray(d3dd, spe, (FLOAT*)&specular, 4);
uniformFS->SetFloatArray(d3dd, pos, (FLOAT*)&zero, 3);
uniformFS->SetFloatArray(d3dd, dir, (FLOAT*)&zero, 3);
uniformFS->SetFloatArray(d3dd, att, (FLOAT*)&attenuation, 3);
uniformFS->SetFloatArray(d3dd, spt, (FLOAT*)&spot, 3);
uniformFS->SetFloat(d3dd, ran, range);
- APILight.cpp - turnOn()
uniformFS->SetFloatArray(d3dd, pos, (FLOAT*)&p, 3);
uniformFS->SetFloatArray(d3dd, dir, (FLOAT*)&o, 3);
uniformFS->SetBool(d3dd, constantLightOn, true);
uniformFS->SetFloatArray(d3dd, pos, (FLOAT*)&p, 3);
uniformFS->SetFloatArray(d3dd, dir, (FLOAT*)&o, 3);
uniformFS->SetBool(d3dd, constantLightOn, true);
- APILight.cpp - update()
uniformFS->SetFloatArray(d3dd, pos, (FLOAT*)&p, 3);
uniformFS->SetFloatArray(d3dd, dir, (FLOAT*)&o, 3);
uniformFS->SetBool(d3dd, constantLightOn, true);
- APILight.cpp - turnOff()
uniformFS->SetBool(d3dd, constantLightOn, false);
- APITexture.cpp - attach()
char str[] = "texOn";
uniformFS->SetBool(d3dd, str, true);
- APITexture.cpp - detach()
char str[] = "texOn";
uniformFS->SetBool(d3dd, str, false);
- Device
- vertexShader.hlsl - Constant Memory
// Types
//
// RawVertex holds the original data for a vertex in the stream
//
struct RawVertex {
float3 position : POSITION; // position in local space
float3 normal : NORMAL; // normal in local space
float2 texCoord : TEXCOORD0; // texture coordinates
};
// TransformedVertex holds the transformed data for the vertex
//
struct TransformedVertex {
float4 position : POSITION; // position in homogeneous clip space
float2 texCoord : TEXCOORD; // texture coordinates
float3 worldPos : TEXCOORD1; // position in world space
float3 worldNor : TEXCOORD2; // lighting normal in world space
float3 toViewer : TEXCOORD3; // direction to viewer in world space
};
// Uniform Data (constant for the stream of vertices)
//
// Geometry
float4x4 viewProjection; // view * projection transformation
float4x4 world; // world transformation
float3 viewPoint; // camera viewpoint for specular calcs
// Lit Vertex
bool litVertex; // omit lighting calculations - already lit
- vertexShader.hlsl - vertexShader()
// vertexShader receives a raw data for a vertex and transforms that data
//
TransformedVertex vertexShader(RawVertex raw) {
TransformedVertex transformed; // result returned by this function
float4 worldPosition; // world position of the vertex
float3 worldNormal; // vertex normal in world space
// Transform the vertex to homogeneous clip coordinates
//
// A more efficient algorithm would accept the world*view*projection
// tranformation as one uniform matrix and avoid the 2-stage product
// This will require a bit of restructuring of the application code.
//
worldPosition = mul(float4(raw.position, 1.0), world); // local to world
transformed.position = mul(worldPosition, viewProjection); //... to clip
transformed.worldPos = worldPosition.xyz;
// Transform the vertex normal to world space. Only the rotation-scaling
// part of the world transformation is used. Since the world
// transformation may contain scaling, the result of this multiplication
// needs to be normalized.
//
worldNormal = mul(raw.normal, (float3x3)world);
worldNormal = normalize(worldNormal);
transformed.worldNor = worldNormal;
// Determine the direction from the camera's viewpoint to this vertex for
// subsequent lighting calculations
//
transformed.toViewer = normalize(viewPoint - worldPosition.xyz);
// pass the texture coordinates along unaltered
//
transformed.texCoord = raw.texCoord;
return transformed;
}
- fragmentShader.hlsl - Constant Memory
#define MLIGHTS 4
#define MTEXTURES 2
#define POINT_LIGHT 0
#define SPOT_LIGHT 1
#define DIRECTIONAL_LIGHT 2
// Types
//
// Light holds the data for a single static light in world space
//
struct Light {
int type; // POINT_LIGHT, SPOT_LIGHT, DIRECTIONAL_LIGHT
float4 ambient;
float4 diffuse;
float4 specular;
float3 direction; // in world space
float3 position; // in world space
float3 attenuation; // .xyz for 1.0f/ (.x + .y * d + .z * d * d)
float3 spot; // .x = cos(phi/2), .y = cos(theta/2), .z = falloff
float range; // where attenuation becomes 0
};
// Material holds the reflectivity properties of the material
//
struct Material {
float4 ambient;
float4 diffuse;
float4 specular;
float power;
};
// RawPixel holds the data for a single fragment of the stream
//
struct RawPixel {
float2 texcoord : TEXCOORD0; // texture coordinate at this fragment
float3 position : TEXCOORD1; // fragment position in world space
float3 normal : TEXCOORD2; // lighting normal in world space
float3 toViewer : TEXCOORD3; // direction to viewer in world space
};
// Uniform Data (constant for a stream of fragments)
//
float4 ambient; // global ambient light - always on
int noLights; // no of active lights
Light light[MLIGHTS]; // static lights
bool lightOn[MLIGHTS]; // light switch
Material material; // material reflectivity
bool lighting; // lighting calculations on?
bool texOn; // texture switch
sampler2D tex; // set by the application
- fragmentShader.hlsl - fragmentShader()
// The fragment shader receives raw fragment data and returns a pixel colour
//
float4 fragmentShader(RawPixel raw) : COLOR {
float4 colour; // result returned by this function
float3 normal; // normal to the fragment
float3 toViewer; // from fragment to the camera
float3 toLightSource; // from fragment to current light source
// lighting contribution accumulators
float3 ambientLight = ambient.xyz;
float3 diffuseLight = (float3)0;
float3 specularLight = (float3)0;
// lighting calculation factors
float diffuseFactor, reflectFactor, distance;
float attenuationFactor, spotFactor, rho;
// normalize the fragment data
normal = normalize(raw.normal);
toViewer = normalize(raw.toViewer);
// perform calculations for each light in turn
for (int i = 0; i < noLights && i < MLIGHTS; i++) {
if (lightOn[i]) {
float diffuseFactor, reflectFactor, factor;
// diffuse and reflection factors
toLightSource = normalize((light[i].type == POINT_LIGHT)?
light[i].position - raw.position : - light[i].direction);
diffuseFactor = saturate(dot(normal, toLightSource));
reflectFactor = saturate(dot(normalize(2 * diffuseFactor *
normal - toLightSource), toViewer));
attenuationFactor = 1.0f;
spotFactor = 1.0f;
if (light[i].type == POINT_LIGHT ||
light[i].type == SPOT_LIGHT) {
// detail calcs for attenuationFactor and spotFactor
distance = length(raw.position - light[i].position);
if (distance < light[i].range) {
attenuationFactor = light[i].attenuation.x +
light[i].attenuation.y * distance +
light[i].attenuation.z * distance * distance;
attenuationFactor = 1.0f / attenuationFactor;
if (light[i].type == SPOT_LIGHT) {
rho = saturate(dot(normalize(light[i].position -
float3(raw.position.x, raw.position.y,
raw.position.z)),
normalize(-light[i].direction)));
if (rho <= light[i].spot.x)
spotFactor = 0.0f;
else if (rho <= light[i].spot.y)
spotFactor = pow(abs(
(rho - light[i].spot.x)/
(light[i].spot.y - light[i].spot.x)),
light[i].spot.z);
}
}
else
attenuationFactor = 0.0f;
}
// accumulate ambient, diffuse, and specular elements of light
//
ambientLight += attenuationFactor * spotFactor *
light[i].ambient.xyz;
diffuseLight += attenuationFactor * spotFactor * diffuseFactor *
light[i].diffuse.xyz;
specularLight += attenuationFactor * spotFactor *
light[i].specular.xyz * pow(reflectFactor, material.power);
}
// apply material reflectivity to each accumulated element of light
// to obtain the colour of the lit fragment
//
colour.xyz =
saturate(material.ambient.xyz * ambientLight) +
saturate(material.diffuse.xyz * diffuseLight) +
saturate(material.specular.xyz * specularLight);
colour.w = material.diffuse.w;
}
// apply texture
//
if (texOn)
colour *= tex2D(tex, raw.texcoord);
return colour;
}
To Do
- reorganize framework code so that vertex shader receives product of world, view, and projection matrices
- store viewProjection matrix as an instance variable in APIDisplay
- pre-multiply viewProjection by world to obtain composite matrix to pass to vertex shader
- add composite matrix to the constant table in the vertex shader
- reorganize framework code to minimize duplication of heading normalization
- perform normalization of heading in APIDisplay::beginDrawFrame()
Resources
Week 7 - Feb 20
This Week
- Effects Framework
- techniques
- passes
- Source Code
- APIBase.h
static ID3DXEffect* effect; // points to effects framework
- APIBase.cpp
ID3DXEffect* APIBase::effect = nullptr; // effects framework
- APIDisplay.h
// Effects Framework handles
D3DXHANDLE viewProjection; // view * projection
D3DXHANDLE viewPoint; // camera viewpoint
D3DXHANDLE ambient; // global ambient color
D3DXHANDLE ambientHandle; // current ambient reflectivity
D3DXHANDLE diffuseHandle; // current diffuse reflectivity
D3DXHANDLE specularHandle; // current specular reflectivity
D3DXHANDLE powerHandle; // current shininess coefficient
D3DXHANDLE worldHandle; // current world transformation
void beginEffect(const char*, unsigned&);
void beginPass(unsigned);
void endPass();
void endEffect();
- APIDisplay.cpp - APIDisplay()
viewProjection = nullptr;
viewPoint = nullptr;
ambient = nullptr;
ambientHandle = nullptr;
diffuseHandle = nullptr;
specularHandle = nullptr;
powerHandle = nullptr;
worldHandle = nullptr;
- APIDisplay.cpp - setup()
LPD3DXBUFFER errorBuffer = NULL;
// create the effects framework
else if (FAILED(D3DXCreateEffectFromFile(d3dd, EFFECT_FILE, 0,
0, D3DXSHADER_DEBUG, 0, &effect, &errorBuffer))) {
release();
error(L"APIDisplay::17 Unable to create the effects framework");
}
if (errorBuffer) errorBuffer->Release();
viewProjection = effect->GetParameterByName(0, "viewProjection");
viewPoint = effect->GetParameterByName(0, "viewPoint");
ambient = effect->GetParameterByName(0, "ambient");
worldHandle = effect->GetParameterByName(0, "world");
ambientHandle = effect->GetParameterByName(0, "material.ambient");
diffuseHandle = effect->GetParameterByName(0, "material.diffuse");
specularHandle = effect->GetParameterByName(0, "material.specular");
powerHandle = effect->GetParameterByName(0, "material.power");
- APIDisplay.cpp - beginDrawFrame()
Matrix& v = *((Matrix*)view);
Matrix viewprojection = v * projection;
Vector heading(v.m13, v.m23, v.m33);
effect->SetMatrix(viewProjection, (D3DXMATRIX*)&viewprojection);
effect->SetVector(viewPoint, (D3DXVECTOR4*)&heading);
Colour colour(red, green, blue);
effect->SetVector(ambient, (D3DXVECTOR4*)&colour);
- APIDisplay.cpp - beginEffect()
void APIDisplay::beginEffect(const char* technique, unsigned& nPasses) {
D3DXHANDLE techniqueHandle = effect->GetTechniqueByName(technique);
effect->SetTechnique(techniqueHandle);
effect->Begin(&nPasses, 0);
}
- APIDisplay.cpp - beginPass()
void APIDisplay::beginPass(unsigned i) {
effect->BeginPass(i);
}
- APIDisplay.cpp - endPass()
void APIDisplay::endPass() {
effect->EndPass();
}
- APIDisplay.cpp - endEffect()
void APIDisplay::endEffect() {
effect->End();
}
- APIDisplay.cpp - setWorld()
effect->SetMatrix(worldHandle, (D3DXMATRIX*)world);
- APIDisplay.cpp - setReflectivity()
effect->SetVector(ambientHandle, (D3DXVECTOR4*)&r.ambient);
effect->SetVector(diffuseHandle, (D3DXVECTOR4*)&r.diffuse);
effect->SetVector(specularHandle, (D3DXVECTOR4*)&r.specular);
effect->SetFloat(powerHandle, r.power);
effect->CommitChanges();
- APIDisplay.cpp - release()
if (effect) {
effect->Release();
effect = NULL;
}
- effects.fx - Constant Memory
#define MLIGHTS 4
#define POINT_LIGHT 0
#define SPOT_LIGHT 1
#define DIRECTIONAL_LIGHT 2
// Types
//
// Light holds the data for a single static light in world space
//
struct Light {
int type; // POINT_LIGHT, SPOT_LIGHT, DIRECTIONAL_LIGHT
float3 ambient;
float3 diffuse;
float3 specular;
float3 direction; // in world space
float3 position; // in world space
float3 attenuation; // .xyz for 1.0f/ (.x + .y * d + .z * d * d)
float3 spot; // .x = cos(phi/2), .y = cos(theta/2), .z = falloff
float range; // where attenuation becomes 0
};
// Material holds the reflectivity properties of the material
//
struct Material {
float4 ambient;
float4 diffuse;
float4 specular;
float power;
};
// RawPixel holds the data for a single fragment of the stream
//
struct RawPixel {
float2 texcoord : TEXCOORD0; // texture coordinate at this fragment
float3 position : TEXCOORD1; // fragment position in world space
float3 normal : TEXCOORD2; // lighting normal in world space
float3 toViewer : TEXCOORD3; // direction to viewer in world space
};
// Uniform Data (constant for a stream of fragments)
//
float4 ambient; // global ambient light - always on
int noLights; // no of static lights
Light light[MLIGHTS]; // static lights
bool lightOn[MLIGHTS]; // light switch
Material material; // material reflectivity
bool texOn; // texture switch
sampler2D tex; // set by the application
// Types
//
// RawVertex holds the original data for a vertex in the stream
//
struct RawVertex {
float3 position : POSITION; // position in local space
float3 normal : NORMAL; // normal in local space
float2 texCoord : TEXCOORD0; // texture coordinates
};
// TransformedVertex holds the transformed data for the vertex
//
struct TransformedVertex {
float4 position : POSITION; // position in homogeneous clip space
float2 texCoord : TEXCOORD; // texture coordinates
float3 worldPos : TEXCOORD1; // position in world space
float3 worldNor : TEXCOORD2; // lighting normal in world space
float3 toViewer : TEXCOORD3; // direction to viewer in world space
};
// Uniform Data (constant for the stream of vertices)
//
// Geometry
float4x4 viewProjection; // view * projection transformation
float4x4 world; // world transformation
float4 viewPoint; // camera viewpoint for specular calcs
// Geometry
float3 heading; // camera heading for specular calcs
// Lit Vertex
bool litVertex; // omit lighting calculations - already lit
- effects.fx - VertexShader
// vertexShader receives a raw data for a vertex and transforms that data
//
TransformedVertex vertexShader(RawVertex raw) {
TransformedVertex transformed; // result returned by this function
float4 worldPosition; // world position of the vertex
float3 worldNormal; // vertex normal in world space
// Transform the vertex to homogeneous clip coordinates
//
// A more efficient algorithm would accept the world*view*projection
// tranformation as one uniform matrix and avoid the 2-stage product
// This will require a bit of restructuring of the application code.
//
worldPosition = mul(float4(raw.position, 1.0), world); // local to world
transformed.position = mul(worldPosition, viewProjection); //... to clip
transformed.worldPos = worldPosition.xyz;
// Transform the vertex normal to world space. Only the rotation-scaling
// part of the world transformation is used. Since the world
// transformation may contain scaling, the result of this multiplication
// needs to be normalized.
//
worldNormal = mul(raw.normal, (float3x3)world);
worldNormal = normalize(worldNormal);
transformed.worldNor = worldNormal;
// Determine the direction from the camera's viewpoint to this vertex for
// subsequent lighting calculations
//
transformed.toViewer = normalize(viewPoint - worldPosition.xyz);
// pass the texture coordinates along unaltered
//
transformed.texCoord = raw.texCoord;
return transformed;
}
- effects.fx - FragmentShader
// The fragment shader receives raw fragment data and returns a pixel colour
//
float4 fragmentShader(RawPixel raw) : COLOR {
float4 colour; // result returned by this function
float3 normal; // normal to the fragment
float3 toViewer; // from fragment to the camera
float3 toLightSource; // from fragment to current light source
// lighting contribution accumulators
float3 ambientLight = ambient.xyz;
float3 diffuseLight = (float3)0;
float3 specularLight = (float3)0;
// lighting calculation factors
float diffuseFactor, reflectFactor, distance;
float attenuationFactor, spotFactor, rho;
// normalize the fragment data
normal = normalize(raw.normal);
toViewer = normalize(raw.toViewer);
// perform calculations for each light in turn
for (int i = 0; i < noLights && i < MLIGHTS; i++) {
if (lightOn[i]) {
float diffuseFactor, reflectFactor, factor;
// diffuse and reflection factors
toLightSource = normalize((light[i].type == POINT_LIGHT)?
light[i].position - raw.position : - light[i].direction);
diffuseFactor = saturate(dot(normal, toLightSource));
reflectFactor = saturate(dot(normalize(2 * diffuseFactor *
normal - toLightSource), toViewer));
attenuationFactor = 1.0f;
spotFactor = 1.0f;
if (light[i].type == POINT_LIGHT ||
light[i].type == SPOT_LIGHT) {
// detail calcs for attenuationFactor and spotFactor
distance = length(raw.position - light[i].position);
if (distance < light[i].range) {
attenuationFactor = light[i].attenuation.x +
light[i].attenuation.y * distance +
light[i].attenuation.z * distance * distance;
attenuationFactor = 1.0f / attenuationFactor;
if (light[i].type == SPOT_LIGHT) {
rho = saturate(dot(normalize(light[i].position -
float3(raw.position.x, raw.position.y,
raw.position.z)),
normalize(-light[i].direction)));
if (rho <= light[i].spot.x)
spotFactor = 0.0f;
else if (rho <= light[i].spot.y)
spotFactor = pow(abs(
(rho - light[i].spot.x)/
(light[i].spot.y - light[i].spot.x)),
light[i].spot.z);
}
}
else
attenuationFactor = 0.0f;
}
// accumulate ambient, diffuse, and specular elements of light
//
ambientLight += attenuationFactor * spotFactor *
light[i].ambient.xyz;
diffuseLight += attenuationFactor * spotFactor * diffuseFactor *
light[i].diffuse.xyz;
specularLight += attenuationFactor * spotFactor *
light[i].specular.xyz * pow(reflectFactor, material.power);
}
}
// apply material reflectivity to each accumulated element of light
// to obtain the colour of the lit fragment
//
colour.xyz =
saturate(material.ambient.xyz * ambientLight) +
saturate(material.diffuse.xyz * diffuseLight) +
saturate(material.specular.xyz * specularLight);
colour.w = material.diffuse.w;
// apply texture
//
if (texOn)
colour *= tex2D(tex, raw.texcoord);
return colour;
}
- effects.fx - technique opaque
technique opaque {
pass {
VertexShader = compile vs_3_0 vertexShader();
PixelShader = compile ps_3_0 fragmentShader();
}
}
- effects.fx - technique translucent
technique translucent {
pass {
VertexShader = compile vs_3_0 vertexShader();
PixelShader = compile ps_3_0 fragmentShader();
}
}
To Do
Resources
Week 8 - Mar 4
This Week
- Frank Luna's notes for DirectX10 (page 306)
- Environment Maps(Google books)
- Environment Maps(Seneca ELibraray)
- Design.cpp
- Design::initialize() - create the skybox object
// initialize initializes the general display design coordinator, creates the
// primitive sets, textures, objects, lights, sounds, cameras, and text items
//
void Design::initialize() {
// ...
// create textures
iTexture* sunset = CreateCubeTexture(L"Islands.dds");
// ...
iObject* skybox = CreateSkybox();
skybox->rotatex(-1.5708f);
skybox->attach(sunset);
setSkybox(skybox);
// ...
}
- Coordinator.cpp
- Coordinator::render() - using different techniques for different objects
void Coordinator::render() {
// adjust framecount and fps
if (now - lastReset <= unitsPerSec)
framecount++;
else {
// recalculate the frame rate
fps = framecount * unitsPerSec / (now - lastReset);
framecount = 0;
lastReset = now;
if (timerText) {
wchar_t str[MAX_DESC + 1];
sprintf(str, fps, L" fps");
timerText->set(str);
}
}
// update the user input devices
userInput->update();
Coordinator::update();
// update the model
update();
// update the audio
audio->setVolume(volume);
audio->setFrequencyRatio(frequency);
audio->update(Camera::getView());
// start rendering
display->beginDrawFrame(Camera::getView());
display->setAmbientLight(ambient.r, ambient.g, ambient.b);
unsigned nPasses;
// render all of the opaque unlit objects
display->beginEffect("opaque", nPasses);
for (unsigned i = 0; i < nPasses; i++) {
display->beginPass(i);
render(OPAQUE_OBJECT);
display->endPass();
}
display->endEffect();
// render all of the translucent unlit objects
display->beginEffect("translucent", nPasses);
for (unsigned i = 0; i < nPasses; i++) {
display->beginPass(i);
render(TRANSLUCENT_OBJECT);
display->endPass();
}
display->endEffect();
// render all of the lit objects
display->beginEffect("litObjects", nPasses);
for (unsigned i = 0; i < nPasses; i++) {
display->beginPass(i);
render(LIT_OBJECT);
display->endPass();
}
display->endEffect();
// render the skybox
display->beginEffect("skybox", nPasses);
if (background && !skybox) {
Rectf fullScreen(0, 0, 1, 1);
display->beginDrawHUD(0);
background->render(fullScreen, true);
display->endDrawHUD();
}
else if (skybox) {
for (unsigned i = 0; i < nPasses; i++) {
display->beginPass(i);
render(SKYBOX);
display->endPass();
}
}
display->endEffect();
display->set(ALPHA_BLEND, false);
display->beginDrawHUD(HUD_ALPHA);
render(ALL_HUDS);
display->endDrawHUD();
display->endDrawFrame();
render(ALL_SOUNDS);
}
- Coordinator::render(iObject*) - render a single object one subset at a time
void Coordinator::render(iObject* object) {
display->setWorld(&object->world());
unsigned nSubsets = object->noSubsets();
for (unsigned i = 0; i < nSubsets; i++) {
iTexture* texture = object->getTexture(i);
if (texture) texture->attach();
display->setReflectivity(object->getReflectivity(i));
object->render(i);
if (texture) texture->detach();
}
}
- Skybox class
- iObject interface - CreateSkybox declaration
class iObject : public Shape, public Base {
public:
// initialization
virtual void attach(iTexture* t) = 0;
virtual void attach(iTexture** t) = 0;
// execution
virtual unsigned noSubsets() const = 0;
virtual void render(unsigned) = 0;
virtual void setTextureFilter(unsigned) = 0;
virtual iTexture* getTexture(unsigned) const = 0;
virtual const void* getReflectivity(unsigned) const = 0;
virtual bool belongsTo(Category category) const = 0;
};
iObject* CreateObject(iGraphic*, const Reflectivity* = nullptr, unsigned = 1u);
iObject* CreateBillboard(BillboardType, iGraphic*,
const Reflectivity* = nullptr);
iObject* CreateSkybox();
iObject* Clone(const iObject*);
- Skybox class - derived from Object
//-------------------------------- Skybox -------------------------------------
//
// A Skybox is an inverted Object that translates with the viewpoint
//
class Skybox : public Object {
public:
Skybox();
void render(unsigned);
};
- CreateSkybox
iObject* CreateSkybox() {
return new Skybox();
}
- Skybox::Skybox - SKYBOX category, 2 x 2 x 2 cube
Skybox::Skybox() : Object(SKYBOX, CreateIBox(-1, -1, -1 * MODEL_Z_AXIS, 1, 1,
1 * MODEL_Z_AXIS)) {
}
- Skybox::render(unsigned) - move skybox centroid to current camera position
void Skybox::render(unsigned) {
Camera* camera = *(Camera**)(Camera::getCurrent());
Vector disp = camera->position() - position();
translate(disp.x, disp.y, disp.z);
Object::render(0);
}
- Texture class
- iTexture interface - CreateCubeTexture declaration
class iTexture : public Base {
public:
virtual void attach() const = 0;
virtual void setFilter(unsigned) const = 0;
virtual void detach() = 0;
virtual void render(const Rectf&, bool = false) = 0;
};
iTexture* CreateTexture(const wchar_t* file, unsigned filter = 0);
iTexture* CreateCubeTexture(const wchar_t* file, unsigned filter = 0);
iTexture* Clone(const iTexture*);
- Texture class - add the cube parameter to constructor
class Texture : public iTexture {
iAPITexture* apiTexture; // points to the api texture
Texture(const Texture&);
virtual ~Texture();
public:
Texture(const wchar_t* file, unsigned filter = 0, bool cube = false);
Texture& operator=(const Texture&);
void* clone() const { return new Texture(*this); }
// execution
void attach() const;
void setFilter(unsigned) const;
void detach();
void render(const Rectf&, bool);
// termination
void suspend();
void release();
};
- CreateCubeTexture - call constructor with true flag for cube
iTexture* CreateCubeTexture(const wchar_t* file, unsigned filter) {
return new Texture(file, filter, true);
}
- Texture::Texture() - create APICubeTexture()
Texture::Texture(const wchar_t* file, unsigned filter, bool cube) {
coordinator->add(this);
wchar_t* fileWithPath = nullptr;
if (file) {
// add the directory to create the relative filename
int len = strlen(file) + strlen(TEXTURE_DIRECTORY) + 1;
fileWithPath = new wchar_t[len + 1];
::nameWithDir(fileWithPath, TEXTURE_DIRECTORY, file, len);
}
// apiTexture on the graphics device
if (cube)
apiTexture = CreateAPICubeTexture(fileWithPath);
else
apiTexture = CreateAPITexture(fileWithPath, filter);
if (fileWithPath) delete [] fileWithPath;
}
- iAPITexture interface - add CreateAPICubeTexture declaration
class iAPITexture {
public:
virtual iAPITexture* clone() const = 0;
// execution
virtual void attach() = 0;
virtual void setFilter(unsigned flags) = 0;
virtual void detach() = 0;
virtual void render(const Rectf&, unsigned char, bool = false) = 0;
// termination
virtual void suspend() = 0;
virtual void release() = 0;
virtual void Delete() const = 0;
};
iAPITexture* CreateAPITexture(const wchar_t* file, unsigned filter);
iAPITexture* CreateAPICubeTexture(const wchar_t* file);
- APITexture.h
- APICubeTexture class definition
class APICubeTexture : public iAPITexture, public APIBase {
wchar_t* file; // points to file with texture image
unsigned filter; // default texture filtering flags
IDirect3DCubeTexture9* tex; // interface to texture COM object
virtual ~APICubeTexture();
void setup();
public:
APICubeTexture(const wchar_t* file);
APICubeTexture(const APICubeTexture&);
iAPITexture& operator=(const APICubeTexture&);
iAPITexture* clone() const { return new APICubeTexture(*this); }
// execution
void attach();
void setFilter(unsigned filter) {}
void detach();
void render(const Rectf&, unsigned char, bool) {}
// suspension
void suspend();
// termination
void release();
void Delete() const { delete this; }
};
- APICubeTexture class implementation
//-------------------------------- APICubeTexture -----------------------------
//
// The APICubeTexture class implements a texture at the API level
//
iAPITexture* CreateAPICubeTexture(const wchar_t* file) {
return new APICubeTexture(file);
}
// constructor initializes the texture identifier
//
APICubeTexture::APICubeTexture(const wchar_t* file) {
if (file) {
int len = strlen(file);
this->file = new wchar_t[len + 1];
strcpy(this->file, file, len);
}
else
this->file = nullptr;
tex = nullptr;
}
APICubeTexture::APICubeTexture(const APICubeTexture& src) {
file = nullptr;
tex = nullptr;
*this = src;
}
iAPITexture& APICubeTexture::operator=(const APICubeTexture& src) {
if (this != &src) {
if (file)
delete [] file;
if (src.file) {
int len = strlen(src.file);
file = new wchar_t[len + 1];
strcpy(file, src.file, len);
}
else
file = nullptr;
suspend();
tex = nullptr;
}
return *this;
}
// setup creates the api texture from the texture file
//
void APICubeTexture::setup() {
// create a texture COM object from the texture file
//
HRESULT hr;
if (file && FAILED(hr = D3DXCreateCubeTextureFromFileEx(d3dd, file,
0, D3DX_DEFAULT, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, D3DX_DEFAULT,
D3DX_DEFAULT, 0, nullptr, nullptr, &tex))) {
error(L"APICubeTexture::11 Failed to create texture COM object from file");
tex = nullptr;
}
}
// attach attaches the api texture to sampling stage i
//
void APICubeTexture::attach() {
if (!tex) setup();
if (tex)
d3dd->SetTexture(0, tex);
}
// detach detaches the api texture from sampling stage 0
//
void APICubeTexture::detach() {
if (tex)
d3dd->SetTexture(0, nullptr);
}
// suspend releases the api texture
//
void APICubeTexture::suspend() {
// release the Interface to the texture COM object
if (tex) {
tex->Release();
tex = nullptr;
}
}
// releases suspends the api texture
//
void APICubeTexture::release() {
suspend();
}
// destructor releases the api texture
//
APICubeTexture::~APICubeTexture() {
release();
}
- effects.fx
- vertex shader outpu structure
//-------------------------------- Skybox -------------------------------------
//
struct FS_Skybox {
float4 pos : POSITION;
float3 tex : TEXCOORD0;
};
- vertex shader
FS_Skybox skyboxVertexShader(float3 pos : POSITION) {
FS_Skybox output = (FS_Skybox) 0;
output.pos = mul(float4(pos, 0), world); // Note the 0, this so the skybox rotates, but without translation
output.pos = mul(output.pos, viewProjection).xyww; // The z coordinate is replaced by w, so that the point is always projected at infinity
output.tex = pos.xzy; // Note that y and z are switched to match the texture coordinate system
return output;
}
- skybox sampler state
texture skyBox;
samplerCUBE skySampler = sampler_state {
texture = <skyBox>;
MagFilter = LINEAR;
Minfilter = LINEAR;
Mipfilter = LINEAR;
AddressU = MIRROR;
AddressV = MIRROR;
AddressW = MIRROR;
};
- fragment shader
float4 skyboxFragmentShader(FS_Skybox input) : COLOR0 {
return texCUBE(skySampler, input.tex);
}
- skybox technique
technique skybox {
pass {
AlphaBlendEnable = false;
ZENABLE = true;
ZWRITEENABLE = false; // By not storing the skybox's z-buffer value, it enables objects behind the skybox to be drawn, giving a realist look (e.g. an airplane in the distance)
CullMode = None;
VertexShader = compile vs_3_0 skyboxVertexShader();
PixelShader = compile ps_3_0 skyboxFragmentShader();
}
}
To Do
Resources
- RB Whitaker's notes on skyboxes
- Koen Samyn's knol
- Game Engineer's demo
- Terragen Texture Tool
- DirectX Utility - DirectX Texture Tool
Week 9 - Mar 11
This Week
- Mathematics of Lighting
- Lighting in Vertex Shaders
- Notation
- Ga - global ambient color
- Ca - material ambient color
- Cd - material diffuse color
- Cs - material specular color
- Lai - ambient color of light i
- Ldi - diffuse color of light i
- Lsi - specular color of light i
- Ldiri - direction vector of light i
- N - normal to the surface at the vertex
- Attenuation and Spotlight Factors
- Atteni - attenuation of light i
- di - distance from light i
- di = |Ldiri|
- a0 - constant attenuation factor
- a1 - linear attenuation factor
- a2 - quadratic attenuation factor
- Atteni = 1/(a0 + a1 di + a2 di2)
- Atteni = [0, 1]
- Spoti - spot factor of light i
- Spoti = {[ri - cos(phii/2)]/[cos(thetai/2) - cos(phii/2)]}fi
- ri - cosine of angle from axis of spotlighti
- ri = norm(- light direction in camera space) . norm(Ldiri)
- phii - penumbra (exterior cone) angle of spotlighti
- thetai - umbra (interior cone) angle of spotlighti
- fi - falloff factor of spotlighti
- Blinn-Phong and Phong
- V - viewpoint vector
- V = norm(Cameraposition - Vertexposition)
- V - viewpoint vector
- Phong - account accurately for position of viewer
- Specular reflectance = (Ri . V)pi
- Ri - reflection vector
- Ri = 2 * (N . Ldiri) N - Ldiri
- pi - true specular power of light i
- Blinn-Phong - use halfway vector instead of reflection vector - adjust power to compensate
- Specular reflectance = (N . Hi)p'i
- Hi - halfway vector
- Hi = norm(V + Ldiri)
- Hi = norm([0,0,1] + Ldiri) - less computationally intensive - assumes that camera is at infinity along z axis
- p'i - adjusted specular power of light i
- Ambient
- Ca * ( Ga + sum [Lai * Atteni * Spoti] )
- Diffuse
- Cd * sum [ Ldi * (N . Ldiri) * Atteni * Spoti ]
- Specular
- Cs * sum [ Lsi * (N . Hi)p'i * Atteni * Spoti ] - Blinn-Phong
- Cs * sum [ Lsi * (Ri . V)pi * Atteni * Spoti ] - Phong
- HLSL Intrinsic Functions
- normalize() - normalize a vector
- dot(,) - dot product of two vectors of any size
- length() - length of a vector
- saturate() - clamp scalar, vector, or matrix to [0, 1]
- Notation
- effects.fx - for unlit vertices and fragments
- Uniform data
#define MLIGHTS 4
#define POINT_LIGHT 0
#define SPOT_LIGHT 1
#define DIRECTIONAL_LIGHT 2
// Types
//
// Light holds the data for a single static light in world space
//
struct Light {
int type; // POINT_LIGHT, SPOT_LIGHT, DIRECTIONAL_LIGHT
float3 ambient;
float3 diffuse;
float3 specular;
float3 direction; // in world space
float3 position; // in world space
float3 attenuation; // .xyz for 1.0f/ (.x + .y * d + .z * d * d)
float3 spot; // .x = cos(phi/2), .y = cos(theta/2), .z = falloff
float range; // where attenuation becomes 0
};
// Material holds the reflectivity properties of the material
//
struct Material {
float4 ambient;
float4 diffuse;
float4 specular;
float power;
};
// Uniform Data (constant for the stream of vertices)
//
// Geometry
float4x4 viewProjection; // view * projection transformation
float4x4 world; // world transformation
float4 viewPoint; // camera viewpoint for specular calcs
float3 heading; // camera heading for specular calcs
int noLights; // no of static lights
Light light[MLIGHTS]; // static lights
bool lightOn[MLIGHTS]; // light switch
// Uniform Data (constant for a stream of fragments)
//
float4 ambient; // global ambient light - always on
Material material; // material reflectivity
bool texOn; // texture switch
texture texMap;
sampler2D tex = sampler_state {
texture = <texMap>;
Filter = MIN_MAG_MIP_LINEAR;
AddressU = MIRROR;
AddressV = MIRROR;
};
- Varying Data - simple unlit objects
//-------------------------------- Simple Unlit Objects -----------------------
//
// UnlitInput holds the original data for a vertex in the stream
//
struct UnlitVSInput {
float3 position : POSITION; // position in local space
float3 normal : NORMAL; // normal in local space
float2 texCoord : TEXCOORD0; // texture coordinates
};
// UnlitOutput holds the transformed data for the vertex
//
struct UnlitVSOutput {
float4 position : POSITION; // position in homogeneous clip space
float2 texCoord : TEXCOORD; // texture coordinates
float3 normal : TEXCOORD1; // lighting normal in world space
float3 toViewer : TEXCOORD2; // direction to viewer in world space
float3 toLight[MLIGHTS] : TEXCOORD3; // light vector
};
// UnlitFSInput holds the input data for a single fragment of the stream
//
struct UnlitFSInput {
float2 texCoord : TEXCOORD0; // texture coordinate at this fragment
float3 normal : TEXCOORD1; // lighting normal in world space
float3 toViewer : TEXCOORD2; // direction to viewer in world space
float3 toLight[MLIGHTS] : TEXCOORD3; // direction from fragment to light i
};
- Vertex Shader
// unlitVShader transforms vertex and lighting data for simple unlit objects
//
UnlitVSOutput unlitVShader(UnlitVSInput input) {
UnlitVSOutput output; // result returned by this function
// Transform the vertex coordinates to homogeneous clip coordinates
//
// A more efficient algorithm would accept the world*view*projection
// tranformation as one uniform matrix and avoid the 2-stage product
// This will require a bit of restructuring of the application code.
//
output.position = mul(float4(input.position, 1.0), world); // local to world
output.position = mul(output.position, viewProjection); //... to clip
// Determine the vector from this vertex to the viewer
output.toViewer = viewPoint.xyz - input.position;
// Determine the vector from this vertex to light source i in local space
// (assumes that light position and direction are specified in local space)
for (int i = 0; i < noLights; i++)
if (light[i].type == DIRECTIONAL_LIGHT)
output.toLight[i] = - light[i].direction;
else
output.toLight[i] = light[i].position - input.position;
// pass the normal and texture coordinates along unaltered
//
output.normal = input.normal;
output.texCoord = input.texCoord;
return output;
}
- Fragment Shader
// This fragment shader processes lighting for noLights lights in object space
// and returns a pixel colour
//
float4 unlitFShader(UnlitFSInput input) : COLOR {
float4 output; // result returned by this function
float3 normal; // normal to the fragment
float3 toViewer; // from fragment to the camera
float3 toLightSource; // from fragment to current light source
// lighting contribution accumulators
float3 ambientLight = ambient.xyz;
float3 diffuseLight = (float3)0;
float3 specularLight = (float3)0;
// lighting calculation factors
float diffuseFactor, reflectFactor, distance, factor;
float attenuationFactor, spotFactor, rho;
// normalize the fragment input
normal = normalize(input.normal);
toViewer = normalize(input.toViewer);
// perform calculations for each light in turn
for (int i = 0; i < noLights && i < MLIGHTS; i++) {
if (lightOn[i]) {
// diffuse and reflection factors
toLightSource = normalize(input.toLight[i]);
diffuseFactor = saturate(dot(normal, toLightSource));
reflectFactor = saturate(dot(normalize(2 * diffuseFactor *
normal - toLightSource), toViewer));
// attenuation
if (light[i].type != DIRECTIONAL_LIGHT) {
distance = length(input.toLight[i]);
if (distance < light[i].range) {
attenuationFactor = light[i].attenuation.x +
light[i].attenuation.y * distance +
light[i].attenuation.z * distance * distance;
attenuationFactor = 1.0f / attenuationFactor;
}
else
attenuationFactor = 0.0f;
}
else
attenuationFactor = 1.0f;
// spot
if (light[i].type == SPOT_LIGHT) {
rho = saturate(dot(toLightSource,
normalize(-light[i].direction)));
if (rho <= light[i].spot.x)
spotFactor = 0.0f;
else if (rho <= light[i].spot.y)
spotFactor = pow(abs(
(rho - light[i].spot.x)/
(light[i].spot.y - light[i].spot.x)),
light[i].spot.z);
else
spotFactor = 1.0f;
}
else
spotFactor = 1.0;
factor = attenuationFactor * spotFactor;
// accumulate ambient, diffuse, and specular elements of light i
ambientLight += factor * light[i].ambient.xyz;
diffuseLight += factor * diffuseFactor * light[i].diffuse.xyz;
specularLight += factor * light[i].specular.xyz *
pow(reflectFactor, material.power);
}
}
// apply material reflectivity to each accumulated element of light
// to obtain the colour of the lit fragment
//
output.xyz =
saturate(material.ambient.xyz * ambientLight) +
saturate(material.diffuse.xyz * diffuseLight) +
saturate(material.specular.xyz * specularLight);
output.w = material.diffuse.w;
// sample the texture
//
if (texOn)
output *= tex2D(tex, input.texCoord);
return output;
}
- techniques - opaque and translucent
technique opaque {
pass {
AlphaBlendEnable = false;
VertexShader = compile vs_3_0 unlitVShader();
PixelShader = compile ps_3_0 unlitFShader();
}
}
technique translucent {
pass {
AlphaBlendEnable = true;
VertexShader = compile vs_3_0 unlitVShader();
PixelShader = compile ps_3_0 unlitFShader();
}
}
To Do
Resources
Week 10 - Mar 18
This Week
- Texturing - Identification
- connection between Design.cpp and effects.fx
- iTexture.h
- add texture and texture state identifier
iTexture* CreateTexture(const wchar_t* file, const char* str,
const char* isOn);
- Texture.h
Texture(const wchar_t*, const char*, const char*);
- Texture.cpp
// constructor initializes the texture identifier
//
Texture::Texture(const wchar_t* file, const char* str,
const char* isOn) : filter(0u) {
if (file) {
int len = strlen(file);
this->file = new wchar_t[len + 1];
strcpy(this->file, file, len);
}
else
this->file = nullptr;
tex = nullptr;
#if PIPELINE == FIXED_FUNCTION
#elif PIPELINE == PROGRAMMABLE
#elif PIPELINE == PROGRAMMABLE_EFFECT
textureHandle = effect->GetParameterByName(0, str);
textureonHandle = effect->GetParameterByName(0, isOn);
#endif
}
effect->SetTexture(textureHandle, tex);
effect->SetBool(textureonHandle, true);
- iAPITexture.h
- add texture and texture state identifier
iAPITexture* CreateAPITexture(const wchar_t* file, const char* str,
const char* isOn);
- APITexture.h
D3DXHANDLE textureHandle; // points to texture
D3DXHANDLE textureonHandle; // points to texture on status
public:
APITexture(const wchar_t*, const char*, const char*);
- APITexture.cpp
// constructor initializes the texture identifier
//
APITexture::APITexture(const wchar_t* file, const char* str,
const char* isOn, AddressMode m) : filter(0u), mode(m) {
if (file) {
int len = strlen(file);
this->file = new wchar_t[len + 1];
strcpy(this->file, file, len);
}
else
this->file = nullptr;
tex = nullptr;
target = nullptr;
#if PIPELINE == FIXED_FUNCTION
#elif PIPELINE == PROGRAMMABLE
#elif PIPELINE == PROGRAMMABLE_EFFECT
textureHandle = effect->GetParameterByName(0, str);
textureonHandle = effect->GetParameterByName(0, isOn);
#endif
}
effect->SetTexture(textureHandle, tex);
effect->SetBool(textureonHandle, true);
- Texturing - Tiling
- iGraphic.h
- add u v parameters, both of which default to 1
iGraphic* CreateBox(float, float, float, float, float, float,
float = 1, float = 1);
iGraphic* CreateIBox(float, float, float, float, float, float,
float = 1, float = 1);
iGraphic* CreateRectangleList(float, float, float, float,
float = 1, float = 1);
iGraphic* CreateIRectangleList(float, float, float, float,
float = 1, float = 1);
iGraphic* CreateMeshBox(float, float, float, float, float,
float, float = 1, float = 1);
- Graphic.cpp
- extend add functions to include u v parameters
//-------------------------------- Graphic Structures -------------------------
//
// prototypes for add() function used by the Create...() functions
void add(VertexList<Vertex>*, const Vector&, const Vector&, const Vector&,
const Vector&, const Vector&, float = 1, float = 1);
void add(IndexedList<Vertex>*, const Vector&, const Vector&, const Vector&,
const Vector&, const Vector&, float = 1, float = 1);
void add(CustomMesh<Vertex>*, const Vector&, const Vector&, const Vector&,
const Vector&, const Vector&, float = 1, float = 1);
// CreateBox builds a triangle vertex list for a brick-like box from two
// extreme points one face at a time with all faces having the same attributes
//
iGraphic* CreateBox(float minx, float miny, float minz, float maxx,
float maxy, float maxz, float u, float v) {
VertexList<Vertex>* vertexList =
(VertexList<Vertex>*)CreateVertexList<Vertex>(TRIANGLE_LIST, 12);
float x = (minx + maxx) / 2;
float y = (miny + maxy) / 2;
float z = (minz + maxz) / 2;
minx -= x;
miny -= y;
minz -= z;
maxx -= x;
maxy -= y;
maxz -= z;
// bounding sphere
float max;
max = maxx > maxy ? maxx : maxy;
max = maxz > max ? maxz : max;
vertexList->setRadius(1.73205f * max);
// locate centroid at origin
Vector p1 = Vector(minx, miny, minz),
p2 = Vector(minx, maxy, minz),
p3 = Vector(maxx, maxy, minz),
p4 = Vector(maxx, miny, minz),
p5 = Vector(minx, miny, maxz),
p6 = Vector(minx, maxy, maxz),
p7 = Vector(maxx, maxy, maxz),
p8 = Vector(maxx, miny, maxz);
add(vertexList, p1, p2, p3, p4, Vector(0, 0, -1), u, v); // front
add(vertexList, p4, p3, p7, p8, Vector(1, 0, 0), u, v); // right
add(vertexList, p8, p7, p6, p5, Vector(0, 0, 1), u, v); // back
add(vertexList, p6, p2, p1, p5, Vector(-1, 0, 0), u, v); // left
add(vertexList, p1, p4, p8, p5, Vector(0, -1, 0), u, v); // bottom
add(vertexList, p2, p6, p7, p3, Vector(0, 1, 0), u, v); // top
return vertexList;
}
iGraphic* CreateIBox(float minx, float miny, float minz, float maxx,
float maxy, float maxz, float u, float v) {
IndexedList<Vertex>* indexedList = (IndexedList<Vertex>*)
CreateIndexedList<Vertex, unsigned short>(TRIANGLE_LIST, 12);
float x = (minx + maxx) / 2;
float y = (miny + maxy) / 2;
float z = (minz + maxz) / 2;
minx -= x;
miny -= y;
minz -= z;
maxx -= x;
maxy -= y;
maxz -= z;
// bounding sphere
float max;
max = maxx > maxy ? maxx : maxy;
max = maxz > max ? maxz : max;
indexedList->setRadius(1.73205f * max);
// locate centroid at origin
Vector p1 = Vector(minx, miny, minz),
p2 = Vector(minx, maxy, minz),
p3 = Vector(maxx, maxy, minz),
p4 = Vector(maxx, miny, minz),
p5 = Vector(minx, miny, maxz),
p6 = Vector(minx, maxy, maxz),
p7 = Vector(maxx, maxy, maxz),
p8 = Vector(maxx, miny, maxz);
add(indexedList, p1, p2, p3, p4, Vector(0, 0, -1), u, v); // front
add(indexedList, p4, p3, p7, p8, Vector(1, 0, 0), u, v); // right
add(indexedList, p8, p7, p6, p5, Vector(0, 0, 1), u, v); // back
add(indexedList, p6, p2, p1, p5, Vector(-1, 0, 0), u, v); // left
add(indexedList, p1, p4, p8, p5, Vector(0, -1, 0), u, v); // bottom
add(indexedList, p2, p6, p7, p3, Vector(0, 1, 0), u, v); // top
return indexedList;
}
// CreateRectangleList builds a triangle list in the x-y plane from its two
// extreme points
//
iGraphic* CreateRectangleList(float minx, float miny, float maxx, float maxy,
float u, float v) {
VertexList<Vertex>* vertexList =
(VertexList<Vertex>*)CreateVertexList<Vertex>(TRIANGLE_LIST, 2);
float x = (minx + maxx) / 2, y = (miny + maxy) / 2;
minx -= x;
miny -= y;
maxx -= x;
maxy -= y;
// bounding sphere
float max;
max = maxx > maxy ? maxx : maxy;
vertexList->setRadius(1.73205f * max);
// locate centroid at origin
Vector p1 = Vector(minx, miny, 0),
p2 = Vector(minx, maxy, 0),
p3 = Vector(maxx, maxy, 0),
p4 = Vector(maxx, miny, 0);
add(vertexList, p1, p2, p3, p4, Vector(0, 0, -1), u, v);
return vertexList;
}
// CreateIRectangleList builds an indexed triangle list in the x-y plane from
// its two extreme points
//
iGraphic* CreateIRectangleList(float minx, float miny, float maxx, float maxy,
float u, float v) {
IndexedList<Vertex>* indexedList = (IndexedList<Vertex>*)
CreateIndexedList<Vertex, unsigned short>(TRIANGLE_LIST, 2);
float x = (minx + maxx) / 2, y = (miny + maxy) / 2;
minx -= x;
miny -= y;
maxx -= x;
maxy -= y;
// bounding sphere
float max;
max = maxx > maxy ? maxx : maxy;
indexedList->setRadius(1.73205f * max);
// locate centroid at origin
Vector p1 = Vector(minx, miny, 0),
p2 = Vector(minx, maxy, 0),
p3 = Vector(maxx, maxy, 0),
p4 = Vector(maxx, miny, 0);
add(indexedList, p1, p2, p3, p4, Vector(0, 0, -1), u, v);
return indexedList;
}
// CreateIRectangleList builds an indexed triangle list in the x-y plane from
// its two extreme points
//
iGraphic* CreateBRectangleList(float minx, float miny, float maxx, float maxy,
float u, float v) {
IndexedList<BumpVertex>* indexedList = (IndexedList<BumpVertex>*)
CreateIndexedList<BumpVertex, unsigned short>(TRIANGLE_LIST, 2);
float x = (minx + maxx) / 2, y = (miny + maxy) / 2;
minx -= x;
miny -= y;
maxx -= x;
maxy -= y;
// bounding sphere
float max;
max = maxx > maxy ? maxx : maxy;
indexedList->setRadius(1.73205f * max);
// locate centroid at origin
Vector p1 = Vector(minx, miny, 0),
p2 = Vector(minx, maxy, 0),
p3 = Vector(maxx, maxy, 0),
p4 = Vector(maxx, miny, 0);
add(indexedList, p1, p2, p3, p4, Vector(0, 0, -1), u, v);
return indexedList;
}
// CreateMeshBox builds a mesh for a brick-like box from two extreme points one
// face at a time with each faces having distinct attributes
//
iGraphic* CreateMeshBox(float minx, float miny, float minz, float maxx,
float maxy, float maxz, float u, float v) {
unsigned attribute[] = {0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5};
CustomMesh<>* mesh = (CustomMesh<>*)
CreateCustomMesh<Vertex, unsigned short>(attribute, 12, 36, 24, 6);
float x = (minx + maxx) / 2, y = (miny + maxy) / 2, z = (minz + maxz) / 2;
minx -= x;
miny -= y;
minz -= z;
maxx -= x;
maxy -= y;
maxz -= z;
// bounding sphere
float max;
max = maxx > maxy ? maxx : maxy;
max = maxz > max ? maxz : max;
mesh->setRadius(1.73205f * max);
// one face at a time
Vector p1 = Vector(minx, miny, minz),
p2 = Vector(minx, maxy, minz),
p3 = Vector(maxx, maxy, minz),
p4 = Vector(maxx, miny, minz),
p5 = Vector(minx, miny, maxz),
p6 = Vector(minx, maxy, maxz),
p7 = Vector(maxx, maxy, maxz),
p8 = Vector(maxx, miny, maxz);
add(mesh, p1, p2, p3, p4, Vector(0, 0, -1), u, v); // front
add(mesh, p4, p3, p7, p8, Vector(1, 0, 0), u, v); // right
add(mesh, p8, p7, p6, p5, Vector(0, 0, 1), u, v); // back
add(mesh, p6, p2, p1, p5, Vector(-1, 0, 0), u, v); // left
add(mesh, p1, p4, p8, p5, Vector(0, -1, 0), u, v); // bottom
add(mesh, p2, p6, p7, p3, Vector(0, 1, 0), u, v); // top
return mesh;
}
void add(VertexList<Vertex>* vertexList, const Vector& p1, const Vector& p2,
const Vector& p3, const Vector& p4, const Vector& n, float u, float v) {
vertexList->add(Vertex(p1, n, 0, v));
vertexList->add(Vertex(p2, n, 0, 0));
vertexList->add(Vertex(p3, n, u, 0));
vertexList->add(Vertex(p1, n, 0, v));
vertexList->add(Vertex(p3, n, u, 0));
vertexList->add(Vertex(p4, n, u, v));
}
void add(IndexedList<Vertex>* indexedList, const Vector& p1, const Vector& p2,
const Vector& p3, const Vector& p4, const Vector& n, float u, float v) {
unsigned v1 = indexedList->add(Vertex(p1, n, 0, v));
unsigned v2 = indexedList->add(Vertex(p2, n, 0, 0));
unsigned v3 = indexedList->add(Vertex(p3, n, u, 0));
unsigned v4 = indexedList->add(Vertex(p4, n, u, v));
indexedList->add(v1);
indexedList->add(v2);
indexedList->add(v3);
indexedList->add(v1);
indexedList->add(v3);
indexedList->add(v4);
}
void add(CustomMesh<Vertex>* mesh, const Vector& p1, const Vector& p2,
const Vector& p3, const Vector& p4, const Vector& n, float u, float v) {
unsigned v1 = mesh->add(Vertex(p1, n, 0, v));
unsigned v2 = mesh->add(Vertex(p2, n, 0, 0));
unsigned v3 = mesh->add(Vertex(p3, n, u, 0));
unsigned v4 = mesh->add(Vertex(p4, n, u, v));
mesh->add(v1);
mesh->add(v2);
mesh->add(v3);
mesh->add(v1);
mesh->add(v3);
mesh->add(v4);
}
- Texturing - Address Modes
- APITexture.h
D3DXHANDLE textureMode; // points to texture mode
- APITexture.cpp
effect->SetInt(textureMode, (int)mode);
- effects.fx - uniform variables
// Addressing Modes
#define TEX_WRAP 0
#define TEX_CLAMP 1
#define TEX_MIRROR 2
//...
bool tex_1_On; // texture switch
int tex_mode; // addressing mode
texture tex_1;
sampler2D tex_1_ww = sampler_state {
texture = <tex_1>;
Filter = MIN_MAG_MIP_LINEAR;
AddressU = WRAP;
AddressV = WRAP;
};
sampler2D tex_1_mm = sampler_state {
texture = <tex_1>;
Filter = MIN_MAG_MIP_LINEAR;
AddressU = MIRROR;
AddressV = MIRROR;
};
sampler2D tex_1_cc = sampler_state {
texture = <tex_1>;
Filter = MIN_MAG_MIP_LINEAR;
AddressU = CLAMP;
AddressV = CLAMP;
};
- effects.fx - fragment shader
// sample the texture
//
if (tex_1_On) {
if (tex_mode == TEX_CLAMP)
output *= tex2D(tex_1_cc, input.texCoord);
else if (tex_mode == TEX_WRAP)
output *= tex2D(tex_1_ww, input.texCoord);
else if (tex_mode == TEX_MIRROR)
output *= tex2D(tex_1_mm, input.texCoord);
}
- Texturing - Multi-Texturing
- effects.fx - uniform variables
bool tex_2_On; // texture switch
texture tex_2;
sampler2D tex_2_ww = sampler_state {
texture = <tex_2>;
Filter = MIN_MAG_MIP_LINEAR;
AddressU = WRAP;
AddressV = WRAP;
};
sampler2D tex_2_mm = sampler_state {
texture = <tex_2>;
Filter = MIN_MAG_MIP_LINEAR;
AddressU = MIRROR;
AddressV = MIRROR;
};
sampler2D tex_2_cc = sampler_state {
texture = <tex_2>;
Filter = MIN_MAG_MIP_LINEAR;
AddressU = CLAMP;
AddressV = CLAMP;
};
- effects.fx - fragment shader
if (tex_2_On) {
if (tex_mode == TEX_CLAMP)
output *= tex2D(tex_2_cc, input.texCoord);
else if (tex_mode == TEX_WRAP)
output *= tex2D(tex_2_ww, input.texCoord);
else if (tex_mode == TEX_MIRROR)
output *= tex2D(tex_2_mm, input.texCoord);
}