Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

D3D11 : Rotating Multiple Triangles

I am new to DirectX, and I have a bit of a problem understanding some of the basics I think. Let's say that I have 10 triangles, and I want to rotate each of them at a different rate so that they 'spin' as time goes by.

So far I have my triangles appearing, but I don't quite get how to rotate them individually. From what I can tell I would do something like this per triangle ?

  1. Compute the rotation matrix, and set it in the constant data.
  2. Update the constant buffer with the constant data.
  3. Update the vertex shader with said data.
  4. DrawPrimitives so that my triangle appears on the screen rotated according to the matrix in step #1. (via the vertex shader)

Is that right? It seems like a lot of back and forth. Am I supposed to rely on the vertex shader to do the transform, or is there a different way to apply the rotation on a per 'object' (triangle) basis ?

I apologize if my question doesn't make sense, like I said, I think I am having a conceptual / understanding issue.

like image 322
A.R. Avatar asked Sep 11 '26 19:09

A.R.


1 Answers

Basically, you have some models (triangles in your case).

Models are arrays of vertices:

struct Vertex
{
    float3 position;
    float3 normal;
    float2 texcoord;
    float4 color;
    // other vertex attributes goes here
};

You create vertex (and index) buffer(s) once, on initialization.

std::vector<Vertex> vertices = { /*vertex data goes here*/ };
VertexBuffer vb = renderer->CreateVertexBuffer(&vertices[0], vertices.size());

Your 3D world is an array of objects which are instances of your model(s).

struct MyObject
{
    float3 position;
    float3 rotation;
    float3 scale;
    // other instance attributes goes here (it can be whatever you want)
};

std::vector<MyObject> objects = { /*objects data goes here*/ };

Basically object's attributes is a modifiers of vertex attributes, so all objects have same model, but looks different in your world (in this example they have different positions).

Position (and also normal, tangent and bitangent) of each vertex typically defined in model space. To move it (transform) to world space you multiply position of each vertex by matrix of current object. You don't want to do it on CPU, because it is very slow. Vertex buffer remains unchanged (of course, you can change it, to achieve effect of deformation, tessellation, etc., but this is not our case).

So, you do transforms in vertex(or geometry) shader . And you must, somehow, send information of current object's transforms (and other instance attributes) to vertex shader.

One way is constant buffer(s).

Assume you have cbuffer in vertex shader:

cbuffer Instance
{
    matrix worldMatrix;
// other instance parameters goes here
};

And you must fill it with data.

Before drawing each object, you updating buffer with current instance data (once per object (multiple times per frame)):

renderer->SetVertexBuffer(vb); // Set needed geometry 
for(int i = 0; i < objects.size(); ++i) // for each object
{
    matrix worldMatrix = CalculateWorldMatrix(objects[i]); // prepare data of current object
    renderer->UpdateConstantBuffer(&worldMatrix); // Tell shaders about current object's attributes (world matrix in our case)
    renderer->Draw(); // or DrawIndexed();
}

For n objects you have n draw calls and n buffer updates.

Another way is instance buffer(s).

You create one more vertex buffer which holds not vertex data, but instance data, prepared to be consumed by shader.

You calculate instance data and create instance buffer once:

for(int i = 0; i < objects.size(); ++i) // for each object
{
    std::vector<Instance> instances;
    instances[i].worldMatrix = CalculateWorldMatrix(objects[i]);
    // other instance parameters goes here
}

VertexBuffer instanceBuffer = renderer->CreateVertexBuffer(&instances[0], instances.size());

And you must also change input layout, so shader will expect instance data in addition to vertex data.

When drawing, you just bind both vertex and instance buffer. No buffer update needed (if your triangles have not been moved). And no matrix calculation needed any more. So, no for loop, and only one (!) draw call.

    renderer->SetVertexBuffers(2, vb, instanceBuffer); // Set needed model data and instances data
    renderer->DrawInstanced(); // or DrawIndexedInstanced();

You only update instance buffer if you objects changing their parameters: position, color, etc.

When drawing complex scenes, you most times use both: constant buffers (for attributes shared for all or many objects: view matrix, projection matrix, etc.) and instancing (if you have objects with same model geometry, but different attributes), to take their advantages.

like image 152
Ivan Aksamentov - Drop Avatar answered Sep 13 '26 09:09

Ivan Aksamentov - Drop