해뜨는 줄 모르고 작업했지만 신년 해돋이는 보고싶어 SkySphere에 실시간 반영되는 하늘을 연출해보았다
요약
- 주요 목표 : 실시간 태양의 고도와 일출/석양 연출하기
- 해결 방안 : SkySphere 액터에서 Light Source 액터를 Now함수로 반환받는 시간단위로 Pitch 회전
- 이루어낸 성과 :
- 엔진콘텐츠의 애셋 내 입맛대로 커스터마이징하기 실천
- SkySphere의 구현방식 이해
- 이후 작업 시 실제 하늘시간 체감 가능
서브퀘스트!
1. 엔진 콘텐츠 BP_SkySphere 파헤치기
2. Now함수를 통해 실시간 고도 계산
3. ColorCurve 조율로 노을/일출 표현
엔진콘텐츠의 Sky Sphere
엔진콘텐츠의 SkySphere액터는 기본 Map에 배치된 상태로 생성된다
BP_SkySphere는 8,192cm지름의 구체의 SM_SkySphere Mash로, 해/ 구름/ 천체의 색상/ 밤하늘의 별 표현을
가능하게 해주는 Material M_Sky_Panning_Clouds2가 입혀져 있다
BP_SkySphere
BP_SkySphere를 Map에 배치한 뒤 Directional Light Actor 항목에 배치된 DirectionalLight 액터를 지정해주면
해당 광원을 햇빛으로 설정해 줄 수 있다
광원의 Rotation을 바꾸고 BP_SkySphere의 Refresh Material bool을 체크해주면 Refresh Material 함수가 호출되며 해당 Rotation값에 맞춰진 해와 대기가 표현된다
Refresh Material
BP_SkySphere의 Deafult 변수 Refresh Material이 true일 때 호출되어
지정된 광원이 있을 때 해당 원의 Pitch값을 Sun Height(태양의 고도)에 계산해 M_Sky_Panning_Clouds2에 적용해주는 함수이다
시간대 광원 각도에 따른 param값 적용은 아래와 같다
| 구분 | 광원 (Pitch) | Sun Height | Time | Horizon Falloff |
|---|---|---|---|---|
| 내용 | Directional Light | 태양의 고도 | Color Curve의 Time 축 | 지평선 구분 선명도 |
| 범위 | -90 ~ 90˚ | -1.0 ~ 1.0 (Map Range) | -1.0 ~ 1.0 | 3.0 ~ 7.0 (Lerp) |
| 일출 | 0.0˚ | 0.0 | 0.0 | 3.0 |
| 정오 | -90.0˚ | 1.0 | 1.0 | 7.0 |
| 일몰 | -180.0˚ | 1.0 | 1.0 | 3.0 |
| 자정 | 90.0˚ | -1.0 | -1.0 | 7.0 |
BP_RealTimeSky
언리얼엔진의 디폴트 애셋을 변경하지 않도록 BP_SkySphere를 복사해 내 콘텐츠에서 커스터마이징을 했다
LightSource Setting
Directional Light가 런타임 중 움직일 수 있게 Movable로 Set해주고, 별도 에디터모드를 위해 Movable이 아닌경우 실시간 하늘표현을 적용하지 않도록 Branch로 분기를 나누었다
☀️Now
언리얼의 Now 함수는 PC의 로컬 시간을 반환해주는 함수이다 1
반환값 year / month / day / hour / minute / second / millisecond 중 hour minute second millisecond로
Tick 마다 Light Source로 지정해둔 DirectionalLight액터의 Y축 회전값에 반영해 주면 시간별 태양의 고도를 연출할 수 있다
Dx2D 공부할 당시 아날로그 시계를 구현하는 방식과 동일하게 시, 분, 초, 밀리초를 계산해주었다
Light Source의 위치를 중심으로 하루 24시간을 360˚ Pitch회전 시 빛의 방향을 바라보며 SkySphere에 태양이 위치하게 되기 때문에
시간 : 매 시간 1/24 * 360 = 15˚씩 회전한다고 볼 수 있고 이런식으로
| 구분 | 단위 | 수식 | 각도 |
|---|---|---|---|
| 시 | 하루 = 24시간 | 1시간 /24 * 360 | 15˚ |
| 분 | 1시간 = 60분 | 1분/24 * 60 * 360 | 0.25˚ |
| 초 | 1분 = 60초 | 1초/24 * 60 * 60 * 360 | 0.0416˚ |
| 밀리초 | 1초 = 1000 밀리초 | 1밀리초/24 * 60 * 60 * 1000 * 360 | 0.000004˚ |
이렇게 회전값이 누적되어야 하니 이 모든 숫자들을 더해준다
마지막으로 Y회전값이 -90˚일 때 햇빛이 지면에 수직으로 닿기 때문에, 초기값에 +90˚ 해주면 00시일때 햇빛의 방향이 수직으로 향하게 하는 가장 어두운 시간을 나타낼 수 있다
다른 변수들은 무시하고 낮의길이를 12시간으로 가정하고, 2024년 1월 1일 예상 일출 시간 오전 7시 47분 2
(7/24) + (47/(24*60)) = 12.04˚를 더해주면 일출시간을 0˚로 맞출 수 있다
Curve Linear Color
엔진콘텐츠로 제공되는 하늘의 색상은 - Horizon(지평선), Cloud(구름), Zenith(천정, 천체의 상단)을 표현해주는 3개의 Curve개체들로 param을 받고있다
-1.0 ~ 1.0으로 변환된 Sun Height 변수의 값을 Time으로 변환해 가변 축, Vector3형태의 Linear Color 값을 상수 축으로 하여 해의 고도에 따라 각 천체의 구성색을 적용해주어 정오의 파란하늘과 자정의 어두운 밤하늘, 일몰/일출 동안의 붉은하늘 표현에 적용해줄 수 있다
시뮬레이션을 통해 해가 뜨고 지는 시간이 좀 더 짧고 강렬하게 표현되도록 Color값을 조율해 주었다
Other Settings
Now함수와 Color Curve 조율만으로 충분한 실시간 하늘적용이 가능하지만,
좀 더 자연스러운 하늘표현을 해주고 싶어 BP_SkySphere의 다른 변수들을 조작해보았다
Sky Atmosphere
BP_SkySphere는 지면을 감싸는 구형 천체의 (내부)표면만 하늘을 흉내내는 Material을 입힌 반면,
Sky Atmosphere는 공간 전체에 대기를 구현하고 그에 따른 빛반사를 계산하여 천체를 표현해 대기질 등 각종 날씨에 따른 하늘을 더욱 광범위하고 자연스럽게 표현할 수 있게 해준다
언리얼 엔진 공식 YouTube에 공개된 Sky Atmosphere 3 영상을 참고하면 필요에 따라 구름 뒤편에 가려져 그림자가 생기는 부분 등 더욱 리얼한 하늘을 구현할 수 있는 모습을 볼 수 있다
프로젝트에 사용하는 방법은 상당히 간단하지만 조작할 수 있는 변수들이 많아 이것저것 실험을 해보며 적용해주었다
- Step1.
Directional Light을 배치한 뒤Sky Atmosphere를 true로 체크 - Step2.
Sky Atmosphere엑터 배치
Sky Light
낮과 밤이 전환되며 런타임 중 빛의 강도와 방향이 실시간으로 변화하기 때문에 라이트 빌드를 한 맵의 경우 명암의 표현이 어색해지곤 한다
라이트맵을 포기하면 빛 표현이 자연스러워지지만, 최적화를 무시할 순 없어 대안으로 Sky Light 액터를 배치하여 빛의 강도와 방향을 추가 적용해 줄 수 있다
Cloud
Material M_Sky_Panning_Clouds2에 적용되는 구름은 Cloud Texture의 색상변환과 이동속도 그리고 투명도 변경으로 표현된다
| 구분 | 변수 | 내용 |
|---|---|---|
| 색상 | Cloud Color | Curve파일을 변수로 Material에 Linear Color값 적용 |
| 이동 속도 | Cloud Speed | BP_SkySphere에 상수 float로 적용 |
| 투명도 | Cloud Opacity | BP_SkySphere에 상수 float로 적용 |
변속없이 같은 모습으로만 이동하는건 너무 지루할 것 같아 각 변수들에 랜덤 값을 적용해 보았다
Star
언리얼에디터의 다양한 툴을 다루는 것에 집중해야 하는 지금은 우선 엔진콘텐츠에서 제공해주는 Star Texture에 만족하고 작업을 이어갔다
BP_SkySphere에서 Sun Height에 따라 Star Brightness값이 변동되는 방식으로 햇빛의 방향이 양수가 되면 서서히 밝아져 하늘에 보여지게 된다
여명/석양에도 은은하게 별빛이 보여지도록 밝기를 조정해 적용했다
생각정리
현실세계의 모든 현상들은 수식화 할 수 있다면 얼마든지 가상공간에 표현하고 시뮬레이트할 수 있다는 점이 프로그래밍의 매력이자 내가 프로그래밍을 재밌게 공부하는 가장 큰 이유다
대기 표현 관련 검색을 하다가 실제 지구의 대기구조와 그로인한 빛반사로 보여지는 하늘을 C#과 HLSL로 표현한 프로그래머의 블로그를 보았다 4
틈틈히 우주과학 관련 영상들을 찾아보며 호기심들을 채우다가 언젠가 프로그래밍으로 가상우주를 구현해 리얼한 공상과학 게임을 만들고 싶단 상상을 해보았다 😂
자연스러운 표현을 위해 랜덤 값을 마구 넣는 방식도 생각해보았지만,
게임 프로젝트의 환경부분에 이러한 비용을 부담하는건 비효율적이라 판단되어 더 가벼운 표현과 수식을 고민해보았다
결과적으로 적용된 하늘의 구현모습은 만족스러웠고 신년 해돋이를 놓치지 않게 되어 더욱 의미있는 작업이었다