Як я можу створити ефект "бачити за стінами"?


103

Божественність: Original Sin 2 має прекрасну систему проникнення. Коли я йду за стіни, з’явиться маска сплеску, а коли я рухаюсь по грі, вона змінюється. Це як шейдер, що розчиняється, і має ефект метаболу.

Як я можу повторити цей ефект, створюючи динамічну маску сплеску, коли гравці йдуть за стіни?

Ви можете побачити бажаний ефект у русі за допомогою цього відео на YouTube .

Зображення


3
Привіт, ви, схоже, додали розширення до свого запитання, вимагаючи додаткових матеріалів поза ним. На цьому етапі - особливо після отримання повної відповіді, яка фактично була частково визнана недійсною вашим запитом щодо додаткової поведінки - вам може бути краще задати нове запитання, а не розширювати свої вимоги. Якщо ви отримали винагороду за ці деталі, вам було б добре, задавши нове запитання, описуючи свою проблему, позначивши це питання для уваги модератора, щоб просити відшкодувати суму, а також опублікувати суму за новим питанням.
doppelgreener

2
Я повернув це питання до попереднього стану через повзучість масштабів. Зважаючи на час між додаванням баунті та редагуванням, що включало зміну обсягу, я припускаю, що ви хотіли отримати різні відповіді на це запитання, тому я залишу щедрість. Як запропонував @doppelgreener, я пропоную вам задати ще одне запитання щодо ваших нових вимог.
Олександр Вейланкорт

@AlexandreVaillancourt ой шкода , що я не знаю , що це , я просто додати опцію на моє запитання , тому що я не люблю ланцюг questions.thanks для зміни ...
Сейєд Morteza Kamali

Відповіді:


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 .

Щоб досягти цього ефекту, замість УФ-координат, використовуйте Полярні координати, а потім встановіть їх на текстуру шуму.

ПВ, як правило, викладаються по сітці як мода, як пікселі на екрані (X = ширина, Y = висота). Полярні координати, однак, використовують біт x і y по-різному. Один визначає, наскільки далеко від центру кола, а інший визначає градуси, від діапазону 0-1, залежно від того, що вам потрібно.

1600px-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


Метабол

то я додаю ефект метаболу з цієї статті : img


Посадка на рахунки

є більше ...

Якщо ви хочете повернути маску, щоб подивитися на камеру, ви можете використовувати дошку Bill :

 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"
}

Ще один підручник з трафаретом:

Left4Dead

Підручник з трафаретом


10
Це хороше пояснення того, як зробити маскування передньої стінки з огляду на те, що вона знаходиться перед персонажем гравця, але чи є спосіб автоматично застосувати цей шейдер лише до стін, що знаходяться перед геометрією персонажа?
пухнастий

10
@fluffy ви можете використовувати Raycast для визначення, коли персонаж знаходиться за стінами, а потім увімкніть Mask.
Сіє Мортеза Камалі
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.