Probleme mit der ersten Schattenzuordnung


9

Ich habe zum ersten Mal eine grundlegende Schattenzuordnung in OpenGL mithilfe von Shadern implementiert und habe einige Probleme. Unten sehen Sie ein Beispiel meiner gerenderten Szene:

Geben Sie hier die Bildbeschreibung ein

Der Prozess der Schattenzuordnung, dem ich folge, besteht darin, dass ich die Szene unter Verwendung einer Ansichtsmatrix aus der Sicht des Lichts und der für das normale Rendern verwendeten Projektions- und Modellmatrizen in den Framebuffer rendere.

Im zweiten Durchgang sende ich die obige MVP-Matrix aus der Sicht des Lichts an den Vertex-Shader, der die Position in den Lichtraum umwandelt. Der Fragment-Shader teilt die Perspektive und ändert die Position in Texturkoordinaten.

Hier ist mein Vertex-Shader:


#version 150 core

uniform mat4 ModelViewMatrix;
uniform mat3 NormalMatrix;
uniform mat4 MVPMatrix;
uniform mat4 lightMVP;

uniform float scale;

in vec3 in_Position;
in vec3 in_Normal;
in vec2 in_TexCoord;

smooth out vec3 pass_Normal;
smooth out vec3 pass_Position;
smooth out vec2 TexCoord;
smooth out vec4 lightspace_Position;

void main(void){
    pass_Normal = NormalMatrix * in_Normal; 
    pass_Position = (ModelViewMatrix * vec4(scale * in_Position, 1.0)).xyz;
    lightspace_Position = lightMVP * vec4(scale * in_Position, 1.0);
    TexCoord = in_TexCoord;

    gl_Position = MVPMatrix * vec4(scale * in_Position, 1.0);
}

Und mein Fragment Shader,


#version 150 core

struct Light{
    vec3 direction;
};

uniform Light light;
uniform sampler2D inSampler;
uniform sampler2D inShadowMap;

smooth in vec3 pass_Normal;
smooth in vec3 pass_Position;
smooth in vec2 TexCoord;
smooth in vec4 lightspace_Position;

out vec4 out_Color;


float CalcShadowFactor(vec4 lightspace_Position){

    vec3 ProjectionCoords = lightspace_Position.xyz / lightspace_Position.w;

    vec2 UVCoords;
    UVCoords.x = 0.5 * ProjectionCoords.x + 0.5;
    UVCoords.y = 0.5 * ProjectionCoords.y + 0.5;

    float Depth = texture(inShadowMap, UVCoords).x;
    if(Depth < (ProjectionCoords.z + 0.001)) return 0.5;
    else return 1.0;
}

void main(void){

    vec3 Normal = normalize(pass_Normal);
    vec3 light_Direction = -normalize(light.direction);
    vec3 camera_Direction = normalize(-pass_Position);
    vec3 half_vector = normalize(camera_Direction + light_Direction);

    float diffuse = max(0.2, dot(Normal, light_Direction));
    vec3 temp_Color = diffuse * vec3(1.0);

    float specular = max( 0.0, dot( Normal, half_vector) );

    float shadowFactor = CalcShadowFactor(lightspace_Position);

    if(diffuse != 0 && shadowFactor > 0.5){
        float fspecular = pow(specular, 128.0);
        temp_Color += fspecular;
    }

    out_Color = vec4(shadowFactor * texture(inSampler, TexCoord).xyz * temp_Color, 1.0);
}

Eines der Probleme ist die Selbstbeschattung, wie Sie auf dem Bild sehen können. Die Kiste hat ihren eigenen Schatten auf sich geworfen. Ich habe versucht, den Polygonversatz zu aktivieren (dh glEnable (POLYGON_OFFSET_FILL), glPolygonOffset (GLfloat, GLfloat)), aber es hat sich nicht viel geändert. Wie Sie im Fragment-Shader sehen, habe ich einen statischen Versatzwert von 0,001 festgelegt, aber ich muss den Wert abhängig von der Entfernung des Lichts ändern, um wünschenswertere Effekte zu erzielen, was nicht sehr praktisch ist. Ich habe auch versucht, beim Rendern des Framebuffers das Front-Face-Culling zu verwenden, was sich auch nicht wesentlich geändert hat.

Das andere Problem ist, dass Pixel außerhalb des Sichtstumpfes des Lichts schattiert werden. Das einzige Objekt, das Schatten werfen soll, ist die Kiste. Ich denke, ich sollte eine geeignetere Projektion auswählen und Matrizen anzeigen, aber ich bin mir nicht sicher, wie ich das machen soll. Was sind einige gängige Praktiken, sollte ich eine orthografische Projektion auswählen?

Wenn ich ein bisschen google, verstehe ich, dass diese Probleme nicht so trivial sind. Hat jemand einfach zu implementierende Lösungen für diese Probleme. Könnten Sie mir zusätzliche Tipps geben?

Bitte fragen Sie mich, ob Sie weitere Informationen zu meinem Code benötigen.

Hier ist ein Vergleich mit und ohne Schattenkartierung einer Nahaufnahme der Kiste. Die Selbstbeschattung ist sichtbarer.


Scheint, als sollten Sie Ihre Probleme genauer beschreiben. Sie haben einen Satz über Ihre Probleme und der nächste Satz schlägt eine Lösung für einen vor. Sagen Sie uns genau, was die Probleme sind und was Sie bereits getan haben, um sie zu beheben.
MichaelHouse

Ich habe meinen Beitrag bearbeitet, ich hoffe es ist jetzt besser.
Grieverheart

Antworten:


6

Sie sollten die Polygone, die dem Licht zugewandt sind, nicht beschatten.

Ich habe Ihren Fragment-Shader in den folgenden Code geändert.

float CalcShadowFactor(vec4 lightspace_Position)
{

    vec3 ProjectionCoords = lightspace_Position.xyz / lightspace_Position.w;

    vec2 UVCoords;
    UVCoords.x = 0.5 * ProjectionCoords.x + 0.5;
    UVCoords.y = 0.5 * ProjectionCoords.y + 0.5;

    float Depth = texture(inShadowMap, UVCoords).x;
    if(Depth < (ProjectionCoords.z + 0.001)) return 0.5;
    else return 1.0;
}

void main(void)
{

    vec3 Normal = normalize(pass_Normal);
    vec3 light_Direction = -normalize(light.direction);
    vec3 camera_Direction = normalize(-pass_Position);
    vec3 half_vector = normalize(camera_Direction + light_Direction);

    float fndotl = dot(Normal, light_Direction);
    float shadowFactor = CalcShadowFactor(lightspace_Position);
    float diffuse = max(0.0, fndotl) * shadowFactor + 0.2;
    vec3 temp_Color = vec3(diffuse);

    float specular = max( 0.0, dot( Normal, half_vector) );

    if(shadowFactor > 0.9){
        float fspecular = pow(specular, 128.0);
        temp_Color += fspecular;
    }

    out_Color = vec4(shadowFactor * texture(inSampler, TexCoord).xyz * temp_Color, 1.0);
}

8

In Bezug auf die Selbstbeschattung bin ich mir nicht sicher, worauf Sie sich beziehen - ich sehe keine "Schattenakne" auf der Kiste in Ihrem Screenshot. Wenn Sie meinen, dass die vom Licht abgewandten Gesichter dunkel sind, dann sind sie es natürlich - sie sollten es sein! :) Die Seitenfläche sieht etwas seltsam aus, die Diagonale ist halb beleuchtet und halb beschattet. Wenn Sie N Punkt L für die Beleuchtung verwenden und gute Normalen haben (dh harte Normalen auf diesem Würfel, keine geglätteten Normalen), sollte dies nicht passieren.

Die Verwendung von Polygonversatz und / oder das Zeichnen der Rückseiten in die Schattenkarte sind die Standardlösungen (nun ... wirklich Problemumgehungen) für Schattenakne.

Bei der Projektionsmatrix sollten Sie sich das Licht als Kamera vorstellen. Wenn es sich um ein Punktlicht handelt, handelt es sich um eine perspektivische Kamera, und wenn es sich um ein gerichtetes Licht handelt, handelt es sich um eine orthografische Kamera. Konstruieren Sie die Projektionsmatrix genauso wie bei einer Kamera unter Verwendung des Sichtfelds, des Seitenverhältnisses usw. des Lichts.

Um den Pixel-Shader darauf zu beschränken, nur die Pixel im Ansichtsstumpf des Lichts zu zeichnen, überprüfen Sie nach der Berechnung der UV-Werte der Schattenkarte, ob sie zwischen 0 und 1 liegen und discardwenn nicht (oder setzen Sie die Farbe des Lichts auf Null). Eine andere Möglichkeit besteht darin, die Textur der Schattenkarte auf den Modus "Am Rand festklemmen" einzustellen und eine Rahmenfarbe von Null festzulegen, um sicherzustellen, dass alles außerhalb der Schattenkarte vollständig beschattet wird.


Hier ist eine Nahaufnahme der Box und ein Vergleich mit und ohne Shadow Mapping Link . Die Selbstbeschattung ist hier besser sichtbar. Können Sie erklären, warum Sie eine perspektivische Kamera für Punktlichter und eine orthografische Kamera für Richtungsangaben verwenden würden?
Grieverheart

Okay, in dieser Einstellung sieht es aus wie Schattenakne. Das Zeichnen von Rückseiten in die Schattenkarte (und nicht in die Vorderflächen) sollte dies beheben. Ich weiß, dass du gesagt hast, dass du das versucht hast, aber vielleicht ist etwas schief gelaufen? Es ist nur so glCullFace(GL_FRONT). Bei Punkt gegen Richtung dreht sich alles um die Strahlen. Kamerastrahlen konvergieren zu einem Punkt in einer perspektivischen Kamera und Lichtstrahlen konvergieren zu einem Punkt in einem Punktlicht. Kamerastrahlen sind für eine orthografische Kamera parallel und Lichtstrahlen sind für ein gerichtetes Licht parallel.
Nathan Reed

Ich verstehe, das macht Sinn. Hier ist ein weiterer Vergleich mit und ohne Gesichtsausschlag. Bei der unteren Option ist das Ausmerzen von Gesichtern aktiviert. Ich werde versuchen, eine orthografische Projektion für meine Lichtansichtsmatrix zu verwenden, die das Problem der Selbstbeschattung jedoch nicht beeinträchtigen sollte, oder? Vielleicht ist mein Clipbereich zu groß (dh ich habe 0,1 bis 100)?
Grieverheart

Die sehen genauso aus. Sie verwenden für den Rest der Szene Back-Face-Culling, oder? Haben Sie tun glEnable(GL_CULL_FACE)?
Nathan Reed

Sie sind nicht genau gleich, schauen Sie sich die Box genau an. Im obigen Bild ist die Schattenlinie konvex, während die untere konkav ist.
Grieverheart

3

Die Pixel außerhalb der Lichtansicht sind dunkel, da sie von außerhalb der Lichttiefenstruktur abgetastet werden.

Wenn Sie den Scheitelpunkt durch die Projektionsmatrix transformieren, erhalten Sie die Clipkoordinaten [lightspace_Position] des Fragments. Sie konvertieren das dann in Texturkoordinaten [UVCoords]. Die Clip-Space-Koordinaten eines Fragments können jedoch immer noch außerhalb des Bereichs von -1,0 bis 1,0 liegen (außerhalb des Bildschirms und daher abgeschnitten), und daher können die konvertierten Texturkoordinaten außerhalb des Bereichs von 0,0 bis 1,0 Ihrer Textur liegen.

Versuche dies:

if(UVCoords.x >= 0.0 && UVCoords.x <= 1.0 && UVCoords.y >= 0.0 && UVCoords.y <= 1.0 && (Depth < (ProjectionCoords.z + 0.001)))
    return 0.5;

Sie können auch eine Bias-Matrix mit Ihrem [lightMVP] multiplizieren und die Koordinatenkonvertierung in Ihrem Fragment-Shader vermeiden.


Die Bias-Matrix ist normalerweise der Weg, den Menschen gehen. Es ist viel schneller als das Verzweigen von Code in einem Fragment-Shader.
Ian Young
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.