裸睡的鱼 发表于 2012-12-30 21:49

Unity3D中实现常见的水面效果教程



一 bump

float4 temp;

temp.xyzw = v.vertex.xzxz * _WaveScale4 / unity_Scale.w + _WaveOffset;

o.bumpuv0 = temp.xy;

o.bumpuv1 = temp.wz;

首先明确unity_Scale.w的含义,个人理解为当前模型的缩放值。

w = 1 / uniform scale.

uniform float4 unity_Scale : xyz components unused; .w contains scale for uniformly scaled objects.

先排除_WaveScale4和WaveOffset因素,就是直接把plane的各点局部坐标在乘系数当成uv坐标。然后用_WaveScale4去放缩,用WaveOffset去滚动。

Vector4 waveSpeed = mat.GetVector( "WaveSpeed" );
float waveScale = mat.GetFloat( "_WaveScale" );
Vector4 waveScale4 = new Vector4(waveScale, waveScale, waveScale * 0.4f, waveScale * 0.45f);

double t = Time.timeSinceLevelLoad / 20.0;

//此帧开始的时间(只读)。这是以秒计算到最后的关卡已经加载完的时间。也就是说,从最后加载的关卡到现在所用的时间。

Vector4 offsetClamped = new Vector4(
(float)System.Math.IEEERemainder(waveSpeed.x * waveScale4.x * t, 1.0),
(float)System.Math.IEEERemainder(waveSpeed.y * waveScale4.y * t, 1.0),
(float)System.Math.IEEERemainder(waveSpeed.z * waveScale4.z * t, 1.0),
(float)System.Math.IEEERemainder(waveSpeed.w * waveScale4.w * t, 1.0)
);
//IEEERemainder返回一指定数字被另一指定数字相除的余数。
//这里这么处理好像是让生成的数据符合ieee规范还是什么,希望有懂的人能告诉我一下。

更多延伸link

mat.SetVector( "_WaveOffset", offsetClamped );
mat.SetVector( "_WaveScale4", waveScale4 );

//这样生成的波形看上去结果还不错,就先用着了。



二 fresnel

对水面来说,当观察者和水面的角度越小时,反射效果越明显,角度越大时,折射效果越明显,称为菲捏尔效果。所以需要根据观察者的角度来计算反射,折射帖图,以及水面颜色的混合方式。物理上正确的菲捏尔向计算比较复杂,通常使用近似的计算方法。



1,最高效的是假设水的颜色只与水面高度和观察者角度有关。
half fresnelTerm = saturate(dot( eyeVector,normal));
finalColor= lerp(reflectiveColor, refractiveColor,fresnelTerm);



2,在某个demo中看到的方法,也是我一直在用的。
R=normalize(eyepos-worldpos);
fastFresnel = R + ( 1.0f-R ) * pow ( 1.0 – dot ( eyeVector, normal ), 5.0);
finalColor = waterColor * lerp(reflectiveColor, refractiveColor,fastFresnel )+ sunlight;



3,u3d中的方法,大致上差不多,用了texture去模拟Fresnel,效果更多样一些。

o.viewDir.xzy = ObjSpaceViewDir(v.vertex);
//得到当前观察方向的向量
//这里用了u3d封装的函数去得到观察方向,不象我们在世界空间中,而是在观察空间中。
// Computes object space view direction

inline float3 ObjSpaceViewDir( in float4 v )
{

float3 objSpaceCameraPos = mul(_World2Object, float4

(_WorldSpaceCameraPos.xyz, 1)).xyz * unity_Scale.w;

return objSpaceCameraPos - v.xyz
;
}
//float4x4 _World2Object 为 Inverse of current world matrix
//float3 _WorldSpaceCameraPos 为 World space position of the camera



half fresnelFac = dot( i.viewDir, bump );






//水平线表示水面。指向上方的向量是像素的法线向量。另一个向量叫做eyeVector,是从相机指向像素的向量。法线向量上的绿色长度表示当前像素的反射量,红色表示折射量。
//那么我们如何找到绿色和红色线段的长度?我们需要将eyevector投影到法线向量上,这可以通过点乘做到:当你点乘eyeVector和法线后就会获得红色线段的长度。而绿色线段长度等于(1- 红色线段长度)。


//这时我们返回fresnelFac





half fresnel = tex2D( _Fresnel, float2(fresnelFac,fresnelFac) ).a;

//根据折射量去采样

//这时我们返回fresnel



uv1.xy += bump * _ReflDistort;
uv2.xy -= bump * _RefrDistort
//反射和折射的扭曲分开处理。

color = lerp( refr, refl, fresnel );
//到这一个基本的水效果就完成了。



//最后开启雾效

//这里我很倒霉,不清楚为什么u3d的fog在观察相机x 轴角度等于0时,用rtt得到的结果是错的。而我一直用0的条件在找错,浪费了大半天的时间。同时还发现个办法就是创建2个同样的cam,这样即使是0,rtt的结果也是正确的。







三 underWater

关于水下u3d自带的思路只是实现了一个blur,如果想在水下正确的看到水上,需要自己修改下refr的实现。

然后就是投影散焦图片序列,u3d自带了个组件,不用自己在写shader。GodRays效果看到也有预置,接下来准备都实际测试一下。



PS: 现在有很多插件已经支持水面效果 这里只做参考

艺术的叛徒 发表于 2013-6-18 16:12

没有工程文件么

luckytjm 发表于 2013-9-15 10:44


感谢楼主的无私分享!{:soso__11402694654016840197_7:}

听海潮 发表于 2013-9-17 16:32


感谢楼主的无私分享!{:soso__11402694654016840197_7:}

在路上呢 发表于 2013-11-25 16:47

感谢楼主   分享很好的教程   

Aulin 发表于 2013-12-2 10:25

kankan                                 

NoBlesse 发表于 2014-2-28 18:03


感谢楼主的无私分享!{:soso__11402694654016840197_7:}

xuanxuan 发表于 2014-3-6 10:51

谢谢你的分享!

092428 发表于 2014-5-16 09:03

我完全看不懂,感觉很牛逼的样子{:5_397:}

为你等待 发表于 2014-5-17 18:24


膜拜中。。。。{:soso__7524161091986203637_5:}
页: [1] 2 3 4 5 6 7 8 9 10
查看完整版本: Unity3D中实现常见的水面效果教程