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

idontknow

unregistriert

11

22.01.2014, 16:21

Ich hab bei meiner Implementierung die Vertex-Normalen bei der Erzeugung der Geometrie direkt berechnet (Mittelwert der Summe der Normalen der anliegenden Faces) und die sind dann im Pixelshader schön interpoliert angekommen! Alles kein Problem - bin etwas verwirrt wos da bei dir brennt bzw. warum das nicht klappen sollte!

TrommlBomml

Community-Fossil

  • »TrommlBomml« ist der Autor dieses Themas

Beiträge: 2 117

Wohnort: Berlin

Beruf: Software-Entwickler

  • Private Nachricht senden

12

23.01.2014, 08:02

Dieser Ansatz funktioniert bei mir nicht, weil ich die Höhenwerte im Vertexshader berechne und somit dort erst die Normalen berechnen kann. Alternative wäre ansonsten der Geometry-Shader. Ist aber glaube ich keine gute Idee.

TrommlBomml

Community-Fossil

  • »TrommlBomml« ist der Autor dieses Themas

Beiträge: 2 117

Wohnort: Berlin

Beruf: Software-Entwickler

  • Private Nachricht senden

13

23.01.2014, 08:26

Ich habe es gestern nicht mehr geschafft, den relevanten Code hinzuzufügen. Das hole ich jetzt nach. Also ich habe ein Terrain mit 2048x2048 Cells, also 2049x2049 Vertices. Ich habe für das Rendering des Terrains einen 64x64 Cells Terrainpatch, also 65x65 Vertices mit y = 0 und einem dazu passenden Indexbuffer. Ich habe nur einen Satz texturkoordinaten, wobei die in jede dimension des Grids von 0.0f bis 1.0f gehen. Ich verwende beim Rendern einen rekursiven Ansatz mit einer einfache Formel: Length(CameraPos - PatchCenter) / QuadSize. Diesen Wert vergleiche ich mit einer Konstanten. Ist der Wert kleiner als die Konstante, dann wird meine Fläche in vier gleich große Teile geteilt und der darauf derselbe algorithmus angewendet. Ist der Wert größer, so render ich meinen 64x64 Patch auf die gerade angewendete Fläche. Ich hoffe das versteht jeder, sonst entwerfe ich eine kurze Zeichnung.

Bei jedem Rendern muss ich natürlich skalierungs- und offsetfaktoren für Heightmap-Texturkoordinaten berechnen. Diese bekommt meine Methode RenderPatch() als Parameter und ruft sich rekursiv mit angepassten Werten für eine Vierteilung auf. Die Berechnung der Texturkoordinatengrenzen kann eigentlich nicht falsh sein, sonst müsste ich ja zwischen den Patches seltsame Artefakte haben, aber sie sind überall. Trotzdem nochmal meine RenderPatch-Methode:

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
void Terrain::RenderPatch(GraphicsDevice* device, Camera *camera, 
                          float offsetHeightX, float offsetHeightY, float scaleHeight, 
                          float offsetPositionX, float offsetPositionY, float scalePosition, 
                          int level)
{


    float centerX = offsetPositionX + _patchSize * scalePosition * 0.5f;
    float centerZ = offsetPositionY - _patchSize * scalePosition * 0.5f;
    XMFLOAT3 patchCenter(centerX, 0.0f, centerZ);
    float triangleSize = scalePosition;

    float distance;
    XMStoreFloat(&distance, XMVector3Length(XMLoadFloat3(&patchCenter) - XMLoadFloat3(&camera->GetPosition())));

    float distanceToSizeQuotient = distance / triangleSize;

    if (distanceToSizeQuotient > _lodParameter || level < 1)
    {
        _terrainEffect->SetTerrainPatchData(XMFLOAT2(offsetHeightX, offsetHeightY), scaleHeight, XMFLOAT2(offsetPositionX, offsetPositionY), scalePosition);
        _terrainEffect->Apply(device->GetImmediateContext());
        device->DrawIndexed(_indexCount);
    }
    else
    {
        float halfHeightMapSize = scaleHeight * 0.5f;
        float halfScalePosition = scalePosition * 0.5f;
        RenderPatch(device, camera, offsetHeightX, offsetHeightY, halfHeightMapSize, 
                                     offsetPositionX, offsetPositionY, halfScalePosition, level-1);

        RenderPatch(device, camera, offsetHeightX+halfHeightMapSize, offsetHeightY, halfHeightMapSize, 
                                     centerX, offsetPositionY, halfScalePosition, level-1);

        RenderPatch(device, camera, offsetHeightX, offsetHeightY+halfHeightMapSize, halfHeightMapSize, 
                                     offsetPositionX, centerZ, halfScalePosition, level-1);

        RenderPatch(device, camera, offsetHeightX+halfHeightMapSize, offsetHeightY+halfHeightMapSize, halfHeightMapSize, 
                                     centerX, centerZ, halfScalePosition, level-1);
    }
}


Wichtig sind offsetHeightX und offsetHeightY sowie scaleHeight. Ich gehe von quadratischen Patches aus, daher kein ScaleX und -Y. Der vollständigkeit halber mein oberster Aufruf auf 2048x2048-Ebene. Dort werden die Texturkoordinaten für die Heightmap logischerweise je dimension von 0.0f bis 1.0f vergeben.

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
void Terrain::CustomDraw(GraphicsDevice* device, SceneNode *sceneNode, Camera *camera)
{
    //Vorbereitung zum Rendern etc. .... weggelasen
    
    float scalePosition = _terrainSize / _patchSize;
    float scaleHeightMap = 1.0f;

        //Parameter 3,4 und 5 sind die Texturkoordinaten für die heightmap, also 0,0,1
    RenderPatch(device, camera, 0,0, scaleHeightMap, -_terrainSize * 0.5f, _terrainSize * 0.5f, scalePosition, _maxLevels);
}


In der RenderPatch-Methode wird bei SetTerrainPatchData() die Parameter für den Vertex Shader zum anpassen der Texturkoordinaten in einen Constant Buffer geschrieben. Anbei der Pixelshader mit relevanten Code:

HLSL-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
cbuffer cbPerScene
{
    float3 LightDir = float3(1.0f,-3,1.0f);
    float3 CameraPosition;
    float HeightMapScale = 200.0f;
    float2 TileBase = float2(10.0f, 10.0f);
}

cbuffer cbPerObject
{
    float4x4 WorldViewProjection;
    float2 PosOffset = 0;
    float PosScale = 1;
    float2 HeightOffset = 0.0;
    float HeightScale = 1.0;
}

Texture2D HeightMap;

SamplerState HeightMapSampler
{
    Filter = MIN_MAG_MIP_LINEAR;
    AddressU = CLAMP;
    AddressV = CLAMP;
};

struct VertexInput
{
    float3 Position : POSITION0;
    float2 Texcoord : TEXCOORD0;
};

struct VertexOutput
{
    float4 Position   : SV_POSITION;
    float2 Texcoord   : TEXCOORD0;
};

float3 GetNormalFromHeightMap(Texture2D tex, SamplerState samp, float2 texCoord, float offsetToNextPixel)
{
    float2 offsets[4];
    offsets[0] = texCoord + float2(-offsetToNextPixel, 0);
    offsets[1] = texCoord + float2(offsetToNextPixel, 0);
    offsets[2] = texCoord + float2(0, -offsetToNextPixel);
    offsets[3] = texCoord + float2(0, offsetToNextPixel);

    float hts[4];

    for(int i = 0; i < 4; i++)
    {
        hts[i] = tex.SampleLevel(samp, offsets[i], 0).x * HeightMapScale;
    }

    const float cellSpace = 1.0f;

    float3 tangent = normalize(float3(2.0f * cellSpace, hts[1] - hts[0], 0.0f));
    float3 bitangent = normalize(float3(0.0f, hts[3] - hts[2], -2.0f * cellSpace));

    return normalize(cross(tangent, bitangent)).xyz;
}

float4 PSMain(VertexOutput input) : SV_TARGET
{
    //relevante Zeile. Ermittelt die Normale.
    float3 normal = normalize(GetNormalFromHeightMap(HeightMap, HeightMapSampler, HeightOffset + input.Texcoord * HeightScale, 1.0f / 64.0f));

    float diffuseFactor = saturate(dot(normal, normalize(-LightDir)));
    float4 materialTextureColor = DiffuseTexture.Sample(DiffuseMapSampler, input.Texcoord * TileBase);
    
    return materialTextureColor * diffuseFactor;
}


Relevant ist die erste Zeile im Pixel Shader. Ich weiß, das 1.0f / 64.0f ist nicht schön und muss an die Patchgröße angepasst werden, aber ich wollte erst den Fehler beseitigen.

Wenn etwas unklar ist bitte Fragen ;)

Werbeanzeige