Schatten

Für Fragen zu Grafik APIs wie DirectX und OpenGL sowie Shaderprogrammierung.
Antworten
Benutzeravatar
Jonathan
Establishment
Beiträge: 2398
Registriert: 04.08.2004, 20:06
Kontaktdaten:

Schatten

Beitrag von Jonathan »

Ich möchte jetzt auch endlich Schatten einbauen (bzw. werde gemocht). Die Grundsätzliche Idee ist ja klar, was man so hört gibt es wohl Stencil Shadows und Shadowmapping, wobei ersteres mittlerweile wohl etwas aus der Mode gekommen ist.
Aber es lohnt sich denke ich doch, nochmal nachzufragen, was heutzutage so best practice ist, welche Artikel dazu empfehlenswert sind und worauf man so ganz allgemein achten sollte. Vielelicht auch persönliche Erfahrungen,welche Verfahren ihr umgesetzt habt, wie komplex die Umsetzung war, wie schön und Effizient das Ergebnis und so weiter.
Umgesetzt wird das ganze übrigens mit OpenGL und GLSL.
Lieber dumm fragen, als dumm bleiben!
https://jonathank.de/games/
Benutzeravatar
Top-OR
Establishment
Beiträge: 330
Registriert: 02.03.2011, 16:32
Echter Name: Jens H.
Wohnort: Esslingen/Dessau
Kontaktdaten:

Re: Schatten

Beitrag von Top-OR »

Moin Jonathan,

Schatten: Eins meiner Lieblingsthemen.
Ich habe bestimmt ein ganzes Jahr (Hobby) mit Schatten zugebracht und auch jetzt ist das Ergebnis noch verbesserungswürdig.
Stencil Shadow hatte ich zuerst eingebaut, aber ich finde persönlich, dass dies nicht unbedingt die bessere Methode ist. Sie sind zwar von der Denke (aus meiner Sicht) eher straight forward als Shadow Maps, aber die Technik hat Ihre Grenzen und Pitfalls.
Inzwischen habe ich mit Shadow Maps Gute Erfahrungen gemacht. Im ersten Moment hat mich von Shadow Maps der ganze Kram mit dem Umrechnen zwischen verschiedenen Vektorräumen mit Hilfe der entsprechenden Matrizen abgehalten, aber ich finde, wenn man sich da einmal durchgewurstet hat, ist es schon eine schöne Technik, die ausreichend gute Ergebnisse erzielen kann.

Hier hast du zwar das Problem, dass du für verschiedene Lichtquellen/Typen verschieden an die Sache herangehen musst: Omnidirektionale Schatten (Point Lights [cubemaps oder Ellipsenhälften]), direktionale Schatten (Sonne [cascaded shadow maps]) oder gerichtete Lightquellen (Strahler ["normales shadow mapping"]) sind eben im Detail unterschiedliche paar Schuhe, die du unterschiedlich abfackeln musst.

Es kommt auch ein bissel drauf an, wie dein Render gebaut ist: Machst du Forward Rendering oder Deferred Rendering?

Ein gutes Tutorial hab ich aus dem Stand nicht, aber ich schau mal : ich hatte mal nen ganzen Sack voll. Ich werd mal nochwas posten, wenns mir unter die Finger kommt...

Was ist dir denn wichtig in deiner Engine? Speed? Welche Lichtquellen willst du haben können? Vielleicht brauchst du ja nicht das "volle Programm".
Zuletzt geändert von Top-OR am 13.09.2011, 14:56, insgesamt 2-mal geändert.
--
Verallgemeinerungen sind IMMER falsch.
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: Schatten

Beitrag von dot »

Nun, normales Shadowmapping mit PCF ist einfach zu implementieren und keine besonders schlechte Lösung. Ansonsten sind grad Variance und Exponential Shadowmaps modern. Zusätzlich kann man natürlich noch Cascaded und Perspective Shadowmapping verwenden.
Benutzeravatar
Schrompf
Moderator
Beiträge: 4887
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas
Wohnort: Dresden
Kontaktdaten:

Re: Schatten

Beitrag von Schrompf »

Ich glaube, heutzutage gibt es quasi keine ernstzunehmenden Methoden außer Shadow Mapping mehr. Ich kann das allerdings nicht aus eigener Erfahrung beurteilen, weil ich außer ein paar vor vielen Jahren stattfindenden Experimenten nie was anderes ausprobiert habe. Hier meine Gedanken zu Shadow Mapping:

- Basis-Implementation sehr einfach. Tiefe aus Licht-Perspektive in Textur rendern, Textur beim Szenen-Rendern für Tiefenvergleich verwenden, fertig. Wenn Du sattelfest in den Koordinatensystem-Details von OpenGL bist, ist alles gut, sonst musst Du Minuszeichen umdrehen, bis es passt.

- Aliasing-Probleme - sprich begrenzte Auflösung der Schatten. Wird früher oder später ein Problem, vor allem für große Lichtquellen wie die Sonne. Methoden dagegen sind Cascaded SM oder Parallel Split SM oder eine frühere Erfindung von mir, kreisförmig komprimiertes SM.

- Selbstschattierung, auch Shadow Acne genannt. Auch viele Spiele im Handel haben dieses Problem, eine perfekte Lösung gibt es nicht. Mit einem Basis-Offset und zusätzlich solide durchgerechnetem Slope-Dependent Bias ist das Problem aber praktisch gelöst.

- Weiche Schatten. Ein beliebtes Thema vieler Forscher. Die einfachste Lösung ist schnell gemacht: viele Samples nehmen und mitteln. Daneben gibt es vor allem Variance SM und zweihundertmillionen Varianten und Detail-Korrekturen davon, die sich einiger Beliebtheit erfreuen. Ich habe Variance SM aber aufgrund der Light Bleeding-Probleme bisher gemieden, obwohl verschiedene Stimmen im Netz meinen, dass das mit einigen der oben genannten zweihundert Millionen Varianten nahezu kein Problem mehr ist.

Ich habe vor einiger Zeit einen weiteren meiner Beiträge zu den Schattenspielereien in eine Demo gegossen. Die Demo enthält auch eine - wie ich finde - minimale Implementation von Basis-ShadowMapping in der ursprünglichsten Form. Wenn Dich nicht stört, dass die Vorlage mit DX9 und Qt geschrieben ist, kannst Du da ja mal reinschauen. Der Thread dazu findet sich hier

Randbemerkung: die dort vorgestellte Weichkanten-Technik ist immernoch nicht ganz ausgereift. Ich habe zwar einige Ideen, wie ich die letzten Artefekte beseitigen könnte, aber ich komme einfach nicht dazu :-(
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: Schatten

Beitrag von dot »

Wenn du mal mit den verschiedenen Techniken rumspielen willst, kannst du einen Blick auf das werfen: http://software.intel.com/en-us/article ... wexplorer/
Benutzeravatar
Zudomon
Establishment
Beiträge: 2257
Registriert: 25.03.2009, 07:20
Kontaktdaten:

Re: Schatten

Beitrag von Zudomon »

Mein alter Thread, in dem auch einige Schattentypen zu sehen sind...
Benutzeravatar
Jonathan
Establishment
Beiträge: 2398
Registriert: 04.08.2004, 20:06
Kontaktdaten:

Re: Schatten

Beitrag von Jonathan »

Ok, also ich bin schonmal einen Schritt weiter. Im Moment will ich Schatten von einem direktionalen Licht. Um das ganze ein wenig zu testen, render ich die Shadowmap in Farbe und projeziere sie dann auf den Boden, anstatt den Z-Test zu machen, um zu schauen, ob die Shadowmap und die Texturkoordinaten stimmen.
Ersteres scheint zu gehen, zweiteres nicht:
ShadowMapping Fehler
ShadowMapping Fehler
Man sieht die Szene und ihre Projektion (rot eingefärbt). Die grünen Pfeile zeigen, welcher Punkt der Szene auf welche Position in der Shadow Map projeziert wird. Man sieht die Szene aus Lichtperspektive, ansich müsste also die Szene deckungsgleich mit der projizierten Szene sein.

Und hier mal der Shader Code:

Code: Alles auswählen

attribute vec3 PositionAtt;   
attribute vec2 TexCoord0Att;   
   
varying vec4 v_ShadowCoord;   
   
void main()   
{   
	v_ShadowCoord= gl_TextureMatrix[7] * vec4(PositionAtt, 1);   
	   
	gl_Position=gl_ModelViewProjectionMatrix*vec4(PositionAtt, 1);   
	v_TexCoord=TexCoord0Att;   
}

Code: Alles auswählen

uniform sampler2D ShadowMap;      
varying vec2 v_TexCoord;      
varying vec4 v_ShadowCoord;      
      
void main()      
{      
	vec2 TexCoord=v_ShadowCoord.xy/v_ShadowCoord.w;  
     
	TexCoord.x-=1; 
	TexCoord=(TexCoord+vec2(1, 1))*0.5; 
     
	gl_FragColor=texture2D(ShadowMap, TexCoord)*vec4(1, 0, 0, 1);      
}
Tja, das mit dem x-=1 hab ich gemacht, weil ich dachte, dadurch diesen Offset beheben zu können (es wurde dadurch auch merklich besser).
Aber wie man an den Pfeilen sehen kann, ist es kein einfacher Offset, der da falsch ist. Ein Vorzeichenwechsel schiend auch falsch zu sein, so bewegt sich die Projektion des Spielers (weiß) wenigstens grob in die selbe Richtung. Achja: TextureMatrix[7] ist die ProjektionsMatirx*Kameramatrix die zum Rendern der Shadowmap benutzt wurde (und in dieser Einstellung auch für die gezeigt Szene).
Und nun bin ich müde und wüsste gerne, ob jemand eine Idee hat, was das für ein Fehler sein könnte.
Lieber dumm fragen, als dumm bleiben!
https://jonathank.de/games/
joggel

Re: Schatten

Beitrag von joggel »

So als Laie:
Und wenn die Fehler schon in der Shadowmap sind?
Also, dass das textuieren richtig funktioniert, aber die Shadowmap ist eben nicht exakt.
Benutzeravatar
Schrompf
Moderator
Beiträge: 4887
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas
Wohnort: Dresden
Kontaktdaten:

Re: Schatten

Beitrag von Schrompf »

Du benutzt anscheinend beim Zeichnen der ShadowMap und beim Anwenden der ShadowMap verschiedene Model/View/Projection-Matrizen.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Benutzeravatar
Jonathan
Establishment
Beiträge: 2398
Registriert: 04.08.2004, 20:06
Kontaktdaten:

Re: Schatten

Beitrag von Jonathan »

Ok, ich hatte das ViewFrustum nicht angepasst, und die ShadowMap hatte 1024x1024 und nicht 1024x768 wie das Spiel.
Allerdings ist es immer noch nicht richtig, die Offsets haben sich etwas geändert aber es sieht prinzipiell noch gleich aus.
Lieber dumm fragen, als dumm bleiben!
https://jonathank.de/games/
Benutzeravatar
Schrompf
Moderator
Beiträge: 4887
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas
Wohnort: Dresden
Kontaktdaten:

Re: Schatten

Beitrag von Schrompf »

Die ShadowMap ist unabhängig von der Bildschirmauflösung. Du machst Dir nur noch mehr Probleme, wenn Du eine "krumme" Auflösung wie 1024x768 wählst. Belass es bei 1024^2 für's Erste. Ansonsten: schau Dir nochmal die ganzen Matrizen an. Setz schlimmstenfalls mal einen Breakpoint in die jeweiligen VertexShader und vergleich die Zahlen. Bis auf ein x2 in _m00 bis _m11 und evtl. ein Minuszeichen in _m11 müssten die Zahlen gleich sein. [edit] Und natürlich eine Translations-Komponente, um von (-1...+1) auf (0...1) zu kommen.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Benutzeravatar
Jonathan
Establishment
Beiträge: 2398
Registriert: 04.08.2004, 20:06
Kontaktdaten:

Re: Schatten

Beitrag von Jonathan »

Ich hab mich evtl. etwas missverständlich ausgedrückt: Die ShadowMap hat die Auflösung 1024^2, das Spiel 1024x768. Ich hatte aber immer den Viewport(1024, 768), was dazu führte, dass die Szene in die ShadowMap verzerrt (oder eben nicht verzerrt) gerendert wurde, d.h. ein oberer Streifen der ShadowMap war leer. Jetzt setze ich den Viewport jeweils richtig und es passt.

Hier mal etwas mehr Code:

Code: Alles auswählen

	//Render the shadow map:
	
	//------- Sun Perspective --------
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glOrtho(-50, 50, -50, 50, -100, 150);

	//ModelView
	Vector3f Up(0, 0, 1);
	Vector3f Z(m_SunDirection);
	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(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=Mrl::TranslationMatrix(-50, -50, 0)*LightView;
	Mrl::SetGLModelViewMatrix(LightView);

	
	glViewport(0, 0, 1024, 1024);
	//________________________________

	//render the shadow map
	m_ShadowFramebuffer.Bind();
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	m_SceneManager.Render();


	m_ShadowFramebuffer.Disable();



	//--------- render the scene -----------
	Matrix ShadowMapMatrix=Mrl::GetGLProjectionMatrix()*LightView;

	Mrl::SetGLTextureMatrix(ShadowMapMatrix, 7);
	m_TerrainShader.Set();
	m_TerrainShader.BindTexture("ShadowMap", m_ShadowMap, 7);

    
      ............
Das mit der Translationskomponente also [-1, 1]->[0, 1] mach ich ja später per Hand, mit dem TexCoord=(TexCoord+vec2(1, 1))*0.5.

Zum Shaderdebuggen benutze ich gDEBugger. Leider hab ich da noch nicht herausgefunden, wie man die Shader selbst debuggen kann, ich kann mir zwar den Code anzeigen und auch zur Laufzeit ändern, aber ich hab noch ncihts gefunden wie ich Breakpoints setzen könnte, oder den Code Schritt für Schritt durchsteppen kann.
Lieber dumm fragen, als dumm bleiben!
https://jonathank.de/games/
Benutzeravatar
Schrompf
Moderator
Beiträge: 4887
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas
Wohnort: Dresden
Kontaktdaten:

Re: Schatten

Beitrag von Schrompf »

Sorry, aber ich war irgendwie wieder von DirectX ausgegangen. Aber schön, dass es jetzt funktioniert!
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Benutzeravatar
Jonathan
Establishment
Beiträge: 2398
Registriert: 04.08.2004, 20:06
Kontaktdaten:

Re: Schatten

Beitrag von Jonathan »

Ne ging noch nicht. Aber ich hab den Fehler gefunden (Projektions- und ModelViewMatrix falschrum multipliziert...)
Danach lief alles ohne irgendwelche Offsets oder Anpassungen, nichtmal ein Vorzeichen musste ich noch rumdrehen:
ZombieMapping.png
Lieber dumm fragen, als dumm bleiben!
https://jonathank.de/games/
Benutzeravatar
Jonathan
Establishment
Beiträge: 2398
Registriert: 04.08.2004, 20:06
Kontaktdaten:

Re: Schatten

Beitrag von Jonathan »

Also irgendwie hab ich gerade nochmal einen Hänger.
Ich habe jetzt die SHadowmap gerendert, also nur noch Tiefenwerte statt Farben. Wenn man gDEBugger glauben mag, dann sieht sie auch sehr vernünftig aus. Allerdings schaffe ich es jetzt nicht, im FragmentShader auf den Tiefenwert zuzugreifen:

Code: Alles auswählen

uniform sampler2D ShadowMap;
uniform sampler2D Tex0;
varying vec2 v_TexCoord;
varying vec4 v_ShadowCoord;

float LinearizeDepth(float zoverw)
{
	float n = -100.0; // camera z near
	float f = 150.0; // camera z far
	return (2.0 * n) / (f + n - zoverw * (f - n));
}

void main()
{ 
	vec2 ShadowCoord=v_ShadowCoord.xy/v_ShadowCoord.w;
	
	ShadowCoord=(ShadowCoord+vec2(1, 1))*0.5;
	
	float Shadow=1;
	if(texture2D(ShadowMap, ShadowCoord).y<0.25)
		Shadow=0.5;
	//gl_FragColor=texture2D(Tex0, v_TexCoord)*texture2D(ShadowMap, ShadowCoord);
    float depth=LinearizeDepth(texture2D(ShadowMap, ShadowCoord).x);
	gl_FragColor=vec4(ShadowCoord.x, depth, depth, 1);
	
	if(depth==1.0)
		gl_FragColor=(1, 0, 0, 1);
	else
		gl_FragColor=(0, 1, 0, 1);
}

Code: Alles auswählen

	//generate the shadow map:
	glGenTextures(1, &m_ShadowMap);
	glBindTexture(GL_TEXTURE_2D, m_ShadowMap);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE);

	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
	glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE, GL_LUMINANCE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE);

	glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, 1024, 1024, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL);


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

	// Instruct openGL that we won't bind a color texture with the currently binded FBO
	//glDrawBuffer(GL_NONE);
	//glReadBuffer(GL_NONE);

	m_ShadowFramebuffer.Disable();
Das Problem ist, ich krieg einfach keine sinnvollen Tiefenwerte. Lustigerweise funktioniert die zweite Zeile, als die Ausgabe von depth, wenn wenigstens eine Komponente eine Texturkoordinate drin hat. Dann ist der Tiefenwert aber entweder 0 oder 1 und nie dazwischen. Im anderne Fall, wenn ich einfach nur depth ausgebe, bekomme ich jedesmal nur 1.
Gut, dann wollte ich überprüfen, ob es wirklich 1 ist oder einfach ein Wert größer 1 oder in der Nähe von 1. Tja, es sollte entweder rot oder grün sein, aber es ist einfach weiß (was ja irgendwie nicht sein kann).

Ich hab irgendwo gelesen, man müsste Tiefenwerte linearisieren, weil sie sonst sehr komisch sind, das mach ich also mit der Funktion aber das ändert auch nichts.

Also sowohl dieses "es wird weiß statt rot oder grün" als auch "depth hat unterschiedliche Werte, abhängig ob ich die Texturkoordinate ausgebe" erscheinen mir vollkommen unlogisch. Irgendwelche Ideen?
Lieber dumm fragen, als dumm bleiben!
https://jonathank.de/games/
Benutzeravatar
Jonathan
Establishment
Beiträge: 2398
Registriert: 04.08.2004, 20:06
Kontaktdaten:

Re: Schatten

Beitrag von Jonathan »

Gelöst. Es ist wichtig, den Compare Mode auf None zu stellen, sonst kann man nur mit SchattenSamplern auf die Textur zugreifen, alle anderen Zugriffe sind undefiniert oder so (was das merkwürdige Verhalten perfekt erklären würde).

Code: Alles auswählen

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE);
Das steht so auch im Code, leider steht kurz darunter ein:

Code: Alles auswählen

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE, GL_LUMINANCE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE);
Das hatte ich wohl im Laufe der Zeit aus einem anderen Beispiel rüberkopiert in der Hoffnung, dass es was bringen könnte (man kennt das ja, man schraubt ewig am Code rum und hat hinterher überhaupt keinen Überblick mehr, was welche Stellen eigentlich machen).

Nunja, jetzt bekomme ich sinnvolle Ergebnisse und kann ans Aufräumen und optimieren gehen.
Lieber dumm fragen, als dumm bleiben!
https://jonathank.de/games/
Benutzeravatar
Jonathan
Establishment
Beiträge: 2398
Registriert: 04.08.2004, 20:06
Kontaktdaten:

Re: Schatten

Beitrag von Jonathan »

Der Vollständigkeit halber:
Sehr nützlich fand ich folgende Tutorials:
http://fabiensanglard.net/shadowmapping/index.php
http://www.fabiensanglard.net/shadowmap ... /index.php
Lieber dumm fragen, als dumm bleiben!
https://jonathank.de/games/
Antworten