假期没事干,就自己找了点事情干,做个水面的效果,曾经在网上看到好多人做的水面都很漂亮,那时,心里就暗暗较劲,自己什么时候也能做出这样完美的水面呢?
花费了3天时间研究书和网站上的方法,终于做出比较不错的水面了。。。以下是愚的简单方法,不一定完全正确,而且我觉得找到一个好的波动方程真的是看人品的。。。网上的东西试验过了,最后还是自己结合起来写了一个波动方程。。。很简单粗糙的,不过效果不错~
1、自己第一天找到了一个网页,上面写的是直接贴图,然后根据法线图计算纹理采样。具体方法是把预先用3ds max做好的normal map进行采样得到该像素点的法线值,模拟定点法线进行反射纹理采样的纹理坐标。
做完以后,效果如图:
效果还不够理想,于是下一步。
2、做水面顶点
上一步做的时候,只是简单的4个顶点,并没有用到任何波动方程,于是摄像机拉近后,只是简单的平面,根本没有起伏的感觉。。。
于是又想起教材上面的红旗飘飘。。。又搜了下顶点的波动方程。。。综合自己不算出众的数学知识,写了个渲染代码,网个图片如图:
3、颜色插值。。。这一步很郁闷,要算法线,自己才疏学浅。。。搞了好半天才想到做导数,但是只知道所谓的切线斜率可以求导得到,然后就不知道怎么做了,原来,切线空间有3个向量表示图形,法线(normal),副法线(binormal)和切线(tangen)三者关系是互相垂直,差乘等于另一个(实际直接取,没有算差乘,不知道为什么,否则有问题。。)
这样以来有了颜色的区分,切线空间转换矩阵也得到了。。。然后加上第一步的方法,就差不多了
如图:
有点样子了吧?

这样,一个完美的水面就搞定了~~~(图不太清晰厄。。。)
至于动态的问题,要先弄好环境空间以后再进行动态贴图。。。加油努力!!
完美的水面,其实每个人都能做出来!
以下是暂时的hlsl effect代码:
float4x4 matWorldViewProj;
float4x4 matWorld;
float4 vecEye;
float4 vecLightDir;
float4x4 matView;
float fTime;
float4 deepColor;
float4 shallowColor;
texture RefractMap;
sampler RefractMapSampler = sampler_state
{
Texture = <RefractMap>;
MinFilter = Linear;
MagFilter = Linear;
MipFilter = Linear;
};
texture ReflectMap;
sampler ReflectMapSampler = sampler_state
{
Texture = <ReflectMap>;
MinFilter = Linear;
MagFilter = Linear;
MipFilter = Linear;
};
texture NormalMap;
sampler NormalMapSampler = sampler_state
{
Texture = <NormalMap>;
MinFilter = Linear;
MagFilter = Linear;
MipFilter = Linear;
};
struct VS_OUTPUT
{
float4 Pos : POSITION;
float2 Tex : TEXCOORD0;
float3x3 matTan2World : TEXCOORD1;
float4 Color : COLOR0;
float3 View : TEXCOORD4;
};
struct Wave
{
float fFreq;
float fLmd;
float fAmp;
float2 vDir;
};
Wave Waves[] =
{
// { 0.20f, 50.0f , 1.00f, float2( 0.5f, 0.1f ) },
{ 0.50f, 500.0f , 25.00f, float2( 0.7f, -0.7f ) },
{ 0.20f, 700.0f , 30.00f, float2( 0.2f, 0.1f ) },
};
float2 EvaluateWave( Wave w, float2 vPos, float time )
{
float dist = dot(w.vDir , vPos) / sqrt(w.vDir.x * w.vDir.x + w.vDir.y * w.vDir.y);
float hight = w.fAmp * sin( time * 2 * 3.14 * w.fFreq + dist / (w.fLmd * w.fFreq) ); //Asin(wt + dot(dir,pos) / v)
float k = w.fAmp * 2 * 3.14 / w.fLmd * cos( time * 2 * 3.14 * w.fFreq + dist / (w.fLmd * w.fFreq) ); //cos(wt + dot(dir,pos) / v)
float2 ret = float2(hight , k);
return ret;
}
VS_OUTPUT VS(float4 Pos : POSITION, float2 Tex : TEXCOORD0)
{
VS_OUTPUT Out = (VS_OUTPUT)0;
Out.Pos = mul(Pos , matWorldViewProj);
Out.Tex = Tex;
float tx = 0.0f;
float bz = 0.0f;
for( int i = 0; i != 2; i++ )
{
float2 ret = EvaluateWave( Waves[i], Pos.xz, fTime);
float k = ret.y;
Out.Pos.y += ret.x;
tx += k * Waves[i].vDir.x / sqrt(Waves[i].vDir.x * Waves[i].vDir.x + Waves[i].vDir.y * Waves[i].vDir.y);
bz += k * Waves[i].vDir.y / sqrt(Waves[i].vDir.x * Waves[i].vDir.x + Waves[i].vDir.y * Waves[i].vDir.y);
}
float3 vT = normalize(float3(tx , 3 , 0));
// float3 vB = normalize(float3(0, 3 , bz));
float3 vN = normalize(float3(-tx , 3 , -bz));
float3 vB = cross(vT , vN);
Out.matTan2World[0] = mul( vT, matWorld);
Out.matTan2World[1] = mul( vB, matWorld);
Out.matTan2World[2] = mul( vN, matWorld);
float3 PosWorld = normalize(mul(Pos , matWorld));
float3 vecEyeDir = vecEye - PosWorld.xyz;
vecEyeDir = normalize(vecEyeDir);
Out.View = vecEyeDir;
float facing = saturate(dot(vecEyeDir , vN));
float4 waterColor1 = lerp(shallowColor,deepColor,facing);
Out.Color = waterColor1 * 0.8 ;
return Out;
}
float4 PS(VS_OUTPUT In) : COLOR
{
float4 Out;
float2 vBump0 = In.Tex + float2(fTime / 10 , fTime / 10);
float2 vBump1 = In.Tex * 2.0f + float2(fTime / 15 , 0);
float2 vBump2 = In.Tex * 4.0f + float2(fTime / 20 , fTime / 10);
float3 bumpNormal0 = tex2D(NormalMapSampler , vBump0);
float3 bumpNormal1 = tex2D(NormalMapSampler , vBump1);
float3 bumpNormal2 = tex2D(NormalMapSampler , vBump2);
float3 bumpNormal = 2 * (bumpNormal0 + bumpNormal1 + bumpNormal2) - 3;
bumpNormal = normalize(mul(In.matTan2World , bumpNormal));
bumpNormal /= 5;
float3 Incident = - normalize(mul(In.matTan2World , In.View));
float4 ReflectColor = tex2D(ReflectMapSampler , In.Tex + bumpNormal);
float4 RefractColor = tex2D(RefractMapSampler , In.Tex + bumpNormal);
float4 color = ReflectColor * 0.8+ RefractColor * 0.4 ;
float4 light = float4(1.0f , 1.0f , 1.0f , 1.0f);
float3 LightDir = normalize(vecLightDir.xyz);
float3 Reflect = normalize(2 * dot(bumpNormal , LightDir) * bumpNormal - LightDir);
float4 spec = light * pow(dot(Reflect , In.View) , 0.2f) * color;
float4 diff = light * dot(bumpNormal , LightDir) * color;
Out = spec * 0.8 + diff * 0.4 + In.Color * 0.8;
return Out;
}
technique TShader
{
pass P0
{
VertexShader = compile vs_2_0 VS();
PixelShader = compile ps_2_0 PS();
}
}