蛋哥的学习笔记之-基于Unity的Shader编程:X-1 音乐水波特效
热度 13
一、要干些啥:
很久很久没有写文档了,前段时间做了个个人很喜欢的,自认为比较原创的小特效,所以写个文档纪念下 (本人特别喜欢音乐)
思路其实很简单,首先用顶点着色器实现一般的水波特效,然后解析音频数据(我这里是用listener获取的数据来解析的),然后划分出低音,中音和高音,最后用是Shader控制显示;然后再加上一些Cubemap啊,Phone反射啊什么的,让它看起来好看一些就OK料~
关于音频解析的,如果你有兴趣的话,还可以参考下这篇文章:
http://tips.hecomi.com/entry/2014/11/11/021147
是讲解如何用音频控制角色口型的,这样在做妹子唱歌动画的时候,就不用一帧一帧K动画鸟~~
二、码儿预览:
首先看下Shader码儿吧:(码儿中有部分模块和变量是没有用到的,有些是我直接拿以前写好的东西搬过来,有些是为了达到我想要的效果调调改改)
/////////////////////////////////////////////////////////////////////////////////////////////////////////
a、ShaderWaterWave.shader 文件: 用以控制水波的Shader
Shader "EggBrother/ShaderWaterWave"
{
Properties
{
_MainTex ("Base (RGB)", 2D) = "white" {}
_TintAmount ("Tint Amount", Range(0, 1)) = 0.5 // 色彩叠加强度
_ColorA ("Color A", Color) = (1, 1, 1, 1) // 第一个颜色
_ColorB ("Color B", Color) = (1, 1, 1, 1) // 第二个颜色
_Speed ("Wave Speed", Range(0.1, 80)) = 5 // 波浪速度
_Frequency ("Wave Frequency", Range(0, 5)) = 2 // 波浪频率
_Amplitude ("Wave Amplitude", Range(0, 1)) = 1 // 波浪振幅
_Displacement ("Displacement", Range(0, 1.0)) = 0.3 // 加入杂波的量
//_SpeedStrength ("Speed Strength", Vector3) = (1, 1, 1)
_Alpha ("Alpha Value", Range(0, 1)) = 0.5 // 透明度
// 用于blinnPhone反射
_SpecularColor ("Specular Color", Color) = (1, 1, 1, 1) // 高光颜色
_SpecPower ("Specular Power", Range(0.1, 100)) = 2 // 高光强度
// 用于cubemap反射
_Cubemap ("Cube Map", CUBE) = "" {} // cubemap
_ReflAmount ("Reflection Amount", Range(0.01, 1)) = 0.5 // 反射强度
// 用于纹理动画
_ScrollSpeedX ("Scroll Speed X", Range(-2, 2)) = -0.5 // X方向速度
_ScrollSpeedY ("Scroll Speed Y", Range(-2, 2)) = -0.5 // Y方向速度
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 300
CGPROGRAM
// 加载我们自定义的模块
#include "EggBrother/EggBrotherLightings.cginc"
#include "EggBrother/EggBrotherFunctions.cginc"
#pragma surface surf EGBlinnPhong vertex:vert alpha
//#pragma target 5.0
////////////////// 顶点处理 /////////////////
fixed _Speed;
fixed _Frequency;
fixed _Amplitude;
fixed _Displacement;
//float3 _SpeedStrength;
samplerCUBE _Cubemap;
fixed _ReflAmount;
fixed _ScrollSpeedX;
fixed _ScrollSpeedY;
struct Input
{
float2 uv_MainTex;
float3 vertColor; // 用以在surf函数中使用
float3 worldRefl;
};
void vert(inout appdata_full v, out Input o)
{
// 使用 DX11 的规则
UNITY_INITIALIZE_OUTPUT(Input, o);
// 根据振幅和频率计算变化值
float time = _Time * _Speed / 2;
float waveValueA = sin((v.vertex.x + v.vertex.z) * _Frequency + time * _Frequency) * _Amplitude;
// 修改顶点位置
v.vertex.xyz = float3(v.vertex.x, v.vertex.y + waveValueA, v.vertex.z);
// 修改顶点法线(用以光照计算)
v.normal = normalize(float3(v.normal.x + waveValueA, v.normal.y, v.normal.z));
// 顶点细分
//float d = tex2Dlod(_MainTex, float4(v.texcoord.xy,0,0)).r * _Displacement;
//v.vertex.xyz += v.normal * d;
// 输出最终颜色
o.vertColor = float3(waveValueA, waveValueA, waveValueA);
}
////////////////// surf 颜色处理 /////////////////
sampler2D _MainTex;
float _TintAmount;
float4 _ColorA;
float4 _ColorB;
float _Alpha;
void surf (Input IN, inout SurfaceOutput o)
{
float3 tintColor = lerp(_ColorA, _ColorB, IN.vertColor).rgb;
// 计算纹理动画
fixed2 scrolledUV = IN.uv_MainTex;
fixed xScrollValue = _ScrollSpeedX * _Time;
fixed yScrollValue = _ScrollSpeedY * _Time;
scrolledUV += fixed2(xScrollValue, yScrollValue);
half4 c = tex2D (_MainTex, scrolledUV);
// 获取Cubemap的uv信息
o.Emission = texCUBE(_Cubemap, IN.worldRefl).rgb * _ReflAmount;
o.Albedo = c.rgb * (tintColor * _TintAmount);
o.Specular = 0.2;
o.Gloss = 1.0;
o.Alpha = c.a * _Alpha;
}
ENDCG
}
FallBack "Diffuse"
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////
b、ShaderWaterBright.shader 文件: 用以控制屏幕特效(亮度、饱和度、对比度等),在这个demo里只使用了亮度控制
Shader "EggBrother/ShaderWaterBright"
{
Properties
{
_MainTex ("Base (RGB)", 2D) = "white" {}
_BlendTex ("Blend Texture", 2D) = "white" {} // 混合贴图
_Opacity ("Blend Opacity", Range(0, 1)) = 1 // 混合指数
_LuminosityAmount ("GrayScale Amount", Range(0.0, 1)) = 1.0 // 灰度值
_DepthPower ("Depth Power", Range(1, 5)) = 1 // 深度值
_BrightnessAmount ("Brightness Amount", Range(0.0, 1)) = 1.0 // 亮度
_SaturationAmount ("Satruation Amount", Range(0.0, 1)) = 1.0 // 饱和度
_ContrastAmount ("Contrast Amount", Range(0.0, 1)) = 1.0 // 对比度
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 200
Pass
{
CGPROGRAM
#pragma vertex vert_img
#pragma fragment frag
#pragma fragmentoption ARB_precision_hint_fastest
#include "UnityCG.cginc"
uniform sampler2D _MainTex;
uniform sampler2D _BlendTex; // 混合贴图
fixed _Opacity; // 混合指数
fixed _LuminosityAmount; // 灰度
fixed _DepthPower; // 深度
fixed _BrightnessAmount; // 亮度
fixed _SaturationAmount; // 饱和度
fixed _ContrastAmount; // 对比度
sampler2D _CameraDepthTexture;
// 根据亮度、饱和度、对比度计算颜色
float3 ContrastSaturationBrightness(float3 color, float brt, float sat, float con)
{
float AvgLumR = 0.5;
float AvgLumG = 0.5;
float AvgLumB = 0.5;
// 系数为CIE颜色匹配函数得来,为行业标准
float3 LuminanceCoeff = float3(0.2125, 0.7154, 0.0721);
// 根据亮度计算
float3 AvgLumin = float3(AvgLumR, AvgLumG, AvgLumB);
float3 brtColor = color * brt;
float intensityf = dot(brtColor, LuminanceCoeff);
float3 intensity = float3(intensityf, intensityf, intensityf);
// 根据饱和度计算
float3 satColor = lerp(intensity, brtColor, sat);
// 根据对比度计算
float3 conColor = lerp(AvgLumin, satColor, con);
// 返回最终结果
return conColor;
}
// 单个通道叠加
fixed OverlayBlendModeSingle(fixed basePixel, fixed blendPixel)
{
if(basePixel < 0.5)
{
return (2.0 * basePixel * blendPixel);
}
else
{
return (1.0 - 2.0 * (1.0 - basePixel) * (1.0 - blendPixel));
}
}
// 叠加混合
fixed4 OverlatBlendMode(fixed4 baseTexture, fixed4 blendPixel)
{
fixed4 finalTex = baseTexture;
finalTex.r = OverlayBlendModeSingle(baseTexture.r, blendPixel.r);
finalTex.g = OverlayBlendModeSingle(baseTexture.g, blendPixel.g);
finalTex.b = OverlayBlendModeSingle(baseTexture.b, blendPixel.b);
return finalTex;
}
fixed4 frag(v2f_img i) : Color
{
fixed4 renderTex = tex2D(_MainTex, i.uv);
// 根据灰度值计算
float luminosity = 0.299 * renderTex.r + 0.587 * renderTex.g + 0.114 * renderTex.b;
fixed4 finalColor = lerp(renderTex, luminosity, _LuminosityAmount);
// 根据深度值计算
float depth = UNITY_SAMPLE_DEPTH(tex2D(_CameraDepthTexture, i.uv.xy));
depth = pow(Linear01Depth(depth), _DepthPower);
finalColor = finalColor * depth;
// 根据从亮度,饱和度,对比度计算
finalColor.rgb = ContrastSaturationBrightness(finalColor.rgb,
_BrightnessAmount,
_SaturationAmount,
_ContrastAmount);
// 混合计算
fixed4 blendTex = tex2D(_BlendTex, i.uv);
// 乘法混合
//fixed4 finalTex = finalColor * blendTex; // 乘法混合
//fixed4 finalTex = finalColor + blendTex; // 加法混合
//fixed4 finalTex = 1.0 - (1.0 - finalColor) * (1.0 - blendTex); // 屏幕混合
fixed4 finalTex = OverlatBlendMode(finalColor, blendTex); // 叠加混合
finalTex = lerp(finalColor, finalTex, _Opacity);
return finalTex;
}
ENDCG
}
}
FallBack "Diffuse"
}
c、AudioEffect.cs 文件: 用以解析音频文件
using UnityEngine;
using System.Collections;
public class AudioEffect : MonoBehaviour {
public float[] spectrum = new float[1024]; // 监听器获取的音乐数据
public int resolution = 1024; // 音乐数据长度
public GameObject WaterWave; // 水波object
public float lowFreqThreshold = 14700; // 低音
public float midFreqThreshold = 29400; // 中音
public float highFreqThreshold = 44100; // 高音
public Transform[] audioCube = new Transform[10]; // 音乐柱子
public float[] freqThreshold = new float[10]; // 音频阈值
public float lowEnhance = 1f, midEnhance = 10f, highEnhance = 100f; //音效增强
private Material m_material; // 材质球
private AudioWaterEffect m_waterBright; // 控制屏幕特效
void Start()
{
m_material = WaterWave.transform.GetComponent().material;
m_waterBright = transform.GetComponent();
}
void Update()
{
spectrum = AudioListener.GetSpectrumData(resolution, 0, FFTWindow.BlackmanHarris);
int deltaFreq = AudioSettings.outputSampleRate / resolution;
// 控制音乐柱子
ControlAudioCubes(deltaFreq);
// 设置水波
ControlWaterWave(deltaFreq);
}
// 设置水波
public void ControlWaterWave(float deltaFreq)
{
float low = 0f, mid = 0f, high = 0f;
for (int i = 0; i < resolution; ++i)
{
float freq = deltaFreq * i;
if (freq <= lowFreqThreshold)
low += spectrum[i];
else if (freq <= midFreqThreshold)
mid += spectrum[i];
else if (freq <= highFreqThreshold)
high += spectrum[i];
}
low *= lowEnhance;
mid *= midEnhance;
high *= highEnhance;
// 设置水波的振幅
mid = mid / 5;
if (WaterWave != null)
{
// 根据中音设置振幅
if (mid > 0.8)
{
mid = 0.8f;
}
else if (mid < 0.1)
{
mid = 0.1f;
}
m_material.SetFloat("_Amplitude", mid);
// 设置亮度
m_waterBright.brightnessAmount = 0.6f + mid / 2;
if (m_waterBright.brightnessAmount > 0.7)
{
m_waterBright.brightnessAmount = 0.7f;
}
else if (m_waterBright.brightnessAmount < 0.6f)
{
m_waterBright.brightnessAmount = 0.6f;
}
}
}
// 控制音乐柱子
public void ControlAudioCubes(float deltaFreq)
{
float[] freqList = new float[10];
for (int i = 0; i < resolution; ++i)
{
float freq = deltaFreq * i;
if (freq <= freqThreshold[0])
{
freqList[0] += spectrum[i];
}
else if (freq <= freqThreshold[1])
{
freqList[1] += spectrum[i];
}
else if (freq <= freqThreshold[2])
{
freqList[2] += spectrum[i];
}
else if (freq <= freqThreshold[3])
{
freqList[3] += spectrum[i];
}
else if (freq <= freqThreshold[4])
{
freqList[4] += spectrum[i];
}
else if (freq <= freqThreshold[5])
{
freqList[5] += spectrum[i];
}
else if (freq <= freqThreshold[6])
{
freqList[6] += spectrum[i];
}
else if (freq <= freqThreshold[7])
{
freqList[7] += spectrum[i];
}
else if (freq <= freqThreshold[8])
{
freqList[8] += spectrum[i];
}
else if (freq <= freqThreshold[9])
{
freqList[9] += spectrum[i];
}
}
for (int i = 0; i < 10; i++)
{
audioCube[i].localScale = new Vector3(audioCube[i].localScale.x, freqList[i] * 2, audioCube[i].localScale.z);
}
}
private void elseif(bool p)
{
throw new System.NotImplementedException();
}
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////
d、AudioWaterEffect.cs 文件: 用以控制屏幕特效
using UnityEngine;
using System.Collections;
[ExecuteInEditMode] // 在没有运行的状态下也可以调试
public class AudioWaterEffect : MonoBehaviour
{
#region Variables
public Shader curShader;
public Texture2D blendTexture;
public float blendOpacity = 1.0f;
public float grayScaleAmount = 1.0f; // 灰度
public float depthPower = 1.0f; // 深度
public float brightnessAmount = 1.0f; // 亮度
public float saturationAmount = 1.0f; // 饱和度
public float contrastAmount = 1.0f; // 对比度
private Material m_curMaterial;
public AudioListener listener;
#endregion
// 检查材质
#region Properties
Material material
{
get
{
if (m_curMaterial == null)
{
m_curMaterial = new Material(curShader);
m_curMaterial.hideFlags = HideFlags.HideAndDontSave;
}
return m_curMaterial;
}
}
#endregion
// 检查平台是否支持特效
void Start ()
{
if (!SystemInfo.supportsImageEffects)
{
enabled = false;
return;
}
if (!curShader && !curShader.isSupported)
{
enabled = false;
}
}
// Unity内置的渲染图像函数
void OnRenderImage(RenderTexture sourceTexture, RenderTexture destTexture)
{
if (curShader != null)
{
// 设置混合纹理
material.SetTexture("_BlendTex", blendTexture);
material.SetFloat("_Opacity", blendOpacity);
// 设置灰度值
material.SetFloat("_LuminosityAmount", grayScaleAmount);
// 设置深度值
material.SetFloat("_DepthPower", depthPower);
// 设置亮度,饱和度,对比度
material.SetFloat("_BrightnessAmount", brightnessAmount);
material.SetFloat("_SaturationAmount", saturationAmount);
material.SetFloat("_ContrastAmount", contrastAmount);
Graphics.Blit(sourceTexture, destTexture, material);
}
else
{
Graphics.Blit(sourceTexture, destTexture);
}
}
// Update is called once per frame
void Update ()
{
// 设置开启深度模式
Camera.main.depthTextureMode = DepthTextureMode.Depth;
grayScaleAmount = Mathf.Clamp(grayScaleAmount, 0.0f, 1.0f);
depthPower = Mathf.Clamp(depthPower, 0.0f, 1.0f);
brightnessAmount = Mathf.Clamp(brightnessAmount, 0.0f, 5.0f);
saturationAmount = Mathf.Clamp(saturationAmount, 0.0f, 5.0f);
contrastAmount = Mathf.Clamp(contrastAmount, 0.0f, 5.0f);
}
void OnDisable()
{
if (m_curMaterial)
{
DestroyImmediate(m_curMaterial);
}
}
}
三、Unity里的设置:
由于代码比较多,就不一行一行说了,太麻烦鸟 自认为注释什么的,也写得挺明白的,如果有不懂的话,再问我吧~~
这里简单截下Unity里面文件放置的位置:
ShaderWaterWave放到水波的材质球上,里面需要一个材质贴图,直接用Unity里面自带的水波贴图就可以了
AudioEffect文件放到摄像机上:
AudioWaterEffect也放到摄像机上
最后场景里放置的物体
四、轻松一下:
《古墓丽影9》劳拉的35种残忍死法,你永远都不知道玩家会怎么去玩游戏~~
路过 |
鸡蛋 |
11
鲜花 |
握手 |
雷人 |