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

DeKugelschieber

Community-Fossil

  • »DeKugelschieber« ist der Autor dieses Themas

Beiträge: 2 641

Wohnort: Rheda-Wiedenbrück

Beruf: Software-Entwickler

  • Private Nachricht senden

1

22.09.2012, 23:02

Shadowmapping

Hi,

ich wollte diesmal eigentlich nicht eure Hilfe in Anspruch nehmen. Aber ich übersehe wohl gerne Dinge oder in diesem Fall weiß ich nicht genau wie ich das gewünschte Ergebnis erziele da ich auch kaum Informationen dazu finde (und die die ich finde sind Unbrauchbar, zumindest für mich).
Es geht also um das Shadowmapping. Dabei habe ich 3 Probleme:

1. Den Tiefenwert in die Shadowmap rendern und das mit GLSL (nicht mit der fixed, so wie ich es vorher hatte). [Bild 1]
2. Für das directional light die orthogonale Projektionsmatrix aufsetzen. Bei 2D Objekten, wie z.B. Sprites ist mir das klar (siehe Bild), aber nicht für 3D Szenen. [Bild 1]
3. Schatten auch auf skalierten Objekten richtig abzubilden (wobei das gut ein Folgefehler sein kann). [Bild 2]

Bild 1:

(Link)


Bild 2:

(Link)


Lösungsansätze:

1. Im Fragment Shader für die Shadowmap:

Quellcode

1
2
3
4
5
6
7
#version 150

precision highp float;

void main(){
    gl_FragDepth = gl_FragCoord.z;
}


Funktioniert zwar mehr oder weniger, der Schatten ist in Bild 1 zu sehen, allerdings auch nicht auf der Rückseite des Würfels. Auf der Textur unten links überhaupt nicht, auch nicht wenn ich mit gl_FragColor irgendeinen Grauwert zuweise (gl_FragColor = vec4(gl_FragCoord.z);).
Auch die Shadowmap auf den Flächen des Würfels kann ich mir nicht erklären, die haben da nichts zu suchen. Hier mal der Fragment Shader für den eigentlich Renderdurchlauf:

Quellcode

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
#version 150

precision highp float;

uniform sampler2D texture0;
uniform sampler2DShadow shadowMap;
uniform vec2 pixelOffset;

in vec2 texCoord;
in vec4 shadowCoord;

void main(){
    vec4 SC = shadowCoord/shadowCoord.w+0.0005;
    
    float D = textureProj(shadowMap, SC);
    
    float shadow = 1.0;
    
    if(shadowCoord.w > 0.0){
        if(D < SC.z){
            shadow = 0.5;
        }
    }
    
    gl_FragColor = shadow*texture(texture0, texCoord.st);
}


(das ganze hab ich auch schon/noch mit PCF, aber so ist es erstmal einfacher)

2. Momentan keiner, ich benutze eine perspektivische Projektion. Versucht habe ich es schon mit ortho(-10, 10, -10, 10, -10, -20) oder sowas in der Art, da wurde auch tatsächlich Schatten angezeigt aber an ganz anderer Stelle und viel zu klein. Bei 2D projektionen kann ich ja als Werte die Pixel nehmen, also Größe des Fensters usw. Aber wie das in 3D funktioniert habe ich noch nicht verstanden und ehrlich gesagt bisher nicht gebraucht.

3. Ich schätze mal das hängt mit dem Rendern und der Shadowmap zusammen -> Folgefehler. Für die Shadowmap wird zur Zeit nur der Würfel gerendert. Im eigentlichen Renderdurchlauf dann die Fläche und der Würfel. Hier mal der gesamte Rendercode, leider etwas unübersichtlich weil noch nicht gekapselt. Das meiste sollte selbsterklärend sein, ich glaube aber auch nicht das es daran liegt...

Ich bin für jede Hilfe Dankbar, ich hoffe ich nerve euch nicht zu viel. Ich hab übrigens jetzt ein Mathebuch zum Thema, muss ich mal mit anfangen... ^^
Früher ging es übrigens schon nur das ich jetzt gerne nurnoch GLSL benutzen würde und keine fixed pipeline mehr...

PS: Schreibt man das eigentlich Shadow map, shadowmap oder wie?

2

22.09.2012, 23:25

Hier ist schonmal eine funktionierende und relativ minimalistische Implementierung in OpenGL:
http://fabiensanglard.net/shadowmapping/index.php

Das Rendern der Tiefenwerte ist eigentlich kein Unterschied zum normalen Rendern, aus Faulheit benutze ich sogar die komplett selben Shader, Render die Szene also 2 mal.
Beim Erstellen des Framebufferobjects habe ich dann lediglich folgende Zeile:

C-/C++-Quelltext

1
m_ShadowFramebuffer.AttachTexture(GL_TEXTURE_2D, m_ShadowMap, GL_DEPTH_ATTACHMENT_EXT);

Es hat dann also nur ein 'Target' für Tiefenwerte, nicht für Farbwerte.
Vor dem Rendern kommt dann noch ein:

C-/C++-Quelltext

1
2
3
4
5
6
//render the shadow map
m_ShadowFramebuffer.Bind();

glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
glClear(GL_DEPTH_BUFFER_BIT);
glCullFace(GL_FRONT);

Also das Schreiben der Farbwerte wird deaktiviert und das Culling umgedreht um Artefakte zu vermeiden (steht auch im ersten Link, wenn ich mich nicht irre).

Die Matrix für das Rendern der Shadowmap berechne ich so:

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
    //------- Sun Perspective --------
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(-ShadowMapSize, ShadowMapSize, -ShadowMapSize, ShadowMapSize, -4*ShadowMapSize, 4*ShadowMapSize);

    //ModelView
    Vector3f Up(0, 0, 1);
    Vector3f Z(-Direction);
    Vector3f X=cross(Z, Up);
    Vector3f Y=cross(X, Z);

    X.normalize();
    Y.normalize();
    Z.normalize();
    
    //well in ogl the +z ist pointing outwards the display, but we want to look along the sun direction
    Z=-Z;

    Matrix LightView = Matrix(  X.x,    Y.x,    Z.x,    0,
                        X.y,    Y.y,    Z.y,    0,
                        X.z,    Y.z,    Z.z,    0,
                        0,      0,      0,      1);
    LightView=TranslationMatrix(-ShadowMapCenter.x, -ShadowMapCenter.y, ShadowMapCenter.z)*LightView;
    Mrl::SetGLModelViewMatrix(LightView);

    m_LightView=LightView;
    m_LightProjection=Mrl::GetGLProjectionMatrix();
    //________________________________

    glViewport(0, 0, m_ShadowMapSize.x, m_ShadowMapSize.y);

Ich baue sie also von Hand aus den 3 Basisvektoren zusammen. Ansich ist das nicht besonders toll, das Frustum hat eine feste Größe die man unabhängig von der Szene setzen muss. Eigentlich sollte man das Lichtfrustum so berechnen, dass das OriginalkameraFrustum komplett darin liegt, dann vermeidet man auf jeden Fall Fehler, weil die Shadowmap nicht die komplette sichtbare Szene abdeckt. Natürlich kann das dann relativ groß werden und die Auflösung der Shadowmap entsprechend schlecht. Dafür gibt es dann verschiedene Optimierungen, Cascaden Shadowmaps etwa, oder eine Verzerrung der Shadowmap um in der Nähe viele Details zu haben und hinten weniger (btw. aus Sicht der Kamera vorne genau so viele wie hinten).
http://developer.download.nvidia.com/SDK…shadow_maps.pdf

Ja, man kann da noch eine Menge machen, ich habe derzeit noch eine recht rudimentäre Implementierung, aber es sieht schon ganz schick aus.
Lieber dumm fragen, als dumm bleiben!

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

3

22.09.2012, 23:54

Render die Szene doch einfach mal mit den Matritzen, die du für die Shadowmap verwendest, dann wird dir vermutlich auffallen, dass die daneben sind...

DeKugelschieber

Community-Fossil

  • »DeKugelschieber« ist der Autor dieses Themas

Beiträge: 2 641

Wohnort: Rheda-Wiedenbrück

Beruf: Software-Entwickler

  • Private Nachricht senden

4

23.09.2012, 00:53

@Jonathan: Danke, bis zu deinen Matrizen kenn ich alles und hab es auch in etwa so. Das Tutorial hat keinen expliziten Shader für die Shadowmap.

Darf ich fragen wie du genau auf

C-/C++-Quelltext

1
glOrtho(-ShadowMapSize, ShadowMapSize, -ShadowMapSize, ShadowMapSize, -4*ShadowMapSize, 4*ShadowMapSize);

kommst? Vor allem das 4*... verwirrt mich etwas.

@dot: Keine Ahnung wie du das immer machst, aber ja ich weiß was du meinst:


(Link)


Ich sende jetzt die Modelview für die Shadowmap nochmal extra, also so:

C-/C++-Quelltext

1
shader->sendUniform4x4("shadowMV", models[0]->getModelview(light->getModelview()).getArray());

Man beachte das die Modelview der Lichtquelle anstatt die der Kamera genommen wird... ist mir halt nicht aufgefallen.

Das mit der ortho Matrix probier ich morgen, ist doch langsam schon etwas spät. Ich frag aber schon mal: welche Technik ist für Pointlights am besten geeignet Shadowmapping umzusetzen? Es gibt ja mehrere, die schlechteste ist wohl 6 Texturen zu erstellen...

DeKugelschieber

Community-Fossil

  • »DeKugelschieber« ist der Autor dieses Themas

Beiträge: 2 641

Wohnort: Rheda-Wiedenbrück

Beruf: Software-Entwickler

  • Private Nachricht senden

5

23.09.2012, 23:07

Ich versteh nicht ganz wie ich die ortho Matrix aufsetzen kann. So wie ich es in vielen Beispielen finde funktioniert es nicht, also es wird kein Schatten angezeigt.

C-/C++-Quelltext

1
2
3
4
o.ortho(-800, 800, -600, 600, -4*800, -4*600); // geht nicht
o.ortho(-800, 800, -600, 600, -800, -600); // auch nein

// keine dieser Lösungen funktioniert, auch nicht mit gleichgroßen Zahlen


Ich bekomme immerhin ein falsches Ergebnis, aber mit Schatten, wenn ich z.B. 10 als Wert nehme. Das kling für mich auch logischer als die Auflösung der Shadowmap da ich ja die Planes für das Frustum haben will. Nur wie bekomme ich die für ein Frustum das meinen gesamten Viewspace abdeckt?

6

23.09.2012, 23:13

Funktioniert das hier?

C-/C++-Quelltext

1
o.ortho(-10, 10, -10, 10, -10, 20);

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

7

23.09.2012, 23:18

Was genau tut o.ortho()? Wie genau kommst du auf die Zahlen? Ich vermute mal die Tutorials, derer du dich da bedienst, beziehen sich auf 2d Rendering mit OpenGL und verwenden eine entsprechende Matrix um mit Pixelkoordinaten arbeiten zu können...

DeKugelschieber

Community-Fossil

  • »DeKugelschieber« ist der Autor dieses Themas

Beiträge: 2 641

Wohnort: Rheda-Wiedenbrück

Beruf: Software-Entwickler

  • Private Nachricht senden

8

23.09.2012, 23:19


(Link)


Aber das:

C-/C++-Quelltext

1
o.ortho(-3.1, 3.1, -3.1, 3.1, 0.1, 100);

Kommt relativ nahe ran, aber es passt nicht perfekt. Ich sag ja ich muss wohl die Planes für das Frustum berechnen, aber naja keine Ahnung wie das geht.

Zitat

Was genau tut o.ortho? Wie genau kommst du auf die Zahlen?

Die Zahlen sind von Jonathan, ortho() erstellt halt eine orthogonale Matrix, so wie glOrtho()... für meine Sprites passt es ja.

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

9

23.09.2012, 23:20

Die Zahlen sind von Jonathan, ortho() erstellt halt eine orthogonale Matrix, so wie glOrtho()... für meine Sprites passt es ja.

Achso, ja dann... ;)

Überleg dir einfach mal folgendes: Wieso genau verwendest du diese orthogonale Projektionsmatrix, was bezweckst du damit?

DeKugelschieber

Community-Fossil

  • »DeKugelschieber« ist der Autor dieses Themas

Beiträge: 2 641

Wohnort: Rheda-Wiedenbrück

Beruf: Software-Entwickler

  • Private Nachricht senden

10

23.09.2012, 23:24

Zitat

Überleg dir einfach mal folgendes: Wieso genau verwendest du diese orthogonale Projektionsmatrix, was bezweckst du damit?

"Geraden" Schatten zu projizieren, dazu dürfen die Vertices also nicht perspektivisch verzerrt werden... ich glaub ich weiß worauf du hinaus willst^^ aber heute nicht mehr, schon zu spät.

Werbeanzeige