Unity&Shader案例篇—膨胀效果

本文介绍如何在Unity中使用Shader实现动态光环效果。通过两个Pass分别渲染基础颜色与带有透明度混合的发光效果,并利用C#脚本控制发光强度随时间变化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原文:http://www.manew.com/thread-99195-1-1.html

一、前言

首先,来看一下效果图,如图所示:
这里写图片描述

这个效果要使得摄像机的Clear Flags为Solid Color模式,如果为其他模式可能会看不到外部那一圈光环。

二、实现原理

1、Shader部分:将需要使用两个Pass块,两个Pass块里输出的颜色不同,并最终使用透明度混合得到最后输出的像素颜色。

●第一个Pass块:这个Pass块顶点和片段程序都比较简单,代码如下:

Pass{
                Tags{ "LightMode" = "ForwardBase" }

                CGPROGRAM

#pragma vertex vert 
#pragma fragment frag


                 float4 _Color;

        float4 vert(float4 vertexPos : POSITION) : SV_POSITION{
                return mul(UNITY_MATRIX_MVP, vertexPos);
        }

                float4 frag(void) : COLOR{
                return _Color;
        }

        ENDCG
        }

只需将模型的顶点和预设的颜色输出就可以了。

●第二个Pass块:这个pass块相对复杂一点,顶点程序主要的计算内容就是顶点的法线方向和相机观察方向的向量,这两个的点积就是膨胀的强度Strength。通过指数函数缩放对Strength和透明度opacity进行计算就会得到膨胀的效果。计算的代码部分为:

float3 normalDirectionT = normalize(normalDirection);
                 float3 viewDirectionT = normalize(viewDirection);
                 float strength = abs(dot(viewDirectionT, normalDirectionT));
                 float opacity = pow(strength, _Strength);

而片段程序也是简单的输出最终的颜色就可以,完整的代码如下:

Pass{
                Tags{"LightMode" = "ForwardBase"        "Queue" = "Transparent"        "RenderType" = "Transparent"}
        //        Cull Front
                ZWrite Off

                Blend SrcAlpha OneMinusSrcAlpha

                CGPROGRAM

#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"

                 float4 _GlowColor;
                 float  _Strength;;
                 float _GlowRange;
        struct vInput {
                float4 vertex : POSITION;
                float4 normal : NORMAL;
        };

        struct v2f {
                float4 position : SV_POSITION;
                float4 col:COLOR;
        };

        v2f vert(vInput i) {
                v2f o;

                float4x4 modelMatrix = _Object2World;
                float4x4 modelMatrixInverse = _World2Object;

                float3 normalDirection = normalize(mul(i.normal, modelMatrixInverse)).xyz;
                float3 viewDirection = normalize(_WorldSpaceCameraPos - mul(modelMatrix, i.vertex).xyz);

                float4 pos = i.vertex + (i.normal * _GlowRange);

                o.position = mul(UNITY_MATRIX_MVP, pos);

                float3 normalDirectionT = normalize(normalDirection);
                float3 viewDirectionT = normalize(viewDirection);
                float strength = abs(dot(viewDirectionT, normalDirectionT));
                float opacity = pow(strength, _Strength);

                float4 col = float4(_GlowColor.xyz, opacity);

                o.col = col;

                return o;
        }

        float4 frag(v2f i) : COLOR{

                return i.col;
        }

                ENDCG
        }

在第二个Pass块中使用到了ZWrite Off命令,也即关闭遮挡,模型所有的面和通道都会被渲染,如果使用了ZWrite On命令,你会发现好像并没有什么变化,还是可以正常运行得到前面的效果。这是因为,Cull命令,默认的是Cull Back,即提出背面,不渲染模型的背面。如果使用Cull Front命令,即不渲染模型的前面,渲染的是模型的背面,得到的效果图如图所示,当然最终的选择就看你想要实现什么样的效果吧。
这里写图片描述

完整的Shader代码:

Shader "CgInUnity/Glow"
{
        Properties{
                _Color("Object's Color", Color) = (0, 1, 0, 1)
                _GlowColor("Glow's Color", Color) = (1, 0, 0, 0)
                _Strength("Glow Strength", Range(5.0, 1.0)) = 2.0
                _GlowRange("GlowRange",Range(0.1,1))=0.3
        }
                SubShader{
                Pass{
                Tags{ "LightMode" = "ForwardBase" }

                CGPROGRAM

#pragma vertex vert 
#pragma fragment frag


                 float4 _Color;

        float4 vert(float4 vertexPos : POSITION) : SV_POSITION{
                return mul(UNITY_MATRIX_MVP, vertexPos);
        }

                float4 frag(void) : COLOR{
                return _Color;
        }

        ENDCG
        }

                Pass{
                Tags{"LightMode" = "ForwardBase"        "Queue" = "Transparent"        "RenderType" = "Transparent"}
        //        Cull Front
                ZWrite Off

                Blend SrcAlpha OneMinusSrcAlpha

                CGPROGRAM

#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"

                 float4 _GlowColor;
                 float  _Strength;;
                 float _GlowRange;
        struct vInput {
                float4 vertex : POSITION;
                float4 normal : NORMAL;
        };

        struct v2f {
                float4 position : SV_POSITION;
                float4 col:COLOR;
        };

        v2f vert(vInput i) {
                v2f o;

                float4x4 modelMatrix = _Object2World;
                float4x4 modelMatrixInverse = _World2Object;

                float3 normalDirection = normalize(mul(i.normal, modelMatrixInverse)).xyz;
                float3 viewDirection = normalize(_WorldSpaceCameraPos - mul(modelMatrix, i.vertex).xyz);

                float4 pos = i.vertex + (i.normal * _GlowRange);

                o.position = mul(UNITY_MATRIX_MVP, pos);

                float3 normalDirectionT = normalize(normalDirection);
                float3 viewDirectionT = normalize(viewDirection);
                float strength = abs(dot(viewDirectionT, normalDirectionT));
                float opacity = pow(strength, _Strength);

                float4 col = float4(_GlowColor.xyz, opacity);

                o.col = col;

                return o;
        }

        float4 frag(v2f i) : COLOR{

                return i.col;
        }

                ENDCG
        }
        }
}

2、控制脚本部分C#代码:

using UnityEngine;
using System.Collections;

public class GlowControl : MonoBehaviour {
    private Material mat;
    private float value;
    [SerializeField]
    float speed=1;

        // Use this for initialization
        void Start () {
        mat = GetComponent<MeshRenderer>().sharedMaterial;

        }

        // Update is called once per frame
        void Update () {

        value = Mathf.PingPong(Time.time * speed, 5);
      ///  Debug.Log(value);
        mat.SetFloat("_Strength", value);


        }
}

就是一个简单控制强度随着时间变换的代码。

三、总结
这个小小的案例我们学习到了怎么使用多个Pass块来渲染同一个物体,以及在多个Pass块中使用透明度混合。我个人认为是一个非常不错的学习案例,不只是因为

它的代码部分非常简洁明了,更重要的是这个案例在很多应用中也是非常有用的。祝好好学习,慢慢变牛。


每天进步一点点。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值