GAM670/DPS905 Weekly Schedule 20121
From Open Source@Seneca
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); }
