실감 공통편 포스트 프로세스 실루엣 및 아웃라인
안녕하세요 박현상입니다
이번 시간에는 3D 오브젝트가 아닌
화면 전체적인 효과를 다루는
포스트 프로세스와 포스트 프로세스에 사용할 수 있는 형태의
머티리얼을 제작해 보겠습니다
이번 학습을 통해 렌더링의 순서와
시각효과를 통한 이미지 변환을 중점적으로 하여
이전 학습한 깊이 값을 표현하는 것을 응용하게 될 것입니다
포스트 프로세스 실루엣
포스트 프로세스 머티리얼을 만들기 전에
우선은 우리가 만들어야 될 효과가 이미 에디터 상에
지금 오브젝트를 선택해 보면 알 수가 있습니다
지금 보시는 것처럼 오브젝트 경계가 주황색으로 나오고 있죠
이런 것들은 개별 머티리얼로도 물론
해결할 수 있는 방법은 있는데 좀 한계가 있고요
화면 전체에 효과를 줘서 구현을 해야 됩니다
이것을 위해서 사용하는 것이 바로 화면 앞에 있는
포스트 프로세스라고 하는 겁니다
보통 줄여서 P, P 그리고
전체적인 공간을 의미하는 볼륨까지 해서
포스트 프로세스 효과를 적용하기 위한 공간
포스트 프로세스 볼륨이라고 얘기를 합니다
포스트 프로세스 볼륨 같은 경우에는
Place Actors 항목에서 Visual Effects 부분을 보시면
첫 번째 항목으로 있죠 이걸 그대로 들고 오면 됩니다
그럼 총 현재 레벨에는
2개의 포스트 프로세스 액터가 위치하게 됩니다
Third Person Map 기준으로 이미 설치가 되어 있는
첫 번째 포스트 프로세스 같은 경우에는
세팅으로 가서 아래쪽으로 내려가 보면
Infinite Extent(Unbound)라는 항목이 체크되어 있습니다
이것은 카메라가 이 영역 안에 들어가지 않아도
효과가 글로벌하게 전체적으로 적용이 된다는 뜻입니다
반면에 이번에 우리가 새로 영상에서 설치한 포스트 프로세스는
볼륨 세팅 항목에 보면
Infinite Extent(Unbound) 항목이 비활성화되어 있죠
그렇기 때문에 우리가 만약에 Bloom의 효과를
기존보다 더 강하게 줬다면
지금은 아무런 화면상의 변화가 없지만
카메라가 이 볼륨의 영역 안으로 들어갔을 때
이런 식으로 더 밝게 Bloom 효과가
빛나는 것을 확인하실 수가 있습니다
일반적인 포스트 프로세스 같은 경우에는
언리얼에서 지금 보시는 것처럼
Bloom 효과뿐만 아니라 여러 가지 다양한 효과들을
굉장히 많이 이미 구현되어 있는 상태입니다
하지만 모든 기능은 만들 수가 없기 때문에
여기에서 포스트 프로세스 머티리얼이라는 항목을 통해서
추가적인 기능을 프로젝트 상황에 맞춰서 만들 수 있도록
따로 마련해 놨습니다
여기에 사용할 포스트 프로세스 머티리얼을 만들기 위해서
콘텐츠 폴더에 뉴 폴더를 해서 PPV라고만 적겠습니다
오른쪽 클릭해서 머티리얼을 생성한 후에
M_SilhouetteLine이라고 이름 지어주도록 하겠습니다
더블 클릭해서 머티리얼 창을 열고
머티리얼 도메인 항목에서 Surface로 되어 있는 것을
Post Process로 변경하면 지금 보는 것처럼
Emissive Color만 활성화되어 있는 걸 볼 수가 있습니다
포스트 프로세스는 기본적으로 이 포스트라는 단어가
이미 어떠한 프로세스, 절차가 끝난 이후의 과정을 의미하는데
여기에서 얘기하는 프로세스라고 함은
화면이 렌더링 되는 절차가 어느 정도 정리되고 난 후에
생성되는 다양한 데이터들이 있습니다
앞 시간에 우리가 그중에 봤던 것이
SceneDepth라고 하는 효과를 봤었고요
그거 말고도 여러분들이 Lit 모드에서
Buffer Visualization에 보면
Base Color만 렌더링 된 화면
Roughness만 렌더링 된 화면
그리고 투명도만 Opacity 항목을 통해서
투명도로 처리되는 화면이라든가 아니면
이 모든 것들을 볼 수 있는 오버뷰를 클릭하게 되면
잠시 머티리얼 창은 닫겠습니다
지금 보시는 것처럼 화면에 최종 결과물을 그리는 데 필요한
다양한 렌더링 된 이미지들을 눈으로 보실 수가 있습니다
우리는 이러한 것들을 보통 렌더 패스라고 얘기합니다
다양한 렌더링 된 이미지들을 종합해서
최종 완성된 화면을 만드는 것이죠
그래서 지금 테두리에 보이는 이미지들이
먼저 기본 패스들이 생성이 되고 난 후에
그 이후에 여기에 있는 데이터들과
추가 다른 데이터들을 종합해서 만들어지는 게
바로 포스트 프로세스 효과입니다
유저들이 보는 건 결국
포스트까지 모두 적용이 된 화면을 보게 되는 것이죠
화면은 다시 되돌리도록 하겠습니다
머티리얼 창으로 돌아가서요
그렇기 때문에 여기에는 별도의 다른
렌더 패스에 사용될 데이터들을
따로 꽂아 줄 필요가 없습니다
최종 결과 화면만 출력될 필요가 있기 때문에
Emissive Color 항목만 현재 활성화되어 있는 것이죠
여기에다가 여러분들이 주로 사용하게 될 이번 노드는
Scene Texture라고 하는 노드입니다
오른쪽 클릭을 해서 SceneTexture라고 검색해 보시면
노드가 하나 이렇게 검색이 되죠 이것을 클릭해 보시면
디테일 창에서 Scene Texture ID 항목을 클릭해 보면
앞서서 봤던 다양한 렌더 패스들이
나열되어 있는 것을 보실 수가 있고요
여기에서 PostProcessInput0이라고 하는 것이
이 컬러 항목을 Emissive Color에다가 연결할 건데
여러분들이 나중에 여기에 있는
PostProcessInput0을 연결할 때는
가급적 직접 연결하는 것도 가능하겠지만
Component Mask를 이용해서
알파 값이 빠진 상태의 RGB 데이터만 이렇게
전달하는 것을 습관화하시는 것이 좋습니다
현재 컬러 노드에서 출력이 되는 것은
RGBA 해서 벡터 4고요
그래서 우리가 최종 화면을 그리는 데 필요한 건
RGB 데이터만 필요하기 때문에 RGB로 출력해서
나중에 컬러하고 믹싱할 수 있게끔 하는 것이죠
한번 예시 삼아서 Constant3Vector로 약간 붉은색을
너무 진하니까 살짝 붉은색을 넣어서
Multiply를 통해서 효과를 섞어주도록 하겠습니다
Component Mask를 통해서 RGB를 섞으면
Emissive Color에 연결했을 때
정상적으로 이렇게 붉은 화면으로 보이는 걸 알 수가 있는데요
반면에 Scene Texture에서 Multiply에 직접 꽂아 넣게 되면
이런 식으로 에러가 출력됩니다
보시면 float4와 float3 간에
문제가 생겼다는 걸 알 수가 있죠
그래서 이런 식으로 우리는 화면을 빨갛게 만드는
포스트 프로세스 효과를 만든 것이고
이렇게 만들어진 머티리얼을 선택해 놓고
포스트 프로세스 머티리얼에 플러스를 눌러서
여기 Choose 항목을 클릭한 다음에 Asset reference
그리고 비어 있는 곳에다가 연결을 하면
M_Silhouette이라고 이렇게 적용이 됐죠
현재 우리가 Unbound 체크가 되어 있지 않은
볼륨에 적용을 했기 때문에
천천히 Unbound를 향해서 들어가 보겠습니다
천천히 들어가 보면 지금 보시는 것처럼
우리가 만든 머티리얼
기존의 렌더링이 끝난 화면 위에다가
붉은색이 얹어진 형태의 결과물을 확인하실 수가 있습니다
포스트 프로세스는 이런 식으로
렌더링 된 결과물에 추가적인 효과를 얹는
다양한 여러 가지 효과들을 모두 총칭해서 이야기하는 겁니다
간단한 포스트 효과를 만들어서
Post Process Volume에 적용해 봤고요
이번에는 본격적인 예제를 만들기에 앞서서
우리가 만들어야 될 아웃라인
이렇게 실루엣 처리를 해줄 수 있는
오브젝트의 가장자리를 별도의 지정한 색깔로
표현할 수 있는 기능을 위해서
반드시 알아야 되는 것 하나를 설명하겠습니다
앞서서 렌더 패스라는 표현을 했는데요
3D 오브젝트를 선택하고 쭉 내려가서
렌더링 항목을 확인해 보시면
아래쪽으로 많이 좀 내려가셔야 될 겁니다
여기 Render CustomDepth Pass라는 것이 있습니다
이것을 활성화해 주셔야 됩니다
Custom Depth Pass에다가 내가 선택한
지금 체크한 오브젝트를 포함시키겠다는 의미인데요
여기에서 보면 Buffer Visualization에서
우리가 SceneDepth라고 하는 항목을 통해서
거리에 따라서 이렇게 지금 보시면
카메라와 가까우면 블랙, 멀면 흰색으로 처리되는 화면을 보셨죠
CustomDepth는 모든 3D 오브젝트가 아닌
내가 선택한 3D 오브젝트만
이런 Depth 계산에 포함시키게끔 하는
별도의 이미지입니다
그래서 SceneDepth는 모든 3D 오브젝트를 대상으로 하는 거고
CustomDepth는 체크해 보면
Buffer Visualization에 있습니다 활성화해 보시면
나머지 배경에 있는 다른 3D 오브젝트들은 포함이 안되고
지금 보는 것처럼 내가 CustomDepth에 포함시키겠다
체크했던 3D 오브젝트만 활성화된 것을 보실 수가 있죠
체크를 해제하고 선택을 빼서 보면
아무것도 없는 검은색입니다
근데 다시 한번 선택해서
Render CustomDepth Pass에 활성화해 보면
거리 값이 렌더링 되는 것을
눈으로 확인할 수 있게 됩니다
우리가 앞서 Depth Fade라는 기능을 배울 때
Depth Fade를 구현하는 기능 중에
SceneDepth 하고요 Pixel Depth 하고
이 두 가지의 차이를 이용해서
Depth Fade라는 기능을 만들어 냈죠
마찬가지 맥락으로 이번 시간의 주요 포인트가 되는 것은
SceneDepth와 CustomDepth
이 두 가지의 차이를 이용해서
오브젝트를 별도로 아웃라인 처리를 해주는
방법에 대해서 다뤄보게 될 겁니다
Scene은 다시 Lit 모드로 돌려놓고요
이 CustomDepth라는 기능을 한번
나머지 간단한 예제를 위해서 만들었던 것은 지우도록 하고
이 PostProcessInput0
Scene Texture의 포스트 프로세스를
CustomDepth 항목을 선택해서 바로 적용해 보겠습니다
적용을 하고 나면 이제 볼륨 안으로 들어가 보면
이미 머티리얼이 적용되어 있죠 이 볼륨 안에는
혹시라도 이렇게 왔다 갔다 하는 것이 번거로우신 분들은
두 번째로 생성한 볼륨에다가 브러시 세팅에서
Unbound 체크하셔도 좋고요 이렇게
그러면 새하얀 화면이 나오게 될 겁니다
Unbound 체크를 끄고
가까이 가서 확인하는 것도 방법입니다
그러면 우리가 Buffer Visualization에서 봤던 것과는 다르게
Buffer Visualization에서는 굉장히 까만 이미지에
약간의 회색 혹은 멀리 떨어져서 보면 이렇게 흰색 정도로
검은색과 흰색의 이미지였던 것에 반해서
실제 Lit 모드를 통해서 렌더링 된 화면을 보면
CustomDepth가 바로 적용이 됐을 때
이런 식으로 굉장히 새하얀 화면을 확인하실 수가 있죠
그래서 올바른 Depth 표현을 위해서
여기 있는 CustomDepth에다가
약간의 처리를 해주도록 할 겁니다
우선 컬러에서 Divide를 이용해서
10,000까지 해주도록 하겠습니다
그리고 연결을 해줍니다
Apply를 눌러서 이제 포스트 적용된 화면을 보면
우리가 선택한 오브젝트는 블랙으로 나오고요
나머지가 전부 다 흰색으로 나오는 걸 보실 수가 있습니다
보이시죠? 그래서 실제로
이걸 이제 Unbound 체크하고 멀리서도 확인해 보겠습니다
멀리서 보면 지금 보시는 것처럼
가까이 있는 오브젝트는 멀어질수록 흰색에 가까워지는데
배경은 여전히 새하얗게 보이는 걸 확인할 수가 있습니다
우선 이 원인부터 말씀을 드리면
우리가 그리고자 하는 건 Depth
CustomDepth든 SceneDepth든 결국엔 Depth
깊이에 대한 것들을 지금 표현하고 있죠
깊이라고 하면 카메라로부터의 거리입니다
우리가 그리고자 하는 이 3D 오브젝트는
카메라로부터 어느 정도 거리가 떨어져 있는 반면에
CustomDepth는 내가 체크하지 않은 나머지는
일절 그리지 않기 때문에
나머지 여백의 공간들은 전부 다
배경을 그리게끔 합니다
그렇기 때문에 거리가 굉장히 먼 것이죠
만약에 이 오브젝트가 현재 저와
1,000 유닛만큼 떨어져 있다고 가정을 하겠습니다
Depth 값이 1,000만큼 떨어져 있다고 가정을 하면
우리가 10,000이라고 하는 수치를 이용해서 나눠줬죠
1,000을 10,000으로 나누면 결괏값이 0.1이 되겠죠
그러면 0.1은 0하고 아주 가깝기 때문에
블랙에 가까운 그레이 톤을 보여주는 거고요
만약에 아주 먼 부분에 있는 여기 끝자락
3,000 정도라고 가정하겠습니다
3,000 유닛이라고 가정하면
0.3 정도 되는 그레이를 표현하게 되는 겁니다
반면에 10,000 유닛을 벗어나는
아주 먼 배경이라고 하면
당연히 10,000보다 크다는 얘기는 1보다 크다는 얘기니까
화이트가 보이게 되는 겁니다
이러한 원리에 의해서 배경색이 화이트가 되는 겁니다
여러분이 Buffer Visualization에서 본
CustomDepth 같은 경우에는
이것을 재가공해서 배경에 대한 값은
빼놓은 상태라고 이해하시면 됩니다
그래서 배경을 1이 아니라
0이 되게끔 처리한 화면을 보신 것이죠
머티리얼 에디터 창으로 돌아와서요
이 Divide 된 결과물에다가
OneMinus라는 기능을 이용해서
Emissive Color에다가 연결해 보겠습니다
갑자기 화면이 까매졌죠
그리고 이렇게 연결된 노드를 Apply 해보면
지금 보시는 것처럼 배경은 까맣고
역으로 가까워질수록 하얗게 바뀝니다
일반적인 CustomDepth였던 상태에서
가까우면 0.1이거나 멀면 0.3일 수도 있겠죠
이랬던 값이 CustomDepth에서 OneMinus를 해줬으니까
1 - 0.1이 되면서 역으로 0.9처럼 밝아진 거고요
1에 가까워졌으니까
여기는 1 - 0.3 해서 0.7이 되면서 마찬가지로 밝아진 거죠
반면에 배경의 무한대에 가까웠던 아주 굉장히 큰 수가
OneMinus에 들어가면 이게 음수가 되어버리면서
여기에 찍히는 값이 엄청나게 낮아진 상태입니다
지금과 같이 이렇게 정리된 값에다가 우리는 깔끔하게
우리가 선택한 오브젝트만 골라낼 수 있도록
0보다 큰 수들은 모두 다 1로 만들고
0보다 작은 수는 모두 다 0으로 만들 수 있게끔
Ceil이라고 하는 올림 함수를 이용해서 마무리 지어줄 겁니다
이것을 연결해 주겠습니다 Apply 해주고요
그러면 이제 값이 얼마가 됐든
아무리 멀리 떨어진다고 하더라도 흰색이 안 바뀌죠
지금 보시는 것처럼 흰색이 바뀌지 않습니다
그러면 앞서서 만들어 놨던 기능들과 이것을
Lerp를 이용해서 한번 합쳐 보면
Scene Texture를 그대로 복사해서
PostProcessInput0으로 텍스처 타입을
Input 되는 데이터를 바꿔주고요
Component Mask 아까 전에 해줬던
색을 색칠하는 과정 그대로 진행할 겁니다
Component Mask 하고 Constant3Vector를
빨간색으로 이렇게 Multiply 해주겠습니다
여기에서 여러분들이 헷갈리시면 안되는 것
현재 우리가 만들어 놓은 화면에서
Custom Pass로 선택해 놓은 오브젝트는 흰색입니다
그럼 흰색에는 이 두 가지가 섞인
기본 렌더링이 끝난 화면과
빨간색이 섞인 데이터를 보여줄 거고요
검은색 영역에는 원래 렌더링 된 이미지를 보여줘야 되니까
Component Mask 항목에서 드래그 해서
Lerp를 LinearInterpolate를 해주시고
Multiply 된 결과를 B에다가
알파에는 우리가 계산해 놓은 Depth 값을
이렇게 최종 연결을 해서 마무리하신 후에
Emissive Color에다가 연결해 주겠습니다
그러면 지금 파랗게 나오죠
우선 저장하고 실제 결과 화면을 보겠습니다
화면이 지금 여기 보면 굉장히 파랗게 나오는 이유는
앞서서 말씀드린 것처럼 배경 쪽에 무한대에 가까운
굉장히 마이너스 큰 수가 들어가 있기 때문에
Lerp와 함께 적용이 되면서 이런 일이 생긴 거라서
여기에 반올림하고 난 후에 숫자를
Saturate를 이용해서 0에서 1 사이로 마무리해 주셔야
Apply를 하더라도 안정되게
여러분들이 오브젝트를 그려낼 수 있게 됩니다
화면을 좀 정리를 하고요
선택해놨던 것도 해제를 한 상태에서 한번 보겠습니다
지금 보면 CustomDepth로 처리를 해놨기 때문에
이 오브젝트가 이렇게 뒤에 가려진다고 하더라도
CustomDepth는 앞서 말씀드린 것처럼
다른 3D 오브젝트를 그리지 않기 때문에
이 영역에다가 우리가 빨간색을 첨가한
이미지를 볼 수 있게 되는 거죠
그래서 마치 오브젝트가 벽 뒤에 가려지더라도
보일 수 있게 하는 효과
이걸 보통 실루엣 효과라고 얘기를 합니다 간단하죠?
실루엣 효과가 되어야 이것을 응용해서
우리는 아웃라인을 만들어 줄 수가 있게 됩니다
이번에는 SceneDepth를 그릴 건데요
CustomDepth와 Divide 항목을
Ctrl+C, V를 해서 복사해 주시고요
Scene Texture에서 Texture ID 항목을
SceneDepth로 선택해 줍니다
결과를 확인해 보면 Apply까지 눌러보면
화면상의 3D 오브젝트들을 대상으로 해서
전체적으로 Depth가 그려지는 게 보이시죠
여기에서 배경은 아주 멀기 때문에
그냥 제가 임의로 무한대라고는 했지만
진짜로 끝없이 커지는 수는 아니고요
9999... 이런 식으로 굉장히 큰 수입니다
반면에 3D 오브젝트가 그려지는 공간들은
10,000 유닛 이내에만 있다면
0에서부터 1 사이의 값으로 나오게 될 겁니다
여기 있는 Divide 10,000이라는 게 결국 거리 값이라서
지금과 같은 상태입니다
여기에서 우리에게 필요한 데이터들을 얻어내기 위해
CustomDepth의 Divide 한 결과에다가
Subtract를 이용해서 SceneDepth를 빼줍니다
그래서 결과를 확인해 보면 이런 식으로
값의 차이를 녹색은 CustomDepth라고 적을 거고요
빨간색으로 SceneDepth를 적을 겁니다
우선 CustomDepth에 대한 값만 적으면
우리가 선택한 오브젝트는
임의의 값으로 0.2 정도라고 적겠습니다
배경은 무한대의 값이고
지금 흰색으로 보이는 영역도 사실
CustomDepth한테는 무한대의 값입니다 가장 큰 수
반면에 SceneDepth의 입장에서 보면
0.2라는 값은 똑같을 겁니다
우리가 선택한 오브젝트는 0.2라는 값은 똑같고
배경도 무한대의 값은 똑같을 건데요
다만 3D 오브젝트가 있던 곳은
0에서 1 사이 값이 될 겁니다
무한대에 가까운 엄청나게 큰 수에서 1을 빼봐야
거의 무한대 - 1이니까 엄청나게 큰 수라는 건 변함이 없죠
반면에 무한대 - 무한대는 0이 될 거고요
0.2 - 0.2 같은 값이 빠지니까 이거는 0이 될 겁니다
그러면 값을 정리해 주기 위해서
앞서서 했던 과정을 똑같이 거칠 겁니다
Ceil과 Saturate를 복사해서요
Subtract 앞에 두고 연결을 해줍니다 결과를 한번 보죠
Ceil을 통해서 소수점이 남아 있을 수도 있는
가능성을 지워낸 거죠
그래서 보면 한 가지 변화가 생겼습니다
뭐냐면 바로 이 부분이죠
화면에 앵글을 조금 더 잘 보이게 다시 한번 바꿔보면
이전에 CustomDepth로만 그렸을 때에는
적용을 해 보면 이렇게 빠져있었고
Ceil과 Saturate를 하기 전에
Subtract를 한 결과만 확인할 때도
지금 보시면 오브젝트가 이 부분이 빠져있었죠
근데 올림을 통해서 이렇게 확인해 보면
사실 이건 올림 때문이긴 한데
여기 거리 값에 의해서 0.2가 있고
멀어질수록 밝아졌었으니까 0.7이 있고 하다면
여기 있는 0.2보다 CustomDepth의 값은
단 0.01이라도 크겠죠 사실은
이렇게 빨간색으로 빗금치는 눈에 보이는 영역은
CustomDepth랑 SceneDepth랑 값이 같을 텐데
제가 녹색으로 빗금치는 영역은 SceneDepth의 값이
제가 앞서서 크다고 표현을 잘못했는데
0.01이라도 작을 겁니다
어쨌든 이 두 값에 차이가 생긴다는 거죠 이 녹색의 영역은
CustomDepth와 SceneDepth 간에
단 0.01이라도 차이가 나면 올림이라는 효과 때문에
이 빗금친 영역은 흰색이 되는 겁니다
이렇게 해서 단순히
언제나 실루엣 효과가 적용돼서 보이던 것에서
이것을 이제 Multiply까지 해줄 건데요
Multiply까지 해서 결과를 보면서
값의 차이를 설명드릴 겁니다
이렇게 Multiply를 해 보면
이런 식으로 딱 가려질 부분만 남습니다
좀 더 정확하게는 CustomDepth가
다른 오브젝트에 의해서 가려진 부분만 남습니다
그래서 이것을 Lerp 해서 적용해 보면
평소에 눈에 보일 때는 정상적인
3D 오브젝트가 가진 컬러가 보여졌다가
다른 오브젝트들에 의해서 가려졌을 때
이런 가려진 부분들에만 실루엣 효과를 넣어서
실제로 눈에 보이지 않는 부분만 골라내 줄 수가 있게 된 거죠
어떻게 이런 일이 생기는지 Saturate까지 끝난 상황의
각각의 대푯값들을 적어보겠습니다
CustomDepth의 Saturate가 끝났을 때 상황입니다
좀 반복된 점이 있어도 이게 헷갈리기 때문에 이해해 주세요
CustomDepth입니다
여기가 1이고 나머지는 전부 다 0이죠
배경이든 3D 오브젝트가 있는 공간이든
여기는 3D 오브젝트가 있는 공간, 여기는 배경 공간입니다
전부 다 0입니다
CustomDepth 오브젝트를 제외하고는
모두 다 0이라는 값이 나오고요
빨간색으로 Subtract 한 SceneDepth가
Saturate 된 결과를 보게 되면
여기는 빨간색으로 적겠습니다
겹쳐진 이만큼의 공간
여기도 1이고요 3D 공간도 1이고 배경도 1입니다
여기는 0이죠 여기는 CustomDepth도 1입니다
여기는 똑같이 1이죠
우리는 이 두 가지를 곱하기했습니다
곱한 결과는 그럼 어떻게 나올까요?
오브젝트가 겹쳐있는 이 영역은
1 x 1이니까 1이 나올 거고요
CustomDepth가 온전하게 보이는 영역은
1과 0이 곱해지니까 결과적으로 0
배경도 마찬가지로 0
3D 오브젝트가 있는 공간도 곱해지면
CustomDepth 때문에 결국엔 0이 됩니다
그래서 우리는 온전히 가려진 영역만
이렇게 고를 수가 있게 되는 것이죠
그리고 일반적인 실루엣 효과라고 하는 것은
가려진 부분만 별도로 표시하는 기능을
보통 실루엣이라고 얘기하니까요
그걸 통해서 우리는 좀 더 정확한
실루엣 효과를 만들 수 있게 됐고요
여기까지가 실루엣 효과의 끝이고요
포스트 프로세스 아웃라인
콘텐츠 브라우저에서 이것을 그대로 응용해서
아웃라인을 그려보겠습니다
현재 우리에게 필요한 건 사실은 이 부분이 아니고
이건 실루엣 때문에 다뤘던 기능이고
실제로 필요한 건 이겁니다
CustomDepth에 Saturate 된 화면
이걸로 다시 한번 돌아가서 보죠
CustomDepth만 있죠
우리의 목표는 선택했을 때 이렇게
아웃라인을 그려주는 거니까
공간이 좀 많이 필요해서 연결을 끊고
불필요한 연결들은 좀 끊어놓은 상태에서
아래쪽에 공간을 좀 많이 확보하겠습니다
그리고 이 노드를 총 2번 복사를 해줄 겁니다
복사를 하는데 여기에서
CustomDepth 노드 Ctrl+C, V 해서 두겠습니다
지금 CustomDepth 노드가 총
똑같은 절차를 가진 게 3개가 나왔죠 이런 식으로
우리는 여기에 InvSize라고 하는 기능을 사용할 겁니다
InvSize 같은 경우에는
우리는 흔히 UV라는 값을 사용합니다
UV는 화면을 기준으로 해서
0, 0에서부터 1, 1까지를 표현하는 건데
우리가 아웃라인을 표현한다는 건
오브젝트가 가진 모양의 바깥쪽으로 이렇게 영역을 잡아서
여기에다가 색칠을 해주는 것이죠 이런 식으로
그래서 우리는 오브젝트가 가진 영역의 바깥에
일정한 거리만큼을 이동시켜야 됩니다
보통 이렇게 이동을 시킬 때 유닛 단위가 아니고
화면을 기준으로 한 포스트 효과니까요
이때 이동의 기준은 픽셀이 됩니다
1px, 2px 이렇게 이동을 시키는 거죠
그리고 InvSize는 UV라고 하는
0에서 1까지 사이를 가진 텍스처 공간에다가
1px을 움직이기 위해서는
과연 0.xxxx이 필요한 것이냐를 알려주는 기능을 합니다
만약에 우리가 500px 짜리 화면을 그린다고
계산하기 좋게 가정을 해보겠습니다
500px에서 우리가 딱 1px만 움직이고 싶어요
UV에다가 어떤 값을 줘서
그러면 결국 500px이 1이 된 거니까
1px은 1/500이 되겠죠
그럼 이것을 수치로 바꿔보면 0.002 정도가 될 겁니다
그럼 우리는 UV라는 값에다가 이 텍스처 공간에다가
0.002 만큼을 더하면 오른쪽으로 가거나 혹은 이렇게
언리얼 기준은 왼쪽 위가 0이고 오른쪽 아래가 1이니까
아래로 한 칸 내려가거나 1px씩을 움직일 수 있게 됩니다
그래서 우리는 여기에다가
Texture Coordinate라고 하는 노드를 이용해서
이게 좌표 값이죠
화면에서의 좌표 값 혹은 이미지 파일의 좌표 값
UV 데이터를 내보내는 건데
기본적으로 Vector2로 되어있으니까
여기에다가 Add를 해서 InvSize를 넣어주고
이렇게 연결을 해주면요
너는 이미지를 1px만큼 이동해라는 뜻이 됩니다 플러스 시켜라
같은 맥락으로 이것을 Subtract 하게 되면요
좌표 값에다가 InvSize를 Subtract 하라고 하면
UV에다가 1px만큼을 마이너스하는 거니까
화면상으로는 왼쪽으로 가게 되겠죠
원래는 X, Y 값이 같이 전달이 되는데
이렇게 X, Y가 가는 방향을 표시할 수가 있을 겁니다
그럼 자연스럽게 이것을 모아보면 어떻게 돼요?
이렇게 X와 Y, X와 Y가 이런 식으로 만나면서
Subtract와 Add 한 결과를 보면
테두리를 이렇게 1px씩 이동해서 만들 수 있게 됩니다
이 두 개의 결과를 Add 해주겠습니다
그러면 이렇게 생각해 볼 수가 있겠죠
원래 Custom Pass 오브젝트의 영역이 있던 것을
빨간색으로 그려보면
1px만큼 이렇게 이동시키고 그리고 이렇게 이동시키고
이런 식으로 대각선으로 이동시키게 된 꼴이 된 겁니다
그리고 두 개가 합쳐져서 이 가운데 영역은 1이었고
1이었으니까 더해지면 2가 되겠죠
그래서 중간에 겹친 영역은 2라는 값이 나오니까
다시 Saturate로 0에서 1 사이 값으로 정리를 해줍니다
그러면 Subtract를 이용해서 가운데를 비워주게 되면
Emissive Color에 연결해 보겠습니다
그럼 이게 어떻게 되는지 잘 보세요
저장해 보면 가운데가 비워지면서
원래 오브젝트가 있던 공간은 그대로 비워지면서
테두리 영역만 남게 됩니다
이게 아웃라인의 원리입니다
우리가 선택한 3D 오브젝트의 주황색 선이
남게 되는 원리가 이런 것이죠
여기에서 우리가 조금 더 효율을 좋게 기능을 만들자면
1px만큼의 값에다가 Multiply를 해서
이것을 pixelMove라는 값으로 이렇게 빼둔다면
이것을 다 Subtract 한 여기에다가 연결해 주겠습니다
0이 아니라 2px만큼 움직이라고 2배를 해준 겁니다
1px만큼 움직일 UV 값의 2배가 되라
한마디로 2px만큼 움직여라라는 뜻이 돼서
Apply를 해 보면요
이전보다 선이 더 두꺼워졌죠
2px만큼 움직여서 그렇습니다
전체적인 노드를 보면서 다시 설명드리면
좌우 이동, 위아래 이동 이런 식으로 표현을 한 건데
사실은 효과가 좀 예쁘게 나오려고 하면
이것을 X, Y를 한 번에 계산해 주는 게 아니고
X는 X대로 왼쪽 오른쪽 해주고
Y는 Y대로 위아래 각자 해줘서 더해지고 난 다음에
사실은 대각선까지 고려되어야 맞습니다
이렇게 사각형이 있다고 가정을 하면
여기에서 2px, 여기에서 2px, 여기에서 2px
이런 식으로 해주고 나면 어디가 남아요?
이 가장자리들이 남죠 이 가장자리들을 위해서
여기는 1.5px만큼만 이동한다든가 이런 식으로
혹은 1px만큼만 이동한다든가 해서
이렇게 빈칸을 추가로 끊어지지 않게끔 연결해 주는 게 맞지만
지금은 영상 관계상 아웃라인이 되는
원리에 대해서만 설명해 드리고
이제 Lerp에 연결해서 이렇게 연결해 보면
실제로 오브젝트에 우리가 원하는
이런 식의 값이 나온 것을 확인할 수가 있고요
가장자리에 이렇게 파란색이 남는 경우들이 있습니다
우리가 오브젝트를 선택하지 않더라도
이런 식으로 가장자리에 이게 남죠
여기에서 참고로 지금 화면을 보시면
제가 따로 조작을 하지 않는다고 하더라도
계속해서 떨림이 있는 것을 보실 수가 있을 텐데요
이것은 Edit에서 프로젝트 세팅에 렌더링 항목을 보시면
Mobile Anti-Aliasing 말고요 좀 더 내려가 보시면
PC 버전에 적용되는 Anti-Aliasing 항목이 있습니다
Default Settings 부분에 있죠
이것을 Multisample Anti-Aliasing으로 바꿨을 때
떨림이 멈추게 됩니다
이것은 Anti-Aliasing 되는 원리 때문에 좀 차이가 있는 건데
Temporal 같은 경우에 작은 사이즈를 그렸다가
나중에 포스트 단계에서 해상도를
우리가 최종 모니터에 그리는 걸로
사이즈를 Upscaling 한다고 보통 얘기를 하죠
이런 식으로 작동하는 반면에
Multisampling 같은 경우에는 반대로
화면에 픽셀이 있으면 이 픽셀을 몇 개로 쪼개서
이 포인트들을 몇 개로 쪼개서 데이터들을 추적할 거냐
픽셀을 더 잘게 쪼개는
지금은 4x MSAA라고 되어 있는 이 부분 있죠
이거는 1개의 픽셀을 4개로 쪼개서 추적하겠다는 얘기고요
이거를 8개로 쪼개거나 2개로 쪼개는 옵션들이 있습니다
그래서 원래 FHD 사이즈면 FHD 그대로
좀 더 정확하게는 이거보다 더 큰 해상도를 했다가
오히려 줄이는 것에 가까운 개념이 Multisampling이고요
Temporal 같은 경우에는 한 사이즈 작은
만약에 FHD라고 하면 이거보다 작은 HD 화면 같은 거
이런 걸로 그렸다가 화면을 FHD에 맞게
늘리는 과정이 있기 때문에
상대적으로 좀 떨림이 있었던 겁니다
이제 이런 것까지 잡아주고 나서
우리가 필요한 값들이 있죠
이를테면 pixelMove를 Distance로 뺐던 것처럼
일정한 거리에서만 그려질 수 있게끔
이 Distance 값도 ScalarParameter를 이용해서
Dist라고만 적겠습니다
이것을 단순하게 1/10인 1,000 유닛으로 바꿔볼게요
모든 것들을 다 1,000 유닛으로 바꾸도록 하겠습니다
나중에 SceneDepth도 다시 활용하려면 있어야겠죠
그리고 컬러도 Convert to Parameter를 이용해서
LineColor 이렇게 만들어 주고요
저장을 한 다음에 보게 되면
1,000 유닛이기 때문에 어디까지만 그려집니까?
카메라로부터 1,000 유닛만큼 떨어진
여기까지만 그려지는 겁니다
그래서 우리가 효과를 거리 값을 조절해서
얼만큼 앞에 있는 것만 그릴 거냐
이건 실루엣도 마찬가지입니다 실루엣을 표시할 때도
내가 10미터 앞까지만 실루엣을 표시할 거냐
100미터 앞에까지 표시할 거냐 이런 것들을
Distance 값을 통해서 여러분들이 조절하시면 됩니다
전체적으로 보면 좀 어려운
개념적으로는 좀 어렵지만 사실 공식으로만 따지면
더하고 올리고 0에서 1로 줄이는 것도
그리고 Divide 하는 것도 결국엔 거의 사칙연산에 가까운
그 이상 복잡한 개념들은 잘 안 쓰이는 효과들이죠
지금 보면 전부 다 Multiply, Subtract, Add, Divide
사칙연산은 다 나왔습니다
우리는 이것을 통해서
실루엣과 아웃라인 효과를 만들어 봤습니다
포스트 프로세스를 사용하여
실루엣 효과와 아웃라인을 제작하였습니다
0에서 1까지의 수치를 시각화하고 계산하여
Custom Pass를 활용한 방식으로
서로 다른 Depth를 계산하여
중복되는 영역을 찾아내는 과정을 다루었습니다
또한 UV를 이동하기 위한 픽셀의 간격 계산 등
단순한 계산식의 반복으로 얻어지는 효과를
확인할 수 있었습니다
이상으로 언리얼 셰이더에 대한 강의를 마치도록 하겠습니다
감사합니다
포스트 프로세스 실루엣
포스트 프로세스 머티리얼 기본 렌더패스(Render Pass)가 렌더링 된 후 처리 효과
Render Pass : 화면을 그리는데 필요한 각각의 정보 (Base Color, Scene Depth 등)
Scene Texture 다양한 렌더 패스 정보를 전달하는 노드
Scene Texture ID : PostProcessInput0 Component Mask를 RGB로 설정하여 연결
Constant3Vector 노드와 Multiply 후 Emissive Color에 연결
포스트 프로세스 머티리얼 적용 PostProcessVolume 액터 선택
Rendering Features > PostProcessMaterial > Choose > Asset reference 설정 후 머티리얼 연결
실루엣 표현 Scene Depth: 월드에 배치된 3D 액터들을 대상으로 거리 값을 표현
Custom Depth: 3D 액터 중 Rendering / Advanced > Render Custom Pass 항목이 활성화된 액터들만 거리 값을 표현
Scene Texture ID: Custom Depth로 설정 Divide 생성 A : Scene Texture, B : 10000
1-x(OneMinus) > Ceil 적용 Scene Textures: PostProcessInput0 > ComponentMask RGB
> Lerp : A와 Multiply A Constant3Vector > Multiply B > Lerp : B
Depth 표현에 Ceil(올림) > Saturate(0, 1로 값을 단순화) > Lerp : Alpha에 연결
Scene Textures 생성: SceneDepth > Divide(A, B : 10,000)
Subtract 생성, A : CustomDepth를 Divide 한 결과, B : SceneDepth Divide 한 결과
Subtract 후, Ceil > Saturate 적용
Multiply 생성 후 Multiply A : CustomDepth ~ Saturate 결과, Multiply B : SceneDepth ~ Saturate 결과
포스트 프로세스 아웃라인
UV 이동 다양한 렌더 패스 정보를 전달하는 노드
Texture Coordinate 생성 Add와 Subtract 노드 생성
SceneTexture 노드 복사
Add, Subtract A : TextureCoordinate Add, Subtract B : SceneTexture > InvSize
계산된 결과를 각각의 CustomDepth 노드 UV에 연결
UV 이동 결과 연산
UV 이동시킨 두 결과를 Add 후 Saturate 적용, Subtract 생성 후 A에 연결
기존의 CustomDepth 효과를 Subtract B에 연결
Subtract 한 결과를 Lerp : Alpha에 연결
픽셀 이동 파라미터 전환
UV 이동시켰던 InvSize와 ScalarParameter(pixelMove, 2)를 Multiply
Multiply 된 결과를 Add와 Subtract 각각 B에 연결 아웃라인 두께 조절