Water bottle effect production


foreword

提示:这里可以添加本文要记录的大概内容:

This sharing is mainly about the water bottle effect, and the ideas are borrowed from https://www.patreon.com/posts/quick-game-art-18245226 This link, but some content is difficult to understand, so I plan to use my own ideas to realize it


提示:以下是本篇文章正文内容,下面案例可供参考

1. Realize the effect

insert image description here

Effectiveness analysis

1. Capsule model, you can modify the shader attribute to modify the height of the water surface of the water bottle.
2. The thickness of the water surface, you can modify the shader attribute to modify the horizontal thickness. 3.
The color of the water bottle surface, you can modify the shader attribute to modify the color of the
horizontal surface. Up and down reversal
5. Fresnel edge effect, strengthen the level
6. Edge expansion, increase the shape

Implementation process

1. Expand the edge and increase the shape

The first is the edge effect. In order to highlight the horizontal shape, one of our Passes is used to achieve the edge effect. Here, the vertices are directly used to expand along the normal direction, and the Fennel effect is added. As our first pass, the depth writing is turned off here to avoid the failure of the depth test of the second pass. The following is the effect.
insert image description here

边缘效果代码如下:
```c
     Pass
        {
            ZWrite Off 
            Cull Back
            Blend SrcAlpha OneMinusSrcAlpha
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag


            #include "UnityCG.cginc"

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

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
                float4 world_pos : TEXCOORD1  ;
                float3 world_normal:TEXCOORD2 ;
            };

            float _EdgePower;
            float _DimPow;

            v2f vert (appdata v)
            {
                v2f o;
                v.vertex.xyz += v.normal * _EdgePower;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.world_pos = mul(unity_ObjectToWorld, v.vertex);
                o.world_normal = UnityObjectToWorldNormal(v.normal);
                return o;
            }

            fixed4 frag (v2f i, fixed facing : VFACE ) : SV_Target
            {
                float3 world_view_dir = normalize(UnityWorldSpaceViewDir(i.world_pos));
                float rim = 1 - pow(saturate(dot(i.world_normal, world_view_dir)), _DimPow);
                return fixed4(1,1,1, rim);
            }
            ENDCG
        }

Capsule model, you can modify the shader attribute to modify the water surface height of the water bottle

Since our horizontal plane has to adapt to the various angles of the water bottle rotation to maintain balance, we need to use the world space to calculate here, and to avoid the world space from affecting the level calculation, we need to switch the center point of the object space to the world space, and then use the world The center position of the space is subtracted from the vertex position of the world space, so that it is not affected by the world space, and the horizontal height is calculated according to the difference. Subtract a control variable from the obtained difference to get our alpha value. We modify the value of the transparent channel to remove the effect. Finally, AlphaToMask On must be added to the Pass mark to remove the transparent part. In the above link, I saw that he directly multiplies the object->world matrix by the xyz of the object space vertex. I don’t quite understand here. The object->world matrix is ​​not a 4x4 matrix. How can it be multiplied by a 3 dimensional coordinates. And the calculated coordinates are not affected by the world space. I find it very strange. If anyone knows, please tell me.
insert image description here
The edge effect code is as follows:

    fixed4 frag (v2f i, fixed facing : VFACE ) : SV_Target
            {
    
                    
                float4 world_center = mul(unity_ObjectToWorld, float4(0, 0, 0,  1));
                float4 world_pos = i.world_pos;
                float4 cache_output1 = world_center - world_pos;
                float cache_output2 = 1 - _Height;
                float mulTime2 = _Time.y * _Speed;           
                float alpha = clamp((cache_output1.y - cache_output2 )/ _Falloff, 0 , 1 ) ;
               
                return fixed4(_Color.rgb , alpha);
            }

The thickness of the water surface, the shader property can be modified to modify the horizontal thickness

For the horizontal thickness, we directly call the step() function. According to the difference calculated above, we subtract a horizontal thickness control variable to get a result that is either 0 or 1, and then make a gradient between Color1 and Color based on this result. I think the functions step() and smoothstep() are very useful when making gradients. One is a non-transitional gradient and the other is a transitional gradient.
insert image description here
The edge effect code is as follows:

    float4 world_center = mul(unity_ObjectToWorld, float4(0, 0, 0,  1));
                float4 world_pos = i.world_pos;
                float4 cache_output1 = world_center - world_pos;
                float cache_output2 = 1 - _Height;
                float alpha = clamp((cache_output1.y - cache_output2 )/ _Falloff, 0 , 1 ) ;
                float cache_output3 = step(cache_output1.y - _Width, cache_output2);
                float4 cache_output4 = lerp(_Color, _Color3, cache_output3);               
                return fixed4(cache_output4.rgb , alpha);

Water bottle surface color, you can modify the shader attribute to modify the horizontal plane color, Fresnel edge effect, strengthen the level

For the horizontal plane color we use the fixed facing: VFACE field, which indicates whether the rendered face is facing the camera, for the fragment shader. At the same time, we also added the Finnier effect

The water bottle satisfies the physical effect, can swing left and right, reverse up and down

I mainly use the ideas in the link for the realization of this method. First swaying side to side, we need to get velocity and force decay weights. I made a simplified version here. The link also takes into account the rotation value, and my side directly considers speed and weakening. The speed is directly obtained by subtracting the coordinates of the previous frame from the coordinates of the current frame to obtain the speed, and the weakening is obtained by making a difference between the speed value and 0 according to the time coefficient to obtain the weakening weight. And integrate it into _WobbleX, _WobbleZ The weights in the two directions are passed to the shader for calculation.
insert image description here

The following is the complete code

shader part

Shader "Unlit/Test14"
{
    
    
    Properties
    {
    
    
        _Height("Height", Float) = 0.1
        _Falloff("Falloff", Float) = 0.1
        _Color("Color", Color) = (1, 0, 0, 0)
        _Color2("Color", Color) = (0, 0, 0, 0)
        _Color3("Color", Color) = (0, 1, 0, 0)
        _Speed("Speed", Float) = 0
        _Size("Size", Float) = 0.1
        _EdgePower("EdgePower", Range(0, 1)) = 0.1
        _DimPow("DimPower", Float) = 1
        _RimColor("RimColor", Color) = (1, 1, 1, 1)
        [HideInInspector] _WobbleX ("WobbleX", Range(-1,1)) = 0.0
        [HideInInspector] _WobbleZ ("WobbleZ", Range(-1,1)) = 0.0
    }
    SubShader
    {
    
    
        Tags {
    
     "RenderType"="Transparent" "Queue"="Transparent"}
        LOD 100
        Pass
        {
    
    
            ZWrite Off 
            Cull Back
            Blend SrcAlpha OneMinusSrcAlpha
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag


            #include "UnityCG.cginc"

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

            struct v2f
            {
    
    
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
                float4 world_pos : TEXCOORD1  ;
                float3 world_normal:TEXCOORD2 ;
            };

            float _EdgePower;
            float _DimPow;

            v2f vert (appdata v)
            {
    
    
                v2f o;
                v.vertex.xyz += v.normal * _EdgePower;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.world_pos = mul(unity_ObjectToWorld, v.vertex);
                o.world_normal = UnityObjectToWorldNormal(v.normal);
                return o;
            }

            fixed4 frag (v2f i, fixed facing : VFACE ) : SV_Target
            {
    
    
                float3 world_view_dir = normalize(UnityWorldSpaceViewDir(i.world_pos));
                float rim = 1 - pow(saturate(dot(i.world_normal, world_view_dir)), _DimPow);
                return fixed4(1,1,1, rim);
            }
            ENDCG
        }
        Pass
        {
    
    

            AlphaToMask On
            Cull Off
            Blend SrcAlpha OneMinusSrcAlpha
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            // make fog work
            #pragma multi_compile_fog

            #include "UnityCG.cginc"

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

            struct v2f
            {
    
    
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
                float4 world_pos : TEXCOORD1  ;
                float3 world_normal: TEXCOORD2 ;
            };

            float _Height;
            float _Falloff;
            float4 _Color;
            float4 _Color2;
            float4 _Color3;
            float _WobbleX, _WobbleZ;
            float _Speed;
            float4 _RimColor;
            float _Size;
            float _DimPow;
            v2f vert (appdata v)
            {
    
    
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.world_pos = mul(unity_ObjectToWorld, v.vertex);
                o.world_normal = UnityObjectToWorldNormal(v.normal);
                return o;
            }

            fixed4 frag (v2f i, fixed facing : VFACE ) : SV_Target
            {
    
    
                float3 world_view_dir = normalize(UnityWorldSpaceViewDir(i.world_pos));
                float rim = 1 - pow(saturate(dot(i.world_normal, world_view_dir)), _DimPow);
               

                facing = facing * 0.5 + 0.5;
                 float4 rim_color = rim * _RimColor * facing;
                float4 world_center = mul(unity_ObjectToWorld, float4(0, 0, 0,  1));
                float4 world_pos = i.world_pos;
                float4 cache_output1 = world_center - world_pos;
                float cache_output2 = 1 - _Height;
                float mulTime2 = _Time.y * _Speed;
                float cache_output_3 = (( _WobbleX * cache_output1.x * sin( mulTime2 ) * _Size ) 
                    +( _WobbleZ * cache_output1.z * sin( mulTime2 ) * _Size ) 
                    + cache_output1.y);
                float alpha = clamp((cache_output_3 - cache_output2 )/ _Falloff, 0 , 1 ) ;
                float cache_output3 = step(cache_output_3 - 0.1, cache_output2);
                float4 cache_output4 = lerp(_Color + rim_color, _Color3, cache_output3);
                float4 finalcolor = lerp(_Color2 , cache_output4, facing);
                return fixed4(finalcolor.rgb + rim_color.rgb , alpha);
            }
            ENDCG
        }

    }
}

cs part

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class MyWobble : MonoBehaviour
{
    
    
    Renderer rend;
    Vector3 lastPos;
    Vector3 velocity;
    public float MaxWobble = 0.03f;
    public float WobbleSpeed = 1f;
    public float Recovery = 1f;
    float wobbleAmountX;
    float wobbleAmountZ;
    float wobbleAmountToAddX;
    float wobbleAmountToAddZ;
    float pulse;
    float time = 0.5f;
    // Start is called before the first frame update
    void Start()
    {
    
    
        rend = GetComponent<Renderer>();
    }

    // Update is called once per frame
    void Update()
    {
    
    
        time += Time.deltaTime;
        // decrease wobble over time
        wobbleAmountToAddX = Mathf.Lerp(wobbleAmountToAddX, 0, Time.deltaTime * (Recovery));
        wobbleAmountToAddZ = Mathf.Lerp(wobbleAmountToAddZ, 0, Time.deltaTime * (Recovery));

        // make a sine wave of the decreasing wobble
        //pulse = 2 * Mathf.PI * WobbleSpeed;
        //wobbleAmountX = wobbleAmountToAddX * Mathf.Sin(pulse * time);
        //wobbleAmountZ = wobbleAmountToAddZ * Mathf.Sin(pulse * time);

        // send it to the shader
        rend.material.SetFloat("_WobbleX", wobbleAmountToAddX);
        rend.material.SetFloat("_WobbleZ", wobbleAmountToAddZ);
        //print(wobbleAmountX + "--------------" +wobbleAmountZ);
        // velocity
        velocity = (lastPos - transform.position) / Time.deltaTime;
        print(velocity);


        // add clamped velocity to wobble
        wobbleAmountToAddX += Mathf.Clamp((velocity.x) * MaxWobble, -MaxWobble, MaxWobble);
        wobbleAmountToAddZ += Mathf.Clamp((velocity.z ) * MaxWobble, -MaxWobble, MaxWobble);
         
        // keep last position
        lastPos = transform.position;
      
    }
}


Summarize

The above is the process of making water bottles, welcome to discuss. Especially in the above link, directly multiply the object->world matrix by the xyz of the object space vertex. I don’t quite understand here. The object->world matrix is ​​not a 4x4 matrix. How can it be multiplied by a 3-dimensional coordinate? Woolen cloth. Moreover, the calculated coordinates are not affected by the world space, and it just works, which is amazing.

Guess you like

Origin blog.csdn.net/weixin_39289457/article/details/124262762