torus.cpp

This example renders a rotating colored and lit torus. It is a more advanced version of the cube::cpp example, which uses more features of Direct3D.

This example demonstrates the following features:

The example is split into a Model class and a View class.

The Model class manages shared rendering resources, such as the vertex and index buffers and the shaders needed for drawing the torus model. It also provides the methods for initializing, updating and rendering the torus.

The View class encapsulates a tk11::Window object along with view-specific variables and resources, such as the view and projection matrices. It is responsible for handling window resize and user input, and it implements the render callback, which calls the render method of the Model.

The Application class creates one instance of Model and two instances of View. This causes two windows to be opened, in which the torus can be seen from different angles.

Here is the vertex shader used by this example (torus.vsh)

//          Copyright Florian Winter 2010 - 2010.
// Distributed under the Boost Software License, Version 1.0.
//    (See accompanying file LICENSE_1_0.txt or copy at
//          http://www.boost.org/LICENSE_1_0.txt)


// Per-scene constants
cbuffer PerScene {
  // Model-view-projection matrix
  float4x4 model_view_projection_matrix;

  // Model matrix
  float4x4 model_matrix;

  // Inverse transpose model matrix
  float4x4 inverse_transpose_model_matrix;
}

struct Output {
  float4 position : SV_POSITION;
  float4 world_position : WORLDPOS;
  float3 normal : MY_NORMAL;
  float2 texcoord : MY_TEXCOORD;
};

// Main shader function
Output main(float4 position : POSITION, float3 normal : NORMAL, float2 texcoord : TEXCOORD) {
  // Transform position using model-view-projection matrix
  // and using model matrix
  Output output;
  output.position = mul(position, model_view_projection_matrix);
  output.world_position = mul(position, model_matrix);
  output.normal = mul(float4(normal, 0), inverse_transpose_model_matrix).xyz;
  output.texcoord = texcoord;
  return output;
}

Here is the pixel shader used by this example (torus.psh)

//          Copyright Florian Winter 2010 - 2010.
// Distributed under the Boost Software License, Version 1.0.
//    (See accompanying file LICENSE_1_0.txt or copy at
//          http://www.boost.org/LICENSE_1_0.txt)


cbuffer PerScene {
  // Position of the light source (in world space)
  float4 light_position;

  // Position of the viewer (in world space)
  float4 viewer_position;

  // Light color
  float4 light_color;

  // Diffuse color #1
  float4 diffuse_color1;

  // Diffuse color #2
  float4 diffuse_color2;

  // Ambient color
  float4 ambient_color;

  // Specilar exponent
  float specular_exponent;
}

float4 main(float4 position : SV_POSITION,
            float4 world_position : WORLDPOS,
            float3 normal : MY_NORMAL,
            float2 texcoord : MY_TEXCOORD) : SV_Target
{
  // Texture
  int i = texcoord.x * 20;
  int j = texcoord.y * 10;
  int c = (i ^ j) & 1;
  float4 diffuse_color = lerp(diffuse_color1, diffuse_color2, c);

  // Phong model
  float3 n = normalize(normal);
  float3 l = normalize(light_position - world_position).xyz;
  float3 v = normalize(viewer_position - world_position).xyz;
  float n_dot_l = dot(n, l);
  float3 h = 2 * n_dot_l * n - l;
  float v_dot_h = dot(v, h);
  float d = clamp(n_dot_l, 0, 1);
  float4 diffuse_contrib = d * diffuse_color;
  float s = clamp(v_dot_h, 0, 1);
  float4 specular_contrib = pow(s, specular_exponent) * light_color;
  float4 ambient_contrib = diffuse_color * ambient_color;
  return ambient_contrib + diffuse_contrib + specular_contrib;
}

Here is the source code:

//          Copyright Florian Winter 2010 - 2010.
// Distributed under the Boost Software License, Version 1.0.
//    (See accompanying file LICENSE_1_0.txt or copy at
//          http://www.boost.org/LICENSE_1_0.txt)

#include <tk11/tk11.hpp>

#include <boost/bind.hpp>

#include <D3DX10Math.h>

using namespace tk11;


// Model class
// Creates resources for, updates and renders the torus model.
class Model {
public:
  Model() {
    // Light source position is fixed
    light_position = D3DXVECTOR4(0, 0, 5, 1);
  }

  // Scene initialization callback.
  // Here we create resources, such as textures, buffers and shaders.
  void init(ID3D11Device_ptr device) {
    // Load shaders
    vertex_shader_code = load_compiled_shader("torus.Vso");
    pixel_shader_code = load_compiled_shader("torus.Pso");

    // Compute torus vertices
    const float pi = 3.1415926535f;
    const float r1 = 1.0f;
    const float r2 = 0.3f;
    std::vector<Vertex> vertices(num_vertices);
    for(std::size_t i=0, index=0; i<=num_rings; ++i) {
      for(std::size_t j=0; j<=num_segments; ++j, ++index) {
        // Compute texture coordinates (surface parameters)
        // Note that u is added to j. This creates a spiral pattern, which
        // allows us to draw the entire torus in one triangle strip without
        // using degenerate triangles.
        // (Yet we still need degenerate triangles to avoid texture
        // wrap-around)
        float u = float(i) / num_rings;
        float v = (float(j) + u) / num_segments;

        // Compute angles
        float u_angle = u * 2 * pi;
        float v_angle = v * 2 * pi;

        // Position
        float x = cos(u_angle) * (r1 + cos(v_angle) * r2);
        float y = sin(u_angle) * (r1 + cos(v_angle) * r2);
        float z = sin(v_angle) * r2;

        // Normal vector
        float nx = cos(u_angle) * cos(v_angle);
        float ny = sin(u_angle) * cos(v_angle);
        float nz = sin(v_angle);

        vertices[index].position = D3DXVECTOR3(x, y, z);
        vertices[index].normal = D3DXVECTOR3(nx, ny, nz);
        vertices[index].texcoords = D3DXVECTOR2(u, v);
      }
    }

    // Create vertex buffer
    vertex_buffer = create_vertex_buffer(device, &vertices[0], vertices.size());

    // Compute torus indices
    std::vector<int> indices(num_indices);
    for(std::size_t i=0, index=0; i<=num_vertices; ++i) {
      indices[index++] = static_cast<int>(i % num_vertices);
      indices[index++] = static_cast<int>((i + num_segments+1) % num_vertices);
    }
    index_buffer = create_index_buffer(device, &indices[0], indices.size());

    // Set up input element descriptions
    D3D11_INPUT_ELEMENT_DESC elements[3] = {
      make_vector_input_element<float, 3>("POSITION"),
      make_vector_input_element<float, 3>("NORMAL"),
      make_vector_input_element<float, 2>("TEXCOORD")
    };

    // Create input layout
    input_layout = create_input_layout(device, elements, 3, vertex_shader_code);

    // Create vertex shader
    vertex_shader = create_vertex_shader(device, vertex_shader_code);

    // Create vertex shader constant buffer
    vertex_shader_cbuffer = create_typed_constant_buffer<VS_Constants>(device);

    // Create pixel shader
    pixel_shader = create_pixel_shader(device, pixel_shader_code);

    // Create pixel shader constant buffer
    pixel_shader_cbuffer = create_typed_constant_buffer<PS_Constants>(device);

    // Create rasterizer state
    D3D11_Rasterizer_Desc rasterizer_desc;
    //rasterizer_desc.FillMode = D3D11_FILL_WIREFRAME;
    rasterizer_state = create_rasterizer_state(device, rasterizer_desc);
  }

  // Scene update callback.
  // Here we update all variables that change over time.
  void update(const Duration& elapsed_time) {
    // Time in seconds
    float t = time_div_float<float>(elapsed_time, seconds(1));

    // Update model matrix
    D3DXMATRIX rot1;
    D3DXMATRIX rot2;
    D3DXVECTOR3 axis1(1,0,0);
    D3DXVECTOR3 axis2(0,1,0);
    float angle1 = t * 2 * 3.1415926535f * 0.02f;
    float angle2 = t * 2 * 3.1415926535f * 0.1f;
    D3DXMatrixRotationAxis(&rot1, &axis1, angle1);
    D3DXMatrixRotationAxis(&rot2, &axis2, angle2);
    D3DXMatrixMultiply(&model_matrix, &rot1, &rot2);
  }

  // Model rendering
  void render(ID3D11DeviceContext_ptr context,
    const D3DXMATRIX& projection_matrix, const D3DXMATRIX& view_matrix,
    const D3DXVECTOR4& viewer_position) const
  {
    // Compute model-view-projection matrix
    // by multiplying model, view and projection matrices
    VS_Constants vs_constants;
    D3DXMatrixMultiply(&vs_constants.model_view_projection_matrix, &model_matrix, &view_matrix);
    D3DXMatrixMultiply(&vs_constants.model_view_projection_matrix, &vs_constants.model_view_projection_matrix, &projection_matrix);

    // A D3DXMATRIX must be transposed before it can be used by a shader
    D3DXMatrixTranspose(&vs_constants.model_view_projection_matrix, &vs_constants.model_view_projection_matrix);

    // Pass model matrix to shader
    vs_constants.model_matrix = model_matrix;

    // A D3DXMATRIX must be transposed before it can be used by a shader
    D3DXMatrixTranspose(&vs_constants.model_matrix, &vs_constants.model_matrix);

    // Compute inverse transpose matrix
    D3DXMatrixInverse(&vs_constants.inverse_transpose_model_matrix, 0, &vs_constants.model_matrix);
    D3DXMatrixTranspose(&vs_constants.inverse_transpose_model_matrix, &vs_constants.inverse_transpose_model_matrix);

    // Update vertex shader constant buffer
    typed_update_constant_buffer(context, vertex_shader_cbuffer, vs_constants);
    
    // Set lighting parameters
    PS_Constants ps_constants;
    ps_constants.light_position = light_position;
    ps_constants.viewer_position = viewer_position;
    ps_constants.light_color = D3DXVECTOR4(1, 1, 1, 1);
    ps_constants.diffuse_color1 = D3DXVECTOR4(1, 0.2f, 0.4f, 1);
    ps_constants.diffuse_color2 = D3DXVECTOR4(0.4f, 0.2f, 1, 1);
    ps_constants.ambient_color = D3DXVECTOR4(0.1f, 0.1f, 0.1f, 1);
    ps_constants.specular_exponent = 8.0f;

    // Update pixel shader constant buffer
    typed_update_constant_buffer(context, pixel_shader_cbuffer, ps_constants);

    // Bind shaders
    context->VSSetShader(vertex_shader.get(), 0, 0);
    context->PSSetShader(pixel_shader.get(), 0, 0);

    // Bind vertex shader constant buffers
    ID3D11Buffer* vs_cbuffers[1] = { vertex_shader_cbuffer.get() };
    context->VSSetConstantBuffers(0, 1, vs_cbuffers);

    // Bind pixel shader constant buffers
    ID3D11Buffer* ps_cbuffers[1] = { pixel_shader_cbuffer.get() };
    context->PSSetConstantBuffers(0, 1, ps_cbuffers);

    // Bind vertex buffer
    ID3D11Buffer* vb_buffers[1] = { vertex_buffer.get() };
    UINT vb_strides[1] = { sizeof(Vertex) };
    UINT vb_offsets[1] = { 0 };
    context->IASetVertexBuffers(0, 1, vb_buffers, vb_strides, vb_offsets);

    // Bind index buffer
    context->IASetIndexBuffer(index_buffer.get(), DXGI_FORMAT_R32_UINT, 0);

    // Bind input layout
    context->IASetInputLayout(input_layout.get());

    // Set primitive topology
    context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);

    // Set rasterizer state
    context->RSSetState(rasterizer_state.get());

    // Draw two triangle strips
    context->DrawIndexed(num_indices, 0, 0);
  }

private:
  // Constants
  static const std::size_t num_segments = 30;
  static const std::size_t num_rings = 100;
  static const std::size_t num_vertices = (num_rings+1) * (num_segments+1);
  static const std::size_t num_indices = num_vertices * 2 + 2;

  // A vertex
  struct Vertex {
    // Position
    D3DXVECTOR3 position;

    // Normal vector
    D3DXVECTOR3 normal;

    // Texture coordinates
    D3DXVECTOR2 texcoords;
  };

  // Constants of the vertex shader
  struct VS_Constants {
    // Model-view-projection transformation matrix.
    // Transforms from model space to screen space.
    D3DXMATRIX model_view_projection_matrix;

    // Model transformation matrix
    // Transforms from model space to world space
    D3DXMATRIX model_matrix;

    // Inverse transpose model transformation matrix.
    // Used for transforming normal vectors
    D3DXMATRIX inverse_transpose_model_matrix;
  };

  // Constants of the pixel shader
  struct PS_Constants {
    // Position of the light source (in world space)
    D3DXVECTOR4 light_position;

    // Position of the viewer (in world space)
    D3DXVECTOR4 viewer_position;

    // Directional Light color
    D3DXVECTOR4 light_color;

    // Diffuse color #1
    D3DXVECTOR4 diffuse_color1;

    // Diffuse color #2
    D3DXVECTOR4 diffuse_color2;

    // Ambient color
    D3DXVECTOR4 ambient_color;

    // Specilar exponent
    float specular_exponent;

    // Padding
    float pad[3];
  };

  // Vertex shader code
  ID3D10Blob_ptr vertex_shader_code;

  // Pixel shader code
  ID3D10Blob_ptr pixel_shader_code;

  // Vertex buffer
  ID3D11Buffer_ptr vertex_buffer;

  // Index buffer
  ID3D11Buffer_ptr index_buffer;

  // Input layout
  ID3D11InputLayout_ptr input_layout;

  // Vertex shader
  ID3D11VertexShader_ptr vertex_shader;

  // Vertex shader constant buffer
  ID3D11Buffer_ptr vertex_shader_cbuffer;

  // Pixel shader
  ID3D11PixelShader_ptr pixel_shader;

  // Pixel shader constant buffer
  ID3D11Buffer_ptr pixel_shader_cbuffer;

  // Rasterizer state
  ID3D11RasterizerState_ptr rasterizer_state;

  // Model matrix
  D3DXMATRIX model_matrix;

  // Light source position (in world space)
  D3DXVECTOR4 light_position;
};


// View class.
// Window and independently controllable camera
class View {
public:
  // Constructor.
  // Creates the window and initializes viewer parameters
  View(Framework& framework, const Model& model, const D3DXVECTOR4 viewer_position)
    : viewer_position(viewer_position)
    , model(model)
    , window(framework, get_window_parameters())
  {
    // Setup camera matrix
    D3DXVECTOR3 eye(viewer_position.x, viewer_position.y, viewer_position.z);
    D3DXVECTOR3 at(0, 0, 0);
    D3DXVECTOR3 up(0, 1, 0);
    D3DXMatrixLookAtLH(&view_matrix, &eye, &at, &up);
  }

  // Resize callback.
  // Here we update all variables which depend on the size of the target window,
  // such as projection matrices and viewport parameters.
  void resize(unsigned int x_resolution, unsigned int y_resolution,
      bool fullscreen, ID3D11Device_ptr device, IDXGISwapChain_ptr swap_chain,
      ID3D11Texture2D_ptr back_buffer)
  {
    // Setup projection matrix
    float aspect = float(x_resolution) / float(y_resolution);
    float fovy = 3.1415926535f / 3;
    float znear = 2;
    float zfar = 100;
    D3DXMatrixPerspectiveFovLH(&projection_matrix, fovy, aspect, znear, zfar);
  }

  // Scene rendering callback.
  // Here we render the scene to the main render target (the back buffer)
  void render(ID3D11DeviceContext_ptr context, ID3D11RenderTargetView_ptr render_target,
    ID3D11DepthStencilView_ptr depth_stencil)
  {
    // Clear render target view
    float clear_color_rgba[] = { 0, 0, 0, 0 };
    context->ClearRenderTargetView(render_target.get(), clear_color_rgba);

    // Clear depth/stencil view
    context->ClearDepthStencilView(depth_stencil.get(), D3D11_CLEAR_DEPTH, 1.0f, 0);

    // Render the model
    model.render(context, projection_matrix, view_matrix, viewer_position);
  }

private:
  // Viewer position
  D3DXVECTOR4 viewer_position;

  // Projection matrix
  D3DXMATRIX projection_matrix;

  // View matrix
  D3DXMATRIX view_matrix;

  // Reference to model
  const Model& model;

  // Rendering window
  Window window;

  // Get window parameters
  // Returns a Window_Parameters structure which defines all parameters for
  // creating the Direct3D window, including callbacks.
  Window_Parameters get_window_parameters() {
    using boost::bind;

    // Start with default parameters
    Window_Parameters parameters;

    // Set windowed mode with 800x600 resolution
    parameters.x_resolution = 800;
    parameters.y_resolution = 600;
    parameters.fullscreen = false;

    // Let the framework create a depth buffer for us
    parameters.auto_depth_stencil = true;

    // Set view callbacks
    parameters.resize_callback =
      bind(&View::resize, this, _1, _2, _3, _4, _5, _6);
    parameters.render_callback =
      bind(&View::render, this, _1, _2, _3);

    return parameters;
  }
};


// Application class
// Holds all resources owned by the application and makes them accessible by
// callbacks.
class Application {
public:
  // Initialize and run application
  // Creates the framework, the model and a single view, then runs the main
  // loop.
  Application()
    : framework(get_framework_parameters())
    , view1(framework, model, D3DXVECTOR4(0, 0, 5, 1))
    , view2(framework, model, D3DXVECTOR4(5, 0, 0, 1))
  {
    framework.run();
  }

private:
  // The model (must be created first)
  Model model;

  // The Tk11 framework
  Framework framework;

  // The views
  View view1;
  View view2;

  // Get framework parameters
  // Returns a Framework_Parameters structure which defines all parameters for
  // creating the Direct3D framework, including global callbacks.
  Framework_Parameters get_framework_parameters() {
    using boost::bind;

    // Framework parameters
    Framework_Parameters parameters;

    // Set global init and update callbacks.
    // These callbacks are invoked on the model, because the model creates and
    // manages all resources and variables which change over time.
    parameters.init_callback = bind(&Model::init, &model, _1);
    parameters.update_callback = bind(&Model::update, &model, _1);
    return parameters;
  }
};


int main() {
  try {
    // Crate and run application
    Application app;

    return 0;
  }
  catch(const boost::exception& e) {
    // Show an error dialog with diagnostic information about the exception
    // which occurred.
    // (Only for debugging. In a real application, you may want to translate
    // exceptions into more readable error messages).
    show_error_dialog(diagnostic_information(e));
    return 1;
  }
  catch(const std::exception& e) {
    // Show an error dialog with the name of the exception.
    // (Only for debugging. In a real application, you may want to translate
    // exceptions into more readable error messages).
    show_error_dialog(e.what());
    return 1;
  }
  catch(...) {
    // Show an error dialog.
    // (Only for debugging. In a real application, you may want to translate
    // exceptions into more readable error messages).
    show_error_dialog("Unknown exception");
    return 1;
  }
}
Tk11 Direct3D 11 Toolkit version 0.2 (SourceForge)
Copyright Florian Winter 2010 - 2010. Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
Generated on Sun Apr 11 20:22:57 2010 for Tk11 by  doxygen 1.6.3