Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

BurningWave

Alter Hase

  • »BurningWave« ist der Autor dieses Themas

Beiträge: 1 106

Wohnort: Filderstadt/Konstanz

Beruf: Student

  • Private Nachricht senden

1

03.01.2013, 01:56

Problem mit Instancing [DirectX 11]

Hallo und ein frohes neues Jahr,

ich bin gerade dabei Instancing in mein DirectX 11 Framework zu integrieren (vornehmlich um Partikeleffekte zu beschleunigen). Die Idee ist, dass die CPU zu jeder Instanz eines Objekts eine World-Matrix berechnet, die zum Transformieren der Vertices benutzt wird. Diese soll per Instance-Puffer für jede Instanz an den Vertexshader übergeben werden und wird dazu in vier float4-Arrays aufgeteilt. An diesem Punkt liegt vermutlich mein Problem. Wenn ich die Weltmatrix per Constant-Puffer, den ich für jede Instanz mappe, übergebe, funktioniert alles (auch wenn diese Variante sehr ineffizient ist). Bei der Übergabe per Instance-Puffer erhalte ich sehr seltsame Ergebnisse. Beispielsweise wird ein Würfel zur Pyramide, wenn er entlang der x-Achse um eine Einheit nach vorne verschoben wurde (siehe Grafik).

Das Inputlayout:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
D3D11_INPUT_ELEMENT_DESC Layout[] =
    {
        {"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0},
        {"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0},
        {"NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0},
        {"TANGENT", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0},
        {"BINORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0},
        {"TEXCOORD", 1, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, 0, D3D11_INPUT_PER_INSTANCE_DATA, 1},
        {"TEXCOORD", 2, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, 16, D3D11_INPUT_PER_INSTANCE_DATA, 1},
        {"TEXCOORD", 3, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, 32, D3D11_INPUT_PER_INSTANCE_DATA, 1},
        {"TEXCOORD", 4, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, 48, D3D11_INPUT_PER_INSTANCE_DATA, 1}
    };


Die relevante Renderfunktion:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
void Object::RenderIndexedInstanced()
{
    ID3D11DeviceContext *ImmediateContext = System->GetDeviceContext();

    float *InstancesRawData = new float[Instances.size() * 16];
    float *CurInstDataPos = InstancesRawData;
    for(auto Instance = Instances.begin(); Instance != Instances.end(); ++Instance)
    {
        // to save performance it is ignored whether instance should be visible or not
        Instance->CalculateMatrices();
        memcpy(CurInstDataPos, &Instance->MatrixWorld, sizeof(Instance->MatrixWorld));
        CurInstDataPos += 16;
    }

    D3D11_SUBRESOURCE_DATA InstanceData;
    ZeroMemory(&InstanceData, sizeof(InstanceData));
    InstanceData.pSysMem = InstancesRawData;

    D3D11_BUFFER_DESC bd;
    ZeroMemory(&bd, sizeof(bd));
    bd.Usage = D3D11_USAGE_DEFAULT;
    bd.ByteWidth = sizeof(float) * 16 * Instances.size();
    bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;

    ID3D11Buffer *InstanceBuffer;
    HRESULT hr = System->GetDevice()->CreateBuffer(&bd, &InstanceData, &InstanceBuffer);
    if(FAILED(hr))
        throw Exception(hr, __LINE__, __FILE__, "Object::RenderIndexedInstanced()", "Creating an instance buffer failed.");

    UINT Strides[2] = {sizeof(Model::Vertex), sizeof(float) * 16};
    UINT Offsets[2] = {0};
    ID3D11Buffer* Buffers[2] = {Mesh->GetVertexBuffer(), InstanceBuffer};
    ImmediateContext->IASetVertexBuffers(0, 2, Buffers, Strides, Offsets);
    ImmediateContext->IASetIndexBuffer(Mesh->GetIndexBuffer(), DXGI_FORMAT_R32_UINT, 0);

    ImmediateContext->DrawIndexedInstanced(Mesh->GetNumIndices(), Instances.size(), 0, 0, 0);

    InstanceBuffer->Release();
    delete [] InstancesRawData;
}


Ausschnitt aus Vertexshader:

HLSL-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
cbuffer CBViewProjection : register(b0)
{
    matrix view;
    matrix projection;
    matrix projectionortho;
    float4 camerapos;
}; 

struct VS_INPUT 
{ 
    float4 pos : POSITION; 
    float2 tex : TEXCOORD0; 
    float3 norm : NORMAL; 
    float3 tan : TANGENT; 
    float3 bin : BINORMAL; 
    float4 transform0 : TEXCOORD1;
    float4 transform1 : TEXCOORD2;
    float4 transform2 : TEXCOORD3;
    float4 transform3 : TEXCOORD4;
};

 PS_INPUT BumpmapVertexShader(VS_INPUT input)
 {
    [...]

    float4x4 world = {input.transform0, input.transform1, input.transform2, input.transform3};

    output.pos = mul(input.pos, world); 
    output.pos = mul(output.pos, view); 
    output.pos = mul(output.pos, projection);

    [...]
}


Bei mehreren Instanzen erhalte ich überhaupt keine klare Struktur mehr (siehe 2. Grafik). Auffällig ist, dass der mittlere Würfel am Koordinatenursprung korrekt dargestellt wird und Rotationen dieses Würfels ebenfalls funktionieren. Skalieren und Verschieben (siehe erste Grafik) verzerrt den Würfel zur Unkenntlichkeit.


(Link)


(Link)


Ich hoffe jemand von euch kann mir helfen.

MfG Julian

Legend

Alter Hase

Beiträge: 731

Beruf: Softwareentwickler

  • Private Nachricht senden

2

03.01.2013, 10:17

Ich hab jetzt auf die schnelle nur unwichtig scheinende Unterschiede zu meinem Code gefunden, aber sicher dass du nicht beim Zusammenbauen der Matrix im Shader Zeilen und Spalten vertauscht hast? Das würde auch erklären warum der Würfel am Ursprung auch rotierbar zu sein scheint - er dreht sich nur in die andere Richtung als gewünscht, aber er dreht sich.
"Wir müssen uns auf unsere Kernkompetenzen konzentrieren!" - "Juhu, wir machen eine Farm auf!"

Netzwerkbibliothek von mir, C#, LGPL: https://sourceforge.net/projects/statetransmitt/

BurningWave

Alter Hase

  • »BurningWave« ist der Autor dieses Themas

Beiträge: 1 106

Wohnort: Filderstadt/Konstanz

Beruf: Student

  • Private Nachricht senden

3

03.01.2013, 12:27

Vielen Dank! Du hattest Recht. Die Zeilen und Spalten waren vertauscht. Ich habe die memcpy-Zeile durch

C-/C++-Quelltext

1
2
for(int i = 0; i < 16; i++)
            CurInstDataPos[i] = reinterpret_cast<float *>(&Instance->MatrixWorld)[(i % 4) * 4 + i / 4];

ersetzt. Jetzt funktioniert es. Gibt es eine Möglichkeit die Matrix im Shader richtig zusammenzubauen (bzw. richtig zu initialisieren ohne Elemente einzeln zuweisen zu müssen)?

Legend

Alter Hase

Beiträge: 731

Beruf: Softwareentwickler

  • Private Nachricht senden

4

03.01.2013, 12:41

Ich kenne leider auch keine schönere Möglichkeit, aber mein HLSL könnte auch besser sein.
"Wir müssen uns auf unsere Kernkompetenzen konzentrieren!" - "Juhu, wir machen eine Farm auf!"

Netzwerkbibliothek von mir, C#, LGPL: https://sourceforge.net/projects/statetransmitt/

Schrompf

Alter Hase

Beiträge: 1 470

Wohnort: Dresden

Beruf: Softwareentwickler

  • Private Nachricht senden

5

03.01.2013, 13:20

Du kannst Matrizen in HLSL als column_major oder row_major markieren. Damit änderst Du das Speicherlayout im Shader, kannst also Deine Matrix dann wieder normal memcpy()en.

Quellcode

1
2
3
4
struct FisherPrice_MyFirstInstancedVertex
{
  column_major float4x3 mWeltMatrix : TEXCOORD2; // bis einschließlich TEXCOORD4
};


[Edit] Ich sollte warnen, dass ich bisher nur DX9 in der Hand hatte. Das sollte so aber auch für DX11 funktionieren.
Häuptling von Dreamworlds. Baut aktuell an nichts konkretem, weil das Vollzeitangestelltenverhältnis ihn fest im Griff hat. Baut daneben nur noch sehr selten an der Open Asset Import Library mit.

BurningWave

Alter Hase

  • »BurningWave« ist der Autor dieses Themas

Beiträge: 1 106

Wohnort: Filderstadt/Konstanz

Beruf: Student

  • Private Nachricht senden

6

03.01.2013, 14:01

Funktioniert auch einfach so mit memcpy():
float4x4 world : TEXCOORD1;

Wenn man viel zu kompliziert denkt :dash:

David_pb

Community-Fossil

Beiträge: 3 886

Beruf: 3D Graphics Programmer

  • Private Nachricht senden

7

03.01.2013, 14:07

Mal was komplett Anderes: du solltest die Ressource für den Puffer nicht unbedingt in RenderIndexedInstanced erzeugen/zerstören.
@D13_Dreinig

BurningWave

Alter Hase

  • »BurningWave« ist der Autor dieses Themas

Beiträge: 1 106

Wohnort: Filderstadt/Konstanz

Beruf: Student

  • Private Nachricht senden

8

03.01.2013, 14:21

Das Problem ist eben, dass der Puffer jedes Frame seine Größe ändern kann, wenn sich die Anzahl der Instanzen ändert. Meines Wissens nach kann man die Größe eines Puffers nicht ändern, oder?

Schrompf

Alter Hase

Beiträge: 1 470

Wohnort: Dresden

Beruf: Softwareentwickler

  • Private Nachricht senden

9

03.01.2013, 14:42

Nein, aber es wäre trotzdem weise, den Puffer einmalig mit einer maximalen Anzahl Instanzen anzulegen und nachher einfach nur die tatsächliche Anzahl davon zu rendern.

Rein interessehalber... es wird wohl unter DX11 genauso eine Variante für häufige Aktualisierungen geben wie unter DX9 das D3DUSAGE_DYNAMIC. Und das war doch eh implementiert wie ein "Altes wegwerfen, neues nehmen". Damit gewinnt man doch durch eine statisch allokierte Resource nur noch evtl. ein bisschen Optimierungspotential im Treiber?
Häuptling von Dreamworlds. Baut aktuell an nichts konkretem, weil das Vollzeitangestelltenverhältnis ihn fest im Griff hat. Baut daneben nur noch sehr selten an der Open Asset Import Library mit.

BurningWave

Alter Hase

  • »BurningWave« ist der Autor dieses Themas

Beiträge: 1 106

Wohnort: Filderstadt/Konstanz

Beruf: Student

  • Private Nachricht senden

10

03.01.2013, 14:47

Wie ich das verstanden habe, muss die Größe (ByteWidth) unabhängig von der Verwendung angegeben werden.
http://msdn.microsoft.com/en-us/library/…2(v=vs.85).aspx

Ich bin mittlerweile zu D3D11_USAGE_IMMUTABLE für die Instanzpuffer umgestiegen.

Werbeanzeige