Shader 섹션 67 에서 템플릿 버퍼를 사용하여 객체 숨기기

27862 단어 Unityshadertech

쉐더를 할 시간이 얼마 안 남았어요.


하마터면 Shader를 할 뻔했다.쉐더를 할 시간이 많지 않을 것 같아서요.
100부까지 열심히 하겠습니다.10년이 걸려도 돼요.
100편의 글을 배웠다면 초보자라도 이해할 수 있을 것 같아요.
이렇게 하는 거야.
※ 초보자는 필기 레벨로 기록
기술 보도로는 도움이 안 될 것 같습니다.

미리 준비하다


아래 참조
Shader 파트 1Unite 2017 애니메이션을 볼 때가 많지 않아요. (기초지식~종이 스크레이퍼로 색깔 바꾸기)

데모


표제의 인상이 전달하기 어려운 시연은 대상을 엄폐함으로써 형성된 표현이다.

구조


상자의 평면에만 표시할 수 있는 객체를 준비했습니다.
이 퍼레이드는 상자인 만큼 옆면은 4면으로, 각각 상영되는 고양이 4마리를 배치했다.
다음은 위 설명을 이해하기 쉬운 시위 행진.

그리고 제목처럼 이 메커니즘은 템플릿 버퍼를 이용한다.

템플릿 버퍼


템플릿 버퍼는 화면 그리기와 달리 확보된 버퍼입니다.
아주 간단하게 설명하자면 픽셀 값을 설정하고 이 값을 참조할 수 있다.
예를 들어 "그리려고 하는 픽셀의 템플릿 버퍼 값이 1이면 검은색으로 칠하고 그렇지 않으면 흰색으로 칠한다."

이 원리를 이용하여'특정 대상을 그린 후의 픽셀'에만 표시되는 대상을 나타낼 수 있다.
이를 위해 다음 두 가지를 제작한다.
• 템플릿 버퍼에 값만 쓰는 Shader
• [미리 그려진 픽셀 템플릿 버퍼 값]을 지정된 값과 비교하여 일관성 있게 임의의 드로잉 처리를 수행하는 Shader

Shader 샘플


우선ステンシルバッファに値を書き込むだけのShader.
Shader "Custom/WriteStencil"
{
    Properties
    {
        _Ref("Ref", Int) = 1
    }

    SubShader
    {
        Tags
        {
            "RenderType" = "Opaque"
            "Queue" = "Geometry-1"
        }
        
        Pass
        {
            //カラーチャンネルに書き込むレンダーターゲットを設定する
            //0の場合、全てのカラーチャンネルが無効化され何も書き込まれない
            ColorMask 0
            ZWrite Off
            //ステンシルバッファに関して
            Stencil
            {
                //ステンシルの値
                Ref [_Ref]

                //ステンシルバッファの値の判定方法
                //Alwaysなのでステンシルバッファのテストは常に通過する
                Comp Always

                //ステンシルバッファに値を書き込むかどうか
                //Replaceなので既存の値をRefの値に置き換える
                Pass Replace
            }
        }
    }
}

ColorMask


ColorMask를 통해 RGBA(드로잉 채널)를 지정할 수 있습니다.
이번에'템플릿 버퍼에만 값 쓰기'가 이 Shader의 역할이에요.
값이 0이면 드로잉 채널이 모두 닫힙니다.

단계 명령


Stncil 명령에서 템플릿 버퍼에 대한 작업을 수행합니다.
우선Ref에서 값을 설정합니다.
그런 다음 비교 함수Comp를 사용하여 이전 Ref 값을 그리려는 픽셀의 깊이 버퍼 값과 비교하여 대상 픽셀을 결정합니다.Pass 비교 함수는 실시간 동작이다.Replace에서 템플릿 버퍼에 Ref 값을 기록합니다.
【참조 링크】: ShaderLab command: Stencil
이러한 일련의 작업은 템플릿 버퍼 값을 보려면 드로잉 프로세스 이전에 수행해야 합니다.템플릿 버퍼의 값이 쓰여지지 않으면 '템플릿 버퍼의 값이 1이기 때문에 ○○' 처리를 할 수 없기 때문이다.
따라서 Que에 1999를 지정하여 다른 대상보다 한 단계 먼저 그리기 처리를 했다.
※ 그나저나 이 쉐더는 아무것도 그려지지 않았는데 프레젠테이션 원고에서 하얗게 변한 것은 다른 반투명 대상이 배치되어 있기 때문이다.

다음은"描画予定であるピクセルのステンシルバッファの値"を指定した値と比較し、合致していた場合に任意の描画処理を行うShader.
Shader "Custom/SimpleGeometryStencil"
{
    Properties
    {
        //ここに書いたものがInspectorに表示される
        _MainTex ("Texture", 2D) = "white" {}
        _Ref("Ref", Int) = 1
        _ScaleFactor ("Scale Factor", Range(0,1.0)) = 0.5
        _PositionFactor("Position Factor", Range(0,1.0)) = 0.5
        _RotationFactor ("Rotation Factor", Range(0,1.0)) = 0.5
    }
    SubShader
    {
        //ステンシルバッファに関して
        Stencil
        {
            //ステンシルの値
            Ref [_Ref]

            //ステンシルバッファの値の判定方法
            //Equalなので"描画しようとしているピクセルのステンシルバッファ"がRefと同じ場合、そのピクセルを描画の処理対象とする
            Comp Equal
        }

        Tags
        {
            "RenderType"="Opaque"
        }

        //両面描画
        Cull Off

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma geometry geom
            #pragma fragment frag

            #include "UnityCG.cginc"

            float _PositionFactor;
            float _RotationFactor;
            float _ScaleFactor;
            sampler2D _MainTex;

            struct appdata
            {
                float4 vertex : POSITION;
                float3 localPos : TEXCOORD0;
                float2 uv : TEXCOORD1;
            };

            //頂点シェーダー
            appdata vert(appdata v)
            {
                appdata o;
                o.localPos = v.vertex.xyz; //ジオメトリーシェーダーで頂点を動かす前に"描画しようとしているピクセル"のローカル座標を保持しておく
                o.uv = v.uv;
                return v;
            }

            struct g2f
            {
                float4 vertex : SV_POSITION;
                float2 uv : TEXCOORD1;
            };

            //回転させる
            //pは回転させたい座標 angleは回転させる角度 axisはどの軸を元に回転させるか 
            float3 rotate(float3 p, float angle, float3 axis)
            {
                float3 a = normalize(axis);
                float s = sin(angle);
                float c = cos(angle);
                float r = 1.0 - c;
                float3x3 m = float3x3(
                    a.x * a.x * r + c, a.y * a.x * r + a.z * s, a.z * a.x * r - a.y * s,
                    a.x * a.y * r - a.z * s, a.y * a.y * r + c, a.z * a.y * r + a.x * s,
                    a.x * a.z * r + a.y * s, a.y * a.z * r - a.x * s, a.z * a.z * r + c
                );

                return mul(m, p);
            }

            //ランダムな値を返す
            float rand(float2 co)
            {
                return frac(sin(dot(co.xy, float2(12.9898, 78.233))) * 43758.5453);
            }

            // ジオメトリシェーダー
            [maxvertexcount(3)]
            void geom(triangle appdata input[3], uint pid : SV_PrimitiveID, inout TriangleStream<g2f> stream)
            {
                // 法線を計算
                float3 vec1 = input[1].vertex - input[0].vertex;
                float3 vec2 = input[2].vertex - input[0].vertex;
                float3 normal = normalize(cross(vec1, vec2));

                //1枚のポリゴンの中心
                float3 center = (input[0].vertex + input[1].vertex + input[2].vertex) / 3;
                float random = 2.0 * rand(center.xy) - 0.5;
                float3 r3 = random.xxx;

                [unroll]
                for (int i = 0; i < 3; i++)
                {
                    appdata v = input[i];
                    g2f o;

                    //ジオメトリーの移動・回転・拡大縮小処理
                    v.vertex.xyz = center + rotate(v.vertex.xyz - center, (pid + _Time.y) * _RotationFactor, r3);
                    v.vertex.xyz = center + (v.vertex.xyz - center) * (1.0 - _ScaleFactor);
                    v.vertex.xyz += normal * _PositionFactor * abs(r3);

                    o.vertex = UnityObjectToClipPos(v.vertex);
                    o.uv = v.uv;

                    stream.Append(o);
                }
            }

            //フラグメントシェーダー
            fixed4 frag(g2f i) : SV_Target
            {
                float4 col = tex2D(_MainTex, i.uv);
                return col;
            }
            ENDCG
        }
    }
}
형상 섀도우가 오랫동안 쓰여 있어 복잡해 보이지만 주의해야 할 것은 스틸 명령이다.
비교 함수에 대한 Equal을
//ステンシルバッファに関して
Stencil
{
    //ステンシルの値
    Ref [_Ref]

    //ステンシルバッファの値の判定方法
    //Equalなので"描画しようとしているピクセルのステンシルバッファ"がRefと同じ場合、そのピクセルを描画の処理対象とする
    Comp Equal
}
Comp에 지정합니다.
따라서 그리려는 픽셀 템플릿 버퍼가 Ref와 함께 있는 경우에만 그리기 작업을 수행할 수 있습니다.

참조 링크


[Unity 면도기 입문] 템플릿 버퍼로 숨겨진 부분 그리기
커튼을 이용한 장치 제작법
홀로렌즈에 동태를 추가해서 맞은편 창문을 볼 수 있어요.
[Unity] 템플릿 버퍼로 창문 만들기

좋은 웹페이지 즐겨찾기