欢迎访问设·集合!

设·集合

您现在的位置是:首页 > 游戏制作 > unity

Unity音乐可视化效果研究

设·集合小编 发布时间:2023-03-02 11:38:52 266次最后更新:2024-11-11 18:02:53

两年前,为项目做演示视频的时候用了一个AE模板弄了一个音乐可视化效果,当时也没想过会要做到项目里面。

后来项目落地的时候用了另外一个效果。前一段时间在学习图形学,突然想起这件事,对于那个未能完成的效果有点不死心,于是重新研究了一下,大致完成了这个效果:

先说下音乐可视化的原理。

原理:

  • 通过unity的内置函数采样音乐频域的数值,我们项目中因为是流媒体传输,所以方法有所不同,总之就是通过一系列代码获取音乐频域数值(关于音乐的频域,时域这些有一大堆的理论,还涉及到非常复杂的算法,有兴趣的可以去百度研究下)。
  • 将频域的数值记录下来,写入一张256像素宽度的贴图,这里参考的是某位大神的脚本,但是因为时间久远,找不到原文了,所以链接也没法给出来。
  • 写一个shader,采样音乐图,根据获取的数值计算想要的效果。


步骤:

  • 在场景中创建一个plane,挂上一个AudioSource 组件,选一首你喜欢的音乐,设置一下播放和循环:
  • 创建脚本,采样音乐的数值,并记录到一张贴图里,并且每帧更新。然后将脚本也挂在plane上面。这里要注意下对更新数值的延迟处理,会让最终的效果有个连续增长和连续减少的效果,如果不做这一步,就会让效果不够连续,会卡卡的感觉。

脚本代码:

using UnityEngine;
using System.Collections;
public class MusicTexture: MonoBehaviour {
	public Material material;
	Range(0.0005f,0.5f)
	public float delay = 0.0166f;
	public float multiplyer = 1.0f;
	HideInInspector
	System.NonSerialized
	public float spactrumDataDelay;
        HideInInspector
        System.NonSerialized
        public Texture2D dataTexture;
        public FilterMode filterMode;	
	int numSamples = 512;

	void Start(){	
            dataTexture = new Texture2D(numSamples, 1, TextureFormat.RGBA32, false);
            dataTexture.filterMode = filterMode;
            material.SetTexture(`_MusicData`, dataTexture);
			spactrumDataDelay = new floatnumSamples;
	}
    
	void Update() {
        float spectrum = new floatnumSamples;
        //音乐采样
        GetComponent
  
   ().GetSpectrumData(spectrum, 0, FFTWindow.BlackmanHarris); int i = 1; while (i < numSamples 1) { float newData = (spectrumi - 1*1.0f*multiplyer); //对更新数据进行延迟处理 if(newData>spactrumDataDelayi-1){ spactrumDataDelayi-1  = (delay*Time.deltaTime); if(spactrumDataDelayi-1 > newData){ spactrumDataDelayi-1 = newData; } }else{ spactrumDataDelayi-1 -= (delay*Time.deltaTime); if(spactrumDataDelayi-1 <0f){ spactrumDataDelayi-1 = 0f; } } // 设置像素颜色 dataTexture.SetPixel(i - 1, 1, new Color((spactrumDataDelayi - 1 * 255.0f), 0, 0, 0)); i  ; } // 更新texture dataTexture.Apply(); } }
  
  • 编写shader,采样传入的音乐贴图,然后用算法做出可视化效果。把plane的材质用上这个shader就可以了。

思路:利用了画圆的算法,用一个圆减另外一个圆画出圆环。将圆弧等分,圆弧弧度对应音乐贴图的UV.x。根据采样值改变圆环的粗细。然后再画一个细一点的圆环,用之前的减去它。

这样说可能有点拗口,下面用图来说明:

画一个圆;

减去一个圆,先画个圆环;

将圆弧等分,并且按照采样值,改变圆环粗细(两个圆的半径);

再画一个,改细一点,并且在每一小段弧中间要加上间隔;

间隔的代码,用step函数对圆弧做处理:

float Arc = step(distance(0.5, frac(arc * divides)), 0.46);
arcIn *= Arc;

相减。

当然思路是这样,代码中用到了各种函数,次序会有些不同。但是结果是一样的。最后加上背景虚化和颜色变化的效果,增加一些参数。

贴上全部shader代码:

Shader `Chain/Effects/MusicEffects`
{
	Properties
	{
		_Color(`Color`,Color) = (1,1,1,1)
		//音乐数据图输入
		_MusicData(`MusicData (Alpha)`, 2D) = `black` {}
		//将圆弧分成多少段
		_Divides(`Divides`,int) = 64
		//背景平滑度
		_Antialias(`Antialias`,Range(0,1)) = 0.1
		//圆半径
		_radius(`Radius`,Range(0,1)) = 0.2
		//变化参数
		_rayOffset(`RayOffset`,Range(0,1)) = 0.1
		//亮度
		_Brightness(`Brightness`,Range(0,2)) = 1

	}
		SubShader
	{
		Tags { `RenderType` = `Transparency` `IgnoreProjection` = `True``RenderQueue` = `Transparency`}
		Blend SrcAlpha One
		ZWrite Off

		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;
			};

			sampler2D _MusicData;
			float4 _MusicData_ST;
			int _Divides;
			fixed4 _Color;
			fixed _Antialias;
			fixed _OutAntialias;
			fixed _radius;
			fixed _rayOffset;
			fixed _Brightness;

			//用于变色的旋转矩阵
			float2x2 rotate2d(float angle) {
				return float2x2(cos(angle),-sin(angle),
								sin(angle),cos(angle));
				}

			//画特效形状的函数
			fixed2 effectShape(float2 uv, fixed2 center, float radius, float antialias, float offset,fixed arc,int divides) {

				float2 radiusVector = center - uv;
				float len = length(radiusVector);
				//圈圈内圆半径,根据音乐数据减小
				float radiusIn = radius - offset;
				//圈圈外圆半径,根据音乐数据增大,设定一个最小增大值,一定会比内圆大0.005
				float radiusOut = radius   offset   0.005;
				//画内圈
				fixed arcIn = step(len, radiusOut) - step(len, radiusIn);
				//根据格子数取余,每一格来一个间隔
				float Arc = step(distance(0.5, frac(arc * divides)), 0.46);
				arcIn *= Arc;
				//外圈
				fixed arcOut = step(len, radiusOut - 0.004) - step(len, radiusIn - 0.004);
				//增加一个渐变输出,用于背景输出
				fixed arcgradient = smoothstep(radiusOut   antialias   0.05, radiusOut, len) - smoothstep(radiusIn - 0.05   antialias,radiusIn - 0.05, len);
				
				fixed arc_col = abs(arcOut - arcIn);

				arc_col = clamp(arc_col, 0.1, 1);
				//测试背景渐变效果
				//arc_col =arcIn;
				//输出方块效果
				//arc_col = arcgradient;
				//输出为两个通道,一个用来存储画的线,一个用来存储背景的渐变
				return fixed2(arc_col, arcgradient);
				}

			v2f vert(appdata v)
				{
					v2f o;
					o.vertex = UnityObjectToClipPos(v.vertex);
					o.uv = TRANSFORM_TEX(v.uv, _MusicData);
					return o;
				}

				fixed4 frag(v2f i) : SV_Target
				{
				//将坐标转换为圆圈的弧度坐标,然后用这个坐标去采样音乐图,获得音乐数据去改变圆圈的半径等
				fixed arcU = atan2(i.uv.x * 2 - 1,i.uv.y * 2 - 1) / UNITY_TWO_PI;
				//将弧度范围从-1~1转换到0~1
				arcU = arcU * 0.5   0.5;
				//取样音乐图的数据,将uv分为格子,取格子起始点的值,因为我们的音乐流数据很奇怪,所以前面加了1-,正常的话去掉
				fixed rayoffset = tex2D(_MusicData,float2(floor((arcU)*_Divides) / _Divides,0)).r;
				//圆圈中心点设在坐标中心
				fixed2 center = fixed2(0.5,0.5);

				//平滑范围最大不超过0.125
				fixed inantialias = _Antialias * 0.125;
				//以时间为旋转速度因子变色的函数
				fixed2 v = mul(rotate2d(_Time.y) , i.uv);

				//绘制
				fixed2 shape = effectShape(i.uv, center, _radius, inantialias, rayoffset*_rayOffset,arcU,_Divides);
				fixed4 final;
				//分拆函数的输出,分别作为背景和线条叠加起来
				final.rgb = shape.y * fixed3(v.x, v.y, 0.7 - v.y*v.x)   shape.x;
				final.rgb *= _Color * _Brightness;
				final.a = shape.x*0.5   shape.y*0.2;
				return final;
				}
				ENDCG
		}
	}
}

总结:

我在做这个效果的时候是根据直观的视觉效果来分析的,还没有考虑优化,只是目测了一下帧率,目前这个效果应该在移动平台上问题不大。

另外如果要替代这个效果,而且算法比较复杂的话,可以考虑把背景发光等效果做成图片叠加可能会更省效率,项目中我是直接用图片替代了整个圆环的算法,不过效率孰优孰劣却没有很严谨的去验证了。

目前只是一个基础效果,如果实际应用的话,加上抗锯齿,还可以考虑加上bloom效果,再配合一些粒子特效应该会很棒,更多效果,可以参考ShaderToy。

后来又看了一下AE源文件的处理方法,发现用到了粒子,模糊,查找边缘后处理等手段。不知道以后会不会有时间再研究一下这种做法。

广告位

热心评论

评论列表