250129

RTFM·2025년 1월 29일

개발 일지

목록 보기
66/350

✅ 오늘 한 일


  • Randome Study :: Non-Euclidean : 면에 따라 다르게 보이기


🎮 Random Study


Non-Euclidean : 면에 따라 다르게 보이기

초기 접근

작업환경 : Unity 2022.3.49, URP
목적 : 특정 오브젝트를 통과했을 때만 보이는 오브젝트 제작

wall 레이어를 할당한 Transparent 벽 오브젝트 뒤에 one 레이어를 할당한 Transparent 큐브 오브젝트가 있다.

Render Objects 렌더러 피쳐를 이용해 투명 벽 오브젝트로 스텐실을 먼저 그리고,
추가로 Render Objects 피쳐를 이용해 스텐실과 값이 같다면 Opaque 머테리얼을 오버라이드하는 식으로 진행하려 했다.

하지만 원하는 대로 작동하지 않고, 오브젝트는 투명한 그대로 유지되었다.
어째서인지 원인을 분석하고, 해결책을 제시하라.

  1. 렌더러의 설정
  • Filtering
    • Opaque Layer Mask : wall, one 레이어 제외한 모든 레이어
    • Transparent Layer Mask : wall, one 레이어 제외한 모든 레이어
  1. wall stencil을 작성하기 위한 Render Objects 렌더러 피쳐 설정
  • Event : AfterRenderingOpaques
  • Filters
    • Queue : Transparent
    • Layer Mask : wall
  • Overrides
    • Stencil : 체크
      • Value : 1
      • Compare Function : Always
  1. wall stencil이 적힌 곳에만 오브젝트를 그리기 위한 Render Objects 렌더러
  • Event : BeforeRenderingSkybox
  • Filters
    • Queue : Transparent
    • Layer Mask : one
  • Overrides
    • Override Mode : Material
      • Material : 평범한 Opaque Lit 머테리얼
    • Stencil : 체크
      • Value : 1
      • Compare Function : Equal

o1과의 질의와 troubleshooting 과정

  • 투명 벽 뒤에 있는 투명 오브젝트가 Transparent Layer Mask에 포함돼있어 렌더링 피쳐 적용 이후 기본 투명 패스 (Transparent 렌더링)에도 다시 그려져 원래의 투명 상태로 덮어써질 수 있다. 레이어 마스크에서 제거해주거나, Render Objects 피쳐의 Event를 AfterRenderingTransparents 뒤에 배치해야 한다.
    • Layer Mask에서 제거해주었으나 해결은 안됨.

  • Transparent Queue는 기본적으로 깊이/스텐실 기록을 하지 않는 경우가 많다고 함
    • Unlit Transparent Shader Graph를 만들고 적용했더니 화면이 이상해짐
  • 아예 처음부터 새로 접근해보라고 함
  • “씬에는 전혀 드러나지 않고(투명하게), 오직 스텐실 버퍼에만 특정 값을 기록”하는 오브젝트를 만들려면, 색상(Color) 출력은 전혀 하지 않으면서 스텐실만 쓰는 별도의 렌더 패스를 만들면 된다고 함.
    1. ColorMask 0 등으로 컬러 버퍼에 아무것도 쓰지 않는다.
    2. Stencil 설정을 통해 원하는 레퍼런스(Ref) 값, 비교(Comp), 패스(Pass) 동작을 설정하여 스텐실 버퍼에 기록한다.
    3. 필요하다면 깊이 버퍼(ZWrite)를 켤지 끌지를 결정한다.
    • 스텐실만 찍고 싶으면 보통 ZWrite On으로 설정하되, “눈에 보이는 색상은 0”으로 하는 Pass를 만들어 “기하(Geometry)가 스텐실/깊이 버퍼에 존재함”을 인식시키는 식이다.
    • 혹은 ZWrite Off로 설정할 수도 있지만, 그 경우 “카메라에서 보는 물체가 뒤에 있는지” 등에 따라 적절히 스텐실이 찍히지 않을 수 있으므로 상황에 따라 선택한다.
    • 표준 Shader Graph만으로는 ColorMask 0을 직접 켜기 어려우므로, 커스텀 쉐이더(혹은 Shader Graph에 HLSL 주입) 필요
Shader "Custom/MagicMirror"
{
    Properties
    {
        // 두 번째 Pass(화면 표시용)에서 사용할 색상/알파
        _Color("Color (For Transparent Pass)", Color) = (1, 1, 1, 0.5)
    }

    SubShader
    {
        // 전체적으로 투명 Queue로 설정 (필요에 따라 변경 가능)
        Tags
        {
            "RenderType"="Transparent"
            "Queue"="Transparent"
            "UniversalMaterialType"="Unlit"  // 혹은 "Lit" 등
        }

        //----------------------------------------------------------------------
        // 1) 스텐실만 찍는 Pass (ColorMask 0)
        //----------------------------------------------------------------------
        Pass
        {
            Name "StencilOnly"
            // URP가 Forward 렌더링 시 이 Pass를 인식하도록
            Tags { "LightMode"="UniversalForward" }

            // 화면 컬러는 전혀 쓰지 않음
            ColorMask 0

            // 깊이 버퍼에 쓰지 않음(= 뒤쪽 물체가 가려지지 않도록)
            ZWrite Off
            ZTest LEqual

            // 스텐실 작성 예: 항상 통과, 통과 시 Ref=1로 교체
            Stencil
            {
                Ref 1
                Comp Always
                Pass Replace
            }

            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            // URP 코어 함수 include
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"

            struct Attributes
            {
                float4 positionOS : POSITION;
            };

            struct Varyings
            {
                float4 positionHCS : SV_POSITION;
            };

            Varyings vert(Attributes IN)
            {
                Varyings OUT;
                OUT.positionHCS = TransformObjectToHClip(IN.positionOS.xyz);
                return OUT;
            }

            half4 frag(Varyings IN) : SV_Target
            {
                // ColorMask 0이므로 실제 화면에는 아무것도 안 찍힘
                return half4(1,1,1,1);
            }
            ENDHLSL
        }

        //----------------------------------------------------------------------
        // 2) 실제 화면에 렌더되는 투명 Pass (알파값으로 반투명 조절)
        //----------------------------------------------------------------------
        Pass
        {
            Name "TransparentVisual"
            Tags { "LightMode"="UniversalForward" }

            // 투명 표면이므로 깊이 버퍼에는 쓰지 않음
            ZWrite Off
            ZTest LEqual

            // 투명 블렌딩 기본 설정
            Blend SrcAlpha OneMinusSrcAlpha

            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"

            struct Attributes
            {
                float4 positionOS : POSITION;
            };

            struct Varyings
            {
                float4 positionHCS : SV_POSITION;
            };

            // 선언한 Properties 참조
            float4 _Color;

            Varyings vert(Attributes IN)
            {
                Varyings OUT;
                OUT.positionHCS = TransformObjectToHClip(IN.positionOS.xyz);
                return OUT;
            }

            half4 frag(Varyings IN) : SV_Target
            {
                // _Color 알파값으로 반투명/불투명 조절
                // 예) _Color = RGBA(1,1,1,0.5)이면 반투명
                //     _Color = RGBA(1,1,1,0)이면 완전 투명 (화면에 안 보임)
                return half4(_Color);
            }
            ENDHLSL
        }
    }
}
  • 작성해준 셰이더를 적용해보았으나 원하는 효과가 발생하지 않음
  • 네가 한다면 렌더러 피쳐를 어떻게 설정할지 물어보고, 따라해봄
  • Stencil > Compare Function > Pass를 Replace로 해야함을 발견
  • 해당 설정을 바꾸었더니 원하는 효과가 발생함

최종 정리

Shader "Custom/MagicMirrorTest"
{
    SubShader
    {
        // 전체적으로 투명 Queue로 설정 (필요에 따라 변경 가능)
        Tags
        {
            "RenderType"="Transparent"
            "Queue"="Transparent"
            "UniversalMaterialType"="Unlit"  // 혹은 "Lit" 등
        }

        //----------------------------------------------------------------------
        // 1) 스텐실만 찍는 Pass (ColorMask 0)
        //----------------------------------------------------------------------
        Pass
        {
            Name "StencilOnly"
            // URP가 Forward 렌더링 시 이 Pass를 인식하도록
            Tags { "LightMode"="UniversalForward" }

            // 화면 컬러는 전혀 쓰지 않음
            ColorMask 0

            // 깊이 버퍼에 쓰지 않음(= 뒤쪽 물체가 가려지지 않도록)
            ZWrite Off
            ZTest LEqual

            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            // URP 코어 함수 include
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"

            struct Attributes
            {
                float4 positionOS : POSITION;
            };

            struct Varyings
            {
                float4 positionHCS : SV_POSITION;
            };

            Varyings vert(Attributes IN)
            {
                Varyings OUT;
                OUT.positionHCS = TransformObjectToHClip(IN.positionOS.xyz);
                return OUT;
            }

            half4 frag(Varyings IN) : SV_Target
            {
                // ColorMask 0이므로 실제 화면에는 아무것도 안 찍힘
                return half4(1,1,1,1);
            }
            ENDHLSL
        }
    }
}

(o1이 짜준 코드에서 불필요한 부분들 제거)

  • 레이어 wall_1, wall_2, wall_3 생성. 각각의 레이어를 가진 벽을 생성.
    • 오브젝트들에 위에 있는 셰이더를 사용한 머테리얼 적용
  • 레이어 one, two, three 생성. 각각의 레이어를 가진 오브젝트를 벽들 뒤에 배치.
    • 오브젝트들에 Unlit Transparent 머테리얼 적용
  • 렌더러 애셋을 새로 만들고 'Stencil Renderer'라 명명한 후 렌더 파이프 라인 애셋에서 Set Default
  • 렌더러 애셋의 Opaque Layer Mask, Transparent Layer Mask에서 wall_1,2,3와 one,two,three 레이어 체크 해제
  • Render Objects 렌더러 피쳐를 생성하고 'WallStencil1'으로 명명.
    • Event : AfterRenderingOpaques
    • Filters
      • Queue : Transparent
      • Layer Mask : wall_1
    • Overrides
      • Stencil : 체크
        • Value : 1
        • Copmpare Function : Always
        • Pass : Replace
        • Fail : Keep
  • Render Objects 렌더러 피쳐를 추가로 생성하고 'Render1'으로 명명.
    • Event : BeforeRenderingSkybox
    • Filters
      • Queue : Transparent
      • Layer Mask : one
    • Overrides
      • Override Mode : Material
        • Material : 눈에 띄는 색깔을 넣은 Lit 머테리얼
      • Stencil : 체크
        • Value : 1
        • Coompare Function : Equal
          • Pass : Keep
          • Fail : Keep
  • wall_2와 two, wall_3와 three에 대해서도 렌더러 피쳐를 추가해주고, stencil 값만 변경.

개선 및 추가 연구할 것들

  • 레이어를 따로 나누지 않고 렌더러 피쳐 2개 만으로 여러 스텐실 (여러 면) 에 해당하는 오브젝트들을 보여줄 수 있는지
  • 성능에 얼마나 영향이 가는지
Shader "Custom/MagicMirror_StencilParam"
{
    Properties
    {
        _Color("Color (For Transparent Pass)", Color) = (1, 1, 1, 0.5)

        // 추가: 스텐실 Ref를 머티리얼 Inspector에서 조절할 수 있게 만듦
        _StencilRef("Stencil Reference", Range(0,255)) = 1
    }

    SubShader
    {
        Tags
        {
            "RenderType"="Transparent"
            "Queue"="Transparent"
            "UniversalMaterialType"="Unlit"
        }

        //----------------------------------------------------------------------
        // 1) 스텐실만 찍는 Pass (ColorMask 0)
        //----------------------------------------------------------------------
        Pass
        {
            Name "StencilOnly"
            Tags { "LightMode"="UniversalForward" }

            ColorMask 0

            ZWrite Off
            ZTest LEqual

            Stencil
            {
                // 머티리얼 프로퍼티 값을 Ref로 사용
                // 즉, Inspector에서 _StencilRef 변경 시 실제 기록값도 변경됨
                Ref [_StencilRef]

                Comp Always
                Pass Replace
            }

            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"

            struct Attributes
            {
                float4 positionOS : POSITION;
            };

            struct Varyings
            {
                float4 positionHCS : SV_POSITION;
            };

            Varyings vert(Attributes IN)
            {
                Varyings OUT;
                OUT.positionHCS = TransformObjectToHClip(IN.positionOS.xyz);
                return OUT;
            }

            half4 frag(Varyings IN) : SV_Target
            {
                return half4(1,1,1,1); // ColorMask 0 이라 화면에는 안 찍힘
            }
            ENDHLSL
        }

        //----------------------------------------------------------------------
        // 2) 화면에 렌더되는 투명 Pass
        //----------------------------------------------------------------------
        Pass
        {
            Name "TransparentVisual"
            Tags { "LightMode"="UniversalForward" }

            ZWrite Off
            ZTest LEqual
            Blend SrcAlpha OneMinusSrcAlpha

            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"

            struct Attributes
            {
                float4 positionOS : POSITION;
            };

            struct Varyings
            {
                float4 positionHCS : SV_POSITION;
            };

            float4 _Color;

            Varyings vert(Attributes IN)
            {
                Varyings OUT;
                OUT.positionHCS = TransformObjectToHClip(IN.positionOS.xyz);
                return OUT;
            }

            half4 frag(Varyings IN) : SV_Target
            {
                return half4(_Color);
            }
            ENDHLSL
        }
    }
}

머테리얼 프로퍼티로 스텐실을 조절할 수 있는 코드도 받긴 했었다.
하지만 o1의 말과는 다르게 stencil이 작성되지 않음.

2개의 댓글

comment-user-thumbnail
2025년 1월 29일

꾸준히 개발자의 길을 걷고 계시는 군요. 잊지 않고 응원하고 있습니다.

1개의 답글