zoukankan      html  css  js  c++  java
  • 蛋哥的学习笔记之-基于Unity的Shader编程:X-1 音乐水波特效

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

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

     
    一、要干些啥:
     
            很久很久没有写文档了,前段时间做了个个人很喜欢的,自认为比较原创的小特效,所以写个文档纪念下(本人特别喜欢音乐)
    思路其实很简单,首先用顶点着色器实现一般的水波特效,然后解析音频数据(我这里是用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种残忍死法,你永远都不知道玩家会怎么去玩游戏~~
  • 相关阅读:
    [YTU]_2917(Shape系列-3)
    c3p0-0.9.2.1.bin用于jdbc链接池
    C#加密解密DES字符串<转>
    Bechmarksql
    PHP中foreach有关引用的问题
    jqgrid 原理2
    jqggrid原理2
    jqgrid1原理
    JQgrid 入门
    搞开发真的是 坑太多了,
  • 原文地址:https://www.cnblogs.com/unity3d-Yang/p/4661711.html
Copyright © 2011-2022 走看看