首先我們在Maya裡面處理下模型和UV,草模型的製作方式有很多,可以根據項目實際需求來選擇。這裡我的草模型如下:
這裡要注意下模型的坐標原點位置和UV的分布,後面在shader裡面會需要根據V向上的值來進行計算,草的底部V的值為0,尖部值為1,主要草地底部就不會動了,越往上擺動的幅度越大。然後我們將這個模型複製四個出來,模型位置不用移動,來修改下UV的坐標位置。
這樣畫草的貼圖時可以給草做一點顏色的變化,看起來更自然一些,下面是草的貼圖。
接下來我們把這五個草的模型隨機複製成一小塊草地,然後把他們Combine成一個Mesh導出FBX。
下面是一個Maya的Mel腳本,選擇這5個草的模型執行下面的腳本,可以自動在設置的範圍內隨機複製出想要的模型數量。
string $orgObjsName[] = `ls -sl`; //隨機縮放的最小比例 float $randScaleMin = 0.5; //隨機縮放的最大比例 float $randScaleMax = 1.5; //複製出來模型的範圍 float $radius = 30; //X軸的旋轉隨機範圍 float $randRotate = 20; //每個草模型複製出來幾個,總數就是這個值乘以選擇的草模型數量 int $objCount = 4;
for($currentObj in $orgObjsName){ for($i=0; $i<$objCount; $i++){ CopyObjsRand($currentObj, $randRotate, $randScaleMin, $randScaleMax, $radius); } }
global proc CopyObjsRand(string $objName, float $rotateRand, float $randScaleMin, float $randScaleMax, float $posRand){ string $newObjsName[] = `duplicate -rr $objName`; float $randRotateX = rand(-$rotateRand,$rotateRand); float $randRotateY = rand(0,360); setAttr ($newObjsName[0] + ".rotate") -type "double3" $randRotateX $randRotateY 0; float $randScale = rand($randScaleMin, $randScaleMax); setAttr ($newObjsName[0] + ".scale") -type "double3" $randScale $randScale 1; float $randPos[] = unit(sphrand(1)) * $posRand; $randPos[1] = 0; setAttr ($newObjsName[0] + ".translate") -type "double3" $randPos[0] $randPos[1] $randPos[2]; }
好了,以上就是在Maya裡面對模型和UV的一些處理,下面我們在Unity裡面來實現模型頂點動畫的Shader.
我們先來看下草的自由來回擺動效果,這個實現很簡單,在頂點的世界坐標加上用正弦或者餘弦函數計算出來的一個偏移值就行了。
float4 worldPos = mul(unity_ObjectToWorld, v.vertex); worldPos.x += sin(_Time.x * 10); o.pos = mul(UNITY_MATRIX_VP, worldPos);
要固定住草的根部,這裡我就要用的模型的UV了,根部的V值為0所以不會有移動
worldPos.x += sin(_Time.x * 10) * v.uv.y;
接下來我們用一張noise貼圖來模擬風浪的效果,實現就是頂點的世界坐標做偏移來採樣noise貼圖,我們先來看下採樣的效果。
v2f vert (appdata v) { v2f o; float4 worldPos = mul(unity_ObjectToWorld, v.vertex); //_WaveControl.w用來調整地圖的大小 float2 samplePos = worldPos.xz / _WaveControl.w; //_WindControl.xyz控制各方向上的速度 samplePos += _Time.x * _WaveControl.xz; fixed waveSample = tex2Dlod(_Noise, float4(samplePos, 0, 0)).r; o.pos = mul(UNITY_MATRIX_VP, worldPos); o.uv = v.uv; o.tempCol = waveSample;
return o; }
fixed4 frag (v2f i) : SV_Target { return fixed4(frac(i.tempCol.x), 0, 0, 1); }
現在我們讓草的頂點也動起來
v2f vert (appdata v) { v2f o; float4 worldPos = mul(unity_ObjectToWorld, v.vertex); float2 samplePos = worldPos.xz / _WaveControl.w; samplePos += _Time.x * -_WaveControl.xz; fixed waveSample = tex2Dlod(_Noise, float4(samplePos, 0, 0)).r; worldPos.x += sin(waveSample * _WindControl.x) * _WaveControl.x * _WindControl.w * v.uv.y; worldPos.z += sin(waveSample * _WindControl.z) * _WaveControl.z * _WindControl.w * v.uv.y; o.pos = mul(UNITY_MATRIX_VP, worldPos); o.uv = v.uv; o.tempCol = waveSample;
return o; } _WaveControl.w = 30
我們可以通過調整_WaveControl.w的參數來控制草隨風擺動的是整體一些還是凌亂一些。
完整的shader代碼如下:
Shader "Custom/GrassVertexAni" { Properties { _MainTex ("Texture", 2D) = "white" {} _Noise("Noise", 2D) = "black" {} _WindControl("WindControl(x:XSpeed y:YSpeed z:ZSpeed w:windMagnitude)",vector) = (1,0,1,0.5) //前面幾個分量表示在各個軸向上自身擺動的速度, w表示擺動的強度 _WaveControl("WaveControl(x:XSpeed y:YSpeed z:ZSpeed w:worldSize)",vector) = (1,0,1,1) //前面幾個分量表示在各個軸向上風浪的速度, w用來模擬地圖的大小,值越小草擺動的越凌亂,越大擺動的越整體 } SubShader { Tags { "RenderType"="Opaque" } LOD 100
Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma multi_compile_instancing
#include "UnityCG.cginc"
struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; UNITY_VERTEX_INPUT_INSTANCE_ID };
struct v2f { float2 uv : TEXCOORD0; float4 pos : SV_POSITION; //float3 tempCol : TEXCOORD1;//用來測試傳遞noise貼圖採樣的結果 };
sampler2D _MainTex; sampler2D _Noise; half4 _WindControl; half4 _WaveControl;
v2f vert (appdata v) { v2f o; UNITY_SETUP_INSTANCE_ID(v); float4 worldPos = mul(unity_ObjectToWorld, v.vertex); float2 samplePos = worldPos.xz / _WaveControl.w; samplePos += _Time.x * -_WaveControl.xz; fixed waveSample = tex2Dlod(_Noise, float4(samplePos, 0, 0)).r; worldPos.x += sin(waveSample * _WindControl.x) * _WaveControl.x * _WindControl.w * v.uv.y; worldPos.z += sin(waveSample * _WindControl.z) * _WaveControl.z * _WindControl.w * v.uv.y; o.pos = mul(UNITY_MATRIX_VP, worldPos); o.uv = v.uv; //o.tempCol = waveSample;
fixed4 frag (v2f i) : SV_Target { fixed4 col = tex2D(_MainTex, i.uv); //return fixed4(frac(i.tempCol.x), 0, 0, 1); return col; } ENDCG } } }
推薦閱讀: