Получение и отбрасывание теней (настраиваемый шейдер GEOMETRY Grass) — Unity C#

Я следил за учебником, сделанным Сэмом Вронски, также известным как. World of Zero (учебник World of Zero), где он кодирует геометрический шейдер. для генератора облака точек травы. Отличный учебник, но мне интересно (и я не нашел подходящего решения после нескольких дней исследований), как я могу реализовать тени для шейдера (отбрасывая и получая тени). Я пытаюсь углубиться в шейдеры, но это пока слишком высокий для меня уровень.

Мой вопрос: как я могу реализовать отбрасывание и получение теней для этого шейдера травы? Код, который существует и работает до сих пор, выглядит следующим образом:

Shader "Custom/GrassGeometryShader" {

    // https://www.youtube.com/watch?v=HY6qFbmbij8 und http://www.battlemaze.com/?p=153

    Properties {
    // --> HDR allows High Dynamic Colors
        [HDR]_BackgroundColor ("Background Color", Color) = (1,0,0,1) // default to red
        [HDR]_ForegroundColor ("Foreground Color", Color) = (0,1,0,1) // default to green 
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _Glossiness ("Smoothness", Range(0,1)) = 0.5
        _Metallic ("Metallic", Range(0,1)) = 0.0
        _Cutoff("Alpha Cuttoff", Range (0,1)) = 0.15 // Wieviel abgeschnitten sien soll
        _GrassHeight("GrasHeight", Float) = 0.25
        _GrassWidt("GrasWidth", Float) = 0.25
        _WindSpeed ("WindSpeed", Float) = 100
        _WindStrength("WindStrength", Float) = 0.05
    }
    SubShader {
        Tags { "RenderType"="Opaque" }
        LOD 200

        Pass
        {

        Cull OFF 
        CGPROGRAM
        #include "UnityCG.cginc" // like: "using" in C# 
        // Vertex-Shader with vert-function
        #pragma vertex vert
        // Fragment-Shader with frag-function
        #pragma fragment frag
        // Geometry-Shader with geom-function 
        #pragma geometry geom

        // Use shader model 3.0 target, to get nicer looking lighting
        #pragma target 4.0 // needs to be 4.0 !

        sampler2D _MainTex;

        // vertex to graphics (v2g)
        struct v2g 
        {
            float4  pos : SV_POSITION;
            float3  norm : NORMAL;
            float2  uv : TEXCOORD0;
            float3 color : TEXCOORD1;
        };

        //graphics to fragments (g2f)
        struct g2f 
        {
            float4  pos : SV_POSITION;
            float3  norm : NORMAL;
            float2  uv : TEXCOORD0;            
            float3 diffuseColor : TEXCOORD1;
            //float3 specularColor : TEXCOORD2;
        };

        half _Glossiness;
        half _Metallic;
        fixed4 _BackgroundColor;
        fixed4 _ForegroundColor;
        half _GrassHeight;
        half _GrassWidth;
        half _Cutoff;
        half _WindStrength;
        half _WindSpeed;

        // Vertex-Shader from Battlemaze.com
        v2g vert(appdata_full v)
        {
            float3 v0 = mul(unity_ObjectToWorld, v.vertex).xyz;

            v2g OUT;
            OUT.pos = v.vertex;
            OUT.norm = v.normal;
            OUT.uv = v.texcoord;
            OUT.color = tex2Dlod(_MainTex, v.texcoord).rgb;
            return OUT;         
        }

        void buldQuad(inout TriangleStream<g2f> triStream, float3 points[4], float3 color) {
            g2f OUT;
            float3 faceNormal = cross(points[1]-points[0], points[2]-points[0]);
            for(int i; i < 4; ++i) {
            OUT.pos = UnityObjectToClipPos(points[i]);
            OUT.norm = faceNormal;
            OUT.diffuseColor = color;
            OUT.uv = float2(i%2, (int)i/2);
            triStream.Append(OUT);
            }
            triStream.RestartStrip();
        }

        // geom-Funktion
        [maxvertexcount(24)]
        void geom(point v2g IN[1], inout TriangleStream<g2f> triStream)
        {
            float3 lightPosition = _WorldSpaceLightPos0;

            float3 perpendicularAngle = float3(0,0,1);
            float3 faceNormal = cross(perpendicularAngle, IN[0].norm); // normal of gras

            float3 v0 = IN[0].pos.xyz; // Tip of the gras
            float3 v1 = IN[0].pos.xyz + IN[0].norm * _GrassHeight; // base of the gras
            float3 v2 = IN[0].pos.xyz + IN[0].norm * _GrassHeight / 2; // middle part (?)

            float3 wind = float3(sin(_Time.x * _WindSpeed + v0.x) + sin(_Time.x * _WindSpeed + v0.z * 2), 0, cos(_Time.x * _WindSpeed + v0.x * 2) + cos(_Time.x * _WindSpeed + v0.z)); // Anzahl oder Stärke der Manipulation an den Eckpunkten 
            // (_Time.x + v0.x + v0.z looks "random", because it's using time + coordinates)

            v1 += wind * _WindStrength;
            v2 += (wind * _WindStrength/2)/2;

            float3 color = (IN[0].color); // color of the gras

            float sin30 = 0.5;
            float sin60 = 0.866f;
            float cos30 = sin60;
            float cos60 = sin30;

            g2f OUT;

            // Quad 1 - the following code could fit in one function (BUT!) it did not work on MacOSX, that's why it's still calculated the long way

        OUT.pos = UnityObjectToClipPos(v0 + perpendicularAngle * 0.5 * _GrassHeight);
        OUT.norm = faceNormal;
        OUT.diffuseColor = color;
        OUT.uv = float2(1, 0);
        triStream.Append(OUT);


        OUT.pos = UnityObjectToClipPos(v1 + perpendicularAngle * 0.5 * _GrassHeight);
        OUT.norm = faceNormal;
        OUT.diffuseColor = color;
        OUT.uv = float2(1, 1);
        triStream.Append(OUT);

        OUT.pos = UnityObjectToClipPos(v0);
        OUT.norm = faceNormal;
        OUT.diffuseColor = color;
        OUT.uv = float2(0.5, 0);
        triStream.Append(OUT);

        OUT.pos = UnityObjectToClipPos(v1);
        OUT.norm = faceNormal;
        OUT.diffuseColor = color;
        OUT.uv = float2(0.5, 1);
        triStream.Append(OUT);

        OUT.pos = UnityObjectToClipPos(v1 - perpendicularAngle * 0.5 * _GrassHeight);
        OUT.norm = faceNormal;
        OUT.diffuseColor = color;
        OUT.uv = float2(0, 1);
        triStream.Append(OUT);

        OUT.pos = UnityObjectToClipPos(v0 - perpendicularAngle * 0.5 * _GrassHeight);
        OUT.norm = faceNormal;
        OUT.diffuseColor = color;
        OUT.uv = float2(0, 0);
        triStream.Append(OUT);

        OUT.pos = UnityObjectToClipPos(v0);
        OUT.norm = faceNormal;
        OUT.diffuseColor = color;
        OUT.uv = float2(0.5, 0);
        triStream.Append(OUT);

        OUT.pos = UnityObjectToClipPos(v1);
        OUT.norm = faceNormal;
        OUT.diffuseColor = color;
        OUT.uv = float2(0.5, 1);
        triStream.Append(OUT);

        // Quad 2

        OUT.pos = UnityObjectToClipPos(v0 + float3(sin60, 0, -cos60) * 0.5 * _GrassHeight);
        OUT.norm = faceNormal;
        OUT.diffuseColor = color;
        OUT.uv = float2(1, 0);
        triStream.Append(OUT);

        OUT.pos = UnityObjectToClipPos(v1 + float3(sin60, 0, -cos60)* 0.5 * _GrassHeight);
        OUT.norm = faceNormal;
        OUT.diffuseColor = color;
        OUT.uv = float2(1, 1);
        triStream.Append(OUT);

        OUT.pos = UnityObjectToClipPos(v0);
        OUT.norm = faceNormal;
        OUT.diffuseColor = color;
        OUT.uv = float2(0.5, 0);
        triStream.Append(OUT);

        OUT.pos = UnityObjectToClipPos(v1);
        OUT.norm = faceNormal;
        OUT.diffuseColor = color;
        OUT.uv = float2(0.5, 1);
        triStream.Append(OUT);

        OUT.pos = UnityObjectToClipPos(v0 - float3(sin60, 0, -cos60) * 0.5 * _GrassHeight);
        OUT.norm = faceNormal;
        OUT.diffuseColor = color;
        OUT.uv = float2(0, 0);
        triStream.Append(OUT);

        OUT.pos = UnityObjectToClipPos(v1 - float3(sin60, 0, -cos60) * 0.5 * _GrassHeight);
        OUT.norm = faceNormal;
        OUT.diffuseColor = color;
        OUT.uv = float2(0, 1);
        triStream.Append(OUT);

        OUT.pos = UnityObjectToClipPos(v0);
        OUT.norm = faceNormal;
        OUT.diffuseColor = color;
        OUT.uv = float2(0.5, 0);
        triStream.Append(OUT);

        OUT.pos = UnityObjectToClipPos(v1);
        OUT.norm = faceNormal;
        OUT.diffuseColor = color;
        OUT.uv = float2(0.5, 1);
        triStream.Append(OUT);

        // Quad 3 - Positive

        OUT.pos = UnityObjectToClipPos(v0 + float3(sin60, 0, cos60) * 0.5 * _GrassHeight);
        OUT.norm = faceNormal;
        OUT.diffuseColor = color;
        OUT.uv = float2(1, 0);
        triStream.Append(OUT);

        OUT.pos = UnityObjectToClipPos(v1 + float3(sin60, 0, cos60)* 0.5 * _GrassHeight);
        OUT.norm = faceNormal;
        OUT.diffuseColor = color;
        OUT.uv = float2(1, 1);
        triStream.Append(OUT);

        OUT.pos = UnityObjectToClipPos(v0);
        OUT.norm = faceNormal;
        OUT.diffuseColor = color;
        OUT.uv = float2(0.5, 0);
        triStream.Append(OUT);

        OUT.pos = UnityObjectToClipPos(v1);
        OUT.norm = faceNormal;
        OUT.diffuseColor = color;
        OUT.uv = float2(0.5, 1);
        triStream.Append(OUT);

        OUT.pos = UnityObjectToClipPos(v0 - float3(sin60, 0, cos60) * 0.5 * _GrassHeight);
        OUT.norm = faceNormal;
        OUT.diffuseColor = color;
        OUT.uv = float2(0, 0);
        triStream.Append(OUT);

        OUT.pos = UnityObjectToClipPos(v1 - float3(sin60, 0, cos60) * 0.5 * _GrassHeight);
        OUT.norm = faceNormal;
        OUT.diffuseColor = color;
        OUT.uv = float2(0, 1);
        triStream.Append(OUT);

        OUT.pos = UnityObjectToClipPos(v0);
        OUT.norm = faceNormal;
        OUT.diffuseColor = color;
        OUT.uv = float2(0.5, 0);
        triStream.Append(OUT);

        OUT.pos = UnityObjectToClipPos(v1);
        OUT.norm = faceNormal;
        OUT.diffuseColor = color;
        OUT.uv = float2(0.5, 1);
        triStream.Append(OUT);

        }

        // Fragment-Shader by Battlemaze.com --> gets input v2g and renders it on screen
        half4 frag(g2f IN) : COLOR
        {
            fixed4 c = tex2D(_MainTex, IN.uv);
            clip(c.a - _Cutoff);
            return c;
            //return float4 (IN.diffuseColor.rgb, 1.0);
        }

        ENDCG
        }
    }
}

Как я уже упоминал, я работаю с машиной под управлением MacOS, которая, к сожалению, не работает с вычислительными шейдерами.

Буду признателен за любую помощь в этом.


person M_NEN    schedule 10.01.2018    source источник
comment
Разве вам не нужен только тег addshadow? docs.unity3d.com/Manual/SL-SurfaceShaders.html   -  person Brice V.    schedule 15.01.2018
comment
@BriceV. Спасибо за предложение. это постоянно выдает мне следующую ошибку: CGPROGRAM не может содержать поверхность #pragma, а также другие программы. Я поместил эти строки: ** #include UnityPBSLighting.cginc** и #pragma surface surf BlinnPhong fullforwardshadows прямо в подшейдере кода выше   -  person M_NEN    schedule 16.01.2018
comment
да, плохо, это может быть только для шейдеров поверхности. Возможно, вам придется изучить создание прохода ShadowCaster самостоятельно (он будет почти идентичен, за исключением пиксельного шейдера). Здесь есть документ: docs.unity3d.com/Manual/SL-VertexFragmentShaderExamples.html в разделе Реализация отбрасывания теней   -  person Brice V.    schedule 16.01.2018
comment
@BriceV. Еще раз Ty за то, что поделился некоторыми хорошими ссылками, но даже спустя несколько часов мой шейдер, похоже, не работает. Мне это надоело... Просто кажется, что это не очень хорошо сочетается с геометрическими шейдерами. Не знаю, как продолжить эту задачу.   -  person M_NEN    schedule 20.01.2018
comment
взгляните на это: github.com/keijiro/StandardGeometryShader   -  person Brice V.    schedule 31.01.2018


Ответы (1)


Чтобы добавить поддержку теневого получения

В начало блока CGPROGRAM добавьте следующее:

#include "AutoLight.cginc"

Также возможно, что вам может понадобиться добавить это:

#pragma multi_compile_fwdbase

Внутри вашей структуры v2g добавьте следующую строку:

SHADOW_COORDS(3) // (3) means we are using TEXCOORD3

Внутри вашего шейдера геометрии для каждой новой вершины добавьте эту строку после того, как вы назначили OUT.pos:

TRANSFER_SHADOW(OUT)

И, наконец, в вашей функции фрагмента добавьте следующее:

half shadow = SHADOW_ATTENUATION(IN)

Теперь переменная «shadow» содержит вашу теневую маску. В освещенном шейдере вы бы умножили это на светлый цвет, но в вашем случае вы можете просто умножить его на выходной цвет альбедо.


Чтобы добавить поддержку отбрасывания теней

Добавьте в свой шейдер следующий проход:

Pass {
    Name "ShadowCaster"
    Tags { "LightMode" = "ShadowCaster" }

    ZWrite On ZTest LEqual

    CGPROGRAM
    #pragma target 2.0

    #pragma multi_compile_shadowcaster

    #pragma vertex vertShadowCaster
    #pragma fragment fragShadowCaster

    #include "UnityStandardShadow.cginc"

    ENDCG
}
person Kalle Halvarsson    schedule 14.03.2019