프로젝션 텍스처 그림자를 제작해 보았습니다.

6960 단어 ShaderUnity

소개



이 기사의 작성은 「이케니에와 눈의 세츠나」그래픽 해설(제2회·그래픽 효과편) 를 참고하겠습니다

매우 공부되었습니다.

이번은 참고 기사 내용의 하나, 투영 텍스처 그림자에 대해 실작해 보고 싶습니다

그림자 맵 작성



우선, 그림자 카메라를 생성하여 광원과 같은 방향을 설치한다 (광시점으로 생각하면 좋다.
Culling Mask를 Fuck하고 피사체만 찍을 수 있도록
그 후 그림자 카메라의 targetTexture를 지정한다.
그 지정된 RenderTexture는 나중에 투영 텍스처 그림자가 참조하는 그림자 맵입니다.



SetReplacementShader



그림자 맵에 그려지는 색은 그림자의 색만으로
그 때문에, 일일 피사체의 material을 바꾸는 것도 번거로운 작업

뭔가 좋은 방법이 있습니까?
SetReplacementShader 정말 소원을 줄 수있을 것 같습니다.

SetReplacementShader
SetReplacementShader를 간단하게 생각하면, shader version의 override 같은 것

SetReplacementShader 제 2 인수가 지정한 태그를 판단 조건
바꾸는 Shader와 material 지정한 Shader 태그의 값이 같으면
그대로 교체

예를 들어, 장면에 Cube와 Sphere가 있습니다.
각각 다른 shader 사용

DemoForShaderReplace.cginc

 #ifndef _DEMO_FOR_SHADERREPLACE
 #define _DEMO_FOR_SHADERREPLACE
 #include "UnityCG.cginc"
 struct appdata
 {
    float4 vertex : POSITION;
    float2 uv : TEXCOORD0;
 };
 struct v2f
 {
    float2 uv : TEXCOORD0;
    float4 vertex : SV_POSITION;
 };
 sampler2D _MainTex;
 float4 _MainTex_ST;
 v2f vert (appdata v)
 {
    v2f o;
    o.vertex = UnityObjectToClipPos(v.vertex);
    o.uv = TRANSFORM_TEX(v.uv, _MainTex);
    return o;
 }
 fixed4 frag (v2f i) : SV_Target
 {
    return tex2D(_MainTex, i.uv);
 }
 #endif //_DEMO_FOR_SHADERREPLACE

DemoSphere.shader
Shader "Unlit/DemoSphere"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" "Name"="Sphere" }
        LOD 100

    Pass
    {
        CGPROGRAM
        #pragma vertex vert
        #pragma fragment frag

        #include "DemoForShaderReplace.cginc"
        ENDCG
    }
}



DemoCube.shader
Shader "Unlit/DemoCube"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" "Name"="Cube" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "DemoForShaderReplace.cginc"
            ENDCG
        }
    }


}



두 shader는 거의 동일하지만 유일한 차별은 맞춤 태그 Name 값이 다릅니다.

다음은 교체 shader

DemoReplace.shader
Shader "Unlit/DemoReplace"
{
    SubShader
    {
        Tags { "Name"="Cube" }

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag_Replace

            #include "DemoForShaderReplace.cginc"

            fixed4 frag_Replace (v2f i) : SV_Target
            {
                return fixed4(1,0,0,1);
            }
            ENDCG
        }  
    }

    SubShader
    {
        Tags { "Name"="Sphere" }

        Pass
        {
             CGPROGRAM
             #pragma vertex vert
             #pragma fragment frag_Replace

             #include "DemoForShaderReplace.cginc"

             fixed4 frag_Replace (v2f i) : SV_Target
             {
                return fixed4(0,1,0,1);
             }
             ENDCG
        }
    }


}

바꾸는 shader는 커스텀 태그 Name 마다
별도의 교체하려는 그리기를 정의합니다.
예는 Cube를 빨간색으로 Sphere를 녹색으로

마지막은 camera.SetReplacementShader 호출한다

Camera.main.SetReplacementShader (Shader.Find("Unlit/DemoReplace"), "Name");

결과


그림자 맵 참조



바닥에 그림자를 비추기 때문에
먼저 그림자 맵의 어디를 참조해야 하는지 알 필요

그림자 맵은 그림자 카메라(광시점)로 찍을 수 있는 결과
그러면 바닥도 그림자 카메라(광시점)로 찍어 그 묘화된 위치를 참조하면 좋다

메인 카메라로 그림자 카메라에 그려진 위치를 찾으려면
그림자 카메라의 뷰 행렬과 프로젝션 행렬 필요

Matrix4x4 view = shadowCamera.worldToCameraMatrix;
Matrix4x4 proj = GL.GetGPUProjectionMatrix (shadowCamera.projectionMatrix, true);
Shader.SetGlobalMatrix("_ProjectionViewProj", proj*view);

두 행렬을 곱합니다. 그 결과를 shader에 알립니다.
마지막으로 shader로 계산
Shader "Shadow/ProjectionShadowGround"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            
            #include "UnityCG.cginc"

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

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 uv_shadow : TEXCOORD1;
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;

            sampler2D _ProjectionShadowMap;
            float4x4  _ProjectionViewProj;
            
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                
                float4 world = mul (unity_ObjectToWorld, v.vertex);
                float4 pos = mul (_ProjectionViewProj, world);
                o.uv_shadow = ComputeScreenPos (pos);
                return o;
            }
            
            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 col = tex2D(_MainTex, i.uv);
                
                fixed4 shadow = tex2D(_ProjectionShadowMap, (i.uv_shadow/i.uv_shadow.w).xy);
                
                return lerp(col, shadow, shadow.a);
            }
            ENDCG
        }
    }
}

결과


리포지토리



github

좋은 웹페이지 즐겨찾기