想要实现一个纪念碑谷中的海面效果,最初的设想是在vertex中,修改顶点坐标来实现,而且也实现出来了,代码:
v2f vert(a2v v) {
v2f o;
float4 offset;
offset.xyzw = float4(0,0,0,0);
float sinx = sin(_Frequency * _Time.y + v.vertex.x * _InvWaveLength + v.vertex.y * _InvWaveLength + v.vertex.z * _InvWaveLength);
offset.y = sinx * _Magnitude;
o.pos = UnityObjectToClipPos(v.vertex+offset);
o.uv = TRANSFORM_TEX(v.texcoord,_MainTex);
o.norm = sinx;
o.worldPos = mul(unity_ObjectToWorld,v.vertex).xyz;
o.sinx = sinx;
return o;
}
但是效果看上去总是怪怪的:
想了老半天哪里怪,为啥跟想象中的不一样呢....我们看一下纪念碑谷中的效果:
这差距也太大了吧...很明显,没有光效啊,那好,加上光效看看效果:
结果还是一样,这是因为我们虽然处理的光,但是顶点的法线却并没有改变,不管顶点怎么变,亮度都是不会改变的,所以我们还需要计算一下法线,法线的计算我们要放在geometry中进行:
[maxvertexcount(3)]
void geom(triangle v2f IN[3], inout TriangleStream<v2f> triStream)
{
float3 v0 = IN[0].pos.xyz;
float3 v1 = IN[1].pos.xyz;
float3 v2 = IN[2].pos.xyz;
float3 vn = normalize(cross(v1 - v0, v2 - v0));
vn = UnityObjectToWorldNormal(vn);
v2f OUT;
OUT.pos = IN[0].pos;
OUT.worldNormal = vn;
OUT.worldPos = IN[0].worldPos;
OUT.uv = IN[0].uv;
triStream.Append(OUT);
OUT.pos = IN[1].pos;
OUT.worldNormal = vn;
OUT.worldPos = IN[1].worldPos;
OUT.uv = IN[1].uv;
triStream.Append(OUT);
OUT.pos = IN[2].pos;
OUT.worldNormal = vn;
OUT.worldPos = IN[2].worldPos;
OUT.uv = IN[2].uv;
triStream.Append(OUT);
}
normalize(cross(v1 - v0, v2 - v0));这里对三角形的两条边做叉积,就得到了平面的法线。
看看现在的效果:
这次有点接近了,但是没有变化,我们加点变化来看看,在vert中加上如下2句,对顶点的xz坐标也加上位移:
offset.x = sin(_Frequency * _Time.y + v.vertex.x);
offset.z = sin(_Frequency * _Time.y + v.vertex.z);
再来看看:
总算是差不多了,想要达到更好的效果还需要进行一些细节的处理,有兴趣的朋友可以自己尝试.
上完整shader:
Shader "Custom/Water" {
Properties {
_Color ("Color", Color) = (1,1,1,1)
_MainTex ("Main Tex", 2D) = "white" {}
_Magnitude ("Distortion Magnitude" , Float) = 1
_Frequency ("Distortion Frequency" , Float) = 1
_InvWaveLength("Distortion Inverse Wave Length",Float) = 10
_Speed("Speed",Float) = 0.5
}
SubShader {
Tags{"IgnoreProjector"="True" "Queue"="Transparent" "RenderType"="Transparent"}
Pass{
CGPROGRAM
#pragma vertex vert
#pragma geometry geom
#pragma fragment frag
#include "Lighting.cginc"
sampler2D _MainTex;
float4 _MainTex_ST;
fixed4 _Color;
float _Magnitude;
float _Frequency;
float _InvWaveLength;
float _Speed;
struct a2v{
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
};
struct v2f {
float4 pos : SV_POSITION;
float3 worldNormal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
float2 uv : TEXCOORD2;
};
v2f vert(a2v v) {
v2f o;
float4 offset;
offset.xyzw = float4(0,0,0,0);
float sinx = sin(_Frequency * _Time.y + v.vertex.x * _InvWaveLength + v.vertex.y * _InvWaveLength + v.vertex.z * _InvWaveLength);
offset.y = sinx * _Magnitude;
offset.x = sin(_Frequency * _Time.y + v.vertex.x);
offset.z = sin(_Frequency * _Time.y + v.vertex.z);
o.pos = UnityObjectToClipPos(v.vertex+offset);
o.uv = TRANSFORM_TEX(v.texcoord,_MainTex);
o.worldPos = mul(unity_ObjectToWorld,v.vertex).xyz;
return o;
}
[maxvertexcount(3)]
void geom(triangle v2f IN[3], inout TriangleStream<v2f> triStream)
{
float3 v0 = IN[0].pos.xyz;
float3 v1 = IN[1].pos.xyz;
float3 v2 = IN[2].pos.xyz;
float3 vn = normalize(cross(v1 - v0, v2 - v0));
vn = UnityObjectToWorldNormal(vn);
v2f OUT;
OUT.pos = IN[0].pos;
OUT.worldNormal = vn;
OUT.worldPos = IN[0].worldPos;
OUT.uv = IN[0].uv;
triStream.Append(OUT);
OUT.pos = IN[1].pos;
OUT.worldNormal = vn;
OUT.worldPos = IN[1].worldPos;
OUT.uv = IN[1].uv;
triStream.Append(OUT);
OUT.pos = IN[2].pos;
OUT.worldNormal = vn;
OUT.worldPos = IN[2].worldPos;
OUT.uv = IN[2].uv;
triStream.Append(OUT);
}
fixed4 frag(v2f i) : SV_Target {
//获取法线方向
fixed3 worldNormal = normalize(i.worldNormal);
//灯光方向
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
fixed4 c = tex2D(_MainTex,i.uv) * dot(worldNormal,worldLightDir);
return c;
}
ENDCG
}
}
FallBack "Transparent/VertexLit"
}