Làm thế nào tôi có thể tạo ra một cái nhìn thấy đằng sau hiệu ứng tường?


103

Divinity: Original Sin 2 có hệ thống nhìn xuyên thấu tuyệt đẹp. Khi tôi đi đằng sau bức tường, một mặt nạ giật gân sẽ xuất hiện, và khi tôi di chuyển xung quanh trò chơi, nó sẽ thay đổi. Nó giống như một shader hòa tan và có hiệu ứng metaball.

Làm cách nào tôi có thể tái tạo hiệu ứng này, tạo mặt nạ giật gân động khi người chơi đi sau bức tường?

Bạn có thể thấy hiệu ứng mong muốn trong chuyển động thông qua video YouTube này .

Hình ảnh, tưởng tượng


3
Xin chào, có vẻ như bạn đã thêm phạm vi creep vào câu hỏi của mình bằng cách yêu cầu các công cụ bổ sung ngoài nó. Ở giai đoạn này - đặc biệt là sau khi nó có câu trả lời đầy đủ, điều này thực sự bị vô hiệu hóa một phần bởi yêu cầu của bạn về hành vi bổ sung - bạn có thể tốt hơn nên hỏi một câu hỏi mới thay vì mở rộng yêu cầu của bạn. Nếu tiền thưởng dành cho những chi tiết đó, bạn sẽ làm tốt bằng cách đặt câu hỏi mới, mô tả vấn đề của bạn, gắn cờ câu hỏi này để người điều hành chú ý để yêu cầu hoàn lại tiền thưởng và đăng tiền thưởng cho câu hỏi mới.
doppelgreener

2
Tôi quay lại câu hỏi này về trạng thái trước đó vì phạm vi creep. Trong khoảng thời gian giữa việc thêm tiền thưởng và chỉnh sửa bao gồm thay đổi phạm vi, tôi giả sử bạn muốn nhận được câu trả lời khác nhau cho câu hỏi này, vì vậy tôi sẽ bỏ tiền thưởng. Theo đề xuất của @doppelgreener, tôi khuyên bạn nên hỏi một câu hỏi khác với các yêu cầu mới của bạn.
Alexandre Vaillancourt

@AlexandreVaillancourt oh xin lỗi tôi không biết điều đó, tôi chỉ thêm một tùy chọn cho câu hỏi của mình vì tôi không thích câu hỏi chuỗi.
Cảm ơn

Câu trả lời:


163

Mặt nạ

Để tạo hiệu ứng này, bạn có thể che các đối tượng bằng cách sử dụng Bộ đệm stpson.

bộ đệm stpson là bộ đệm cho mục đích chung cho phép bạn lưu trữ một số nguyên 8 bit bổ sung (nghĩa là một giá trị từ 0-255) cho mỗi pixel được vẽ trên màn hình. Giống như các shader tính toán các giá trị RGB để xác định màu của các pixel trên màn hình và các giá trị z cho độ sâu của các pixel đó được vẽ vào bộ đệm độ sâu, chúng cũng có thể viết một giá trị tùy ý cho từng pixel đó vào bộ đệm stpson. Những giá trị stpson sau đó có thể được truy vấn và so sánh bằng các lần đổ bóng tiếp theo để xác định cách các pixel nên được tổng hợp trên màn hình.

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

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

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

Hình ảnh, tưởng tượng

Mặt nạ stent:

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

Tường stent:

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

Hãy thực hiện.

sử dụng này như mặt nạ:

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

sử dụng điều này như bức tường:

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

Phân tích hiệu quả

Nếu bạn muốn có một kết cấu thủ tục , bạn cần một số tiếng ồn.

Hình ảnh, tưởng tượng bạn có thể thấy shader này trong ShaderToy .

Để tạo hiệu ứng này, thay vì sử dụng Tọa độ UV, hãy sử dụng Tọa độ cực sau đó đặt thành kết cấu nhiễu.

Các Uv thường được bố trí theo dạng lưới như thời trang, như màn hình pixel na (X = width, Y = height). Các tọa độ cực, tuy nhiên, sử dụng bit x và ya khác nhau. Người ta xác định khoảng cách từ tâm của vòng tròn là bao xa và người kia xác định độ, từ phạm vi 0-1, tùy thuộc vào những gì bạn cần.

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

một giải pháp khác là sử dụng tiếng ồn worley:

2018-01-05_8-16-16

bạn có thể thấy shader này trong ShaderToy


Metaball

sau đó tôi thêm hiệu ứng metaball từ bài viết này : img


Nội trú hóa đơn

có nhiều...

Nếu bạn muốn xoay mặt nạ, để nhìn vào máy ảnh của mình, bạn có thể sử dụng bảng 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));

đây là mặt nạ với nội trú Bill:

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

Kết quả cuối cùng:

2018-01-04_20-18-39

nguồn có sẵn: https://github.com/smkplus/Divinity-Origin-Sin-2


Liên kết hữu ích

Tôi tìm thấy một hướng dẫn tốt thực hiện hiệu ứng này bằng cách làm tan biến thế giới:

Hình ảnh1

Hòa tan thế giới Phần 1

Hòa tan thế giới Phần 2

Hình ảnh, tưởng tượng

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

Một hướng dẫn khác về stprint:

Trái4Dead

Hướng dẫn stent


10
Đây là một lời giải thích tốt về cách làm mặt nạ của bức tường phía trước cho kiến ​​thức về nó ở phía trước nhân vật người chơi, nhưng có cách nào để tự động áp dụng shader này cho các bức tường phía trước hình học của nhân vật không?
lông mịn

10
@fluffy bạn có thể sử dụng Raycast để phát hiện khi nhân vật ở phía sau bức tường, sau đó bật Mask.
Seyed Morteza Kamali
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.