For me
[DirectX12] 24) Terrain 본문
Terrain - 지형
Mesh는 수많은 삼각형으로 이루어져 있는데, Terrian의 경사가 있으면 있을수록 더 많은 삼각형이 생김.
=> Tessellation 을 사용하여 먼거리에 있는 지형은 삼각형의 개수를 줄임
terrain texture, height texture을 사용
Resource.cpp
z | ||||||
... | ||||||
2 | ||||||
1 | 2 | ... | x |
z * x개 만큼의 표
정점 개수는 이것보다 한개가 더 많음 (for문에서 +1 하는 이유)
1. 표를 돌며 정점을 하나씩 만드는 과정
2. 임의의 점에 대해서 삼각형을 만듬 (삼각형 두개로 이루어진 사각형으로 이루어짐)
3. 텍스쳐를 불러와 추가
더보기
shared_ptr<Mesh> Resources::LoadTerrainMesh(int32 sizeX, int32 sizeZ)
{
vector<Vertex> vec;
for (int32 z = 0; z < sizeZ + 1; z++)
{
for (int32 x = 0; x < sizeX + 1; x++)
{
Vertex vtx;
vtx.pos = Vec3(static_cast<float>(x), 0, static_cast<float>(z));
vtx.uv = Vec2(static_cast<float>(x), static_cast<float>(sizeZ - z));
vtx.normal = Vec3(0.f, 1.f, 0.f);
vtx.tangent = Vec3(1.f, 0.f, 0.f);
vec.push_back(vtx);
}
}
vector<uint32> idx;
for (int32 z = 0; z < sizeZ; z++)
{
for (int32 x = 0; x < sizeX; x++)
{
// [0]
// | \
// [2] - [1]
idx.push_back((sizeX + 1) * (z + 1) + (x));
idx.push_back((sizeX + 1) * (z)+(x + 1));
idx.push_back((sizeX + 1) * (z)+(x));
// [1] - [2]
// \ |
// [0]
idx.push_back((sizeX + 1) * (z)+(x + 1));
idx.push_back((sizeX + 1) * (z + 1) + (x));
idx.push_back((sizeX + 1) * (z + 1) + (x + 1));
}
}
shared_ptr<Mesh> findMesh = Get<Mesh>(L"Terrain");
if (findMesh)
{
findMesh->Create(vec, idx);
return findMesh;
}
shared_ptr<Mesh> mesh = make_shared<Mesh>();
mesh->Create(vec, idx);
Add(L"Terrain", mesh);
return mesh;
}
//Shader
// Terrain
{
ShaderInfo info =
{
SHADER_TYPE::DEFERRED,
RASTERIZER_TYPE::CULL_BACK,
DEPTH_STENCIL_TYPE::LESS,
BLEND_TYPE::DEFAULT,
D3D_PRIMITIVE_TOPOLOGY_3_CONTROL_POINT_PATCHLIST
};
ShaderArg arg =
{
"VS_Main",
"HS_Main",
"DS_Main",
"",
"PS_Main",
};
shared_ptr<Shader> shader = make_shared<Shader>();
shader->CreateGraphicsShader(L"..\\Resources\\Shader\\terrain.fx", info, arg);
Add<Shader>(L"Terrain", shader);
}
//Material
// Terrain
{
shared_ptr<Shader> shader = GET_SINGLE(Resources)->Get<Shader>(L"Terrain");
shared_ptr<Texture> texture = GET_SINGLE(Resources)->Load<Texture>(L"Terrain", L"..\\Resources\\Texture\\Terrain\\terrain.jpg");
shared_ptr<Material> material = make_shared<Material>();
material->SetShader(shader);
material->SetTexture(0, texture);
Add<Material>(L"Terrain", material);
}
TerrainShader
Tessellation을 사용
Hull Shader |
ConstantHS를 이전과 다르게 Camera 거리에 따라 Level을 나누도록 함(3가지 level) min , Max Distance (1000 ~ 5000) 이라고 가정 ▶1000 일 때 가장 가까이 온 상태 4 ( Max ) , 5000 일 때 가장 멀리 간 상태 1 ( min ) ▶1000 보다 작거나, 5000보다 클 경우 상태 유지 Resource에서 생성한 삼각형에서 Edge 번호는 반대로 매겨짐 (edgePos : 중앙 값) 카메라와 얼마나 멀리 떨어져 있는지 계산하여 Level Check |
Domain Shader |
deferred Shader와 같은 역할을 해줌 기존에 만들었던 uv(0~A) 를 실제 uv(0~1)로 바꾸어줌 높이맵 높이를 적용 높이가 변경되었기 때문에 Tangent, Binormal 재계산 ( 위치한 점을 , 옆에있는 점을 기준으로 Tangent, Binormal 계산 후 normal 값 구함) 이후 deferred 로 Rendering 할것이기 때문에 Pixel Shader사용 |
더보기
#ifndef _TERRAIN_FX_
#define _TERRAIN_FX_
#include "params.fx"
#include "utils.fx"
///utils.fx 추가///
float CalculateTessLevel(float3 cameraWorldPos, float3 patchPos, float min, float max, float maxLv)
{
float distance = length(patchPos - cameraWorldPos);
if (distance < min)
return maxLv;
if (distance > max)
return 1.f;
float ratio = (distance - min) / (max - min);
float level = (maxLv - 1.f) * (1.f - ratio);
return level;
}
//////////////////////////////////////////////
// --------------
// 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.f / mapWidth, 1.f / 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
Terrain.h
더보기
#pragma once
#include "Component.h"
class Terrain : public Component
{
public:
Terrain();
virtual ~Terrain();
void Init(int32 sizeX, int32 sizeZ);
virtual void FinalUpdate() override;
private:
int32 _sizeX = 15;
int32 _sizeZ = 15;
float _maxTesselation = 4.f;
shared_ptr<class Material> _materials;
};
Terrain.cpp
메인 카메라 추출하여 정보값 넘김
더보기
#include "pch.h"
#include "Terrain.h"
#include "Resources.h"
#include "SceneManager.h"
#include "Scene.h"
#include "Camera.h"
#include "Transform.h"
#include "MeshRenderer.h"
#include "Material.h"
#include "Texture.h"
Terrain::Terrain() : Component(COMPONENT_TYPE::TERRAIN)
{
}
Terrain::~Terrain()
{
}
void Terrain::Init(int32 sizeX, int32 sizeZ)
{
_sizeX = sizeX;
_sizeZ = sizeZ;
_materials = GET_SINGLE(Resources)->Get<Material>(L"Terrain");
_materials->SetInt(1, _sizeX);
_materials->SetInt(2, _sizeZ);
_materials->SetFloat(0, _maxTesselation);
shared_ptr<Texture> heightMap = GET_SINGLE(Resources)->Load<Texture>(L"HeightMap", L"..\\Resources\\Texture\\Terrain\\height.jpg");
Vec2 v = Vec2(heightMap->GetWidth(), heightMap->GetHeight());
_materials->SetVec2(0, Vec2(heightMap->GetWidth(), heightMap->GetHeight()));
_materials->SetVec2(1, Vec2(1000.f, 5000.f));
_materials->SetTexture(2, heightMap);
shared_ptr<MeshRenderer> meshRenderer = GetGameObject()->GetMeshRenderer();
{
shared_ptr<Mesh> mesh = GET_SINGLE(Resources)->LoadTerrainMesh(sizeX, sizeZ);
meshRenderer->SetMesh(mesh);
}
{
shared_ptr<Material> material = GET_SINGLE(Resources)->Get<Material>(L"Terrain");
meshRenderer->SetMaterial(material);
}
}
void Terrain::FinalUpdate()
{
shared_ptr<Camera> mainCamera = GET_SINGLE(SceneManager)->GetActiveScene()->GetMainCamera();
if (mainCamera == nullptr)
return;
Vec3 pos = mainCamera->GetTransform()->GetLocalPosition();
_materials->SetVec4(0, Vec4(pos.x, pos.y, pos.z, 0));
}
'DirectX12' 카테고리의 다른 글
[DirectX12] 26) Mesh / Animation (0) | 2023.01.22 |
---|---|
[DirectX12] 25) Picking (0) | 2023.01.20 |
[DirectX12] 23) Tesellation (0) | 2023.01.20 |
[DirectX12] 22) Instancing (0) | 2023.01.20 |
[DirectX12] 21) Particle System (0) | 2023.01.19 |