(DirectX : Basic) 25. Skybox

Posted by : at

Category : DirectX



하늘을 처리해야 할텐데 어떻게 처리하나? 단순위 캐릭터 위에 그리면 될까??
일단 캐릭터 머리위에 단순히 하늘을 그리는 형태로 만드는 것은 맞다
쉬워 보이지만 몇가지 고려해야할 사항이 있는데

  1. 캐릭터가 움직여서 하늘의 끝에 닿으면 안된다.
  2. 하늘뒤에 어떠한 물체도 놓이게 해선 안된다(그럴필요가 없으니)
  • 하늘을 처리하는 한 가지 팁은 하늘의 위치는 카메라의 기준과 동일(0, 0, 0) -> 카메라가 이동하면 하늘도 같이 이동
  • SRT중 Translation은 적용하지 않고(어차피 카메라와 같은 위치에 있어야 하기에) Rotation만 적용이 되게 만들면 된다.
  • Local Space에서 World는 무시하고 View에서도 Rotation만 적용하면 된다.

Culling

랜더링 과정에서 그릴지 말지를 결정(필요없는 부분은 그리지 말자는 명령)
우선 이런 Culling이 왜 필요한가?
랜더링이란게 연산이 많이 필요하다. Vertex의 연산 Pixel 사이의 Rasterizer연산등… 이런 연산을 조금이라도 줄이기 위해 Culling의 개념이 필요하다

랜더링 파이프라인에서 아래와 같이 CD3DX12_RASTERIZER_DESC옵션을 넣는데 옵션의 의미는 아래와 같다

void Shader::Init(const wstring& path, ShaderInfo info)
{
	// ..

	_pipelineDesc.RasterizerState = CD3DX12_RASTERIZER_DESC(D3D12_DEFAULT);

	// ...
explicit CD3DX12_RASTERIZER_DESC( CD3DX12_DEFAULT )
{
	FillMode = D3D12_FILL_MODE_SOLID;
	CullMode = D3D12_CULL_MODE_BACK;		// 반시계 방향의 인덱스는 그리지 않겠다
	FrontCounterClockwise = FALSE;
	// Front를 시계 방향(Clockwise)로 둔다
	// 반 시계 방향(CounterClockwise)는 Culling(랜더링에서 그리지 않겠다.)

	// ...

시계방향 반시계방향의 의미는 인덱스 버퍼에서 결정된다.

갑자기 culling은 왜 언급하나??
하늘(skybox)는 카메라(캐릭터) 입장에서는 skybox안에서 그려줘야한다.
따라서 culling이 되지 않게 구현하기 위해선 인덱스 방향을 반대로 줘야한다

// 인덱스 버퍼 시계/반시계 방향에 따라 culling여부 판별
enum class RASTERIZER_TYPE
{
	CULL_NONE,
	CULL_FRONT,
	CULL_BACK,
	WIREFRAME,
};

// depth stencil 비교연산 처리방법 결정
// 같은 최대 깊이더라도 하늘을 마지막에 그려야함.
enum class DEPTH_STENCIL_TYPE
{
	LESS,
	LESS_EQUAL,
	GREATER,
	GREATER_EQUAL,
};
void Shader::Init(const wstring& path, ShaderInfo info)
{
	//..

	switch (info.rasterizerType)
	{
	case RASTERIZER_TYPE::CULL_BACK:
		_pipelineDesc.RasterizerState.FillMode = D3D12_FILL_MODE_SOLID;
		_pipelineDesc.RasterizerState.CullMode = D3D12_CULL_MODE_BACK;
		break;
	case RASTERIZER_TYPE::CULL_FRONT:
		_pipelineDesc.RasterizerState.FillMode = D3D12_FILL_MODE_SOLID;
		_pipelineDesc.RasterizerState.CullMode = D3D12_CULL_MODE_FRONT;
		break;
	case RASTERIZER_TYPE::CULL_NONE:
		_pipelineDesc.RasterizerState.FillMode = D3D12_FILL_MODE_SOLID;
		_pipelineDesc.RasterizerState.CullMode = D3D12_CULL_MODE_NONE;
		break;
	case RASTERIZER_TYPE::WIREFRAME:
		_pipelineDesc.RasterizerState.FillMode = D3D12_FILL_MODE_WIREFRAME;
		_pipelineDesc.RasterizerState.CullMode = D3D12_CULL_MODE_NONE;
		break;
	}

	switch (info.depthStencilType)
	{
	case DEPTH_STENCIL_TYPE::LESS:
		_pipelineDesc.DepthStencilState.DepthEnable = TRUE;
		_pipelineDesc.DepthStencilState.DepthFunc = D3D12_COMPARISON_FUNC_LESS;
		break;
	case DEPTH_STENCIL_TYPE::LESS_EQUAL:
		_pipelineDesc.DepthStencilState.DepthEnable = TRUE;
		_pipelineDesc.DepthStencilState.DepthFunc = D3D12_COMPARISON_FUNC_LESS_EQUAL;
		break;
	case DEPTH_STENCIL_TYPE::GREATER:
		_pipelineDesc.DepthStencilState.DepthEnable = TRUE;
		_pipelineDesc.DepthStencilState.DepthFunc = D3D12_COMPARISON_FUNC_GREATER;
		break;
	case DEPTH_STENCIL_TYPE::GREATER_EQUAL:
		_pipelineDesc.DepthStencilState.DepthEnable = TRUE;
		_pipelineDesc.DepthStencilState.DepthFunc = D3D12_COMPARISON_FUNC_GREATER_EQUAL;
		break;
	}

	DEVICE->CreateGraphicsPipelineState(&_pipelineDesc, IID_PPV_ARGS(&_pipelineState));

    // ...
shared_ptr<Scene> SceneManager::LoadTestScene()
{
	shared_ptr<Scene> scene = make_shared<Scene>();

#pragma region Camera
	shared_ptr<GameObject> camera = make_shared<GameObject>();
	camera->AddComponent(make_shared<Transform>());
	camera->AddComponent(make_shared<Camera>()); // Near=1, Far=1000, FOV=45도
	camera->AddComponent(make_shared<TestCameraScript>());
	camera->GetTransform()->SetLocalPosition(Vec3(0.f, 0.f, 0.f));
	scene->AddGameObject(camera);
#pragma endregion

#pragma region SkyBox
	{
		shared_ptr<GameObject> skybox = make_shared<GameObject>();
		skybox->AddComponent(make_shared<Transform>());
		shared_ptr<MeshRenderer> meshRenderer = make_shared<MeshRenderer>();
		{
			shared_ptr<Mesh> sphereMesh = GET_SINGLE(Resources)->LoadSphereMesh();
			meshRenderer->SetMesh(sphereMesh);
		}
		{
			shared_ptr<Shader> shader = make_shared<Shader>();
			shared_ptr<Texture> texture = make_shared<Texture>();
			shader->Init(L"..\\Resources\\Shader\\skybox.hlsli",
				{ RASTERIZER_TYPE::CULL_NONE, DEPTH_STENCIL_TYPE::LESS_EQUAL });
			// 쉐이더 init시 RASTERIZER_TYPE::CULL_NONE, DEPTH_STENCIL_TYPE::LESS_EQUAL 옵션을 넣는다.
			// RASTERIZER_TYPE::CULL_NONE : cull을 하지말라 (cull_front로 해도 동작함)
			texture->Init(L"..\\Resources\\Texture\\Sky01.jpg");
			shared_ptr<Material> material = make_shared<Material>();
			material->SetShader(shader);
			material->SetTexture(0, texture);
			meshRenderer->SetMaterial(material);
		}
		skybox->AddComponent(meshRenderer);
		scene->AddGameObject(skybox);
	}
#pragma endregion

    // ...
#ifndef _SKYBOX_HLSLI_
#define _SKYBOX_HLSLI_

#include "params.hlsli"

struct VS_IN
{
    float3 localPos : POSITION;
    float2 uv : TEXCOORD;
};

struct VS_OUT
{
    float4 pos : SV_Position;
    float2 uv : TEXCOORD;
};

VS_OUT VS_Main(VS_IN input)
{
    VS_OUT output = (VS_OUT)0;

    // float4(input.localPos, 0) : Translation은 하지 않고 Rotation만 적용한다(마지막 값을 0으로 둠)
    float4 viewPos = mul(float4(input.localPos, 0), g_matView);

	/*
	// 참고 View Matrix는 아래와 같고 right/up/look . x/y/z는 단위벡터
	// c*right/up/look 은 Translation 정보이다.
	// 따라서 마지막 항에 0을 넣으면(input.localPos, 0) Translation 정보는 사라지고
	// Rotation정보만 남게된다.
	right.x  up.x  look.x  0
	right.y  up.y  look.y  0
	right.z  up.z  look.z  0
	-c*right -c*up -c*look 1
	*/

    float4 clipSpacePos = mul(viewPos, g_matProjection);

	// clipSpacePos.xyww : z(깊이)에 w를 넣는다
    // w/w=1이기 때문에 항상 깊이가 1로 유지된다
    output.pos = clipSpacePos.xyww;
    output.uv = input.uv;

    return output;
}

float4 PS_Main(VS_OUT input) : SV_Target
{
     float4 color = g_tex_0.Sample(g_sam_0, input.uv);
     return color;
}

#endif

About Taehyung Kim

안녕하세요? 8년차 현업 C++ 개발자 김태형이라고 합니다. 😁 C/C++을 사랑하며 다양한 사람과의 협업을 즐깁니다. ☕ 꾸준한 자기개발을 미덕이라 생각하며 노력중이며, 제가 얻은 지식을 홈페이지에 정리 중입니다. 좀 더 상세한 제 이력서 혹은 Private 프로젝트 접근 권한을 원하신다면 메일주세요. 😎

Star
Useful Links