Open main menu

CDOT Wiki β

GAM670/DPS905 Weekly Schedule 20121

Revision as of 07:50, 17 February 2012 by Chris Szalwinski (talk | contribs) (Fragment Shader)

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

To Do

  1. add your name to the student list
  2. 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()
  • Geometry
    • Plane
      normal + constant - examples
      equation of a plane: dot(n, x) + D = 0
      positive side of a plane dot(n, x) + D > 0
  • Collision Detection
    types of colliders
    spheres
    planes
    axis-aligned bounding boxes
    oriented bounding boxes

To Do

  • Research the feature that you are going to add and prepare a plan of action
  • Prepare a team page for your team so that repos can be ordered
  • Add a section to your team page to track your project and solicit commentary

Resources

Week 3 - Jan 23

This Week

  • Collision Detection (cont'd)
    Shape
    Shape : Frame
    Shape::setRadius()
    Shape::getRadius()
    Shape::setRadius(float r);
    Shape::setRadius(float x, float y, float z);
    Shape::getRadius() const { return radius; }
    Shape::setPlane(Vector n, float d);
    Shape::setAxisAligned(Vector min, Vector max);
  • Comprehensive Camerawork
    rotation about an axis
    order of rotation matters
    Euler angles
    3-2-1 angles
    gimbal lock
    StephenSeefeld.net
    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)
    Dorst's site
    Hitzer's site
  • Visibility Determination
    • test a point for presence within a set of planes
      normal calculations - general rotation matrix - vector and angle
    • ViewFrustum
      parameter - view * projection
      6 planes
      near and far planes
      left and right planes
      top and bottom planes
      coding
      constructor
      ViewFrustum::contains()
    • Finite Size of Objects
      Expansion of the View Frustum
  • Index Buffers
    amount of storage needed for vertex data
    duplication of vertex data
    indexing
    indexed primitives

To Do

Resources


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
    Custom Mesh
    Create a Mesh
    FVF settings
    DrawSubset
    DrawIndexedPrimitive parameters
    APIGraphic.h code
    X File
    Create Mesh from File
  • SkyBox
    definition of a skybox
    attachment to camera
    inverted coordinates
    skybox textures
    Graphic.cpp code
    more complicated forms - skydome
  • Billboards
    definition, purpose of a billboard
    types of billboards
    screen-aligned - useful for annotation text, lens flares
    normal is opposite to camera heading
    up is camera->up
    axial - useful for cylindrical symmetry - trees (textured object does not face straight on)
    up is fixed
    normal faces the viewer as much as possible
    view_plane - no distortion - useful for
    normal is fixed (opposite to camera heading)
    up is open to change
    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
    Object.h and Object.cpp code
  • Texture Filtering
    mip maps
    multum in parvo
    texture creation
    APITexture::SetSamplerState()
  • DirectX Errors
    DirectX Utilities - Lookup Tool
    APIDisplay::restore() example

To Do

Resources

Week 5 - Feb 6

This Week

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();
D3DXCompileShaderFromFile()
D3DXGetPixelShaderProfile()
IDirect3DDevice9::CreatePixelShader()
        d3dd->SetPixelShader(fragmentShader);
IDirect3DDevice9::SetPixelShader()
APIDisplay.cpp - beginDrawFrame()
        Colour colour(red, green, blue);
        uniformFS->SetFloatArray(d3dd, "ambient", (FLOAT*)&colour, 4);
        uniformFS->SetInt(d3dd, "noLights", 4);
ID3DXConstantTable::SetFloatArray()
ID3DXConstantTable::SetInt()
APIDisplay.cpp - set()
                uniformFS->SetBool(d3dd, "lighting", b);
ID3DXConstantTable::SetBool()
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]);
ID3DXConstantTable::SetFloat()
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()
// 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 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 Display
    • 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