Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

1

21.03.2015, 14:17

Shadow Mapping: Schatten flackern

Hi,

ich hab jetzt Shadow Mapping implementiert, aber die Ränder der Schatten flackern, wenn sich die Lichtquelle bewegt...

Hat jemand eine Idee, wie man das beheben könnte? Gibt es vielleicht ein gutes Tutorial dafür?

Die benutzten Sprachen sind HLSL und C++.

Grüße,
Magogan
Cube Universe
Entdecke fremde Welten auf deiner epischen Reise durchs Universum.

David Scherfgen

Administrator

Beiträge: 10 382

Wohnort: Hildesheim

Beruf: Wissenschaftlicher Mitarbeiter

  • Private Nachricht senden

2

21.03.2015, 14:30

Was heißt "flackern"? Zeig mal ein paar Bilder.
Ich tippe auf zu kleinen Bias oder schlechte Ausnutzung der Z-Auflösung.

3

21.03.2015, 14:32

Ne, daran liegt es vermutlich nicht. Die Schatten an sich sehen richtig aus, solange sich die Lichtquelle nicht bewegt. Wenn sie sich aber bewegt, dann springen die Kanten der Schatten hin und her.
Cube Universe
Entdecke fremde Welten auf deiner epischen Reise durchs Universum.

David Scherfgen

Administrator

Beiträge: 10 382

Wohnort: Hildesheim

Beruf: Wissenschaftlicher Mitarbeiter

  • Private Nachricht senden

4

21.03.2015, 14:39

Ohne es zu sehen, schwer zu sagen ... aber das ist gewissermaßen normal, wegen der diskreten Natur der Shadow Map. Du kannst PCF (Percentage Closer Filtering - such mal danach) betreiben, um die Schatten etwas weichzuzeichnen und dieses Phänomen abzumildern. Die Auflösung der Shadow Map zu erhöhen sollte auch helfen.

TrommlBomml

Community-Fossil

Beiträge: 2 117

Wohnort: Berlin

Beruf: Software-Entwickler

  • Private Nachricht senden

5

21.03.2015, 15:20

David hat denke ich schon recht. Um auszuprobieren, ob es das ist, vergrößere deine Depthmap, dann sollte es etwas weniger an den Kanten springen. Ansonsten liegt es vielleicht am ShadowMap Format. Nutzt du floating point Texturen?

6

21.03.2015, 15:43

Ich nutze das Format R24X8_TYPELESS (also 24 bit UNORM) für den Depth Buffer, der gleichzeitig die Depth Texture ist.

Vermutlich liegt es daran, dass ich einen zu großen Teil der Welt mit einer Depth Texture abdecken will. Ich werde mal versuchen, LiSPSM zu implementieren...
Cube Universe
Entdecke fremde Welten auf deiner epischen Reise durchs Universum.

SlinDev

Treue Seele

Beiträge: 142

Wohnort: Lübeck

Beruf: Programmierer

  • Private Nachricht senden

7

21.03.2015, 15:45

Ich denke auch, dass das Problem die beschränkte Auflösung der Shadowmap ist. Filtern hilft etwas das ganze zu verstecken, aber auch nur eingeschränkt. Je nach Lichtquelle reicht eventuell eine höhere Auflösung aus. Wenn es um die Sonne geht, macht es zum Beispiel Skyrim so, dass diese nur alle x-Sekunden ganz kurz bewegt wird (sieht scheiße aus) oder GTA V bewegt die Sonne meistens nur, wenn der Spieler sich gerade bewegt und das Flackern entsprechend nicht mehr so auffällt (was zumindest in diesem Spiel ziemlich gut funktioniert). Die einzige gut funktionierende Lösung wäre die Shadowmapauflösung so weit zu erhöhen, dass ein Shadowmap Pixel auf einen Pixel auf dem Bildschirm fällt (oder kleiner), dazu gibt es ein paar Techniken basierend auf Cascaded Shadowmapping, die das ganze auf den tatsächlich sichtbaren Bereich beschränken, was erstaunlich gut funktioniert.

Edit: LiSPSM macht das ganze eher noch schlimmer, da die Kanten dann auch flackern wenn die Kamera sich bewegt und es keine Möglichkeit gibt das ordentlich zu unterdrücken. Außerdem ist insbesondere der Worst Case ziemlich unbrauchbar. Das gilt so größtenteils auch für die andere Techniken die versuchen die Projektion zur Kamera hin zu verzerren. Außerdem macht es gleichmäßiges Filtern deutlich schwieriger.
Ich würde dir CSM empfehlen, machen nach wie vor die meisten und es funktioniert aus meiner Erfahrung auch ziemlich gut.

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »SlinDev« (21.03.2015, 15:52)


8

21.03.2015, 17:05

Wie erstelle ich denn die View-Projection Matrizen für die einzelnen Texturen beim Cascaded Shadow Mapping? Mir fehlt da gerade der Ansatz, aktuell habe ich nur eine Matrix die einen Quader um die Kamera herum auf die Textur (bzw. in den View Space) abbildet. Aber ich muss ja das ganze View Frustum aufteilen irgendwie...
Cube Universe
Entdecke fremde Welten auf deiner epischen Reise durchs Universum.

SlinDev

Treue Seele

Beiträge: 142

Wohnort: Lübeck

Beruf: Programmierer

  • Private Nachricht senden

9

21.03.2015, 19:03

Dazu gibt es verschiedene Ansätze. Grundsätzlich wird das Kamerafrustum in mehrere Frustums aufgeteilt und dann für jedes dieser Teilfrustums eine Depthmap gerendert. Der Ansatz den ich implementiert habe, ist der, jeweils eine Kamera über dem Zentrum von jedem dieser Teilfrustums zu platzieren und die orthogonale Projektion so zu skalieren, dass das entsprechende Teilfrustum genau hinein passt. Dabei ist wichtig, dass die Position der Kamera so ist, dass die Blickrichtung die Richtung der Lichtquelle ist und "über dem Zentrum" bedeutet dass die Kamera zwischen Lichtquelle und dem Zentrum des Frustums ist.
Außerdem hilft es die jeweiligen Teillichtquellen im "Raster" der Texturauflösung zu "snappen", also immer nur um ganze Pixel die Position zu verändern. Eigentlich müsste man dann das Frustum der Depthkamera noch in jede Richtung um einen Pixel erweitern, aber da diese eh eher großzügig bemessen sind, habe ich auch ohne noch keine Probleme festgestellt.

In Code sieht das bei mir dann so aus:

Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
Matrix Camera::MakeShadowSplit(Camera *camera, Light *light, float near, float far)
    {
        //Get camera frustum extends to be covered by the split
        Vector3 nearcenter = camera->ToWorld(Vector3(0.0f, 0.0f, near));
        Vector3 farcorner1 = camera->ToWorld(Vector3(1.0f, 1.0f, far));
        Vector3 farcorner2 = camera->ToWorld(Vector3(-1.0f, -1.0f, far));
        Vector3 farcenter = (farcorner1+farcorner2)*0.5f;
        Vector3 center = (nearcenter+farcenter)*0.5f;
        
        //Calculate the size of a pixel in world units
        float dist = center.GetDistance(farcorner1);
        Vector3 pixelsize = Vector3(Vector2(dist*2.0f), 1.0f)/Vector3(_frame.width, _frame.height, 1.0f);
        
        //Place the light camera 500 units above the splits center
        Vector3 pos = center-light->GetForward()*500.0f;
        
        //Transform the position to light space
        Matrix rot = light->GetWorldRotation().GetRotationMatrix();
        pos = rot.GetInverse()*pos;
        
        //Snap to the pixel grid
        pos /= pixelsize;
        pos.x = floorf(pos.x);
        pos.y = floorf(pos.y);
        pos.z = floorf(pos.z);
        pos *= pixelsize;
        
        //Transform back and place the camera there
        pos = rot*pos;
        SetPosition(pos);
        
        //Set the light camera frustum
        _clipFar = 500.0f + dist * 2.0f;
        _orthoLeft = -dist;
        _orthoRight = dist;
        _orthoBottom = -dist;
        _orthoTop = dist;
        
        //Update the projection matrix
        _dirtyProjection = true;
        UpdateProjection();
        
        //Return the resulting matrix
        Matrix projview = _projectionMatrix * GetWorldTransform().GetInverse();
        return projview;
    }


Das Objekt selbst ist eine der Depthkameras. Der Parameter "camera" ist die Spielerkamera und "near" und "far" sind die Clipdistanzen deren Bereich von der Depthkamera abgedeckt werden soll. "Near" und "far" lassen sich einfach per hand festlegen, linear oder logarithmisch, oder wie in einem GPU Gems Artikel vorgeschlagen eine Kombination aus linear und logarithmisch.
Zum rendern habe ich instancing und layered Rendering ausprobiert, war aber auf meinem Macbook durch den benötigten Geometry Shader langsamer als einfach mehrfach komplett zu rendern. Zum Filtern empfehle ich Hardware PCF, eventuell 4*4, wird aber auf meiner 650M ziemlich schnell sehr langsam mit größerem Kernel.

Bei mir sieht das ganze dann so aus:


Und ich hab gerade festgestellt dass ich schonmal einen etwas ausführlicheren Blogbeitrag zu dem Thema geschrieben habe: http://rayne3d.com/blog/05-18-2014-shado…ectional-lights

10

22.03.2015, 15:16

Ich habe mir jetzt selbst etwas überlegt. Der folgende Code sollte eigentlich die Ecken des Viewing Frustums der Kamera (im Koordinatensystem der Welt) in das Koordinatensystem der Lichtquelle überführen, dann die Boundng Box ausrechnen, das Licht in der Mitte der Bounding Box platzieren und das wieder zurück transfomieren in das Koordinatensystem der Welt... Aber irgendwie geht das nicht so richtig. Sieht von euch vielleicht jemand einen Fehler oder hat einen Tipp?

Also das Viewing Frustum im Koordinatensystem der Welt wird korrekt berechnet, daran kann es nicht liegen. Die Transformation zwischen den Koordinatensystemen scheint nicht korrekt zu sein...

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
void DirectionalLight::Render(ID3D11DeviceContext* DeviceContext, Camera_t* Camera, DOUBLE3 Direction, RenderFunction_t RenderFunction){
    DeviceContext->RSSetViewports(1, &Viewport);
    float CameraNear = Camera->GetNear(), CameraFar = Camera->GetFar(), FoV_rad=Camera->GetFoVRadians(), ViewRatio=Camera->GetViewRatio();
    double LengthPerUnit = 1.0 / (pow(2, NumTextures) - 1) * (CameraFar-CameraNear);
    uint64_t CurrentDistance = 0; //0 to (2^NumTextures)-1
    DOUBLE3 CameraUp = Camera->GetUpVector(), CameraRight = Camera->GetRightVector();
    DOUBLE3 CameraPosition = Camera->GetPosition();
    DOUBLE3 CameraDirection = Camera->GetDirection();

    double LightAngleY, LightAngleZ;
    double LightLength = Direction.Length();
    LightAngleY = acos(Direction.z / Direction.Length());
    LightAngleZ = 
        (Direction.y > 0) ? 
            atan(Direction.x / Direction.y) + 3*M_PI_2
        :
        (
            (Direction.y < 0) ? 
                atan(Direction.x / Direction.y) + M_PI_2 
            :
                (Direction.x > 0 ? 0 : M_PI)
        );

    XMMATRIX RotationMatrix = XMMatrixMultiply(XMMatrixRotationY(LightAngleY), XMMatrixRotationZ(LightAngleZ));
    XMMATRIX RotationInverse = XMMatrixMultiply(XMMatrixRotationZ(-LightAngleZ), XMMatrixRotationY(-LightAngleY));

    for (unsigned int i = 0; i < NumTextures; ++i){
        uint64_t CurrentSize = (uint64_t)pow(2, i);

        DeviceContext->ClearDepthStencilView(DepthStencilViews[i], D3D11_CLEAR_DEPTH, 1.0f, 0);
        DeviceContext->OMSetRenderTargets(0, 0, DepthStencilViews[i]);
        Frustum LightViewFrustum;
        
        double CurrentNear = CurrentDistance*LengthPerUnit + CameraNear;
        double CurrentFar = (CurrentDistance + CurrentSize)*LengthPerUnit + CameraNear;

        DOUBLE3 NearCenter = CameraPosition + CurrentNear * CameraDirection;
        DOUBLE3 FarCenter = CameraPosition + CurrentFar * CameraDirection;
        
        double NearHeight2 = tan(FoV_rad / 2) * CurrentNear;
        double FarHeight2 = tan(FoV_rad / 2) * CurrentFar;
        double NearWidth2 = NearHeight2 * ViewRatio;
        double FarWidth2 = FarHeight2 * ViewRatio;

        XMFLOAT3 Edges[8];

        Edges[0] = FarCenter + (FarHeight2) * CameraUp - (FarWidth2) * CameraRight;
        Edges[1] = FarCenter + (FarHeight2) * CameraUp + (FarWidth2) * CameraRight;
        Edges[2] = FarCenter - (FarHeight2) * CameraUp - (FarWidth2) * CameraRight;
        Edges[3] = FarCenter - (FarHeight2) * CameraUp + (FarWidth2) * CameraRight;

        Edges[4] = NearCenter + (NearHeight2)* CameraUp - (NearWidth2)* CameraRight;
        Edges[5] = NearCenter + (NearHeight2)* CameraUp + (NearWidth2)* CameraRight;
        Edges[6] = NearCenter - (NearHeight2)* CameraUp - (NearWidth2)* CameraRight;
        Edges[7] = NearCenter - (NearHeight2)* CameraUp + (NearWidth2)* CameraRight;

        XMFLOAT3 Test;
        DOUBLE3 Min(std::numeric_limits<double>::infinity(), std::numeric_limits<double>::infinity(), std::numeric_limits<double>::infinity()), Max(-std::numeric_limits<double>::infinity(), -std::numeric_limits<double>::infinity(), -std::numeric_limits<double>::infinity());
        for (int j = 0; j < 8; ++j){
            XMStoreFloat3(&Test, XMVector3TransformCoord(XMLoadFloat3(&Edges[j]), RotationInverse));
            if (Min.x > Test.x){
                Min.x = Test.x;
            }
            if (Max.x < Test.x){
                Max.x = Test.x;
            }

            if (Min.y > Test.y){
                Min.y = Test.y;
            }
            if (Max.y < Test.y){
                Max.y = Test.y;
            }

            if (Min.z > Test.z){
                Min.z = Test.z;
            }
            if (Max.z < Test.z){
                Max.z = Test.z;
            }
        }

        XMFLOAT3 FocusPosition((Max.x + Min.x) / 2, (Max.y + Min.y) / 2, Max.z + 0.5f);
        XMStoreFloat3(&FocusPosition, XMVector3TransformCoord(XMLoadFloat3(&FocusPosition), RotationMatrix));

        XMFLOAT3 LightPosition = DOUBLE3(FocusPosition) - Distance*Direction;
        XMFLOAT3 Up(0.0f,1.0f,0.0f);
        XMStoreFloat3(&Up, XMVector3TransformCoord(XMLoadFloat3(&Up), RotationMatrix));


        XMMATRIX ViewMatrix = XMMatrixLookAtLH(XMLoadFloat3(&LightPosition), XMLoadFloat3(&FocusPosition), XMLoadFloat3(&Up)),
            ProjectionMatrix = XMMatrixOrthographicLH((Max.x - Min.x)*1.05, (Max.y - Min.y)*1.05, 0, Distance);

        
        ViewProjectionMatricesT[i] = XMMatrixTranspose(XMMatrixMultiply(ViewMatrix, ProjectionMatrix));
        LightViewFrustum.Update(Distance, ViewMatrix, ProjectionMatrix);
        
        RenderFunction(DeviceContext, &LightViewFrustum, ViewProjectionMatricesT[i]);

        CurrentDistance += CurrentSize;
    }
    
}
Cube Universe
Entdecke fremde Welten auf deiner epischen Reise durchs Universum.

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »Magogan« (22.03.2015, 16:21)


Werbeanzeige