유니티 엔진에서 스텐실 버퍼 및 ZWrite 사용 방법 - Whitmem
유니티 엔진에서 스텐실 버퍼 및 ZWrite 사용 방법
게임 개발 및 엔진
2026-01-07 21:24 게시 991967580ba94ea72d8e

0
0
46
이 페이지는 외부 공간에 무단 복제할 수 없으며 오직 있는 그대로 게시되며 부정확한 내용을 포함할 수 있습니다. 법률이 허용하는 한 가이드 라인에 맞춰 게시 내용을 인용하거나 출처로 표기할 수 있습니다.
This page is not to be distributed to external services; it is provided as is and may contain inaccuracies.
저작물 참고 필자도 해당 내용을 공부하고 있는 입장으로 주로 오픈 문서, 포럼, AI 질의 응답을 활용하여 관련 내용을 공부하였습니다. 따라서 틀린 내용이 있을 수 있습니다. 다만, 타인(AI 등)의 저작물을 침해하지 않도록 순서, 과정, 스크린샷, 코드 등 다른 저작물을 활용 또는 포함하지 않았으며, AI 등 인터넷 자료는 오로지 기술 원리 이해의 목표로만 활용하고, 본 게시글에 언급된 과정, 흐름, 본문, 게시한 코드는 AI의 복붙 없이 모두 필자가 사전에 알고 있는 그래픽스 기술과 인터넷 상에 공개된 개념을 통해 모두 직접 시도하며 직접 작성하였음을 밝힙니다. 부득이하게 어떤 자료를 인용하는 경우는 링크로 출처를 남기고 인용합니다.
유니티 엔진에서 스텐실 버퍼를 사용해서 마스크로 사용 하는 방법
Unity 공식 홈페이지에서 제공하는 Documentation 을 참고하였다. 관련 내용은 https://docs.unity3d.com/kr/2019.4/Manual/SL-Stencil.html에서 조회할 수 있다.
스텐실 버퍼는 어떤 메시를 쉐이더로 그릴 때 특정 영역에 그릴 것인지 말 것인지 비교할 수 있는 맵이라고 보면 된다.
어떤 메시가 그려지면서 스텐실 버퍼의 같은 영역에 어떤 기록을 남기는데, 다음 메시를 그릴 시점(다른 드로우 콜)에 해당 버퍼를 참조하여 그릴지 말지를 설정할 수 있다.
스텐실 버퍼를 사용하는 방법은 매우 간단하다. 쉐이더의 HLSL 를 작성하는 영역 바로 위에 Stencil 영역을 만들어주면 된다.
Ref 는 연산할 값을 넣으면 되고, Comp는 비교 방식, Pass 는 성공시 쓸 값을 넣으면 된다.
공식 문서를 보면 알 수 있다시피, ReadMask, WriteMask, Fail, ZFail 와 같은 추가 속성들이 있다. 우선 이 메시에 대해서는 1이라는 값을 항상 스텐실 버퍼에 써 볼 것이므로 위와 같이 설정해준다.
Comp always 의 의미는 해당 픽셀이 어떤 비교 연산을 거칠 것이고, 해당 비교 연산의 성공 여부에 따라 스텐실을 그릴 것인지, 말 것인지를 지정할 수 있다.
한편 비교에 실패한 픽셀은 그려지지 않는다.
우선 쉐이더를 연결하면 평소와 같게 위와 같이 화면이 그려진다. 내부적으로는 스텐실 버퍼 영역에 1이라는 값이 써진 것이다.
한편 한 객체를 더 그려본다. 이 객체는 기존 큐브와 약간 겹치게 배치하였고 쉐이더는 텍스처를 배치하였다. 당연하겠지만 두 큐브가 서로 겹쳐서 그려질 것이다. 여기서 새로운 객체에 대해서 기존 스텐실 버퍼가 1인 경우에 그리도록 할 수 있는데 바로 2번째 객체에도 스텐실 조건을 넣어주는 것이다.
비교 값을 1로 하고, 같은 경우에만 해당 쉐이더가 그려지게 해준다. Pass 통과시에는 별도 스텐실 버퍼 값을 변경하지는 않는다. 스텐실 버퍼가 통과한다는 의미는 검사를 하여 위 조건에 만족한 경우 어떤 값으로 바꿀 것인지에 대한 설정이다. 즉 지정한 Ref로 다시 쓸 것인지, 0을 쓸것인지, 증가할 건지, 반전 할 것인지를 지정할 수 있는 것이다.
그리고 순서를 지정해줘야 한다. 각각 메시는 내부적으로 드로우콜 순서를 가진다. 맵에 배치된 것과는 별개이다. 여기서는 첫 번째 객체에 의해 스텐실 버퍼가 먼저 그려지고, 해당 정보를 참고하여 두 번째 객체를 렌더링하는 것이므로 첫 번째 객체가 먼저 그려지게끔 설정해야 한다.
첫 번째 객체의 Material 설정에 들어간 뒤, Render Queue값이 보이는데, 이 값의 index를 낮추면 낮출수록 먼저 드로우 된다고 보면된다. 즉 스텐실이 먼저 그려져야하는 객체들의 숫자를 줄여준다. 필자의 경우는 1999으로 하였다.
그리고 그려보면 해당 객체는 겹치는 영역에만 드로우되는 것을 볼 수 있다. 이 효과를 사용해서 다양한 현상을 구현할 수 있다.
특정 문에만 비치는 내부 방이라든지...의 효과 말이다.
스텐실 버퍼는 카메라 시점으로 렌더링되는 버퍼 공간이기 때문에 다른 객체에 신경 쓸 필요 없이 손쉽게 적용할 수 있다는 장점이 있다.
더 나아가 비트마스크로도 설정을 할 수 있는데, 이는 보통 다양한 플래그를 구현하기 위해서 사용한다.
예를 들어, 배경, 객체 각각 스텐실을 다르게 기록했을 때 배경, 객체 둘다 겹쳐서 보이게끔 구현하고 싶을 때가 있다.
위 예시를 보자. 메인 객체 중에서, 첫 번째, 두 번째 흰 큐브에만 겹쳐서 그려지는 것을 볼 수 있다. 첫 번째 큐브는 스텐실 버퍼에 1을 비트로, 두 번째 큐브는 스텐실 버퍼에 2를 비트로, 세 번째 큐브는 스텐실 버퍼에 4를 비트로 작성한다. 비트는 플래그 목적으로 사용한다. 왜 1, 2를 작성하다가 갑자기 3이 아닌 4를 작성하는지 이해가 안된다면 비트 플래그 원리에 대해서 이해를 해야 한다. 이러한 복잡한 방법을 쓰는 이유는 무엇보다 메모리 절약 및 성능 때문이다. 아무데나 if 문을 사용하면 좋겠지만 현실적으로 불가능 한 부분이 매우 많다.
쉐이더 1,2,3
쉐이더 1에서는 1이라는 값을 스텐실 버퍼에 쓰는데, 이 때 WriteMask 라는 부분이 보인다. WriteMask 는 쓰려는 값에서 AND 연산 후에 1인 부분만 스텐실 버퍼에 기록한다. 즉 위에서는 0b00000001 을 버퍼에 작성하는 데 0b00000001 & 0b00000001(WriteMask) 에서 1이 나오는 부분만 기록한다. 즉 Ref 값을 0b11111111 로 하여도 상관이 없다. WriteMask에 의해 1인 부분만 기록되기 때문이다. 두 번째 큐브, 세 번째 큐브도 같은 원리로, 0b00000010, 0b00000100 에 각각 그려준다.
겹칠 쉐이더
이제 겹쳐서 그리기 위한 객체의 쉐이더를 작성한다.
여기서는 ReadMask라는 것을 볼 수 있는데, 일단 기존 스텐실 버퍼에서 읽을 값을 Mask를 씌워서 읽는다 3은 0b0000011 이다. 즉 비트 플래그에서 이진 수의 1인 위치를 보자면, 정수 1과 정수 2와의 성분을 모두 가지고 있는 셈이다. 즉 비트 위치 (정수 1또는 2)가 기록된 스텐실 버퍼의 값을 읽으면 아무것도 없으면 0b00000000, 1이 기록되어 있으면 0b00000001, 2가 기록되어 있으면 0b00000010, 1 및 2 둘다 기록되어 있으면 0b00000011 가 반환된다. 여기서 0이 아닌 경우(Ref 0, Comp NotEqual) 그려주는 것이다.
따라서 3인 큐브에는 안그려지는 것이다. ReadMask 에 세 번째 큐브도 계산 기준에 넣어주면 같이 그려질 것이다.
아무튼 이 방법들은 큐브가 겹치는 부분에만 그려진다.
큐브가 안에 있는 경우 스텐실 버퍼에서 그릴 수 있는 영역 중에 그나마 앞에 출력될 수 있는 부분만 큐브가 그려지게 된다. 이러면 벽에 가린 부분만 벽에서만 볼 수 있게 구현할 수가 없다.
이 때 겹쳐 그리고자 하는 큐브의 ZTest를 비활성화 해주면 된다. 정확히는 기존 거리에 상관 없이 무조건 새로 그리도록 ZTest Always 명령을 내려주면 된다. ZTest 는 3D 객체를 그릴 때 이미 앞에 무엇이 그려져있는 경우 그려지지 않도록 깊이 버퍼를 비교하여 그려질 대상이라고 여기는 경우에만 그린다. 즉 상관 없도록 Always 로 해주는 경우 그리는 시점에 무조건 다시 그리고, 해당 깊이로 다시 쓴다. (깊이를 다시 써지게 않게 하려면 ZWrite 를 Off 하면 된다. 또는 다른 방식으로 다른 객체들의 ZWrite 를 모두 Off 해주면 이 객체를 그릴 때 이미 그려진 것이 없으니 ZTest 와 관련 없이 그려질 것이다.)
뒤에 있는 객체임에도 앞에 그려지는 것을 볼 수 있다. 우리가 여기에 스텐실 버퍼를 사용한 이유는
위와 같이 해당 영역을 투과하는 픽셀에만 메시가 그려지는 것이다.
댓글 0개
댓글을 작성하는 경우 댓글 처리 방침에 동의하는 것으로 간주됩니다. 댓글을 작성하면 일회용 인증키가 발급되며, 해당 키를 분실하는 경우 댓글을 제거할 수 없습니다. 댓글을 작성하면 사용자 IP가 영구적으로 기록 및 부분 공개됩니다.
확인
Whitmemit 개인 일지 블로그는 개인이 운영하는 정보 공유 공간으로 사용자의 민감한 개인 정보를 직접 요구하거나 요청하지 않습니다. 기본적인 사이트 방문시 처리되는 처리 정보에 대해서는 '사이트 처리 방침'을 참고하십시오. 추가적인 기능의 제공을 위하여 쿠키 정보를 사용하고 있습니다. Whitmemit 에서 처리하는 정보는 식별 용도로 사용되며 기타 글꼴 및 폰트 라이브러리에서 쿠키 정보를 사용할 수 있습니다.
이 자료는 모두 필수 자료로 간주되며, 사이트 이용을 하거나, 탐색하는 경우 동의로 간주합니다.