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

LukasBanana

Alter Hase

  • »LukasBanana« ist der Autor dieses Themas

Beiträge: 1 097

Beruf: Shader Tools Programmer

  • Private Nachricht senden

1

05.01.2011, 16:51

Probleme mit TangentSpace

Hallo,
ich dachte eig. ich könnte inzwischen mit dem TangentSpace umgehen, aber anscheinend funktioniert das immer noch nicht richtig.
Jetzt wo ich ParallaxOcclusionMapping implementiert habe, kommt der Fehler nur noch mehr zum Vorschein als bei BumpMapping.
Ich brechne den TangentSpace für jedes Dreieck mit folgender Funktion:

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
dim::matrix4f getTangentSpace(
    const dim::vector3df PosA, const dim::vector3df PosB, const dim::vector3df PosC, // Pos -> Vertex position (in ObjectSpace)
    const dim::point2df MapA, const dim::point2df MapB, const dim::point2df MapC, // Map -> Texture koordinaten
    dim::vector3df &Tangent, dim::vector3df &Binormal, dim::vector3df &Normal)
{
    const dim::vector3df v1(PosB - PosA);
    const dim::vector3df v2(PosC - PosA);
    
    const dim::point2df st1(MapB - MapA);
    const dim::point2df st2(MapC - MapA);
    
    Normal = v1.cross(v2);
    
    f32 coef = 1.0f / (st1.X * st2.Y - st2.X * st1.Y);
    
    Tangent.X = coef * ((v1.X * st2.Y) + (v2.X * -st1.Y));
    Tangent.Y = coef * ((v1.Y * st2.Y) + (v2.Y * -st1.Y));
    Tangent.Z = coef * ((v1.Z * st2.Y) + (v2.Z * -st1.Y));
    
    Binormal = Normal.cross(Tangent);
    
    Tangent .normalize();
    Binormal.normalize();
    Normal  .normalize();
    
    return dim::matrix4f(
        Tangent.X, Binormal.X, Normal.X, 0,
        Tangent.Y, Binormal.Y, Normal.Y, 0,
        Tangent.Z, Binormal.Z, Normal.Z, 0,
        0,         0,          0,        1
    );
}

Ich meine eigentlich, dass das stimmen muss, aber in manchen Ausrichtungen stimmt das immer noch nicht. Wenn ich einen einfachen Würfel nehme ist das BumpMapping und ParallaxOcclusionMapping nu auf folgenden Flächen korrekt: (betrachtet aus einem Linkshändigen Koordinaten System)
Links (X-), Rechts (X+)
Zusätzlich auf folgenden Flächen funktioniert das ParallaxOM noch richtig:
Front (Z-)

Muss ich im Shader irgend welche Fälle speziell unterscheiden? Oder stimmt an meiner Methode zur Berechnung des TangentSpace etwas nicht?

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

2

05.01.2011, 17:22

Was genau bedeutet "stimmt nicht"? Den Faktor coef kannst du übrigens wegschmeißen weil du ihn sowieso wegnormalisierst ;)
Enthalten deine Vertices alle 3 Vektoren? Wenn du den letzten Vektor im Shader übers Kreuzprodukt berechnest musst du aufpassen dass der Tangentspace auch die genau andere Orientierung wie dein normales Koordinatensystem haben kann und das Kreuzprodukt dann genau falsch herum zeigt. Darum übergibt man dann z.B. in der vierten Koordinate von T einen Faktor (+1 oder -1) mit dem der im Shader berechnete Vektor multipliziert wird um die Orientierung richtig zu bekommen. Abgesehen davon ist die Annahme dass der Tangentspace orthogonal ist generell natürlich nur eine Näherung.

LukasBanana

Alter Hase

  • »LukasBanana« ist der Autor dieses Themas

Beiträge: 1 097

Beruf: Shader Tools Programmer

  • Private Nachricht senden

3

05.01.2011, 17:41

Naja mit "stimmt nicht" meine ich, dass die finale NormalMatrix eben nicht der korrekten Ausrichtung entspricht, die sie haben soll. Das äußert sich dadurch, dass der Stein von unten angeleuchtet wird, wenn er aber von oben angeleuchtet werden sollte.
Dadurch wirkt der Stein (oder irgend ein anderes Material) eingedrückt. D.h. wo ein Berg sichtbar sein soll, erscheint ein Tal und umgekehrt.

Ein Kreuzprodukt führe ich im Shader gar nicht mehr aus. Das sieht bei mir in etwa so aus (GLSL; in HLSL sehr ähnlich):

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
// Tangent und Binormal uebergebe ich per Texture Koordinaten weil ich keine "attribute" verwenden kann/will.
vec3 Tangent    = gl_MultiTexCoord3.xyz;
vec3 Binormal   = gl_MultiTexCoord4.xyz;
vec3 Normal = gl_Normal.xyz;

mat3 NormalMatrix; // Dies ist ein "varying" damit ich sie im PixelShader verwenden kann

NormalMatrix[0] = mat3(WorldMatrix) * Tangent;
NormalMatrix[1] = mat3(WorldMatrix) * Binormal;
NormalMatrix[2] = mat3(WorldMatrix) * Normal;


Im PixelShader normiere ich die drei Vektoren natürlich noch mal.

PS: Was meinst du mit "Enthalten deine Vertices alle 3 Vektoren?"?

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

4

05.01.2011, 18:11

Oh, ich hab natürlich übersehen dass du die Binormale ja von Anfang an über das Kreuzprodukt berechnest. Machs mal so:

C-/C++-Quelltext

1
2
3
    Binormal.X = -(v1.X * st2.X) + (v2.X * st1.X);
    Binormal.Y = -(v1.Y * st2.X) + (v2.Y * st1.X);
    Binormal.Z = -(v1.Z * st2.X) + (v2.Z * st1.X);

Beiträge: 774

Beruf: Student

  • Private Nachricht senden

5

05.01.2011, 18:13

Mit "Enthalten deine Vertices alle 3 Vektoren?" meint dot wohl, ob du deinem Vertex Normalen, Tangent *und* Binormalen übergibst - die Antwort ist offenkundig ja ^^.
Es ist übrigens wirklich zu empfehlen die Binormale erst im Vertexshader mit Kreuzprodukt zu errechnen. Das kann Performancevorteile mit sich bringen (Stichwort Vertex Compression).
Wär übreinges außerdem besser, wenn du aus deinen 3 Vektoren erst eine Matrix erstellst (statt sie einzeln einzuschreiben) und diese dann einmalig mit der WorldMatrix zu multiplizieren (spart dir einige wertvolle Zyklen ^^). [Aber das nur nebensächlich soweit]

Was du allerdings mit WorldMatrix * Tangentmatrix machen willst versteh ich nicht ganz - wenn du eine Matrix haben willst, die aus den Worldspace in den Tangentspace transformiert müsstest du doch WorldInv*TangentspaceMatrix machen wenn ich mich nicht täusche (?). Ich für meinen Teil hab früher immer alle Sachen (Lichter, Kamera usw.) im Objektspace angegeben und dann mithilfe der Matrix aus Tangent, Binormal, Normal in den Tangentspace gebracht und in diesem dann weiter gerechnet.

Zu den Tangents selbst.. hab gerade nen älteres Codestück nochmal hergekramt und gemerkt, dass das dezent falsch sein muss (ich werf da scheins etliche tangents weg indem ich die Vertices immer wieder überschreib statt das Zeug Dreiecksweise zu speichern und dann Durchschnittsvektoren zu bilden oô .. ich war jung :lol: ). Am besten du vertraust da auf dot ^^

LukasBanana

Alter Hase

  • »LukasBanana« ist der Autor dieses Themas

Beiträge: 1 097

Beruf: Shader Tools Programmer

  • Private Nachricht senden

6

05.01.2011, 18:39

Also beim ParallaxOM äußert sich das Problem so, dass der Vektor ViewVertexDir (Richtung von Kamera zu Vertex Position) bei manchen Ausrichtungen von der falschen Seite kommt.
Im Anhang könnt ihr auch gleich sehen, wie das dann aussieht. Die 3 Linien zeigen die Ausrichtung von Tangente (Rot), Binormale (Grün) und Normale (Blau)

EDIT:
Ich hab grade in diesem Beispiel gesehen, dass nicht die Normale aus der NormalMap mit der TangentSpace Matrix transformiert wird, sondern der LightDir Vektor.
Kann man beide Methoden nehmen oder ist es falsch, die Normale aus der NormalMap zu transformieren??
»LukasBanana« hat folgendes Bild angehängt:
  • Demonstration3.JPG

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »LukasBanana« (05.01.2011, 18:49)


BlueCobold

Community-Fossil

Beiträge: 10 738

Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer

  • Private Nachricht senden

7

05.01.2011, 19:57

Das Bild sieht irgendwie... falsch aus. Fischauge. Ist das Bild von dir oder aus dem Beispiel?
Teamleiter von Rickety Racquet (ehemals das "Foren-Projekt") und von Marble Theory

Willkommen auf SPPRO, auch dir wird man zu Unity oder zur Unreal-Engine raten, ganz bestimmt.[/Sarkasmus]

LukasBanana

Alter Hase

  • »LukasBanana« ist der Autor dieses Themas

Beiträge: 1 097

Beruf: Shader Tools Programmer

  • Private Nachricht senden

8

08.01.2011, 19:43

So hab das Problem jetzt schon seit ein paar Tagen gelöst, wollte hier nur noch mal kunnt tun, wo das Problem war:
Der Tangent Space wurde nicht korrekt berechnet. Ich habe die Rechnung für die Binormale angepasst, ähnlich wie dot es mir empfahl. Das eigentliche Problem war aber genau das, was ich bereits ahnte.
Es hing daran, dass Tangente und Binormale in einigen Fällen (je nach Ausrichtung der Fläche) genau die falsche Richtung zeigten (um 90°, 180° oder 270° falsch ausgerichtet).
Auch habe ich noch mal nachgesehen, wie der Tangent space in der Irrlicht Engine berechnet wird. Das hatte zwar nicht komplett gepasst, aber ein Teil davon, half mir die Berechnungen zu korrigieren.
Meine komplette Lösung könnt ihr euch dann im Source-Code meiner 3D Engine zum nächsten Release anschauen (sp::math::getTangentSpace) ;)
Gruß und Danke nochmal,
Lukas

Werbeanzeige