Index: binaries/data/mods/public/shaders/glsl/water_high.fs =================================================================== --- binaries/data/mods/public/shaders/glsl/water_high.fs +++ binaries/data/mods/public/shaders/glsl/water_high.fs @@ -117,24 +117,24 @@ { float density = fogParams.x; float maxFog = fogParams.y; - + const float LOG2 = 1.442695; float z = gl_FragCoord.z / gl_FragCoord.w; float fogFactor = exp2(-density * density * z * z * LOG2); - + fogFactor = fogFactor * (1.0 - maxFog) + maxFog; - + fogFactor = clamp(fogFactor, 0.0, 1.0); - + return mix(fogColor, color, fogFactor); } void main() -{ +{ float fresnel; vec2 reflCoords, refrCoords; vec3 reflColor, refrColor, specular; - float losMod, reflMod; + float losMod; // Calculate water normals. @@ -153,7 +153,7 @@ ww1.x = wwInterp.x * WindCosSin.x - wwInterp.z * WindCosSin.y; ww1.z = wwInterp.x * WindCosSin.y + wwInterp.z * WindCosSin.x; ww1.y = wwInterp.y; - + // Flatten them based on waviness. vec3 n = normalize(mix(vec3(0.0,1.0,0.0), ww1, clamp(baseBump + fwaviness/flattenism,0.0,1.0))); @@ -166,13 +166,13 @@ #endif n = vec3(-n.x,n.y,-n.z); // The final wave normal vector. - + // How perpendicular to the normal our view is. Used for fresnel. float ndotv = clamp(dot(n, v),0.0,1.0); - + // Fresnel for "how much reflection vs how much refraction". fresnel = clamp(((pow(1.1 - ndotv, 2.0)) * 1.5), 0.1, 0.75); // Approximation. I'm using 1.1 and not 1.0 because it causes artifacts, see #1714 - + // Specular lighting vectors vec3 specVector = reflect(sunDir, ww1); @@ -181,105 +181,114 @@ specular = specIntensity*1.2 * mix(vec3(1.5), sunColor,0.5); +#if USE_SHADOWS_ON_WATER && USE_SHADOW + float shadow = get_shadow(vec4(v_shadow.xy, v_shadow.zw)); +#endif + + // for refraction, we want to adjust the value by v.y slightly otherwise it gets too different between "from above" and "from the sides". + // And it looks weird (again, we are not used to seeing water from above). + float fixedVy = max(v.y,0.01); + + float murky = mix(200.0,0.1,pow(murkiness,0.25)); + float depth; #if USE_REAL_DEPTH // Don't change these two. They should match the values in the config (TODO: dec uniforms). float zNear = 2.0; float zFar = 4096.0; - - // Okay so here it's a tad complicated. I want to distort the depth buffer along the waves for a nice effect. - // However this causes a problem around underwater objects (think fishes): on some pixels, the depth will be seen as the same as the fishes' - // and the color will be grass ( cause I don't distort the refraction coord by exactly the same stuff) - // Also, things like towers with the feet in water would cause the buffer to see the depth as actually negative in some places. - // So what I do is first check the undistorted depth, then I compare with the distorted value and fix. + + // Compute real depth at the target point. float water_b = gl_FragCoord.z; float water_n = 2.0 * water_b - 1.0; float waterDBuffer = 2.0 * zNear * zFar / (zFar + zNear - water_n * (zFar - zNear)); - + float undisto_z_b = texture2D(depthTex, (gl_FragCoord.xy) / screenSize).x; float undisto_z_n = 2.0 * undisto_z_b - 1.0; float waterDepth_undistorted = (2.0 * zNear * zFar / (zFar + zNear - undisto_z_n * (zFar - zNear)) - waterDBuffer); - - vec2 depthCoord = clamp((gl_FragCoord.xy) / screenSize - n.xz*clamp( waterDepth_undistorted/400.0,0.0,0.05) , 0.001, 0.999); - - float z_b = texture2D(depthTex, depthCoord).x; - - if (z_b < undisto_z_b) - z_b = undisto_z_b; - float z_n = 2.0 * z_b - 1.0; - - depth = (2.0 * zNear * zFar / (zFar + zNear - z_n * (zFar - zNear)) - waterDBuffer); + + // Set depth to the depth at the undistorted point. + depth = waterDepth_undistorted; #else + // fake depth computation: take the value at the vertex, add some if we are looking at a more oblique angle. depth = waterDepth / (min(0.5,v.y)*1.5*min(0.5,v.y)*2.0); #endif - + #if USE_FANCY_EFFECTS depth = max(depth,fancyeffects.a); #endif - -#if USE_SHADOWS_ON_WATER && USE_SHADOW - float shadow = get_shadow(vec4(v_shadow.xy, v_shadow.zw)); -#endif - - // for refraction, we want to adjust the value by v.y slightly otherwise it gets too different between "from above" and "from the sides". - // And it looks weird (again, we are not used to seeing water from above). - float fixedVy = max(v.y,0.01); - - float murky = mix(200.0,0.1,pow(murkiness,0.25)); - + #if USE_REFRACTION + // for refraction we want to distort more as depth goes down. + // 1) compute a distortion based on depth at the pixel. + // 2) Re-sample the depth at the target point + // 3) Sample refraction texture + // distoFactor controls the amount of distortion relative to wave normals. float distoFactor = 0.5 + clamp(depth/2.0,0.0,7.0); - + +#if USE_REAL_DEPTH + vec2 depthCoord = clamp((gl_FragCoord.xy) / screenSize - n.xz * distoFactor / refractionCoords.z, 0.001, 0.999); + float z_b = texture2D(depthTex, depthCoord).x; + float z_n = 2.0 * z_b - 1.0; + float newDepth = (2.0 * zNear * zFar / (zFar + zNear - z_n * (zFar - zNear)) - waterDBuffer); + + // try to correct for fish. In general they'd look weirder without this fix. + if (depth > newDepth + 3.0) + distoFactor /= 2.0; // this in general will not fall on the fish but still look distorted. + else + depth = newDepth; +#endif + // Distort the texture coords under where the water is to simulate refraction. refrCoords = (0.5 * refractionCoords.xy - n.xz * distoFactor) / refractionCoords.z + 0.5; vec3 refColor = texture2D(refractionMap, refrCoords).rgb; // Note, the refraction map is cleared using (255, 0, 0), so pixels outside of the water plane are pure red. // If we get a pure red fragment, use an undistorted/less distorted coord instead. - if (refColor.r > 0.999 && refColor.g < 0.001) - { - refrCoords = (0.5*refractionCoords.xy) / refractionCoords.z + 0.5; - refColor = texture2D(refractionMap, refrCoords).rgb; + // blur the refraction map, distoring using n so that it looks more random than it really is + // and thus looks much better. + float blur = (0.3+clamp(n.x,-0.1,0.1))/refractionCoords.z; + + vec4 blurColor = vec4(refColor, 1.0); + + vec4 tex = texture2D(refractionMap, refrCoords + vec2(blur+n.x, blur+n.z)); + blurColor += vec4(tex.rgb * tex.a, tex.a); + tex = texture2D(refractionMap, refrCoords + vec2(-blur, blur+n.z)); + blurColor += vec4(tex.rgb * tex.a, tex.a); + tex = texture2D(refractionMap, refrCoords + vec2(-blur, -blur+n.x)); + blurColor += vec4(tex.rgb * tex.a, tex.a); + tex = texture2D(refractionMap, refrCoords + vec2(blur+n.z, -blur)); + blurColor += vec4(tex.rgb * tex.a, tex.a); + blurColor /= blurColor.a; + float blurFactor = (distoFactor/7.0); + refColor = (refColor + blurColor.rgb * blurFactor) / (1.0+blurFactor); - if (refColor.r > 0.999 && refColor.g < 0.001) - fresnel = 1.0; - } - else - { - // blur the refraction map, distoring using n so that it looks more random than it really is - // and thus looks much better. - float blur = (0.3+clamp(n.x,-0.1,0.1))/refractionCoords.z; - - vec3 blurColor = texture2D(refractionMap, refrCoords + vec2(blur+n.x, blur+n.z)).rgb; - blurColor += texture2D(refractionMap, refrCoords + vec2(-blur, blur+n.z)).rgb; - blurColor += texture2D(refractionMap, refrCoords + vec2(-blur, -blur+n.x)).rgb; - blurColor += texture2D(refractionMap, refrCoords + vec2(blur+n.z, -blur)).rgb; - blurColor /= 4.0; - float blurFactor = (distoFactor/7.0); - refColor = (refColor + blurColor * blurFactor) / (1.0+blurFactor); - } - // Apply water tint and murk color. float extFact = max(0.0,1.0 - (depth*fixedVy/murky)); float ColextFact = max(0.0,1.0 - (depth*fixedVy/murky)); vec3 colll = mix(refColor*tint,refColor,ColextFact); - + refrColor = mix(color, colll, extFact); #else // Apply water tint and murk color only. float extFact = max(0.0,1.0 - (depth*fixedVy/murky)); float ColextFact = max(0.0,1.0 - (depth*fixedVy/murky)); vec3 colll = mix(color*tint,color,ColextFact); - + refrColor = mix(color, colll, extFact); #endif - -#if USE_REFLECTION + // Reflections - // We use real reflections against the skybox, and distort a texture of objects closer. + // 3 level of settings: + // -If a player has refraction and reflection disabled, we return a gradient of blue based on the Y component. + // -If a player has refraction OR reflection, we return a reflection of the actual skybox used. + // -If a player has reflection enabled, we also return a reflection of actual entities where applicable. + + float reflMod = 0.75; vec3 eye = reflect(v,n); +#if USE_REFLECTION || USE_REFRACTION +#if USE_REFLECTION float refVY = clamp(v.y*2.0,0.05,1.0); // Distort the reflection coords based on waves. @@ -290,6 +299,8 @@ if (refTex.a < 0.99) { +#endif + // Calculate where we intersect with the skycube. Ray myRay = Ray(vec3(worldPos.x/4.0,worldPos.y,worldPos.z/4.0),eye); vec3 start = vec3(-1500.0 + mapSize/2.0,-100.0,-1500.0 + mapSize/2.0); @@ -297,25 +308,29 @@ float tmin = IntersectBox(myRay,start,end); vec4 newpos = vec4(-worldPos.x/4.0,worldPos.y,-worldPos.z/4.0,1.0) + vec4(eye * tmin,0.0) - vec4(-mapSize/2.0,worldPos.y,-mapSize/2.0,0.0); newpos *= skyBoxRot; - newpos.y *= 4.0; + newpos.y *= 4.0; +#if !USE_REFLECTION + reflColor = textureCube(skyCube, newpos.rgb).rgb; +#else // Interpolate between the sky color and nearby objects. reflColor = mix(textureCube(skyCube, newpos.rgb).rgb, refTex.rgb, refTex.a); } - // reflMod is used to reduce the intensity of sky reflections, which otherwise are too extreme. reflMod = max(refTex.a, 0.75); +#endif + #else - reflMod = 0.75; - reflColor = vec3(0.15, 0.7, 0.82); + // Simplest case for reflection, return a gradient of blue based on Y component. + reflColor = mix(vec3(0.76, 0.84, 0.92), vec3(0.24, 0.43, 0.71), -eye.y); #endif - + losMod = texture2D(losMap, losCoords.st).a; losMod = losMod < 0.03 ? 0.0 : losMod; - + vec3 color; #if USE_SHADOWS_ON_WATER && USE_SHADOW float fresShadow = mix(fresnel, fresnel*shadow, 0.05 + murkiness*0.2); - color = mix(refrColor, reflColor, fresShadow); + color = mix(refrColor, reflColor, fresShadow * reflMod); #else color = mix(refrColor, reflColor, fresnel * reflMod); #endif @@ -332,14 +347,14 @@ #if USE_FANCY_EFFECTS vec4 FoamEffects = texture2D(waterEffectsTexOther, gl_FragCoord.xy/screenSize); - + vec3 foam1 = texture2D(normalMap, (normalCoords.st + normalCoords.zw * BigMovement*waviness/10.0) * (baseScale - waviness/wavyEffect)).aaa; vec3 foam2 = texture2D(normalMap2, (normalCoords.st + normalCoords.zw * BigMovement*waviness/10.0) * (baseScale - waviness/wavyEffect)).aaa; vec3 foam3 = texture2D(normalMap, normalCoords.st/6.0 - normalCoords.zw * 0.02).aaa; vec3 foam4 = texture2D(normalMap2, normalCoords.st/6.0 - normalCoords.zw * 0.02).aaa; vec3 foaminterp = mix(foam1, foam2, moddedTime); foaminterp *= mix(foam3, foam4, moddedTime); - + foam1.x = foaminterp.x * WindCosSin.x - foaminterp.z * WindCosSin.y; color += FoamEffects.r * FoamEffects.a * 0.4 + pow(foam1.x * (5.0 + waviness), 2.6 - waviness / 5.5); @@ -350,7 +365,7 @@ #if !USE_REFRACTION alpha = 1.4 - extFact; #endif - + #if USE_FANCY_EFFECTS if (fancyeffects.a < 0.05 && waterDepth < -1.0 ) alpha = 0.0; Index: source/renderer/Renderer.cpp =================================================================== --- source/renderer/Renderer.cpp +++ source/renderer/Renderer.cpp @@ -1135,7 +1135,7 @@ camera.m_Orientation.Scale(1, -1, 1); camera.m_Orientation.Translate(0, 2*wm.m_WaterHeight, 0); camera.UpdateFrustum(scissor); - camera.ClipFrustum(CVector4D(0, 1, 0, -wm.m_WaterHeight)); + camera.ClipFrustum(CVector4D(0, 1, 0, -wm.m_WaterHeight + 2.0f)); SViewPort vp; vp.m_Height = wm.m_RefTextureSize; Index: source/renderer/TerrainRenderer.cpp =================================================================== --- source/renderer/TerrainRenderer.cpp +++ source/renderer/TerrainRenderer.cpp @@ -756,8 +756,6 @@ if (WaterMgr->m_WaterRefraction) m->fancyWaterShader->BindTexture(str_refractionMap, WaterMgr->m_RefractionTexture); - if (WaterMgr->m_WaterReflection) - m->fancyWaterShader->BindTexture(str_skyCube, g_Renderer.GetSkyManager()->GetSkyCube()); m->fancyWaterShader->BindTexture(str_reflectionMap, WaterMgr->m_ReflectionTexture); m->fancyWaterShader->BindTexture(str_losMap, losTexture.GetTextureSmooth()); @@ -767,8 +765,9 @@ m->fancyWaterShader->Uniform(str_transform, g_Renderer.GetViewCamera().GetViewProjection()); //TODO: bind only what's needed - if (WaterMgr->m_WaterReflection) + if (WaterMgr->m_WaterRefraction || WaterMgr->m_WaterReflection) { + m->fancyWaterShader->BindTexture(str_skyCube, g_Renderer.GetSkyManager()->GetSkyCube()); // TODO: check that this rotates in the right direction. CMatrix3D skyBoxRotation; skyBoxRotation.SetIdentity(); Index: source/renderer/WaterManager.cpp =================================================================== --- source/renderer/WaterManager.cpp +++ source/renderer/WaterManager.cpp @@ -256,7 +256,7 @@ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT); - glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB8, (GLsizei)m_RefTextureSize, (GLsizei)m_RefTextureSize, 0, GL_RGB, GL_UNSIGNED_BYTE, 0); + glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, (GLsizei)m_RefTextureSize, (GLsizei)m_RefTextureSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); // Create depth textures glGenTextures(1, &m_ReflFboDepthTexture);