OpenGL Schadowmap relativ zur kamera

Für Fragen zu Grafik APIs wie DirectX und OpenGL sowie Shaderprogrammierung.
Antworten
Andy90
Beiträge: 70
Registriert: 08.10.2023, 13:02

OpenGL Schadowmap relativ zur kamera

Beitrag von Andy90 »

Hallo, ich habe mir gestern mal das Tutorial für Shadowmaps auf https://learnopengl.com/Advanced-Lighti ... ow-Mapping angeschaut. Soweit ist auch alles klar und ich bekomme es hin, dass meine Objekte Schatten werfen. Jetzt ist ja aber der Bildausschnitt in dem Tutorial fest definiert, wie kann ich es am besten bewerkstelligen das er den Bildausschnitt der Shadowmap abhängig von der Kamera macht? Aktuell verfolge ich den Ansatz, dass ich die Projektion Matrix anhand der Kamera Position definiere also etwa so

Code: Alles auswählen

        public mat4 GenerateLightSpaceMatrix()
        {
            //new vec3(-2f, 4.0f, -1.0f)
            float near_plane = 1.0f, far_plane = 7.5f;

            float left = _camX - 10f;
            float right = _camX + 10f;
            float top = _camY + 10f;
            float bottom = _camY - 10f;

            // Könnte auch funktionieren
            //float top = _camZ + 10f;
            //float bottom = _camZ - 10f;

            mat4 lightProjection = mat4.Ortho(left, right, bottom, top, near_plane, far_plane);
            mat4 lightView = mat4.LookAt(lightPos, new vec3(0.0f, 0.0f, 0.0f), new vec3(0.0f, 1.0f, 0.0f));

            return lightProjection * lightView;
        }
Screenshot 2023-11-01 192118.png
was bis jetzt auch gut funktioniert. Ist das so richtig, oder gibt's da andere Methoden das zu bewerkstelligen?
Benutzeravatar
Schrompf
Moderator
Beiträge: 4924
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas
Wohnort: Dresden
Kontaktdaten:

Re: OpenGL Schadowmap relativ zur kamera

Beitrag von Schrompf »

So geht's, aber dann hast Du einigen ShadowMap-Platz auf den Bereich hinter der Kamera verschwendet.

Was ich stattdessen empfehle: stelle Dein Frustum auf, also die Pyramide, die das Sichtfeld Deiner Kamera darstellt. Stelle außerdem das Koordinatensystem Deines Lichtes auf, also drei Vektoren entlang und quer zur Lichtrichtung. Bei Dir kommt das Licht anscheinen direkt von oben, da isses einfach. Dann kannst Du alle fünf Ecken Deiner Frustum-Pyramide in dieses Koordinatensystem transformieren und kannst eine BoundingBox aufmachen. Und die ist Deine ShadowMap.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Andy90
Beiträge: 70
Registriert: 08.10.2023, 13:02

Re: OpenGL Schadowmap relativ zur kamera

Beitrag von Andy90 »

ok das ist ein interessanter Ansatz, vielen dank :) Ich werde das mal ausprobieren. Ich merke schon, Schatten richtig und vor allem gut zu rendern ist eine Wissenschaft für sich :D
Benutzeravatar
dot
Establishment
Beiträge: 1736
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: OpenGL Schadowmap relativ zur kamera

Beitrag von dot »

Andy90 hat geschrieben: 01.11.2023, 22:17 Ich merke schon, Schatten richtig und vor allem gut zu rendern ist eine Wissenschaft für sich :D
Es ist und bleibt ein ungelöstes Problem der Computergrafik… ^^
Benutzeravatar
Schrompf
Moderator
Beiträge: 4924
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas
Wohnort: Dresden
Kontaktdaten:

Re: OpenGL Schadowmap relativ zur kamera

Beitrag von Schrompf »

Ich hab den Verdacht, dass der Wunsch nach Raytraced Shadows in allen möglichen Medien vor allem davon kommt, dass man Cascaded Virtual Moment Shadow Maps mit Normal Dependent Offset und Ketchup und Pommes quasi nie "gelöst" bekommt. Auch Jahre später findet man sich wieder, da doch noch schnell nen PostFx drüber zu jagen, um ein paar Fehlpixel zu fixen oder noch ein Offset bissl feiner einzustellen.

Grundsätzlich aber: bissl Offset gegen Selbstschatten, Augen zu und 64 Samples in ner Schleife im Shader, heutige GPUs können das stressarm, außer MeisterQ's Holz-GPU.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Andy90
Beiträge: 70
Registriert: 08.10.2023, 13:02

Re: OpenGL Schadowmap relativ zur kamera

Beitrag von Andy90 »

Hallo :) da ich gerade die Schatten in mein kleines Framework implementiere habe ich den ansatz genutzt welchen du vorgeschlagen hast und positioniere die Shadowmap relativ zum Kamera Frustum.

Code: Alles auswählen

public mat4 GenerateLightspaceMatrix(Camera camera, Viewport viewport, Light lightSource)
        {
            var cam = (PerspectiveCamera) camera;

            mat4 lightView = mat4.LookAt(lightSource.Location.ToGlmVec3(), new vec3(0), new vec3(0.0f, 1.0f, 0.0f));
            var frustumCorners = cam.GetFrustum(viewport).ToList(lightView);

            vec3 min = new vec3();
            vec3 max = new vec3();

            for (int i = 0; i < frustumCorners.Count; i++)
            {
                if (frustumCorners[i].x < min.x)
                    min.x = frustumCorners[i].x;
                if (frustumCorners[i].y < min.y)
                    min.y = frustumCorners[i].y;
                if (frustumCorners[i].z < min.z)
                    min.z = frustumCorners[i].z;

                if (frustumCorners[i].x > max.x)
                    max.x = frustumCorners[i].x;
                if (frustumCorners[i].y > max.y)
                    max.y = frustumCorners[i].y;
                if (frustumCorners[i].z > max.z)
                    max.z = frustumCorners[i].z;
            }

            float l = min.x - 10f;
            float r = max.x + 10f;
            float b = min.y - 10f;
            float t = max.y + 10f;

            float n = -max.z;
            float f = -min.z;

            mat4 lightProjection = mat4.Ortho(l, r, b, t, n, f);
            return lightProjection * lightView;
        }
das ist was ich aktuell nutze, gefunden habe ich das auf https://gamedev.stackexchange.com/quest ... the-camera. Hier mal die ersten Screenshots
Screenshot 2024-09-04 000843.png
Screenshot 2024-09-02 161202.png
Jetzt ist nur meine Frage kann ich die objekte ganz normal auf die shadowmap rendern, also mit den normalen render funktionen und shadern oder sollte ich dafür extra shader erstellen?
Benutzeravatar
Schrompf
Moderator
Beiträge: 4924
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas
Wohnort: Dresden
Kontaktdaten:

Re: OpenGL Schadowmap relativ zur kamera

Beitrag von Schrompf »

Ich hab's nie benutzt, aber ich bin sicher, OpenGL hat einen DepthOnlyOutput-Modus, wo gar kein Shader ausgeführt wird, aber die GPU so schnell wie möglich den ZBuffer beschreibt. Den kannst Du dann mit bissl Tricksen als Textur anklemmen und als ShadowMap samplen.

Ich bin Oldschool respektive hab nie ne moderne API benutzt, ich habe nen separaten Shader, der die normierte Tiefe als Farbwert ausgibt.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Andy90
Beiträge: 70
Registriert: 08.10.2023, 13:02

Re: OpenGL Schadowmap relativ zur kamera

Beitrag von Andy90 »

Werde es einfach mal ausprobieren. Finde aber den Ansatz mit dem frustum recht praktisch. So hat man eine gute Schatten Qualität und immer korrekte schatten.
Benutzeravatar
Jonathan
Establishment
Beiträge: 2452
Registriert: 04.08.2004, 20:06
Kontaktdaten:

Re: OpenGL Schadowmap relativ zur kamera

Beitrag von Jonathan »

Andy90 hat geschrieben: 04.09.2024, 20:32 Jetzt ist nur meine Frage kann ich die objekte ganz normal auf die shadowmap rendern, also mit den normalen render funktionen und shadern oder sollte ich dafür extra shader erstellen?
Also ich verwende einfach die ganz normalen Shader die ich zum Rendern auch schon benutze. Was funktioniert und einfach ist, aber nicht unbedingt toll. Besser wäre es natürlich, nur die Tiefenwerte rauszurechnen.

"DepthOnlyOutput" bei OpenGL hab ich noch nie gehört, mag aber gerne daran liegen, dass ich nie explizit danach gesucht habe. Aber ich denke auch damit kann es nicht mit einer Zeile Code getan sein. Mal schauen, was für vernünftiges Shadowmap-Rendering passieren muss:

- Shader brauchst du ja trotzdem garantiert. Vertex-Shader berechnen ja erst die Position, aber auch Fragment Shader können ja Fragmente discarden. Sie könnten ggf. auch die Tiefe eines Fragmentes anhand einer Textur verändern. Im Allgemeinen wirst du also andere Shader brauchen.

- Bei mir (ist wohl Sache des Grafikkartentreibers?) ist der Shadercompiler sehr gut darin ungenutze Dinge rauszuschmeißen. Es sollte also kein Problem sein quasi den identischen Shader Code zu kompilieren aber irgendwie verständlich zu machen, dass man nur am Tiefenwert interessiert ist. Ob man jetzt den Shader minimal anpasst (Ausgabefarbe auf einen konstanten Wert setzen) oder irgendeinen OpenGL Befehl benutzt, ist zweitranging, das wird beides irgendwie gehen und sehr ähnlich sein.

- Trotzdem muss man den Shader 2 mal kompillieren und entsprechend anders optimieren. Ich kann mir nicht vorstellen, dass OpenGL jeden Shader absolut immer 2 mal kompilliert nur für den Fall, dass man den ja mal für Shadow-Maps benutzen kann. Das wird man also wohl selber machen müssen.

- Schlussendlich muss man ja auch Texturen und Buffer etc. setzen. Manche davon sind für die Tiefeninformationen egal. Aber selbst wenn man 2 Renderjobs an OpenGL schickt macht es ja einen großen Unterschied, ob man zwischendrin eine Textur setzt, egal ob sie schlussendlich verwendet wird oder nicht. Für Shadowmaps wirst du am Ende vermutlich sehr viel mit dem selben "Material" rendern können, was Clientseitig einfach effizienter sein sollte.

Und schlussendlich willst du ja vielleicht für die Shadowmap auch ganz andere Dinge rendern. ViewFrustum Culling mit dem Kamerafrustum wäre z.B. eine schlechte Idee, vlt. willst du ein paar dynamische Objekte weglassen oder aus Performance-Gründen andere Geometrien als Proxy rendern. Das ist jetzt schon ziemlich optional, aber es verdeutlicht die Tatsache das Shadowmap rendern ein komplett separater Render-Pass ist, inklusive anderer Shader, anderer Texturen / Ressourcen und ggf. anderer Meshs.

Ich ignoriere das wie gesagt einfach, was definitiv funktioniert, aber halt nicht schnell ist. Ich würde es demnächst Umstellen auf ein System wo man oben genannte Tatsache vernünftig abbildet und dann damit effizient rendern kann.
Lieber dumm fragen, als dumm bleiben!
https://jonathank.de/games/
Andy90
Beiträge: 70
Registriert: 08.10.2023, 13:02

Re: OpenGL Schadowmap relativ zur kamera

Beitrag von Andy90 »

Hallo :) wie Jonathan sagt funktioniert es ebenso, einfach mit den selben shadern die objekte auf die Shadowmap zu rendern. Was ich nun auch erstmal so mache. Wie sich das mit der Performance hält kann ich jetzt auch noch nicht sagen. Es spart jedenfalls einiges an arbeit. Aktuell rendere ich den Shadowpass wie folgt

Code: Alles auswählen

 public override void OnRender(Game game, IRenderDevice renderDevice)
        {
            // Shadowpass
            renderDevice.SetCamera(game.Viewport, this.Camera);

            var lightProjectionMatrix = Light.GetLightProjectionMatrix(Sun, (PerspectiveCamera)Camera, game.Viewport);
            var lightViewMatrix = Light.GetLightViewMatrix(Sun);
            var lightSpaceMatrix = Utils.CalculateLightspaceMatrix(lightProjectionMatrix, lightViewMatrix);

            renderDevice.PrepareShadowPass(ShadowMap, lightSpaceMatrix);
            renderDevice.SetProjectionMatrix(lightProjectionMatrix);
            renderDevice.SetViewMatrix(lightViewMatrix);
            
            if (this.Sun.CastShadows)
            {
                foreach (var layer in this.Layer)
                {
                    foreach (var item in layer.Elements)
                    {
                        if(item.Enabled && item.CastShadows)
                        {
                            item.OnRender(game, renderDevice);
                        }
                    }
                }
            }
            renderDevice.FinishShadowPass(game.Viewport);
            Debug.WriteLine($"Rendered Shadowmap with error: {renderDevice.GetError()}");

            // Normal Pass (Set Camera needs to set again for the new matrices!
            renderDevice.SetCamera(game.Viewport, this.Camera);

            if (this.Skybox != null)
                renderDevice.DrawSkyBox(this.Skybox);

            // Before scene preperation even
            if (this.BeforeScenePreperation != null)
                this.BeforeScenePreperation(this, game, renderDevice);

            // Scene rendering
            renderDevice.PrepareSceneRendering(this);

            if (this.BeforeSceneRender != null)
                this.BeforeSceneRender(this, game, renderDevice);

            foreach (var item in Layer)
            {
                item.OnRender(game, renderDevice);
            }

            if (this.AfterSceneRender != null)
                this.AfterSceneRender(this, game, renderDevice);

            renderDevice.FinishSceneRendering(this);


            // Canvas canvas preperation
            if (this.BeforeCanvasPreperation != null)
                this.BeforeCanvasPreperation(this, game, renderDevice);

            // Cavas rendering
            renderDevice.PrepareCanvasRendering(this, null);

            if (this.BeforeCanvasRender != null)
                this.BeforeCanvasRender(this, game, renderDevice);

            foreach (var item in Canvas)
            {
                item.OnRender(game, renderDevice, this);
            }

            if (this.AfterCanvasRender != null)
                this.AfterCanvasRender(this, game, renderDevice);

            renderDevice.FinishCanvasRendering(this, null);
        }
Sind jetzt viele funktionen dabei wie die szene normal gerendert wird. Aber funktioniert gut, selbst mit 3D animationen.
Benutzeravatar
Krishty
Establishment
Beiträge: 8290
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: OpenGL Schadowmap relativ zur kamera

Beitrag von Krishty »

glColorMask(0, 0, 0, 0) sollte dem Grafiktreiber mitteilen, dass nur die Tiefe relevant ist, und den Rest sollte er selber optimieren können.

Shader doppelt kompilieren klingt nach einem Maintenance-Alptraum, aber wenn du Zeit übrig hast würde ich einen Vergleich mit glColorMask natürlich begrüßen!

Bin mir 80 % sicher, dass Color Mask der Standard-Weg ist, auf den Treiber optimiert wurden.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Jonathan
Establishment
Beiträge: 2452
Registriert: 04.08.2004, 20:06
Kontaktdaten:

Re: OpenGL Schadowmap relativ zur kamera

Beitrag von Jonathan »

Krishty hat geschrieben: 05.09.2024, 13:15 glColorMask(0, 0, 0, 0) sollte dem Grafiktreiber mitteilen, dass nur die Tiefe relevant ist, und den Rest sollte er selber optimieren können.
Ich frage mich gerade, welchen Unterschied das macht? Für meine Shadow Map erstelle ich einen Framebuffer, und der hat entsprechend ein GL_DEPTH_ATTACHMENT aber eben kein GL_COLOR_ATTACHMENT. D.h. Colorwrite sollte ja per default schon aus sein (gibt ja gar keinen Speicher dafür), also sollte dann auch ein glColorMask keinen Einfluss mehr haben.

Wenn der Shader einmal kompilliert ist, sollte man ja irgendwo irgendeine Form von optimiertem Assembler-Code haben. Ich würde davon ausgehen, dass das als ganzes optimiert ist, beispielsweise könnte ein Fragment-Discard ja von einer Textur abhängen, und das auf eine komplizierte Art und Weise - was man dann nicht zweimal (für Farbe und Tiefe) berechnen will. Insofern stelle ich es mir schwierig vor, dass man einen kompillierten Shader nehmen und einfach so als äquivalent optimierten Depth-Only Shader ausführen kann.

Ohnehin gibt es dann ja später auch Techniken wie Moment-Shadow-Mapping, bei denen man nicht nur simple Tiefenwerte raus schreibt, sondern mehr berechnet. Spätestens da muss man dann ja zwingend andere Fragment-Shader benutzen, als für Depth-only. Und wenn man das einbauen will ist es sicherlich sehr nützlich wie Client-Seitig Shadow-Mapping schon als eigenständiger Render-Pass betrachtet wird.

Ich müsste echt mal meine Engine dementsprechend umschreiben. Da könnte man dann auch gleich noch sowas einbauen wie einen Render-Pass der die Mini-Map in einem hübschen Stil rendert. Hmhmm, mal sehen.
Lieber dumm fragen, als dumm bleiben!
https://jonathank.de/games/
Benutzeravatar
Jonathan
Establishment
Beiträge: 2452
Registriert: 04.08.2004, 20:06
Kontaktdaten:

Re: OpenGL Schadowmap relativ zur kamera

Beitrag von Jonathan »

Krishty hat geschrieben: 05.09.2024, 13:15aber wenn du Zeit übrig hast würde ich einen Vergleich mit glColorMask natürlich begrüßen!
Hahah, ja. Ehrlich gesagt könnte ich mir vorstellen, dass ich generell noch wie vor 10 Jahren render und heute eigentlich sowieso einfach viel mehr Dreiecke raushauen müsste. Ich wäre nicht komplett überrascht, wenn eine moderne Grafikkarte von dem bisschen extra Shading gar nichts merkt und das eigentliche Problem die zig-hundert Objekte mit wenig Polygonen sind, die ich als einzelne Draw-Calls raushaue...
Lieber dumm fragen, als dumm bleiben!
https://jonathank.de/games/
Benutzeravatar
Krishty
Establishment
Beiträge: 8290
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: OpenGL Schadowmap relativ zur kamera

Beitrag von Krishty »

Jonathan hat geschrieben: 05.09.2024, 14:38Wenn der Shader einmal kompilliert ist, sollte man ja irgendwo irgendeine Form von optimiertem Assembler-Code haben. Ich würde davon ausgehen, dass das als ganzes optimiert ist, beispielsweise könnte ein Fragment-Discard ja von einer Textur abhängen, und das auf eine komplizierte Art und Weise - was man dann nicht zweimal (für Farbe und Tiefe) berechnen will. Insofern stelle ich es mir schwierig vor, dass man einen kompillierten Shader nehmen und einfach so als äquivalent optimierten Depth-Only Shader ausführen kann.
Shader werden nur vorkompiliert; die tatsächliche Optimierung findet beim Binden an die Pipeline statt. Das Ergebnis wird dann mit einem Hash des Pipeline-Zustands gecacht.

Das war in den 00ern ein riesiges Performance-Problem und der Grund, warum viele Spiele ihren ersten Frame versteckt haben (er brauchte halt eine Sekunde, weil da alle Shader zum ersten Mal optimiert wurden). Mit D3D11 Dynamic Shader Linkage wurde es besser und mit Vulkan nochmal, aber das alte GL dürfte noch die On-Demand-Optimierung machen.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Jonathan
Establishment
Beiträge: 2452
Registriert: 04.08.2004, 20:06
Kontaktdaten:

Re: OpenGL Schadowmap relativ zur kamera

Beitrag von Jonathan »

Ach, da schau mal einer an. Nun gut, dann sollte der einzige relevante Performanceunterschied ja das Setzen/Aktivieren nutzloser Ressourcen wie unbenötigte Uniforms und Texturen zu sein. Oder Dinge wie Interlaced-Vertex-Buffer wo dann Daten wie UV-Koordinaten oder Vertex-Colors stehen, die für das Shadow-Mapping nicht benötigt werden. Da könnte man einiges an Bandbreite verschwenden, wenn die Daten nicht benötigt und trotzdem übertragen werden müssen. Es sei denn, das Speicherlayout wird umgebogen oder die Daten nicht blockweise übertragen.
Lieber dumm fragen, als dumm bleiben!
https://jonathank.de/games/
Benutzeravatar
Krishty
Establishment
Beiträge: 8290
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: OpenGL Schadowmap relativ zur kamera

Beitrag von Krishty »

Wenn Alpha Blending deaktiviert ist, der Shader aber Alpha komplett berechnet, ist das ja schon einiges an ALU, das man einsparen kann. (In HLSL sieht es zwar immer so aus, als würden Shader mit vollen RGBA-Vektoren rechnen, tatsächlich ist die Harware aber seit 15 Jahren skalar.)

Ich weiß auch nicht, wie viel die Uniforms da rein spielen – ob etwa die Berechnung eines schwarzen Lichts weggeschmissen werden kann. Activision hatte IIRC unter D3D10 für Gott-Shader geworben (riesige Shader, die alle Materialien per if behandeln, statt vieler einzelner Shader pro Material), um die Pipeline nicht so oft unterbrechen zu müssen. Da könnte der Treiber mit sowas natürlich viel rausholen 🤷
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Matthias Gubisch
Establishment
Beiträge: 479
Registriert: 01.03.2009, 19:09

Re: OpenGL Schadowmap relativ zur kamera

Beitrag von Matthias Gubisch »

Also mit DX12 reicht es einfach keinen pixel shader sondern nur den vertex shader zu setzen und schon hat mein ein depth only output
Vielleicht gibt es bei OGL je einen ähnlichen Trick?
Bevor man den Kopf schüttelt, sollte man sich vergewissern einen zu haben
Antworten