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:
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.
quelle
Antworten:
Sie sollten die Polygone, die dem Licht zugewandt sind, nicht beschatten.
Ich habe Ihren Fragment-Shader in den folgenden Code geändert.
quelle
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
discard
wenn 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.quelle
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.glEnable(GL_CULL_FACE)
?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:
Sie können auch eine Bias-Matrix mit Ihrem [lightMVP] multiplizieren und die Koordinatenkonvertierung in Ihrem Fragment-Shader vermeiden.
quelle