주의! 본 정리 글은 '클래스 101'의 '언리얼 엔진 중급, 영화 퀄리티의 3D 메타버스를 창조해요.' 강의를 수강하고 정리한 글입니다. 해당 글은 아래 원칙 하에 작성되었습니다.
1. 강의에서 다룬 내용 기재를 최소화하고, 강의 흐름에 맞게 개인적으로 학습하는 과정을 기록한다.
2. 강의에서 제공하는 자료를 첨부하지 않는다.
3. 내용을 보충할 자료가 필요한 경우, 직접 제작한 자료를 사용한다.
(2023.06.01 추가)
클래스 101 문의 결과 저작권 문제없다는 답변받았습니다.
(2023.06.02 추가)
언리얼 엔진 중급, 영화 퀄리티의 3D 메타버스를 창조해요. | RYU Russell 러셀
언리얼 엔진 5 가 정식 출시되었습니다! 언리얼 엔진 5는 기존 언리얼 엔진 사용자들을 위해 언리얼 엔진 4와 호환되도록 제작되었으며, 언리얼 엔진 4 사용자라면 쉽게 즉시 언리얼 엔진 5를 사
class101.net
들어가며
오늘 정리할 내용은 'Chapter 4. 블루프린트를 활용해 환경 디자인을 더욱 다채롭게'이다. 이번 글을 통해 아래와 같은 내용을 정리할 예정이다. 1, 4번에만 블루프린트를 적어놓긴 했는데 사실 블루프린트는 머티리얼이든, 오브젝트든 어디에나 쓰이기 때문에 전부 블루프린트와 관련 있다고 봐도 좋을 것 같다.
- 블루프린트를 활용하여 레벨 오브젝트 관리하기
- Flipbook을 활용하여 비오는 효과 연출하기
- HDRI를 활용하여 페이크 창문 만들기
- 블루프린트를 활용하여 신호등 구성하기
블루프린트를 활용하여 레벨 오브젝트 관리하기
Static Mesh Component
모든 static mesh는 하위에 static mesh component를 갖는다. static mesh component란 static mesh의 기능을 실질적으로 수행하는 부분을 말하는데 static mesh 뿐만 아니라 대부분의 오브젝트는 하위에 '~ Component'를 갖고 있고 이를 통해 기능을 수행한다고 보면 될 것 같다.
그럼 component라는 이름이 붙는 항목은 어떤 기능을 수행할까? static mesh로 예를 들자면, static mesh라는 오브젝트를 어떤 mesh의 형태로 보여주고 무슨 material을 씌울 건지 월드에 전달하는 것이다.
그러니까 정리를 하자면 고정된 mesh 오브젝트라는 개념의 static mesh가 있고, 이를 월드에 배치하기 위해서는 static mesh actor를 사용해야 한다. 그리고 이 actor는 껍데기에 불과한데 이것이 실제로 기능하도록 만들기 위해서는 하위에 static mesh component가 존재해야 한다.
사실 actor를 배치할 때 자동으로 하위에 component가 붙어서 나오기 때문에 이런 개념이 있다는 것 정도만 알아두면 될 것 같다. 자세한 내용은 언리얼 엔진 공식 문서의 '액터'와 '컴포넌트'를 살펴보도록 하자.
블루프린트로 Static Mesh Component 조작하기
그렇다면 이러한 static mesh component가 있다는 걸 알아서 뭘 할 수 있을까? 바로 블루프린트에서 직접 조작이 가능하다. 다음의 이미지를 보자.
위의 이미지는 블루프린트를 통해 static mesh component를 추가하는 이미지이다. 이미지를 보면 'Split Struct Pin'이라는 부분이 있는데 이 부분을 통해 위치나 회전, 크기 등의 vector3 데이터를 float 데이터로 쪼갤 수 있고, 이를 조정하여 블루프린트를 통해 오브젝트를 조작할 수 있다.
이를 통해 아래의 왼쪽 이미지와 같이 'Promote to Variable'로 mesh를 할당하는 부분을 변수로 승격시킨 뒤 원하는 mesh를 넣어 손 쉽게 배치 및 관리를 할 수 있다. 이때 오른쪽 이미지처럼 'Instance Editable' 옵션을 체크하거나 Variables 부분의 눈 아이콘을 활성화하여 블루프린트 내부에서만 mesh를 관리할 수 있는 것이 아닌 해당 오브젝트를 인스턴스 화하여 detail panel에서도 mesh를 관리할 수 있게 된다.
만약 이 옵션을 비활성화할 경우 해당 블루프린트를 여러 개 배치했을 때 내부에서 mesh를 변경하는 순간 모든 mesh가 바뀌게 된다. 하지만 활성화를 했고 특정 블루프린트의 mesh를 다른 걸로 변경했다면 블루프린트 내부에서 mesh를 변경하더라도 해당 오브젝트의 mesh가 오버라이드 됐기 때문에 변경된 상태 그대로 유지된다.
블루프린트로 레벨 오브젝트 관리하기
이렇게 블루프린트 내부에서 static mesh component를 조작함으로써 레벨 오브젝트를 손쉽게 관리할 수 있다. 아래는 특정 mesh를 회전 값, 크기 등의 랜덤한 변화를 주며 반복적으로 배치할 수 있게 구성한 블루프린트 이미지다. 이때 seed 값을 통해 내부에 변화가 있어도 이전 배치와 동일하게 배치되도록 구성한 게 포인트이다.
언리얼 5를 통해 블루프린트에서 사칙 연산을 하면, 언리얼 4와는 다르게 자료형마다 사칙 연산 함수가 준비되어 있지 않다. 통일된 사칙 연산 함수를 사용하기 때문에 출력되는 자료형이 연결할 자료형과 맞지 않을 수 있다. 따라서 연결할 때 자료형이 맞지 않다면 아래의 이미지와 같이 형변환을 해주어야 한다.
블루프린트로 하늘 시스템 조작하기
블루프린트를 이용하면 다양한 조작이 가능하다. 이번 강의에서는 월드의 시간 값에 따라 태양의 위치가 바뀌도록 하여 시간이 흐르는 듯한 효과를 만들어봤다.
이를 설명하기에 앞서 construction script와 event graph의 개념부터 알아보자. construction script는 편집하는 단계에서 변경 사항을 즉각적으로 적용하고 확인하기 위한 블루프린트이고, event graph는 런타임에서 동작하도록 구성하는 블루프린트라고 할 수 있다.
따라서 우선 편집 단계에서 시간의 흐름을 테스트하기 위해 아래와 같이 블루프린트를 구성했다.
위의 이미지에 보이는 time of day와 sun angle을 실제로 테스트 했을 때 정상적으로 동작하는 걸 확인할 수 있었다. 위 블루프린트의 map range unclamped의 경우, 입력 값의 범위를 출력 값의 범위에 비례하게 변환해서 출력하는 노드인데 이를 통해 별 다른 계산 없이 손쉽게 태양의 회전 값을 조절할 수 있었다. 더 자세한 설명은 언리얼 공식 문서의 'Map Range Unclamped'를 참고하자.
런타임에서 동작하도록 구성하기 전에 frame마다 일정 시간이 흐르도록 처리하면 fps(frame per second) 값에 따라 시간의 흐름이 달라질 수 있기에 한 번 내 개발 환경의 fps를 확인해봤다. 아래 이미지와 같이 viewport option에서 show fps로 확인할 수 있다.
나는 50 fps 조금 넘게 나오는데 이는 유저마다 다를 수 있기에 시간의 흐름이 동일하도록 처리해 줄 필요가 있었다. 따라서 event graph의 event tick node에서 delta second를 사용하여 동일하게 흐르도록 처리했다. delta second는 현재 환경에서 frame 사이의 시간을 계산한 결과인데 50 fps 라면 delta second는 1/50인 0.02가 된다.
아래는 시간에 따라 태양의 높이가 달라지도록 구성한 블루프린트인데 print string node를 통해 delta time 값을 확인할 수 있다. 더 자세한 내용은 언리얼 공식 문서의 '이벤트'에서 event tick 부분을 확인하도록 하자.
이렇게 아래 gif와 같이 시간 값에 따라 태양의 각도가 바뀌도록 구성하여 시간이 흐르는 것처럼 만들 수 있었다.
이 외에도 블루프린트를 통해 post processing 작업을 할 수 있었고, 이러한 설정 변수들을 categorizing하여 detail panel에서 보기 좋게 정리할 수 있다는 걸 배웠다.
이렇게 블루프린트를 이용하여 레벨에 배치된 액터들을 효과적으로 관리하는 방법을 알아봤다.
Flipbook을 활용하여 비오는 효과 연출하기
Engine Content
이번 강의에서는 Flipbook에 대해 알아보기 전에 언리얼 엔진에 내장되어 있는 texture를 확인해봤다. 아래와 같이 content browser의 settings에서 show engine content를 통해 숨겨진 파일들을 확인할 수 있었는데 이를 종종 리소스 적용 전 더미 데이터로 사용한다고 한다. flipbook 또한 이 중 한 texture를 가져와서 테스트했다.
아! 추가로, 강의 때 들었던 건데 언리얼에서 사용하는 texture의 크기는 2의 제곱수여야 한다. 2의 제곱수가 아니면 LOD(Level Of Detail) bias 적용이 안 돼서 LOD를 못 쓴다고 한다. UI처럼 LOD를 안 쓰는 경우가 아니라면 texture의 크기를 2의 제곱수로 맞춰주도록 하자.
Flipbook 이란?
Flipbook이란 하나의 texture를 행렬로 나눠서 연속 재생하는 것을 말한다. 블루프린트로 texture를 바꾸는 것이 아닌 material 안에서 작업을 처리하기 때문에 연산 비용이 비교적 저렴하다는 장점이 있다. flipbook node를 열어보면 아래와 같이 구성되어 있다.
강의에서는 아래 이미지처럼 1~4까지 적힌 texture를 2x2 행렬로 취급해서 1~4까지 바뀌는 material을 만들었는데 time node를 어떻게 사용하는 거고 왜 사용하는지 몰라서 찾아봤다.
아래 PrismaticaDev 채널의 The Time Node 영상을 확인해보니 플레이한 시점에서 0부터 무한히 흐른 시간을 출력하는 노드로 보인다. time node를 사용한 이유를 알기 위해서는 flipbook node의 animation phase에 대해 알아야 하는데 간단히 짐작하기로 시간 값을 가져오기 위해서 사용했다기보다 계속 애니메이션이 재생되도록 하기 위해 사용한 것 같다.
Flipbook node의 animation phase는 0부터 1 사이의 값을 가지는데 이 애니메이션의 어떤 장면을 노출할 지 정하는 부분이다. 예를 들어 1~4까지 2x2로 구성된 texture는 총 4개의 장면으로 구성되는데 그럼 1/4, 즉 0.25 단위로 장면이 바뀌는 것이다. 확인해보니 0.5일 때나, 1.5일 때나, 1000.5일 때나 똑같은 장면이어서 자동으로 소수 값으로 전환하는 것 같았다. 따라서 time node는 예상 그대로 애니메이션을 계속 재생하기 위하기 위해 사용한 것임을 알 수 있었다.
flipbook node에 대한 더 자세한 설명을 원한다면 언리얼 공식 문서의 '플립북 애니메이션 렌더'를 참고하자. 결과는 gif와 같다.
Flipbook node를 활용하여 비오는 효과 연출하기
러셀님은 강의 자료를 통해 4x4 짜리 비 오는 효과의 normal texture를 제공했다. 이것을 이용하여 아래와 같이 normal animation으로 만들었다.
이를 puddle에 적용하기 위해서 puddle layer의 normal을 다루는 부분을 찾아 들어갔다. 여기서 BlendAngleCorrectedNormals node로 만들어둔 normal과 혼합했는데, flatten normal node를 통해 ripple flatten 값에 따라 얼마나 혼합할지 설정할 수 있도록 구성했다.
위 노드들에 대해 부가 설명을 하자면 flatten normal node는 normal map이 1에 가까울수록 평평해지며 0일 경우 원래의 normal map을 출력하는 노드이고 BlendAngleCorrectedNormals node는 그냥 normal map을 혼합하는 노드라고 생각하면 될 것 같다.
이 ripple flatten 값은 material parameter correction에 넣어 set scalar parameter value를 통해 아래와 같이 조절했다. 노드들을 간단하게 설명하자면 sequence node는 위에 선을 따라 모두 수행한 뒤 다음 선을 수행하는 노드고, branch node는 condition에 따라 분기가 나뉘는 노드, set scalar parameter value는 앞서 말했듯이 material parameter correction 값을 가져와서 조정하는 노드이다.
material parameter correction과 노드들에 대한 설명은 다음 문서들을 참고하자.
- material parameter correction : '머티리얼 파라미터 콜렉션'
- sequence node, branch node : '흐름 제어'
- set scalar parameter value node : 'set scalar parameter value'
이를 조금 더 자연스럽게 하기 위해 지난번에 만들어놓은 Triplanar node로 월드를 기준으로 맵핑했고, frac node는 소수점 아래 부분만 가져오는 node인데 이 노드를 거치면 잘리는 듯이 보이던 현상이 해결된다. 왜 해결되는지 찾아봤는데 아무리 찾아봐도 모르겠다.. 아는 사람은 부디 나에게 가르침을.. frac node에 대한 자세한 내용은 언리얼 공식 문서의 'Math 표현식'을 참고하자.
아래는 이렇게 만든 블루프린트를 이용하여 비 오는 정도를 서서히 바꾸는 것을 찍은 gif이다.
HDRI를 활용하여 페이크 창문 만들기
HDRI
HDRI는 High Dynamic Range Image의 약자로 32bit 포맷의 360도 이미지이다. 이 이미지를 맵핑하여 큐브 맵을 만들 수 있다. 큐브 맵에 대한 자세한 설명은 언리얼 공식 문서의 '큐브맵'을 참고하자.
일반적인 이미지는 8bit나 16bit 포맷이다. HDRI는 왜 32bit 포맷을 사용할까? 바로 빛의 정보를 저장하기 때문이다. 따라서 HDRI를 사용하면 emissive color 같은 부분과 연결해서 수치를 조정했을 때 빛이 있는 부분이 극적으로 바뀌게 된다.
HDRI를 활용하여 페이크 창문 만들기
아래와 같이 Interior Cubemap을 통해 타일링하여 HDR 이미지를 적용한 material을 평면 오브젝트에 적용하면 다음 gif와 같이 만들어진다. 여기서 randomize rotation node는 tiling 된 부분들에 랜덤 회전 값을 적용해줘서 약간의 변화를 주는 역할을 한다.
위의 gif를 보면 평면에 HDRI가 적용된 걸 확인할 수 있는데 이를 응용하여 저렴하게 깊이감이 느껴지는 환경을 구축할 수 있다. 아래에서 창문까지 넣은 환경을 보면 아직 애매한 감이 없지 않아 있지만 효과적으로 내부를 표현할 수 있는 걸 확인할 수 있다.
이전에 배웠던 걸 응용해서 밤에는 불이 켜지고, 낮에는 불이 꺼지는 걸 만들어보려고 했는데 만들고 보니까 천천히 꺼지고 켜지는 거면 창문에 비치는 하늘인데 거꾸로 돼있으니 도대체 뭘 만든 건가 싶었다 ㅋㅋㅋㅋ 일단은 이런 식으로 응용할 수 있다는 것만 확인하고 넘어갔다.
아래 gif는 블루프린트를 통해 emissive intensity를 조정한 건데 전체가 밝아지는 것 이상으로 내부 창문 빛이 밝아지는 건 위에서 말했듯이 HDR 이미지가 32bit 포맷으로 빛 정보를 갖기 때문이다. 아무튼 이렇게 페이크 창문을 만들어봤다.
언리얼에서 빛을 반사하는 방식
추가로 Imperfection texture를 활용하는 과정에서 언리얼이 빛을 반사하는 방식에 대해서 배웠다. 언리얼은 크게 2가지 방식으로 빛을 반사한다고 한다.
첫 번째로, Screen Space Reflection(SSR)이다. SSR은 현재 화면에서 이미 렌더 된 부분을 그대로 반사에 활용함으로써 저렴한 비용으로 퀄리티 좋은 반사를 구현한다. 다만 화면에 렌더되지 않은 오브젝트의 반사는 처리하지 못한다는 한계를 갖고 있다.
두 번째로, Realtime Capture이다. sky light actor의 real time capture와 같은데, real time capture는 현재 환경을 1 frame에 1번 큐브 맵으로 생성해서 빛이 닿지 않는 부분에 간접광을 표현하는 역할을 한다. 이 기능은 라이팅만을 위해 사용하기에 기본 해상도가 낮다. 따라서 skylight cubemap resolution을 올려서 반사의 품질을 높일 수 있지만 비용이 높아진다는 문제가 있기에 올려도 512 정도가 적당하다고 한다.
블루프린트를 활용하여 신호등 구성하기
에셋 옮기기
신호등을 구성하는 강의에서는 러셀님이 제공해주신 에셋을 활용하여 신호등을 만들고 블루프린트 작업을 진행했다. 다른 레벨에서 신호등을 조립하고 가져왔는데 이때 migration하는 방법을 배웠다. 단순이 window 탐색기에서 옮기면 파일이 깨질 수 있기에 가능하면 migration을 하라고 언급하셨다.
mesh 합치기
언리얼 엔진에서는 mesh가 많아지면 이 오브젝트 하나하나에 대해 별도의 연산자를 사용하기에 최적화 부분에서 안 좋은데 이를 drawcall 많이 발생한다고 표현한다. 언리얼 공식 문서의 'CPU 프로파일링'에서 CPU 비용 최적화 방법을 보면 여러 mesh를 합쳐서 drawcall을 줄이는 방법이 가장 처음에 나올 정도로 mesh를 관리하는 건 중요하다.
다만, 전부 묶어버리는 건 바람직하지 않은 게 언리얼은 일부분만 보여도 전부 렌더링하기 때문에 거대한 오브젝트를 합쳐버리면 오히려 최적화 부분에서 안 좋을 수 있다. 이는 'Freeze Rendering' 명령어를 통해 확인할 수 있다. 오브젝트의 일부분만 본 상태에서 freeze rendering 명령어를 치고 다른 부분을 봐보면 안 보일 것이다.
아무튼 신호등의 경우, 거대한 오브젝트가 아니기에 최적화 외에 편하게 관리하기 위해서라도 mesh를 합칠 필요가 있었다. 언리얼 5는 mesh를 합칠 때 언리얼 4와 다르게 아래 이미지와 같이 상단 actor에서 merge actors로 합쳐야 한다. 더 알아보고 싶다면 언리얼 공식 문서의 '액터 병합'을 참고하자.
추가로, mesh를 배치할 때 다른 오브젝트를 복사해서 위치 정렬한 뒤 replace해서 깔끔하게 배치하는 방법도 배웠다.
Material Parameter를 다루는 방법
material parameter는 크게 아래 3가지 방법으로 다룰 수 있다. 상황에 맞게 적절하게 사용하면 효과적으로 material을 다룰 수 있어 보인다.
- Material parameter collection : parameter의 원본을 변경하기에 모든 material에 공통적으로 적용하고 싶을 때 사용할 수 있다.
- Set scalar parameter value on materials : 현재 오브젝트에 이미 적용된 material의 parameter를 빠르게 변경할 수 있다.
- create dynamic material instance : 해당 노드에 지정한 material로 instance를 생성하고 기존 material에 덮어씌워 parameter를 제어할 수 있다. parameter를 변경하기 위해서는 set scalar parameter value node와 함께 사용해야 한다.
Custom Event 활용하기
Custom event는 event graph에서 활용할 수 있는 사용자 설정 함수라고 생각하면 될 것 같다. 호출하는 것부터 인자를 전달받는 것까지 실제 프로그래밍과 크게 다르지 않았다. Custom event를 잘 활용해서 모듈화를 해놓으면 블루프린트를 효과적으로 다룰 수 있어 보였다. 자세한 내용은 언리얼 공식 문서의 '커스텀 이벤트'를 참고하자.
블루프린트를 활용하여 신호등 구성하기
블루프린트로 신호등을 구성하는 건 새로운 것 없이 앞서 배운 것을 활용하여 진행했다. 우선 아래와 같이 material을 만들어 적용했다. 숫자판 material을 구성하는 과정에서 mesh의 형태가 달라서 깔끔하게 보기 힘들었는데 이는 mesh preview 기능으로 해결할 수 있었다.
이렇게 만든 material을 활용해 블루프린트를 구성했다.
아래 gif를 보면 잘 출력되는 걸 확인할 수 있다. 블루프린트는 프로그래밍을 해본 사람이면 금방 익숙해질 것 같고, 실제로 몇 번 활용해보면 기본적인 부분은 잘 다룰 수 있을 것 같다.
후기
아니.. 일단 자기 전에 빠르게 쓰려고 했는데 벌써 아침이다.. 너무 힘들다.. 강의를 보는 것보다 정리하는 게 더 오래 걸린다.. 힘들어서 중간부터는 날림으로 작성했다.. 정리하면서 확실히 이해는 되고 있어서 도움은 되는데.. 재밌는데 힘들어..
이제 러셀님 강의는 반 정도 수강했는데 일단 지금 뭘 하려고 욕심을 부리기보다는 나머지 반을 빠르게 듣고 정리한 뒤에 배운 내용으로 작은 레벨 디자인 프로젝트 하나를 진행해보면 확실히 체화할 수 있을 것 같다.
그리고 글 같은 경우도 예전에는 영어랑 한글을 혼용해서 썼는데 어떻게 작성해야 할지 그 스타일에 대해 감이 잡힌 것 같다. 그리고 이번에는 gif를 많이 활용했는데 확실히 시각적인 자료를 많이 넣다 보니 보기가 좋아진 것 같아 만족스럽다.
최근의 나는 유독 예민해져서 작은 일에도 과민 반응을 하게 됐다. 불만족스러운 상황과 나 자신의 미흡한 성과로 인한 초조함이 그 원인인 것 같은데, 팀 프로젝트에 대한 미련을 잠시 내려놓고 안 되는 일에 계속 집착하기보다 개인 발전을 위해 시간을 보내니 힘들긴 해도 마음은 편해진 것 같다.
나 자신을 믿는다면 타인의 시선에 휘둘리기보다는 내 이상을 관철해보자. 내가 나를 믿지 않는다면 누가 나를 믿을까? 나는 나를 조금 더 아끼고 사랑할 필요가 있다. 앞으로는 의심하기보다 있는 그대로를 바라보며 여유롭게 살아보도록 노력하자.
'개발 > 언리얼' 카테고리의 다른 글
[Blueprint] 데미지 처리와 콜리전 채널 (0) | 2023.12.25 |
---|---|
[Blueprint] 기묘한 For Loop와 Delay의 관계 (0) | 2023.12.24 |
[RYU Russell, 언리얼 엔진 중급 클래스] 배운 내용 정리 (4) [完] (1) | 2022.08.16 |
[RYU Russell, 언리얼 엔진 중급 클래스] 배운 내용 정리 (2) (0) | 2022.07.15 |
[RYU Russell, 언리얼 엔진 중급 클래스] 배운 내용 정리 (1) (0) | 2022.07.08 |