Как создать эффект «видеть за стенами»?

103

Divinity: Original Sin 2 имеет красивую прозрачную систему. Когда я иду за стенами, появляется всплеск маски, а когда я двигаюсь по игре, она меняется. Это как растворяющий шейдер, и имеет эффект метабола.

Как я могу воспроизвести этот эффект, создавая динамическую маску всплеска, когда игроки идут за стенами?

Вы можете увидеть желаемый эффект в движении через это видео на YouTube .

Образ

Сейед Мортеза Камали
источник
3
Привет, вы, кажется, добавили область видимости в свой вопрос, запросив дополнительные материалы за его пределами. На этом этапе - особенно после того, как он получил полный ответ, который был частично признан недействительным вашим запросом о дополнительном поведении - вам лучше задать новый вопрос, а не расширять свои требования. Если за эти детали была назначена награда, вы бы хорошо справились, задав новый вопрос, описав свою проблему, пометив этот вопрос для внимания модератора, чтобы запросить возмещение за вознаграждение и опубликовав вознаграждение по новому вопросу.
августа
2
Я откатил этот вопрос в предыдущее состояние из-за ползучести области. Учитывая время между добавлением награды и редактированием, которое включало изменение области, я предполагаю, что вы хотели получить разные ответы на этот вопрос, поэтому я оставлю премию вверх. По предложению @doppelgreener, я предлагаю вам задать еще один вопрос с вашими новыми требованиями.
Александр Vaillancourt
@AlexandreVaillancourt ой, извините, я этого не знал, я просто добавил опцию к своему вопросу, потому что мне не нравятся цепные вопросы. Спасибо за изменение ...
Сейед Мортеза Камали

Ответы:

163

маскировка

Чтобы создать этот эффект, вы можете маскировать объекты с помощью буфера трафарета.

буфер трафарета - это буфер общего назначения, который позволяет хранить дополнительное 8-битное целое число (т. е. значение от 0 до 255) для каждого пикселя, отображаемого на экране. Так же, как шейдеры вычисляют значения RGB для определения цвета пикселей на экране и значения z для глубины этих пикселей, отрисованных в буфере глубины, они также могут записать произвольное значение для каждого из этих пикселей в буфер трафарета. Эти значения трафарета могут затем запрашиваться и сравниваться последующими проходами шейдера, чтобы определить, как пиксели должны быть скомпонованы на экране.

https://docs.unity3d.com/Manual/SL-Stencil.html

https://alastaira.wordpress.com/2014/12/27/using-the-stencil-buffer-in-unity-free/

http://www.codingwithunity.com/2016/01/stencil-buffer-shader-for-special.html

образ

Маска Трафарет:

Stencil 
{
    Ref 1 // ReferenceValue = 1
    Comp NotEqual // Only render pixels whose reference value differs from the value in the buffer.
}

Настенный трафарет:

Stencil
{
    Ref 1 // ReferenceValue = 1
    Comp Always // Comparison Function - Make the stencil test always pass.
    Pass Replace // Write the reference value into the buffer.
}

Давайте реализуем.

используйте это как маску:

Shader "Custom/SimpleMask"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _CutOff("CutOff", Range(0,1)) = 0
    }
    SubShader
    {
        LOD 100
        Blend One OneMinusSrcAlpha
        Tags { "Queue" = "Geometry-1" }  // Write to the stencil buffer before drawing any geometry to the screen
        ColorMask 0 // Don't write to any colour channels
        ZWrite Off // Don't write to the Depth buffer
        // Write the value 1 to the stencil buffer
        Stencil
        {
            Ref 1
            Comp Always
            Pass Replace
        }

        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 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
            float _CutOff;

            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
            {
                fixed4 col = tex2D(_MainTex, i.uv);
                float dissolve = step(col, _CutOff);
                clip(_CutOff-dissolve);
                return float4(1,1,1,1)*dissolve;
            }
            ENDCG
        }
    }
}

используйте это как стену:

Shader "Custom/Wall" {
    Properties {
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _Glossiness ("Smoothness", Range(0,1)) = 0.5
        _Metallic ("Metallic", Range(0,1)) = 0.0
    }
    SubShader {
        Blend SrcAlpha OneMinusSrcAlpha
        Tags { "RenderType"="Opaque" }
        LOD 200

        Stencil {
            Ref 1
            Comp NotEqual
        }

        CGPROGRAM
        // Physically based Standard lighting model, and enable shadows on all light types
        #pragma surface surf Standard fullforwardshadows

        // Use shader model 3.0 target, to get nicer looking lighting
        #pragma target 3.0

        sampler2D _MainTex;

        struct Input {
            float2 uv_MainTex;
        };

        half _Glossiness;
        half _Metallic;
        fixed4 _Color;

        void surf (Input IN, inout SurfaceOutputStandard o) {
            // Albedo comes from a texture tinted by color
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
            o.Albedo = c.rgb;
            // Metallic and smoothness come from slider variables
            o.Metallic = _Metallic;
            o.Smoothness = _Glossiness;
            o.Alpha = c.a;
        }
        ENDCG
    }
    FallBack "Diffuse"
}

Анализ эффекта

Если вы хотите иметь процедурную текстуру , вам нужны шумы.

образ Вы можете увидеть этот шейдер в ShaderToy .

Чтобы создать этот эффект, вместо использования UV-координат используйте Polar Coordinates, а затем установите для него текстуру шума.

Uvs обычно располагаются в виде сетки, как пиксели на экране (X = ширина, Y = высота). Полярные координаты, однако, используют биты x и ya по-разному. Один определяет, насколько далеко от центра круга он находится, а другой определяет градусы в диапазоне 0-1, в зависимости от того, что вам нужно.

1600 пикселей-sf_radialuvs

Shader "Smkgames/NoisyMask" {
    Properties {
        _MainTex ("MainTex", 2D) = "white" {}
        _Thickness ("Thickness", Range(0, 1)) = 0.25
        _NoiseRadius ("Noise Radius", Range(0, 1)) = 1
        _CircleRadius("Circle Radius", Range(0, 1)) = 0.5
        _Speed("Speed", Float) = 0.5
    }
    SubShader {
        Tags {"Queue"="Transparent" "IgnoreProjector"="true" "RenderType"="Transparent"}
        ZWrite Off 
        Blend SrcAlpha OneMinusSrcAlpha 
        Cull Off

        Pass {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #pragma target 3.0
            uniform sampler2D _MainTex; uniform float4 _MainTex_ST;
            uniform float _Thickness,_NoiseRadius,_CircleRadius,_Speed;

            struct VertexInput {
                float4 vertex : POSITION;
                float2 texcoord0 : TEXCOORD0;
            };
            struct VertexOutput {
                float4 pos : SV_POSITION;
                float2 uv0 : TEXCOORD0;
                float4 posWorld : TEXCOORD1;

            };
            VertexOutput vert (VertexInput v) {
                VertexOutput o = (VertexOutput)0;
                o.uv0 = v.texcoord0;

                o.pos = UnityObjectToClipPos(v.vertex);
                o.posWorld = mul(unity_ObjectToWorld, v.vertex);
                return o;
            }
            float4 frag(VertexOutput i, float facing : VFACE) : COLOR {

                float2 uv = (i.uv0*2.0+-1.0); // Remapping uv from [0,1] to [-1,1]
                float circleMask = step(length(uv),_NoiseRadius); // Making circle by LENGTH of the vector from the pixel to the center
                float circleMiddle = step(length(uv),_CircleRadius); // Making circle by LENGTH of the vector from the pixel to the center
                float2 polaruv = float2(length(uv),((atan2(uv.g,uv.r)/6.283185)+0.5)); // Making Polar
                polaruv += _Time.y*_Speed/10;
                float4 _MainTex_var = tex2D(_MainTex,TRANSFORM_TEX(polaruv, _MainTex)); // BackGround Noise
                float Noise = (circleMask*step(_MainTex_var.r,_Thickness)); // Masking Background Noise
                float3 finalColor = float3(Noise,Noise,Noise);
                return fixed4(finalColor+circleMiddle,(finalColor+circleMiddle).r);
            }
            ENDCG
        }
    }
    FallBack "Diffuse"
}

другое решение использует шум шума:

2018-01-05_8-16-16

Вы можете увидеть этот шейдер в ShaderToy


Metaball

затем я добавляю эффект метабола из этой статьи : IMG


Биллинг посадка

есть больше ...

Если вы хотите повернуть маску, чтобы посмотреть на свою камеру, вы можете использовать доску Билла :

 output.pos = mul(UNITY_MATRIX_P, 
              mul(UNITY_MATRIX_MV, float4(0.0, 0.0, 0.0, 1.0))
              + float4(input.vertex.x, input.vertex.y, 0.0, 0.0));

это маска с посадкой Билла:

Shader "Custom/Mask/SimpleMaskBillBoard"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _CutOff("CutOff", Range(0,1)) = 0
        _Radius("Radius", Range(0,1)) = 0.2
        _Speed("speed", Float) = 1
        _ScaleX ("Scale X", Float) = 1.0
        _ScaleY ("Scale Y", Float) = 1.0
    }
    SubShader
    {
        LOD 100
        Blend One OneMinusSrcAlpha
        Tags { "Queue" = "Geometry-1" }  // Write to the stencil buffer before drawing any geometry to the screen
        ColorMask 0 // Don't write to any colour channels
        ZWrite Off // Don't write to the Depth buffer

        // Write the value 1 to the stencil buffer
        Stencil
        {
            Ref 1
            Comp Always
            Pass Replace
        }

        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 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
            float _CutOff;
            float _Speed;
            float _Radius;
            float _ScaleX,_ScaleY;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = mul(UNITY_MATRIX_P, 
                    mul(UNITY_MATRIX_MV, float4(0.0, 0.0, 0.0, 1.0))
                    + float4(v.vertex.x, v.vertex.y, 0.0, 0.0)
                    * float4(_ScaleX, _ScaleY, 1.0, 1.0));

                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 col = tex2D(_MainTex, i.uv);
                float dissolve = step(col, _CutOff);
                clip(_CutOff-dissolve);
                return dissolve;
            }
            ENDCG
        }
    }
}

Конечный результат:

2018-01-04_20-18-39

Источник доступен: https://github.com/smkplus/Divinity-Origin-Sin-2


Полезные ссылки

Я нашел хороший учебник, который реализовал этот эффект, Растворяя мир:

Изображение1

Растворяя мир Часть 1

Растворяя мир Часть 2

Образ

Shader "Custom/DissolveBasedOnViewDistance" {
    Properties{
        _MainTex("Albedo (RGB)", 2D) = "white" {}
        _Center("Dissolve Center", Vector) = (0,0,0,0)
        _Interpolation("Dissolve Interpolation", Range(0,5)) = 0.8
        _DissTexture("Dissolve Texture", 2D) = "white" {}
    }

        SubShader{
        Tags { "RenderType" = "Opaque" }
        LOD 200


            CGPROGRAM

        #pragma surface surf Standard vertex:vert addshadow

        #pragma target 3.0

        struct Input {
            float2 uv_MainTex;
            float2 uv_DissTexture;
            float3 worldPos;
            float viewDist;
        };



        sampler2D _MainTex;
        sampler2D _DissTexture;
        half _Interpolation;
        float4 _Center;


        // Computes world space view direction
        // inline float3 WorldSpaceViewDir( in float4 v )
        // {
        //     return _WorldSpaceCameraPos.xyz - mul(_Object2World, v).xyz;
        // }


        void vert(inout appdata_full v,out Input o){
            UNITY_INITIALIZE_OUTPUT(Input,o);

         half3 viewDirW = WorldSpaceViewDir(v.vertex);
         o.viewDist = length(viewDirW);

        }

        void surf(Input IN, inout SurfaceOutputStandard o) {


            float l = length(_Center - IN.worldPos.xyz);

            clip(saturate(IN.viewDist - l + (tex2D(_DissTexture, IN.uv_DissTexture) * _Interpolation * saturate(IN.viewDist))) - 0.5);

         o.Albedo = tex2D(_MainTex,IN.uv_MainTex);
        }
        ENDCG
        }
        Fallback "Diffuse"
}

Еще один учебник по трафарету:

Оставленные для мертвых

Учебник по трафарету

Сейед Мортеза Камали
источник
10
Это хорошее объяснение того, как сделать маскировку передней стены, если знать, что она находится перед персонажем игрока, но есть ли способ автоматически применять этот шейдер только к стенам, которые находятся перед геометрией персонажа?
пушистый
10
@fluffy вы можете использовать Raycast для определения, когда персонаж находится за стеной, а затем включить маску.
Сейед Мортеза Камали