顶点扭曲Shader案例

💡 可以在Shader当中构建旋转矩阵,来实现顶点的旋转,从而达到模型的扭曲效果

Shader "ShaderLeaning/VertexDistortion/vf1"
{

 Properties{


 }
 SubShader{

 Pass{
 CGPROGRAM

 #pragma vertex vert
 #pragma fragment frag

 #include "UnityCG.cginc"


 struct v2f
 {
 float4 pos: POSITION;
 fixed4 col: COLOR;
 };

 v2f vert(appdata_base v)
 {
 v2f o; 

 float angle=length(v.vertex)*_SinTime.w;//计算顶点到中心点的模长,与SinTime相乘,实现角度的周期性变化

 float4x4 m={
 cos(angle),0,sin(angle),0,
 0,1,0,0,
 -sin(angle),0,cos(angle),0,
 0,0,0,1
 };//构建绕Y轴旋转的矩阵

 float4 p=mul(m,v.vertex);//将旋转矩阵与顶点相乘

 o.pos=UnityObjectToClipPos(p);
 o.col=fixed4(0,1,1,1);

 return o;
 }

 fixed4 frag(v2f v):COLOR
 {
 return v.col;
 }
 ENDCG
 }
 }
}

Screenshot 2023-03-09 at 9.50.00 PM.png

💡 但是每次顶点程序计算当中,都进行了矩阵乘法运算,掌握了矩阵运算规则 可以发现,顶点只是在x,与z方向上移动,所以y值没有必要参与矩阵乘法 所以可以根据这个规律编写优化的代码,提高执行效率

//优化顶点扭曲变换的代码
 float x= x=v.vertex.x*cos(angle)+v.vertex.z*sin(angle);
 float z=v.vertex.x*(-sin(angle))+v.vertex.z*cos(angle);
 v.vertex.x=x;
 v.vertex.z=z;


 o.pos=UnityObjectToClipPos(v.vertex);
  • xyz同时旋转扭曲

    Shader "ShaderLeaning/VertexDistortion/vf1"
    {
    
    Properties{
    
    
    }
    SubShader{
    
    Pass{
    CGPROGRAM
    
    #pragma vertex vert
    #pragma fragment frag
    
    #include "UnityCG.cginc"
    
    
    struct v2f
    {
    float4 pos: POSITION;
    fixed4 col: COLOR;
    };
    
    v2f vert(appdata_base v)
    {
    v2f o; 
    
    float angle=length(v.vertex)*_SinTime.w*1.5; 
    float4x4 mx={
    1,0,0,0,
    0,cos(angle),-sin(angle),0,
    0,sin(angle),cos(angle),0,
    0,0,0,1
    };
    
    float4x4 my={
    cos(angle),0,sin(angle),0,
    0,1,0,0,
    -sin(angle),0,cos(angle),0,
    0,0,0,1
    };
    
    float4x4 mz={
    cos(angle),-sin(angle),0,0,
    sin(angle),cos(angle),0,0,
    0,0,1,0,
    0,0,0,1
    };
    
    v.vertex= mul(mz,mul(my,mul(mx,v.vertex)));
    
    o.pos=UnityObjectToClipPos(v.vertex);
    o.col=fixed4(0,1,1,1);
    
    return o;
    }
    
    fixed4 frag(v2f v):COLOR
    {
    return v.col;
    }
    ENDCG
    }
    }
    

Screenshot 2023-03-09 at 10.29.17 PM.png

实现根据根据音量大小实现波动效果

Shader "ShaderLeaning/VertexDistortion/vf2"
{

 Properties{


 }
 SubShader{

 Pass{
 CGPROGRAM

 #pragma vertex vert
 #pragma fragment frag

 #include "UnityCG.cginc"

 uniform float _Height=1;

 struct v2f
 {
 float4 pos: POSITION;
 fixed4 col: COLOR;
 };

 v2f vert(appdata_base v)
 {
 v2f o; 

 float angle=length(v.vertex)*_Time.y;


 v.vertex.x=v.vertex.x* (sin(angle)/16+1);
 v.vertex.z=v.vertex.z* (cos(angle)/16+1);
 v.vertex.y=v.vertex.y+(sin(angle)*cos(angle))*_Height;


 o.pos=UnityObjectToClipPos(v.vertex);
 o.col=fixed4(0,1,1,1);

 return o;
 }

 fixed4 frag(v2f v):COLOR
 {
 return v.col;
 }
 ENDCG
 }
 }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class VoiceControl : MonoBehaviour
{

[SerializeField] float multiplyer=10;
 AudioClip clip;
 string device=null;

 Material mat;
 // Start is called before the first frame update
 void Start()
 {
 device= Microphone.devices[0];
 clip=Microphone.Start(device,true,999,44100);
 mat=GetComponent().sharedMaterial;
 }

 private void Update() {
 var volume= GetVolume();

 mat.SetFloat("_Height",volume*multiplyer);
 //Debug.Log(volume);
 }

 public float GetVolume()
 {
 if(Microphone.IsRecording(device))
 {
 int sampleSize=128;
 float[] samples=new float[sampleSize];
 int startPosition= Microphone.GetPosition(device)-(sampleSize+1);
 clip.GetData(samples,startPosition);
 float levelMax=0;
 for (int i = 0; i < sampleSize; i++)
 {
 float wavePeak=samples[i];
 if(levelMax<wavePeak)
 {
 levelMax=wavePeak;
 }
 }
 return levelMax;
 }
 return 0;
 }

}

Screenshot 2023-03-09 at 11.50.41 PM.png