GAM670/DPS905 Weekly Schedule 20121
GAM670/DPS905 | Weekly Schedule | Student List | Project Requirements | Teams and their Projects | Student Resources
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
- DirectX Errors
- DirectX Utilities - Lookup Tool
- APIDisplay::restore() example
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 <>
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;
}