Ich habe VSM (und auch ESM) in meine Engine implementiert, aber die Ergebnisse sind für mich nicht so, wie ich es erwartet und in vielen im Netzwerk veröffentlichten Beispielen gesehen habe.
Ich habe die Filterung von Schattenkarten auf GL_LINEAR gesetzt, aber wenn ich das Ergebnis mit der normalen Schattenkarte vergleiche, ist es sichtbar schlechter.
Ich habe versucht, Momente direkt im Punktlicht-Shader zu berechnen oder sie aus der Textur zu erhalten, wie es in den meisten Tutorials der Fall ist, aber die Ergebnisse sind dieselben.
Code:
uniform samplerCubeShadow shadowMap;
...
vec4 worldSpace=inverse(ViewMatrix)*vec4(pos,1);
vec4 coord=LightViewMatrix*worldSpace;
vec4 abs_coord=abs(coord);
float fs_z=-max(abs_coord.x, max(abs_coord.y, abs_coord.z));
vec4 clip=LightProjectionMatrix*vec4(0.0,0.0,fs_z,1.0);
float d2=(clip.z / clip.w)*0.5+0.5; // clamp to [0..1]
...
float shadowTexel=texture(shadowMap,vec4(coord.xyz,d2));
// VSM (Variance Shadow Map)
// get partial derivatives
float dx = dFdx(d2);
float dy = dFdy(d2);
vec2 moments = vec2(d2, d2*d2+0.25*(dx*dx+dy*dy));
return chebychevInequality(moments, shadowTexel);
Mit diesem Code erhalte ich Ergebnisse wie auf dem Bild oben. Ich habe auch versucht, nicht samplerCubeShadow, sondern samplerCube zu verwenden, aber die Ergebnisse sind noch schlechter. Zuerst bekam ich harte Schatten. Zweitens füllen Schatten den Bereich nicht so, wie sie sollten, wenn sie Momente von einer anderen Textur erhalten. Schauen Sie sich den zweiten Bildschirm an. Hier sehen Sie auch die generierte Würfelkarte. Es ist nicht ähnlich wie in der Tiefenkarte, selbst wenn ich Tiefe / Moment1 in alle 3 Kanäle eingebe.
Shader für Momente:
// Vartex shader
gl_Position=ModelViewProjectionMatrix*Vertex;
v_position=gl_Position;
// Fragment shader
float depth = v_position.z / v_position.w ;
depth = depth * 0.5 + 0.5; //Don't forget to move away from unit cube ([-1,1]) to [0,1] coordinate system
float moment1 = depth;
float moment2 = depth * depth;
// Adjusting moments (this is sort of bias per pixel) using derivative
float dx = dFdx(depth);
float dy = dFdy(depth);
moment2 += 0.25*(dx*dx+dy*dy) ;
FragColor = vec4( moment1,moment2, 0.0, 0.0 );
Ich stecke wirklich fest. Ich hoffe du hilfst mi meine Probleme zu lösen.
BEARBEITEN:
Ich habe die Lösung für das zweite Problem gefunden. Ich hatte das Mischen aktiviert und es gab mir eine falsche Tiefenkarte.
Ich bekomme auch ein besseres Ergebnis für das erste Problem, aber jetzt kämpfe ich mit der richtigen Tiefe, um es mit der Tiefe der Schattenkarte zu vergleichen.
In einfachen SM verwende ich diesen Code:
vec4 worldSpace=inverse(ViewMatrix)*vec4(pos,1);
vec4 coord=LightViewMatrix*worldSpace;
vec4 abs_coord=abs(coord);
float fs_z=-max(abs_coord.x, max(abs_coord.y, abs_coord.z));
vec4 clip=LightProjectionMatrix*vec4(0.0,0.0,fs_z,1.0);
float d=(clip.z / clip.w)*0.5+0.5; // clamp to [0..1]
Dabei ist pos die Position im Ansichtsbereich. Dann lese ich Werte aus der Schattenkarte mit:
texture(shadowMap,vec4(coord.xyz,d))
In VSM speichere ich die Tiefe im R-Kanal in der RG32F-Textur. Der Tiefenwert wird folgendermaßen berechnet:
// VS
gl_Position=ModelViewProjectionMatrix*Vertex;
v_position=gl_Position;
// FS
float depth = v_position.z/v_position.w;
depth = depth * 0.5 + 0.5;
Dann verwende ich im Shader für Punktlicht den Koordinatenvektor (wie in Standard-SM), um Werte aus der Schattenkarte zu lesen, und dies funktioniert in Ordnung. Aber das Problem ist in diesem Teil:
// No shadow if depth of fragment is in front
if ( moments.x <= distance)
return 1.0;
In welchen Koordinaten soll Abstand sein? In welchen Koordinaten habe ich Tiefe von der Schattenkarte? Es sollte linear sein? Könnte mir das jemand erklären? Ich bin im Moment ein wenig verwirrt, ich habe viele Möglichkeiten ausprobiert, um dies zu erreichen, und alle Ergebnisse sind nicht so, wie ich es erwartet habe.
EDIT2: Nach dem JarkkoL-Tipp und diesem Tutorial habe ich meinen Code geändert. Jetzt speichere ich Tiefe mit diesem Code:
// VS
v_position=ModelViewMatrix*Vertex;
gl_Position=ProjectionMatrix*v_position;
// FS
const float Near = 0.1;
const float Far = 90.0; // camera far plane
const float LinearDepthConstant = 1.0 / (Far - Near);
float depth = length(v_position)*LinearDepthConstant;
Und ich vergleiche es mit dem Wert, den ich auf diese Weise erhalte:
float dist=length( vec3(inverse(ViewMatrix)*vec4(pos,1.0)) - PointLight.position )*LinearDepthConstant; // pos is read from depth buffer and is in view space so I want invert it to world space as it was in tutorial
Und hier ist das Ergebnis:
Im roten Kreis habe ich sichtbare Ränder zwischen Würfelkartenflächen markiert. Es stimmt immer noch etwas nicht. Ich denke, dass es etwas mit dem Umkehren der View Matrix sein könnte, aber ich bin mir nicht sicher.
Antworten:
In Bezug auf Cubemap-Nähte können Sie einfach über Kanten filtern.
Sehen:
glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
In Bezug auf Ihre tatsächliche Schattenqualität ergibt sich der gesamte Nutzen der VSM- und ESM-Techniken aus der speziellen Form, die der Sichtbarkeitstest annimmt. Sie möchten wahrscheinlich einen konstanten Faktor für die Über- oder Unterdunkelung der Schatten einführen, damit die Kante nicht so hart ist.
Für ESM-Daten ist dies unkompliziert:
quelle