서의 공간

29. Water 본문

Graphics API/DirectX 11 - Rastertek

29. Water

홍서의 2021. 1. 21. 14:21

이번 튜토리얼에서는 HLSL 및 C++를 사용해 DirectX 11에서 물을 구현하는 방법을 다룬다. 

 

물 구현은 다양한 방법이 있으며 각각에는 장단점이 있다. 이 튜토리얼에서 다룰 방법은 반사적이고 굴절하는 물이다(reflective and refractive water). 가장 보기 좋은 기술 중 하나이지만 파도의 높이가 너무 크면 물의 가장자리에서 약간의 문제가 있다. 따라서 반사하고 굴절하는 물은 강, 연못 및 수영장과 같이 파도 높이가 작은 수역에 적합하다. 호수와 바다가 반고체 상태인 경우 이 기술을 확장시켜 사용할 수 있을 것이다.

 

먼저 반사하고 굴절하는 물 기술 적용을 용이하게 하는 간단한 씬을 만들 것이다. 씬에는 평평한 바닥과 물을 담을 수 있는 대리석 수조가 있다. 또한 돌기둥도 배치하여 물이 반사하는 물체가 있도록 한다. 기본 씬은 다음과 같다.

그런 다음 두 개의 삼각형으로 평평한 물 쿼드(평면)를 만들고 이를 변환하여 수조 내부에 배치한다. 물 오브젝트에 텍스처를 주지 않고 대신 씬의 반사상을 물 텍스처로 사용한다. 카메라 위치/각도를 기준으로 물의 높이에서 씬의 반사상을 렌더링하고 그 반사상을 가지는 물을 생성한다.

이제 반사하는 물이 있으므로 굴절하는 효과를 추가한다. 굴절은 기본적으로 반사의 역이다. 반사에서 수면 높이 위에 있는 것을 렌더링하지만 굴절에서는 수면 아래에 있는 것을 렌더링 한다. 이 기술은 반사 효과를 약간 수정할뿐이므로 렌더 투 텍스처(반사상을 가지는 텍스처)로 굴절상 텍스처를 생성하여 수면에 매핑한다. 또한 물 아래에서 볼 수 있는 것은수조의 바닥뿐이다. 따라서 굴절상을 만들 때는 땅과 기둥을 렌더링 해서는 안된다. 다음은 노란색으로 강조한 굴절 텍스처이다. 이 이미지에서는 굴절의 효과를 명확히 보이기 위해 일부러 욕조를 렌더링 하지 않았다.

이제 반사 텍스처와 워터 쿼드에 매핑 된 굴절 텍스처를 만들었다. 이 두 텍스처를 선형 보간을 사용하여 결합한다. 다음 이미지는 두 텍스처를 결합한 결과를 렌더링 한 것이다.

반사하고 굴절하는 물 효과를 향상시키기 위해 물의 잔물결을 시뮬레이션 하는 노멀 맵을 추가한다. Bump Mapping 튜토리얼에서 노멀 맵을 사용하여 범프를 만드는 방법을 이미 설명했으며 이 튜토리얼에서는 비슷한 방식으로 잔물결을 생성한다. 범프 매핑에서 픽셀 당 조명을 결정하기 위해 범프 노멀과 함께 조명 방향을 사용했었다. 그러나 물을 사용할때는 파도가 물 아래에 있는 물체를 왜곡하는 것과 같은 방식으로 텍스처 샘플링 위치를 왜곡하기 위해 노멀을 사용한다. 또한 이 튜토리얼에서 Y축을 따라 노멀 맵을 변환하여 잔물결을 이동하고 움직이는 물을 시뮬레이션한다. 다음은 적용할 노멀 맵이다.

마지막으로 주목해야할 점이 있다. 일부 그래픽 엔진은 속도를 높이기 위해 매 프레임마다 값 비싼 텍스처 렌더링을 제거하여 15-30 프레임마다 한 번 씩만 반사 및 굴절 텍스처를 업데이트 한다는 것이다.


Framework

RefractionShaderClass 및 WaterShaderClass가 추가되었다.


Water.vs

Reflection 튜토리얼과 마찬가지로 반사 행렬이 필요하다.

PixelnputType에는 반사 텍스처 좌표와 굴절 텍스처 좌표 변수가 추가되었다.

WaterVertexShader()에서는 Reflection 튜토리얼처럼 반사 프로젝션 월드 행렬을 만들고 이것으로부터 반사 좌표를 계산한다.

굴절 좌표는 뷰 프로젝션 월드 행렬을 사용한다는 점을 제외하면 반사 좌표와 동일한 방식으로 계산된다. 

 

Water.ps

물 셰이더에는 세 개의 텍스처가 필요하다. 씬 반사를 위한 반사 텍스처, 굴절을 위한 굴절 텍스처, 마지막으로 잔물결을 시뮬레이션 할 노멀 맵 텍스처이다.

상수 버퍼인 WaterBuffer는 각 프레임의 텍스처 샘플링 좌표를 변환하여 물 움직임을 시뮬레이션 하는데 사용된다. reflectRefractScale 변수는 노멀 맵과 관련해 잔물결의 크기를 제어하는데 사용한다. 일부 노멀 맵은 노멀의 급격한 상승과 하강이 약간 다를것이다. 이것을 제어하는 변수가 있다면 매우 유용하다.

WaterPixelShader()에서 처음에는 각 프레임에 업데이트된 이동 변수를 사용해 Y축을 따라 물 노멀 맵 텍스처를 이동하여 모션을 시뮬레이션 한다. 그다음 반사 및 굴절 좌표를 모두 -1~+1 범위의 텍스처 좌표로 변환한다. 노멀 맵에서 입력 픽셀의 노멀을 샘플링하고 범위는 -1에서 +1 범위로 확장한다. 이제 노멀 맵 값으로 반사 및 굴절 좌표를 왜곡한다. 이것은 물결이 빛을 왜곡하는 것처럼 우리의 시야를 왜곡하기 위해 -1~+1로 전환해 잔물결 효과를 만든다. 노멀 맵 값에 reflectRefractScale을 곱해 잔물결이 자연스럽게 보이도록 한다.

다음으로 업데이트 된 텍스처 샘플링 좌표를 기반으로 반사 및 굴절 픽셀을 샘플링한다.

마지막으로 선형 보간을 사용하여 반사 및 굴절 픽셀을 결합한다.


WaterShaderClass.h

 

'Graphics API > DirectX 11 - Rastertek' 카테고리의 다른 글

32. Glass and Ice  (0) 2021.01.30
7. 3D Model Rendering  (0) 2021.01.07
6. Diffuse Lighting  (0) 2021.01.07
5. Texturing  (0) 2021.01.07
4. Buffers, Shaders, and HLSL  (0) 2021.01.07
Comments