水面渲染

来源:互联网 发布:自动外呼软件 编辑:程序博客网 时间:2024/04/28 06:26

    Avatar 引擎中的水面渲染,是用一种比较高效并且真实感强的方法实现的。水面的波纹模拟没有采用顶点波动方式,而是使用法向贴图来模拟。其效果可参见之前的几篇文章的图片。

    首先需要实现的是水面倒影,水面倒影的渲染需要把摄像机置于水面的另一面对称位置,然后渲染整个场景至纹理(render to texture)。一般需要使用平面裁剪将水面以下的物体裁剪掉,避免错误的倒影,但在本方法中使用了一种特殊的方法,通过修改投影矩阵将视截体的裁剪近平面与水面重合,从而将水面下的物体裁剪掉,修改后的投影矩阵也叫近斜平面裁剪投影矩阵。计算方法如下

CMatrix4& CMatrix4::ObliqueNearPlaneClipping(const CMatrix4& proj, const CPlane& clip) {float x = ((clip.m_fNormal[0] > 0? 1: (clip.m_fNormal[0] < 0? -1: 0)) + proj.m_fValue[8]) / proj.m_fValue[0];float y = ((clip.m_fNormal[1] > 0? 1: (clip.m_fNormal[1] < 0? -1: 0)) + proj.m_fValue[9]) / proj.m_fValue[5];float z = -1.0f;float w = (1.0f + proj.m_fValue[10]) / proj.m_fValue[14];float scale = 2.0f / (clip.m_fNormal[0] * x + clip.m_fNormal[1] * y + clip.m_fNormal[2] * z + clip.m_fDistance * w);// 修改投影矩阵的第三行m_fValue[0] = proj.m_fValue[0];m_fValue[1] = proj.m_fValue[1];m_fValue[2] = clip.m_fNormal[0] * scale;m_fValue[3] = proj.m_fValue[3];m_fValue[4] = proj.m_fValue[4];m_fValue[5] = proj.m_fValue[5];m_fValue[6] = clip.m_fNormal[1] * scale;m_fValue[7] = proj.m_fValue[7];m_fValue[8] = proj.m_fValue[8];m_fValue[9] = proj.m_fValue[9];m_fValue[10] = clip.m_fNormal[2] * scale + 1.0f;m_fValue[11] = proj.m_fValue[11];m_fValue[12] = proj.m_fValue[12];m_fValue[13] = proj.m_fValue[13];m_fValue[14] = clip.m_fDistance * scale;m_fValue[15] = proj.m_fValue[15];return *this;}
CMatrix4 内部为列优先排列,同OpenGL


    水面的几何结构是一个平面,四个顶点两个三角形,至于如何产生波浪,秘密全在 shader 中

attribute vec4 aPosition;attribute vec4 aNormal;attribute vec2 aTexCoord;attribute vec4 aColor;uniform mat4 uProjMatrix;uniform mat4 uViewMatrix;uniform mat4 uModelMatrix;uniform float uWaveLength;uniform vec2 uWaveMovement;uniform vec4 uLightPos;varying vec2 vWavesTexCoord;varying vec3 vTexCoord;varying vec3 vPosition;varying vec3 vNormal;varying vec4 vLightPos;void main(){mat4 modelView = uViewMatrix * uModelMatrix;vec4 position = modelView * aPosition;vec4 normal = normalize(modelView * aNormal);vec4 finalPos = uProjMatrix * position;vWavesTexCoord = (aPosition.xy / uWaveLength) + uWaveMovement;vTexCoord.x = 0.5 * (finalPos.w + finalPos.x);vTexCoord.y = 0.5 * (finalPos.w + finalPos.y);vTexCoord.z = finalPos.w;vPosition = position.xyz;vNormal = normal.xyz;vLightPos = uViewMatrix * uLightPos;gl_Position = finalPos;}
#ifdef GL_FRAGMENT_PRECISION_HIGHprecision highp float;#elseprecision mediump float;#endifuniform sampler2D uTexture[2];uniform vec4 uLightColor;uniform float uWaveHeight;uniform vec4 uWaterColor;varying vec2 vWavesTexCoord;varying vec3 vTexCoord;varying vec3 vPosition;varying vec3 vNormal;varying vec4 vLightPos;void main(){vec4 normalColor = texture2D(uTexture[0], vWavesTexCoord.xy);vec2 waveMovement = uWaveHeight * (normalColor.xy - 0.5);vec2 projTexCoord = clamp((vTexCoord.xy / vTexCoord.z) + waveMovement, 0.0, 1.0);vec4 reflectiveColor = texture2D(uTexture[1], vec2(projTexCoord.x, -projTexCoord.y));vec3 vPos = normalize(vPosition);// 光照计算float specular = 0.0;vec3 lightVec = vLightPos.xyz;if (vLightPos.w != 0.0){lightVec = normalize(lightVec - vPosition);}vec3 r = reflect(lightVec, vNormal);specular = pow(max(0.0, dot(r, vPos)), 64.0) * 0.5;vec4 color = uWaterColor * reflectiveColor + vec4(uLightColor.rgb, 1.0) * specular;// 反射系数float fresnel = 1.0 - abs(dot(vNormal, -vPos));color.a = uWaterColor.a * fresnel + specular;gl_FragColor = color;}
在 fragment shader 中计算 fresnel 是为了实现远处的水面反射光强而透射光弱,近处的水面看上去透光强而反射光弱,这也就是菲涅尔效应。

0 0
原创粉丝点击