效果
最近要做个产品展示的案例,突然想到之前看到的一个汽车商业案例,点击不同的颜色车漆很顺滑的过渡到目标颜色,非常炫酷,于是就想着实现一下,废话不多说,先来看效果。
实现过程
要实现这种效果肯定得整shader,我对shader是一窍不通,没办法只能去问chatgpt,给出了这样的代码;
Shader "Custom/GradientShader"
{
Properties
{
_Center ("Center", Vector) = (0,0,0,0)
_Color1 ("Color 1", Color) = (1,1,1,1)
_Color2 ("Color 2", Color) = (0,0,0,1)
_Radius ("Radius", Float) = 0.0
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 200
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;
};
float4 _Center;
fixed4 _Color1;
fixed4 _Color2;
float _Radius;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
return o;
}
fixed4 frag (v2f i) : SV_Target
{
float dist = distance(i.uv, _Center.xy);
float t = smoothstep(0.0, 1.0, dist / _Radius);
return lerp(_Color1, _Color2, t);
}
ENDCG
}
}
}
控制的脚本如下;
using UnityEngine;
public class GradientAnimation : MonoBehaviour
{
public Material material;
public Color color1 = Color.white;
public Color color2 = Color.black;
public float duration = 5.0f;
private Vector2 center;
private float time;
void Start()
{
// 随机生成起始位置
center = new Vector2(Random.Range(0.0f, 1.0f), Random.Range(0.0f, 1.0f));
material.SetVector("_Center", new Vector4(center.x, center.y, 0, 0));
material.SetColor("_Color1", color1);
material.SetColor("_Color2", color2);
material.SetFloat("_Radius", 0.0f); // 初始半径为0
}
void Update()
{
time += Time.deltaTime;
float radius = Mathf.Lerp(0.0f, 1.414f, time / duration); // 在duration时间内线性插值
material.SetFloat("_Radius", radius);
// 确保半径不会超过最大值
if (time >= duration)
{
material.SetFloat("_Radius", 1.414f); // 最大半径sqrt(2) ≈ 1.414, 覆盖整个纹理
}
}
}
我试了下,可以实现,但是效果不好。这个shader一些基本的像金属度啥的都没有,只是纯色,效果很一般,不是我想要的结果。
没办法,接着查。我想着shader是不是可以像C#那样继承某个shader的特性,查了一下,不行,放弃。
后来了解到可以通过shader graph很快创建一个shader,我就想是不是可以在shader graph里实现一个基础shader,再把代码里的内容转成shader graph里的元素加在一起不就可以了吗。正好在b站上看到一个基础shader的制作教程,说干就干。
基础shader大家可以看这个视频:
2022版Unity Shader Graph教程01-1 Subgraph 超简单ShaderGraph入门
接下来讲讲如何在shader graph里如何实现,基础的不讲了,自己看视频。
制作shader graph
添加属性
先添加四个属性;我这里还添加了个NoiseScale,用于控制噪声的缩放。
获取UV坐标
创建一个“UV”节点,用来获取纹理坐标;
计算距离
创建一个Subtract
节点,将UV
节点的输出连接到Subtract
节点的A
输入。
在Blackboard
上将Center
属性拖到节点编辑器中,连接到Subtract
节点的B
输入。
创建一个Length
节点,将Subtract
节点的输出连接到Length
节点的输入。
添加噪声
创建一个Simple Noise
节点,用于生成噪声纹理。
创建一个Multiply
节点,将Length
节点的输出连接到Multiply
节点的A
输入。
在Blackboard
上将NoiseScale
属性拖到节点编辑器中,连接到Multiply
节点的B
输入。
将Multiply
节点的输出连接到Simple Noise
节点的UV
输入。
混合距离和噪声
创建一个Add
节点,将Length
节点的输出连接到Add
节点的A
输入。
将Simple Noise
节点的输出连接到Add
节点的B
输入。
创建一个Divide
节点,将Add
节点的输出连接到Divide
节点的A
输入。
在Blackboard
上将Radius
属性拖到节点编辑器中,连接到Divide
节点的B
输入。
创建一个Smoothstep
节点,将Divide
节点的输出连接到Smoothstep
节点的In
输入。
创建一个Lerp
节点,在Blackboard
上将MainColor
和TargetColor
属性分别拖到节点编辑器中,连接到Lerp
节点的A
和B
输入。
将Smoothstep
节点的输出连接到Lerp
节点的T
输入。
连接输出
将Lerp
节点的输出连接到PBR Master
节点的Base Color
输入。
总结
shader graph大家按这个流程基本上能搞定的,如果实在搞不定我这边也上传上去了,可以在这个地址下载:Unity中从随机位置逐渐扩散的颜色渐变动画效果的shaderGraph。
脚本控制渐变
接下来就简单了,脚本控制Radius就能实现渐变效果呢,这里贴上关键代码,具体怎么实现各位自用发挥吧。
using System.Collections.Generic;
using UnityEngine;
public class Vehicle : MonoBehaviour
{
[SerializeField] private List<MeshRenderer> shells;
[SerializeField] private float radiusMax = 5f;
[SerializeField] private float duration = 4f;
private bool changeColor = false;
private float timer = 0;
[SerializeField]private Color currentColor;
private void Start()
{
for (int i = 0; i < shells.Count; i++)
{
shells[i].material.SetColor("_MainColor", currentColor);
}
}
private void Update()
{
if (changeColor)
{
timer += Time.deltaTime;
float radius = Mathf.Lerp(0.0f,radiusMax,timer/duration);
for (int i = 0; i < shells.Count; i++)
{
shells[i].material.SetFloat("_Radius", radius);
}
if (radius>=radiusMax)
{
timer = 0;
changeColor = false;
}
}
}
/// <summary>
/// 改变颜色
/// </summary>
/// <param name="color"></param>
public void ChangeColor(Color color)
{
if (changeColor) return;
for (int i = 0; i < shells.Count; i++)
{
Vector2 center = new Vector2(Random.Range(0.0f, 1.0f), Random.Range(0.0f, 1.0f));
shells[i].material.SetVector("_Center", new Vector4(center.x, center.y, 0, 0));
shells[i].material.SetColor("_MainColor", currentColor);
shells[i].material.SetColor("_TargetColor", color);
shells[i].material.SetFloat("_Radius", 0.0f);
}
currentColor = color;
changeColor = true;
}
}
ok!效果是实现了,但是跟我当时看到的那个效果还是有差距的,可惜找不到那个案例了,以后有时间再优化吧,就这样吧!有用的话一键三连吧!
转载请注明出处: