蛋哥的学习笔记之-基于Unity的Shader编程:X-1 音乐水波特效

蛋哥的学习笔记之-基于Unity的Shader编程:X-1 音乐水波特效

热度 13728 2015-7-11 23:34 |个人分类:蛋哥的学习笔记之-基于Unity的Shader编程音乐UnityShader水波ShaderShaderShaderShader

一、要干些啥:

很久很久没有写文档了,前段时间做了个个人很喜欢的,自认为比较原创的小特效,所以写个文档纪念下(本人特别喜欢音乐)

思路其实很简单,首先用顶点着色器实现一般的水波特效,然后解析音频数据(我这里是用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<MeshRenderer>().material;
        m_waterBright = transform.GetComponent<AudioWaterEffect>();
    }

    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


鲜花


握手

雷人
时间: 2024-10-25 05:43:28

蛋哥的学习笔记之-基于Unity的Shader编程:X-1 音乐水波特效的相关文章

嵌入式Linux学习笔记(基于S5PV210 TQ210)

基于S5PV210.TQ210平台. 本文更多的是教会大家如何学习! 包括如下内容: 1.前言 2.开发环境搭建 3.制作交叉编译器 4.裸机编程 4.1.汇编学习 4.2.S5PV210启动流程 4.3.点亮一个LED 4.4.串口 4.5.实现printf 4.6.时钟配置 4.7.重定位 4.8.DDR 4.9.NAND读写 4.11.LCD操作 5.移植u-boot(基于u-boot-2014.4版本) 5.1.概述 5.2.u-boot配置过程分析 5.3.u-boot编译过程分析 5

马哥Linux学习笔记之四——DNS

1.BIND:Berkeley Internet Name Domain DNS:Domian Name Service 域名解析 2. Http 3.PAM 插入式认证模块 4.SMTP/POP3/IMAP4:Mail Server 5.域名 www.baidu.com这是一个主机名(FQDN,Full Qualified Domain Name,完全限定域名),com是一个域名,baidu.com也是一个域名,域名是好多主机的集合. 域名解析起后面有一个数据库,解析就是一个查询的过程.域名解

nginx学习笔记之基于端口的虚拟主机基于主机名的虚拟主机root、alias、index配置

nginx学习笔记之基于端口的虚拟主机基于主机名的虚拟主机root.alias.index配置 实验环境: centos 测试节点IP:172.16.3.101 基于端口的虚拟主机: vim /etc/nginx/nginx.conf # 向里面的http {}里面加入如下内容   server { # server定义一个虚拟主机         listen 8080; # 监听本机所有IP端口8080         server_name www.test.com; # 虚拟主机名为:w

马哥Linux学习笔记之五——邮件服务

1.SMTP只负责将邮件发送到服务器,其他的,像身份认证检测邮件,都不能完成.(25/tcp) ESMTP:Extended SMTP POP3:Post Office Protocol IMAP4:Internet Mail Access Protocol 2.Open Relay:开放式中继 就是人家的邮件从这个邮件服务器过,你都帮忙转发,这样就会成为产生垃圾邮件的根源.所以都要关闭Open Relay 3.SASL,Simple Authintication Secure Layer,简单

马哥Linux学习笔记之三——加密

1.明文传输 ftp,http,smtp,telnet 2.机密性:plaintext-->转换规则-->ciphertext ciphertext-->转换规则-->plaintext 完整性:单项加密算法,提取数据特征码.输入一样,输出必然一样:雪崩效应,输入的微小改变,将会引起结果的巨大改变:无论原始数据是多少,结果大小都是相同的:不可逆,无法根据特征码还原原来的数据. 3.密钥 4.对称加密:有加密算法,有密钥 5.密钥交换算法 IKE(Internet Key Excha

马哥Linux学习笔记之二——网络

1.MAC(Media Access Control,介质访问控制) 是解决底层数据通信冲突的解决方案.因此,给每一个接入这样网络的主机一个id标示符,这个id标示符就叫做MAC地址. 2.CSMA/CD(Carrier Sense Multipath Access Collision Detection,载波侦听多路访问冲突检测),线型网络 以太网Ethernet最核心的标志. 3.Token-Ring(IBM)技术 环形网络解决冲突的方案.但是由于IBM的专利限制没能推广开来. 4.星形网络

马哥Linux学习笔记之一

1.在执行脚本的时候可以用./或者. ./(点空格点斜杠)来执行脚本 ./ 表示启动了另一个Shell去执行脚本(另一个进程),所以点斜杠执行脚本时,设置的环境变量会随着进程的退出而结束,其中的环境变量设置对当前Shell不起作用. . ./相当于source ./执行脚本,source是执行脚本当中的命令,也就是说在当前进程中执行命令,所以其中的环境变量的设置会对当前Shell其作用. 2. 读取硬盘上的文件 读取硬盘上的文件先要将文件读取到内存上,再将其在内存上地址交给进程,这样进程就可以读

nodejs学习笔记(基于v7.4.0)

nodejs学习笔记 一.buffer: 全局对象(单例   Buffer.from   Buffer.alloc   Buffer.allocUnsafe ) 编码类型 描述 ascii 仅仅用于7位ascall数据编码,速度快,如果设置了将会剥离高位 utf8 多字节的编码的Unicode字符,网页文档大部分默认都为它. utf16le 小端编码的Unicode字符,2或者4个字节 ucs2 utf16le的别名 base64 Base64是网络上最常见的用于传输8Bit字节代码的编码方式之

Shader编程学习笔记(八)—— Surface Shader 2

Surface Shader 上一小结主要了解了Surface Shader使用了“#pragma surface surf Standard fullforwardshadows”指令的意义,这一小节主要了解“surf”surface函数. void surf (Input IN, inout SurfaceOutputStandard o) { // Albedo comes from a texture tinted by color fixed4 c = tex2D (_MainTex,