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; } }