Terrain

 

  • 지형을 표현할 때 사용
  • 무수한 삼각형의 조합으로 이루어져 있다.
  • 지형의 메쉬 정점들의 x축과 z축의 정점 간격이 일정하다.
  • 덮어질 높이 텍스처의 높이(y축) 정보를 추가하여 저장한다. (height map)

 

 


 

 

<terrain.fx>

 

 

더보기
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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
#ifndef _TERRAIN_FX_
#define _TERRAIN_FX_
 
#include "params.fx"
#include "utils.fx"
 
// --------------
// Vertex Shader
// --------------
 
struct VS_IN
{
    float3 pos : POSITION;
    float2 uv : TEXCOORD;
};
 
struct VS_OUT
{
    float3 pos : POSITION;
    float2 uv : TEXCOORD;
};
 
VS_OUT VS_Main(VS_IN input)
{
    VS_OUT output = input;
 
    return output;
}
 
// --------------
// Hull Shader
// --------------
 
struct PatchTess
{
    float edgeTess[3] : SV_TessFactor;
    float insideTess : SV_InsideTessFactor;
};
 
struct HS_OUT
{
    float3 pos : POSITION;
    float2 uv : TEXCOORD;
};
 
// Constant HS
PatchTess ConstantHS(InputPatch<VS_OUT, 3> input, int patchID : SV_PrimitiveID)
{
    PatchTess output = (PatchTess)0.f;
 
    float minDistance = g_vec2_1.x;
    float maxDistance = g_vec2_1.y;
 
    float3 edge0Pos = (input[1].pos + input[2].pos) / 2.f;
    float3 edge1Pos = (input[2].pos + input[0].pos) / 2.f;
    float3 edge2Pos = (input[0].pos + input[1].pos) / 2.f;
 
    edge0Pos = mul(float4(edge0Pos, 1.f), g_matWorld).xyz;
    edge1Pos = mul(float4(edge1Pos, 1.f), g_matWorld).xyz;
    edge2Pos = mul(float4(edge2Pos, 1.f), g_matWorld).xyz;
 
    float edge0TessLevel = CalculateTessLevel(g_vec4_0.xyz, edge0Pos, minDistance, maxDistance, 4.f);
    float edge1TessLevel = CalculateTessLevel(g_vec4_0.xyz, edge1Pos, minDistance, maxDistance, 4.f);
    float edge2TessLevel = CalculateTessLevel(g_vec4_0.xyz, edge2Pos, minDistance, maxDistance, 4.f);
 
    output.edgeTess[0= edge0TessLevel;
    output.edgeTess[1= edge1TessLevel;
    output.edgeTess[2= edge2TessLevel;
    output.insideTess = edge2TessLevel;
 
    return output;
}
 
// Control Point HS
[domain("tri")] // 패치의 종류 (tri, quad, isoline)
[partitioning("fractional_odd")] // subdivision mode (integer 소수점 무시, fractional_even, fractional_odd)
[outputtopology("triangle_cw")] // (triangle_cw, triangle_ccw, line)
[outputcontrolpoints(3)] // 하나의 입력 패치에 대해, HS가 출력할 제어점 개수
[patchconstantfunc("ConstantHS")] // ConstantHS 함수 이름
HS_OUT HS_Main(InputPatch<VS_OUT, 3> input, int vertexIdx : SV_OutputControlPointID, int patchID : SV_PrimitiveID)
{
    HS_OUT output = (HS_OUT)0.f;
 
    output.pos = input[vertexIdx].pos;
    output.uv = input[vertexIdx].uv;
 
    return output;
}
 
// [Terrain Shader]
// g_int_1      : TileX
// g_int_2      : TileZ
// g_float_0    : Max Tessellation Level
// g_vec2_0     : HeightMap Resolution
// g_vec2_1     : Min/Max Tessellation Distance
// g_vec4_0     : Camera Position
// g_tex_0      : Diffuse Texture
// g_tex_1      : Normal Texture
// g_tex_2      : HeightMap Texture
 
// --------------
// Domain Shader
// --------------
 
struct DS_OUT
{
    float4 pos : SV_Position;
    float2 uv : TEXCOORD;
    float3 viewPos : POSITION;
    float3 viewNormal : NORMAL;
    float3 viewTangent : TANGENT;
    float3 viewBinormal : BINORMAL;
};
 
[domain("tri")]
DS_OUT DS_Main(const OutputPatch<HS_OUT, 3> input, float3 location : SV_DomainLocation, PatchTess patch)
{
    DS_OUT output = (DS_OUT)0.f;
 
    float3 localPos = input[0].pos * location[0+ input[1].pos * location[1+ input[2].pos * location[2];
    float2 uv = input[0].uv * location[0+ input[1].uv * location[1+ input[2].uv * location[2];
 
    int tileCountX = g_int_1;
    int tileCountZ = g_int_2;
    int mapWidth = g_vec2_0.x;
    int mapHeight = g_vec2_0.y;
 
    float2 fullUV = float2(uv.x / (float)tileCountX, uv.y / (float)tileCountZ);
    float height = g_tex_2.SampleLevel(g_sam_0, fullUV, 0).x;
 
    // 높이맵 높이 적용
    localPos.y = height;
 
    float2 deltaUV = float2(1./ mapWidth, 1./ mapHeight);
    float2 deltaPos = float2(tileCountX * deltaUV.x, tileCountZ * deltaUV.y);
 
    float upHeight = g_tex_2.SampleLevel(g_sam_0, float2(fullUV.x, fullUV.y - deltaUV.y), 0).x;
    float downHeight = g_tex_2.SampleLevel(g_sam_0, float2(fullUV.x, fullUV.y + deltaUV.y), 0).x;
    float rightHeight = g_tex_2.SampleLevel(g_sam_0, float2(fullUV.x + deltaUV.x, fullUV.y), 0).x;
    float leftHeight = g_tex_2.SampleLevel(g_sam_0, float2(fullUV.x - deltaUV.x, fullUV.y), 0).x;
 
    float3 localTangent = float3(localPos.x + deltaPos.x, rightHeight, localPos.z) - float3(localPos.x - deltaPos.x, leftHeight, localPos.z);
    float3 localBinormal = float3(localPos.x, upHeight, localPos.z + deltaPos.y) - float3(localPos.x, downHeight, localPos.z - deltaPos.y);
 
    output.pos = mul(float4(localPos, 1.f), g_matWVP);
    output.viewPos = mul(float4(localPos, 1.f), g_matWV).xyz;
 
    output.viewTangent = normalize(mul(float4(localTangent, 0.f), g_matWV)).xyz;
    output.viewBinormal = normalize(mul(float4(localBinormal, 0.f), g_matWV)).xyz;
    output.viewNormal = normalize(cross(output.viewBinormal, output.viewTangent));
 
    output.uv = uv;
 
    return output;
}
 
// --------------
// Pixel Shader
// --------------
 
struct PS_OUT
{
    float4 position : SV_Target0;
    float4 normal : SV_Target1;
    float4 color : SV_Target2;
};
 
PS_OUT PS_Main(DS_OUT input)
{
    PS_OUT output = (PS_OUT)0;
 
    float4 color = float4(1.f, 1.f, 1.f, 1.f);
    if (g_tex_on_0 == 1)
        color = g_tex_0.Sample(g_sam_0, input.uv);
 
    float3 viewNormal = input.viewNormal;
    if (g_tex_on_1 == 1)
    {
        // [0,255] 범위에서 [0,1]로 변환
        float3 tangentSpaceNormal = g_tex_1.Sample(g_sam_0, input.uv).xyz;
        // [0,1] 범위에서 [-1,1]로 변환
        tangentSpaceNormal = (tangentSpaceNormal - 0.5f) * 2.f;
        float3x3 matTBN = { input.viewTangent, input.viewBinormal, input.viewNormal };
        viewNormal = normalize(mul(tangentSpaceNormal, matTBN));
    }
 
    output.position = float4(input.viewPos.xyz, 0.f);
    output.normal = float4(viewNormal.xyz, 0.f);
    output.color = color;
 
    return output;
}
 
#endif
cs

 

 

  • ConstantHS()
    • min, maxDistance = 카메라와의 거리를 통해 Tessellation Level을 조정하기 위한 변수이다.
    • edgePos = 삼각형의 Index에서 중앙의 위치를 구하기 위한 변수이다.
    • edgeTessLevel = edge와 카메라와의 거리를 계산하여 거리에 따른 Tessellation Level을 계산해서 지정해준다.
    • 이후 각각의 edgeTess와 insideTess에게 Tessellation Level을 건네준 뒤 반환해준다.

 

  • DS_Main()
    • tileCountX      : TileX
      tileCountZ      : TileZ
      mapWidth,     : HeightMap Resolution
      mapHeight
      g_tex_2          : HeightMap Texture
    • 우선 Grid 단위의 UV를 0~1 사이의 실제 UV 좌표로 변환시켜주어 fullUV에 저장한다.
    • 이후 높이 텍스쳐를 불어온 값을 localPos.y값에 적용시켜 주고
      또 1.f에 map의 resolutin에 따른 값을 나눔으로써 deltaUV, 타일의 갯수와 dealtaUV를 곱해준 것을 dataPos로써 구해준 뒤
      자신의 근처 정점들의 높이를 통해 Tanget와 Binormal을 구해준 다음 외적을 통해 Normal값을 구해준다.

 

 


 

 

결과

 

 

 

  • 높이 텍스쳐(높이 맵)를 활용하여 울퉁불퉁한 지형을 구현하였고,
  • 카메라와의 거리가 가까워질수록 메쉬의 표현이 더욱 자세해지는 것을 볼 수 있다.

'Grahpics' 카테고리의 다른 글

[Graphics] DirectX12 - Tessellation  (0) 2022.02.16
[Graphics] DirectX12 - Shadow Mapping  (0) 2022.02.15
[Graphics] DirectX12 - Instancing  (0) 2022.02.14
[Graphics] DirectX12 - Particle System  (0) 2022.02.14
[Graphics] DirectX12 - Compute Shader  (0) 2022.02.13

+ Recent posts