본문 바로가기
공부/DirectX3D

[DirectX3D] 환경 맵핑(Environment Mapping)

by MY블로그 2023. 8. 8.

환경 맵핑 ?

환경 맵핑은 컴퓨터 그래픽스에서 사용되는 기술중 하나 입니다.

3D 모델의 표면에 주변 환경(배경)을 반사시켜 실시간으로 더욱 현실적인 느낌을 줄 수 있습니다.

주로 반사, 굴절, 환경등의 변환에 따른 질감을 향상시키는데 사용합니다.

환경 맵핑값의 변화에 따른 반사 확인하기

위의 영상에는 무료 배포된 던전앤파이터의 "던파 연단된 칼날" 폰트를 사용하였습니다.

 

>NEXON LEVEL UP (레벨업) – 넥슨 브랜드 인벤토리

LEVEL UP의 첫 단계로 게임에서 경험했던 느낌을 서체에 담아 표현했습니다. 서체의 기능적인 부분 외에도 즐거운 경험들을 전달하고자 하는 또 다른 도전의 시작입니다.

levelup.nexon.com

 

-아래접은글 > DirectWrite(DWRITE) 복습-

더보기

DWRITE 복습

    //DWRITE->GetDC()->BeginDraw(); // 오브젝트보다 아래 렌더해야될경우 사용
    DWRITE->RenderText(L"환경맵핑값", RECT{5,5,(int)App.GetWidth(),(int)App.GetHeight()}, 75.0f,
        L"던파 연단된 칼날", Color(1, 1, 1, 1), DWRITE_FONT_WEIGHT_ULTRA_BLACK,
        DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_ULTRA_EXPANDED);
    DWRITE->RenderText(to_wstring(environment), RECT{ 5,85,(int)App.GetWidth(),(int)App.GetHeight() }, 75.0f,
        L"던파 연단된 칼날", Color(1, 1, 1, 1), DWRITE_FONT_WEIGHT_ULTRA_BLACK,
        DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_ULTRA_EXPANDED);
    //DWRITE->GetDC()->EndDraw();// 오브젝트보다 아래 렌더해야될경우 사용

환경 맵핑은 크게 두가지 방식으로 구현될 수 있습니다.

1. 스크린 스페이스 환경 맵핑 (Screen Space Environment Mapping)

화면 공간(Screen Space)에서 주변 환경을 반사한 것처럼 보이게 하는 기술입니다.

화면상의 픽셀 좌표와 뷰 방향 벡터를 사용하여 환경 맵을 계산하며, 주로 큐브 캡(Cube Map)이나 정확도를 높이기 위해 패럴랙스 맵핑(Parallax Mapping) 등과 함께 사용될 수 있습니다.

스크린 스페이스 환경 맵핑 구현 단계

1. 큐브맵 생성

주변 환경을 표현하기 위한 큐브 맵이 필요합니다.

큐브 맵은 6개의 면으로 이루어진 큐브 형태의 텍스처 입니다.

큐브맵 생성에 관하여 다음 자료를 참고합니다.

https://rhksgml78.tistory.com/364

 

[DirectX3D] DxTex 큐브맵(CubeMap) 만들기

DirectX3D 줄여서 D3D 에서 Animation & Terrain 이후 CubeMap을 배우게 되어 큐브맵(확장자 .dds)를 만들기위한 방법을 정리합니다. Step 1 우선 큐브맵을 만들기위한 프로그램으로 DxTex가 필요합니다. 해당 프

rhksgml78.tistory.com

2. 뷰 프로젝션 행렬 계산

화면 공간에서 환경 맵을 계산하기 위해 현재 카메라의 뷰 프로젝션 행렬을 사용합니다.

이 행렬은 3D 공간에서 2D 화면 공간으로의 변환을 나타 냅니다.

3. 픽셀 쉐이더에서 환경 맵 계산

각 필셀의 스크린 좌표와 뷰 방향 벡터를 사용하여, 해당 픽셀이 보는 방향에 해당하는 큐브 맵 텍셀을 찾습니다. 이를 위하여 뷰 방향 벡터를 큐브 맵 좌표로 변환하여 해당 텍셀을 샘플링 합니다.

4. 환경 맵 샘플링

큐브 맵에서 얻은 텍셀을 사용하여 환경 맵 샘플을 가져옵니다.

이렇게 얻은 환경 맵 샘플은 해당 픽셀의 반사된 환경을 나타내는 텍셀 값이 됩니다.

5. 반사된 빛 계산

환경 맵 샘플과 물체의 표면 속성(반사율, 광택 등)을 사용하여 반사된 빛을 계산합니다.

이는 물체 표면의 색상에 환경 맵 샘플을 조합하여 최종 픽셀 색상을 계산하는데 사용됩니다.

6. 픽셀 색상 결정

계산이 끝난 반사된 빛과 다른 조명 모델을 고려하여 픽셀의 최종 색상을 결정합니다.

이를 화면에 출력하면, 모델의 표면이 주변 환경을 반사한 것처럼 보이게 됩니다.


2. 큐브 맵 환경 맵핑 (Cube Map Environment Mapping)

큐브 맵은 주위 환경을 6개의 면으로 구성된 큐브로 나타낸 것입니다.

이 방식은 주변 환경을 큐브 맵으로 캡처하고, 3D 모델의 표면에서 해당 큐브 맵을 참조하여 반사된 환경을 계산합니다.

이렇게 하면 모델의 모든 방향에서 주변환경을 반사하는 효과를 줄 수 있습니다.

큐브 맵 환경 맵핑 구현 단계

1. 큐브 맵 생성

주변 환경을 표현하기 위한 큐브 맵을 생성합니다.

2. 카메라 위치 설정

큐브 맵을 생성할 때 사용한 카메라 위치를 기준으로 큐브 맵을 렌더링 합니다.

이때 카메라는 큐브의 중심을 향하도록 배치합니다. 큐브의 각 면은 카메라 위치에서 해당 방향을 바라보는 시야를 나타내며, 이 시야에서 환경을 캡처 합니다.

*현재 프레임 워크에서 큐브맵은 항상 카메라의 위치에 존재합니다.

3. 크기 조절 및 렌더링 설정

큐브 맵을 렌더 타겟으로 사용하여 각 면을 렌더링합니다.

큐브 맵의 텍스처 크기는 고려해야 할 사항 중 하나이며, 렌더 타겟의 크기를 적절하게 설정해야 합니다.

4. 큐브맵의 6면을 각각 렌더링

큐브 맵을 구성하는 6개의 면을 각각 렌더링 합니다.

각 렌더링 단계에서는 해당 방향을 바라보는 카메라를 설정하고, 해당 환경을 캡처하여 큐브 맵의 해당 면에 텍스처로 저장합니다.

5. 모델 렌더링과 큐브 맵 사용

큐브 맵 생성이 완료되면, 주어진 3D 모델을 렌더링하면서 큐브 맵을 사용하여 반사된 환경을 시뮬레이션 합니다.

각  필셀의 뷰 방향 벡터를 큐브 맵 좌표로 면환하여 해당 방향에서의 환경을 가져옵니다.

6. 환경 맵 샘플링 및 렌더링

픽셀 쉐이더에서는 변환된 뷰 방향을 큐브 맵 좌표로 사용하여 큐브 맵에서 환경을 샘플링 합니다.

이렇게 얻은 환경 맵 샘플링을 사용하여 모델의 표면에서 반사된 빛을 계산하고 물체의 색상을 결정합니다.

7. 픽셀 색상 결정

계산이 끝난 반사된 빛과 다른 조명 모델을 조합하여 픽셀의 최종 색상을 결정하고 화면에 출력합니다.


현재 프레임 워크에 추가된 부분 ( Common.hlsl 쉐이더 파일에 추가)

아래의 내용은 개인 공부용 입니다.

쉐이더파일 : Common.hlsl

// 생략

// 전역 범위에서
// Backgrown
TextureCube TextureBG : register(t4);
SamplerState SamplerBG : register(s4);

//생략

// DirLighting 함수 다음에 환경맵핑 함수 추가
float3 EnvironmentMapping(float3 Normal, float3 wPosition)
{
	 [flatten]
	if (environment != 0.0f) // 입력되는 environment 값이 있을떄
	{
		float3 ViewDir = normalize(wPosition - ViewPos.xyz);
		/*
		ViewDir은 카메라가 오브젝트를 바라보는 방향벡터를 의미합니다.
		*/
		
		
		float3 reflection = reflect(ViewDir, Normal);
		/*
		반사합수를 사용합니다.(이전 ReflectLight 사용했던것처럼)
		반사할때 필요한것은 평면 내지 노멀이 필요합니다.
		그래야 접선면에서 어떻게 반사했는지가 확인이 가능합니다.
		즉 위의 반사는
		카메라가 오브젝트를 바라보는 방향벡터에서
		법선을 기준으로 반사방향에 있는 텍스처를 입혀주겠다는 것입니다.
		*/
		
		return TextureBG.Sample(SamplerBG, reflection.xyz) * environment;
		/*
		위의 계산이 완료되고나면
		현재사용하는 배경색상(TextureBG.Sample(SamplerBG, reflection.xyz)) 에
		환경값(environment)을 곱하여 반환 합니다.
		*/
    }
	
    return float3(0, 0, 0); // 입력되는 environment 값이 없을때
}

// 마지막 Lighting 함수의 내부에 환경 맵핑 관련 계산 추가
float4 Lighting(float4 BaseColor, float2 Uv, float3 Normal, float3 wPosition)
{
	float3 SpecularMap = SpecularMapping(Uv);
	
	float4 Result = float4(DirLighting(BaseColor.rgb, SpecularMap,
    Normal, wPosition),
    BaseColor.a);
	
	if (Result.a > Opacity)
		Result.a = Opacity;
	
	// 광원은 아직 추가하지 않았으므로 광원 수만큼의 반복문 생략
	
	//발광
	//Ambient
	Result.rgb += Ka.rgb * BaseColor.rgb;
    //Emissive
	Result.rgb += EmissiveMapping(BaseColor.rgb, Uv, Normal, wPosition);
	
	//Environment
	Result.rgb += EnvironmentMapping(Normal, wPosition);
	/*
	맵핑할떄 UV를 가져오는것이아닌 노멀과 좌표를 사용합니다.
	왜냐하면 현재 Uv(float2 Uv)값의 input이 position 값이기 때문입니다.
	또한 빛계선처럼 결과값에 +=  합산하는 방식입니다.
	*/
	
	return saturate(Result); // 0~1의 값으로 반환
}

 

 

댓글