我是靠谱客的博主 矮小蜗牛,最近开发中收集的这篇文章主要介绍水瓶效果制作前言一、实现效果效果分析总结,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述


前言

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

本次分享主要为水瓶效果,思路借鉴于https://www.patreon.com/posts/quick-game-art-18245226 该链接,不过部分内容较难理解,所以打算使用自己的思路实现一下


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

一、实现效果

在这里插入图片描述

效果分析

1.胶囊体模型,可修改shader属性修改水瓶水面高度
2.水面厚度,可修改shader属性修改水平厚度
3.水瓶面颜色,可修改shader属性修改水平面颜色
4.水瓶满足物理效果,可左右摇曳,上下反转
5.菲涅尔边缘效果,加强层级
6.边缘拓展,增加形体

实现流程

1.边缘拓展,增加形体

首先是边缘效果,为了突出水平的的形状,我们的一个Pass用于边缘效果实现。这里直接使用了顶点沿法线方向拓展,并且追加了菲尼尔效果,作为我们第一个Pass这里关闭深度写入,避免第二个Pass深度测试失败。以下是实现效果。
在这里插入图片描述

边缘效果代码如下:
```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
        }

胶囊体模型,可修改shader属性修改水瓶水面高度

由于我们的水平面要要适应水瓶旋转的各个角度保持平衡,所以我们这里需要使用世界空间来计算,又避免世界空间影响水平高度计算,我们需要将对象空间的中心点切换到世界空间,再用世界空间的中心位置减去世界空间的顶点位置,这样既不受世界空间的影响,也根据差值计算水平高度。得到的差值再减去一个控制变量,得到我们alpha值,我们就是通过修改透明通道的值来做剔除效果,最后Pass标记上当然要加上 AlphaToMask On 剔除透明部分。在上面的链接里面我看到他直接用object->world的矩阵乘以对象空间顶点的xyz,这里我是不太理解的,object->world矩阵不是一个4x4的矩阵,怎么能乘以一个3维坐标呢。而且算出来的坐标也不受世界空间影响,我是觉得很奇怪的,有谁知道可以跟我讲一下。
在这里插入图片描述
边缘效果代码如下:

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

水面厚度,可修改shader属性修改水平厚度

水平厚度我们直接调用step()函数,根据以上计算的差值,我们再减去一个水平厚度控制变量,得到一个非0即1的结果,再根据这个结果在Color1,以及Color之间做渐变。step()和smoothstep()这个函数我觉得在做渐变时候很管用,一个是无过渡式渐变,一个是过渡式渐变。
在这里插入图片描述
边缘效果代码如下:

    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);

水瓶面颜色,可修改shader属性修改水平面颜色,菲涅尔边缘效果,加强层级

水平面颜色我们使用了fixed facing : VFACE 字段,该字段表示被渲染的面是否朝向摄像机,用于片段着色器。同时我们也追加菲尼尔效果

水瓶满足物理效果,可左右摇曳,上下反转

这个的实现方式我主要借鉴链接里的思路。首先左右摇曳,我们需要得到速度以及力的衰弱权重。我这里做了简化版。链接还考虑了旋转值,我这边就直接考量速度与衰弱。速度就直接拿上一帧的坐标减去当前帧的坐标得到速度,衰弱就根据时间系数在速度值与0之间做差值得到衰弱权重。并将其整合成_WobbleX,_WobbleZ两个方向上的权重传入shader计算。
在这里插入图片描述

以下是完整代码

shader部分

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部分

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


总结

以上就是水瓶制作的流程,欢迎讨论。特别是在上面的链接里面直接用object->world的矩阵乘以对象空间顶点的xyz,这里我是不太理解的,object->world矩阵不是一个4x4的矩阵,怎么能乘以一个3维坐标呢。而且算出来的坐标也不受世界空间影响,而且刚好能用,神奇。

最后

以上就是矮小蜗牛为你收集整理的水瓶效果制作前言一、实现效果效果分析总结的全部内容,希望文章能够帮你解决水瓶效果制作前言一、实现效果效果分析总结所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(63)

评论列表共有 0 条评论

立即
投稿
返回
顶部