diff --git a/engine/src/core-data/Common/MatDefs/Water/Textures/caustics.jpg b/engine/src/core-data/Common/MatDefs/Water/Textures/caustics.jpg new file mode 100644 index 000000000..fb4f21cfe Binary files /dev/null and b/engine/src/core-data/Common/MatDefs/Water/Textures/caustics.jpg differ diff --git a/engine/src/core-data/Common/MatDefs/Water/Water.frag b/engine/src/core-data/Common/MatDefs/Water/Water.frag index 89c98e24f..5cf91d504 100644 --- a/engine/src/core-data/Common/MatDefs/Water/Water.frag +++ b/engine/src/core-data/Common/MatDefs/Water/Water.frag @@ -8,6 +8,7 @@ uniform sampler2D m_Texture; uniform sampler2D m_DepthTexture; uniform sampler2D m_NormalMap; uniform sampler2D m_FoamMap; +uniform sampler2D m_CausticsMap; uniform sampler2D m_ReflectionMap; uniform mat4 m_ViewProjectionMatrixInverse; @@ -114,6 +115,112 @@ float fresnelTerm(in vec3 normal,in vec3 eyeVec){ return saturate(fresnel * (1.0 - saturate(m_R0)) + m_R0 - m_RefractionStrength); } +vec2 m_FrustumNearFar=vec2(1.0,50); +const float LOG2 = 1.442695; + +vec4 underWater(){ + + + float sceneDepth = texture2D(m_DepthTexture, texCoord).r; + vec3 color2 = texture2D(m_Texture, texCoord).rgb; + + vec3 position = getPosition(sceneDepth, texCoord); + float level = m_WaterHeight; + + vec3 eyeVec = position - m_CameraPosition; + + // Find intersection with water surface + vec3 eyeVecNorm = normalize(eyeVec); + float t = (level - m_CameraPosition.y) / eyeVecNorm.y; + vec3 surfacePoint = m_CameraPosition + eyeVecNorm * t; + + vec2 texC = vec2(0.0); + + float cameraDepth = length(m_CameraPosition - surfacePoint); + texC = (surfacePoint.xz + eyeVecNorm.xz) * scale + m_Time * 0.03 * m_WindDirection; + float bias = texture(m_HeightMap, texC).r; + level += bias * m_MaxAmplitude; + t = (level - m_CameraPosition.y) / eyeVecNorm.y; + surfacePoint = m_CameraPosition + eyeVecNorm * t; + eyeVecNorm = normalize(m_CameraPosition - surfacePoint); + + // Find normal of water surface + float normal1 = textureOffset(m_HeightMap, texC, ivec2(-1, 0)).r; + float normal2 = textureOffset(m_HeightMap, texC, ivec2( 1, 0)).r; + float normal3 = textureOffset(m_HeightMap, texC, ivec2( 0, -1)).r; + float normal4 = textureOffset(m_HeightMap, texC, ivec2( 0, 1)).r; + + vec3 myNormal = normalize(vec3((normal1 - normal2) * m_MaxAmplitude,m_NormalScale,(normal3 - normal4) * m_MaxAmplitude)); + vec3 normal = myNormal*-1.0; + float fresnel = fresnelTerm(normal, eyeVecNorm); + + vec3 refraction = color2; + #ifdef ENABLE_REFRACTION + texC = texCoord.xy *sin (fresnel+1.0); + refraction = texture2D(m_Texture, texC).rgb; + #endif + + float waterCol = saturate(length(m_LightColor.rgb) / m_SunScale); + refraction = mix(mix(refraction, m_DeepWaterColor.rgb * waterCol, m_WaterTransparency), m_WaterColor.rgb* waterCol,m_WaterTransparency); + + vec3 foam = vec3(0.0); + #ifdef ENABLE_FOAM + texC = (surfacePoint.xz + eyeVecNorm.xz * 0.1) * 0.05 + m_Time * 0.05 * m_WindDirection + sin(m_Time * 0.001 + position.x) * 0.005; + vec2 texCoord2 = (surfacePoint.xz + eyeVecNorm.xz * 0.1) * 0.05 + m_Time * 0.1 * m_WindDirection + sin(m_Time * 0.001 + position.z) * 0.005; + + if(m_MaxAmplitude - m_FoamExistence.z> 0.0001){ + foam += ((texture2D(m_FoamMap, texC) + texture2D(m_FoamMap, texCoord2)) * m_FoamIntensity * m_FoamIntensity * 0.3 * + saturate((level - (m_WaterHeight + m_FoamExistence.z)) / (m_MaxAmplitude - m_FoamExistence.z))).rgb; + } + foam *= m_LightColor.rgb; + #endif + + + + vec3 specular = vec3(0.0); + vec3 color ; + float fogFactor; + + if(position.y>level){ + #ifdef ENABLE_SPECULAR + if(step(0.9999,sceneDepth)==1.0){ + vec3 lightDir=normalize(m_LightDir); + vec3 mirrorEye = (2.0 * dot(eyeVecNorm, normal) * normal - eyeVecNorm); + float dotSpec = saturate(dot(mirrorEye.xyz, -lightDir) * 0.5 + 0.5); + specular = vec3((1.0 - fresnel) * saturate(-lightDir.y) * ((pow(dotSpec, 512.0)) * (m_Shininess * 1.8 + 0.2))); + specular += specular * 25.0 * saturate(m_Shininess - 0.05); + specular=specular * m_LightColor.rgb * 100.0; + } + #endif + float fogIntensity= 8 * m_WaterTransparency; + fogFactor = exp2( -fogIntensity * fogIntensity * cameraDepth * 0.03 * LOG2 ); + fogFactor = clamp(fogFactor, 0.0, 1.0); + color =mix(m_DeepWaterColor.rgb,refraction,fogFactor); + specular=specular*fogFactor; + color = saturate(color + max(specular, foam )); + }else{ + vec3 caustics = vec3(0.0); + #ifdef ENABLE_CAUSTICS + vec2 windDirection=m_WindDirection; + texC = (position.xz + eyeVecNorm.xz * 0.1) * 0.05 + m_Time * 0.05 * windDirection + sin(m_Time + position.x) * 0.01; + vec2 texCoord2 = (position.xz + eyeVecNorm.xz * 0.1) * 0.05 + m_Time * 0.05 * windDirection + sin(m_Time + position.z) * 0.01; + caustics += (texture2D(m_CausticsMap, texC)+ texture2D(m_CausticsMap, texCoord2)).rgb; + caustics *= m_WaterColor.rgb; + color=mix(color2, caustics,0.6); + #else + color=color2; + #endif + + float fogDepth= (2.0 * m_FrustumNearFar.x) / (m_FrustumNearFar.y + m_FrustumNearFar.x - sceneDepth* (m_FrustumNearFar.y-m_FrustumNearFar.x)); + float fogIntensity= 18 * m_WaterTransparency; + fogFactor = exp2( -fogIntensity * fogIntensity * fogDepth * fogDepth * LOG2 ); + fogFactor = clamp(fogFactor, 0.0, 1.0); + color =mix(m_DeepWaterColor.rgb,color,fogFactor); + } + + return vec4(color, 1.0); +} + void main(){ float sceneDepth = texture2D(m_DepthTexture, texCoord).r; float isAtFarPlane = step(0.99998, sceneDepth); @@ -125,10 +232,10 @@ void main(){ float level = m_WaterHeight; - // If we are underwater let's leave out complex computations + // If we are underwater let's go to under water function if(level >= m_CameraPosition.y){ - gl_FragColor = vec4(color2, 1.0); - return; + gl_FragColor = underWater(); + return ; } //#ifndef ENABLE_RIPPLES @@ -284,10 +391,6 @@ void main(){ // to calculate the derivatives for all these pixels by using step()! // That way we won't get pixels around the edges of the terrain, // Where the derivatives are undefined -/* float coef=1.0; - if(position.y level){ color = color2; } diff --git a/engine/src/core-data/Common/MatDefs/Water/Water.j3md b/engine/src/core-data/Common/MatDefs/Water/Water.j3md index b1bda5481..4d8a55a68 100644 --- a/engine/src/core-data/Common/MatDefs/Water/Water.j3md +++ b/engine/src/core-data/Common/MatDefs/Water/Water.j3md @@ -4,6 +4,7 @@ MaterialDef Advanced Water { Int NumSamples Int NumSamplesDepth Texture2D FoamMap + Texture2D CausticsMap Texture2D NormalMap Texture2D ReflectionMap Texture2D HeightMap @@ -39,10 +40,12 @@ MaterialDef Advanced Water { Boolean UseHQShoreline Boolean UseSpecular Boolean UseFoam + Boolean UseCaustics Boolean UseRefraction + } - Technique { + Technique { VertexShader GLSL150 : Common/MatDefs/Post/Post15.vert FragmentShader GLSL150 : Common/MatDefs/Water/Water15.frag @@ -51,8 +54,14 @@ MaterialDef Advanced Water { } Defines { - RESOLVE_MS : NumSamples + RESOLVE_MS : NumSamples RESOLVE_DEPTH_MS : NumSamplesDepth + ENABLE_RIPPLES : UseRipples + ENABLE_HQ_SHORELINE : UseHQShoreline + ENABLE_SPECULAR : UseSpecular + ENABLE_FOAM : UseFoam + ENABLE_CAUSTICS : UseCaustics + ENABLE_REFRACTION : UseRefraction } } @@ -68,7 +77,9 @@ MaterialDef Advanced Water { ENABLE_HQ_SHORELINE : UseHQShoreline ENABLE_SPECULAR : UseSpecular ENABLE_FOAM : UseFoam + ENABLE_CAUSTICS : UseCaustics ENABLE_REFRACTION : UseRefraction + } } diff --git a/engine/src/core-data/Common/MatDefs/Water/Water15.frag b/engine/src/core-data/Common/MatDefs/Water/Water15.frag index ee3d74487..8a9d7a736 100644 --- a/engine/src/core-data/Common/MatDefs/Water/Water15.frag +++ b/engine/src/core-data/Common/MatDefs/Water/Water15.frag @@ -12,6 +12,7 @@ uniform DEPTHTEXTURE m_DepthTexture; uniform sampler2D m_HeightMap; uniform sampler2D m_NormalMap; uniform sampler2D m_FoamMap; +uniform sampler2D m_CausticsMap; uniform sampler2D m_ReflectionMap; uniform mat4 m_ViewProjectionMatrixInverse; @@ -38,11 +39,6 @@ uniform vec2 m_WindDirection; uniform float m_SunScale; uniform float m_WaveScale; -uniform bool m_UseRipples, - m_UseHQShoreline, - m_UseSpecular, - m_UseFoam, - m_UseRefraction; vec2 scale = vec2(m_WaveScale, m_WaveScale); float refractionScale = m_WaveScale; @@ -115,8 +111,124 @@ float fresnelTerm(in vec3 normal,in vec3 eyeVec){ return saturate(fresnel * (1.0 - saturate(m_R0)) + m_R0 - m_RefractionStrength); } +vec2 m_FrustumNearFar=vec2(1.0,50); +const float LOG2 = 1.442695; + +vec4 underWater(int sampleNum){ + + + float sceneDepth = fetchTextureSample(m_DepthTexture, texCoord, sampleNum).r; + vec3 color2 = fetchTextureSample(m_Texture, texCoord, sampleNum).rgb; + + vec3 position = getPosition(sceneDepth, texCoord); + float level = m_WaterHeight; + + vec3 eyeVec = position - m_CameraPosition; + + // Find intersection with water surface + vec3 eyeVecNorm = normalize(eyeVec); + float t = (level - m_CameraPosition.y) / eyeVecNorm.y; + vec3 surfacePoint = m_CameraPosition + eyeVecNorm * t; + + vec2 texC = vec2(0.0); + + float cameraDepth = length(m_CameraPosition - surfacePoint); + texC = (surfacePoint.xz + eyeVecNorm.xz) * scale + m_Time * 0.03 * m_WindDirection; + float bias = texture(m_HeightMap, texC).r; + level += bias * m_MaxAmplitude; + t = (level - m_CameraPosition.y) / eyeVecNorm.y; + surfacePoint = m_CameraPosition + eyeVecNorm * t; + eyeVecNorm = normalize(m_CameraPosition - surfacePoint); + + // Find normal of water surface + float normal1 = textureOffset(m_HeightMap, texC, ivec2(-1, 0)).r; + float normal2 = textureOffset(m_HeightMap, texC, ivec2( 1, 0)).r; + float normal3 = textureOffset(m_HeightMap, texC, ivec2( 0, -1)).r; + float normal4 = textureOffset(m_HeightMap, texC, ivec2( 0, 1)).r; + + vec3 myNormal = normalize(vec3((normal1 - normal2) * m_MaxAmplitude,m_NormalScale,(normal3 - normal4) * m_MaxAmplitude)); + vec3 normal = myNormal*-1.0; + float fresnel = fresnelTerm(normal, eyeVecNorm); + + vec3 refraction = color2; + #ifdef ENABLE_REFRACTION + texC = texCoord.xy *sin (fresnel+1.0); + #ifdef RESOLVE_MS + ivec2 iTexC = ivec2(texC * textureSize(m_Texture)); + refraction = texelFetch(m_Texture, iTexC, sampleNum).rgb; + #else + ivec2 iTexC = ivec2(texC * textureSize(m_Texture, 0)); + refraction = texelFetch(m_Texture, iTexC, 0).rgb; + #endif + #endif + + float waterCol = saturate(length(m_LightColor.rgb) / m_SunScale); + refraction = mix(mix(refraction, m_DeepWaterColor.rgb * waterCol, m_WaterTransparency), m_WaterColor.rgb* waterCol,m_WaterTransparency); + + vec3 foam = vec3(0.0); + #ifdef ENABLE_FOAM + texC = (surfacePoint.xz + eyeVecNorm.xz * 0.1) * 0.05 + m_Time * 0.05 * m_WindDirection + sin(m_Time * 0.001 + position.x) * 0.005; + vec2 texCoord2 = (surfacePoint.xz + eyeVecNorm.xz * 0.1) * 0.05 + m_Time * 0.1 * m_WindDirection + sin(m_Time * 0.001 + position.z) * 0.005; + + if(m_MaxAmplitude - m_FoamExistence.z> 0.0001){ + foam += ((texture2D(m_FoamMap, texC) + texture2D(m_FoamMap, texCoord2)) * m_FoamIntensity * m_FoamIntensity * 0.3 * + saturate((level - (m_WaterHeight + m_FoamExistence.z)) / (m_MaxAmplitude - m_FoamExistence.z))).rgb; + } + foam *= m_LightColor.rgb; + #endif + + + + vec3 specular = vec3(0.0); + vec3 color ; + float fogFactor; + + if(position.y>level){ + #ifdef ENABLE_SPECULAR + if(step(0.9999,sceneDepth)==1.0){ + vec3 lightDir=normalize(m_LightDir); + vec3 mirrorEye = (2.0 * dot(eyeVecNorm, normal) * normal - eyeVecNorm); + float dotSpec = saturate(dot(mirrorEye.xyz, -lightDir) * 0.5 + 0.5); + specular = vec3((1.0 - fresnel) * saturate(-lightDir.y) * ((pow(dotSpec, 512.0)) * (m_Shininess * 1.8 + 0.2))); + specular += specular * 25.0 * saturate(m_Shininess - 0.05); + specular=specular * m_LightColor.rgb * 100.0; + } + #endif + float fogIntensity= 8 * m_WaterTransparency; + fogFactor = exp2( -fogIntensity * fogIntensity * cameraDepth * 0.03 * LOG2 ); + fogFactor = clamp(fogFactor, 0.0, 1.0); + color =mix(m_DeepWaterColor.rgb,refraction,fogFactor); + specular=specular*fogFactor; + color = saturate(color + max(specular, foam )); + }else{ + vec3 caustics = vec3(0.0); + #ifdef ENABLE_CAUSTICS + vec2 windDirection=m_WindDirection; + texC = (position.xz + eyeVecNorm.xz * 0.1) * 0.05 + m_Time * 0.05 * windDirection + sin(m_Time + position.x) * 0.01; + vec2 texCoord2 = (position.xz + eyeVecNorm.xz * 0.1) * 0.05 + m_Time * 0.05 * windDirection + sin(m_Time + position.z) * 0.01; + caustics += (texture2D(m_CausticsMap, texC)+ texture2D(m_CausticsMap, texCoord2)).rgb; + caustics *= m_WaterColor.rgb; + color=mix(color2, caustics,0.6); + #else + color=color2; + #endif + + float fogDepth= (2.0 * m_FrustumNearFar.x) / (m_FrustumNearFar.y + m_FrustumNearFar.x - sceneDepth* (m_FrustumNearFar.y-m_FrustumNearFar.x)); + float fogIntensity= 18 * m_WaterTransparency; + fogFactor = exp2( -fogIntensity * fogIntensity * fogDepth * fogDepth * LOG2 ); + fogFactor = clamp(fogFactor, 0.0, 1.0); + color =mix(m_DeepWaterColor.rgb,color,fogFactor); + } + + return vec4(color, 1.0); +} // NOTE: This will be called even for single-sampling vec4 main_multiSample(int sampleNum){ + // If we are underwater let's call the underwater function + if(m_WaterHeight >= m_CameraPosition.y){ + + return underWater(sampleNum); + } float sceneDepth = fetchTextureSample(m_DepthTexture, texCoord, sampleNum).r; vec3 color2 = fetchTextureSample(m_Texture, texCoord, sampleNum).rgb; @@ -125,22 +237,17 @@ vec4 main_multiSample(int sampleNum){ vec3 position = getPosition(sceneDepth, texCoord); float level = m_WaterHeight; - - // If we are underwater let's leave out complex computations - if(level >= m_CameraPosition.y){ - return vec4(color2, 1.0); - } - + float isAtFarPlane = step(0.99998, sceneDepth); //#ifndef ENABLE_RIPPLES // This optimization won't work on NVIDIA cards if ripples are enabled if(position.y > level + m_MaxAmplitude + isAtFarPlane * 100.0){ + return vec4(color2, 1.0); } //#endif - vec3 eyeVec = position - m_CameraPosition; - float diff = level - position.y; + vec3 eyeVec = position - m_CameraPosition; float cameraDepth = m_CameraPosition.y - position.y; // Find intersection with water surface @@ -150,9 +257,9 @@ vec4 main_multiSample(int sampleNum){ vec2 texC = vec2(0.0); int samples = 1; - if (m_UseHQShoreline){ + #ifdef ENABLE_HQ_SHORELINE samples = 10; - } + #endif float biasFactor = 1.0 / samples; for (int i = 0; i < samples; i++){ @@ -187,7 +294,7 @@ vec4 main_multiSample(int sampleNum){ vec3 myNormal = normalize(vec3((normal1 - normal2) * m_MaxAmplitude,m_NormalScale,(normal3 - normal4) * m_MaxAmplitude)); vec3 normal = vec3(0.0); - if (m_UseRipples){ + #ifdef ENABLE_RIPPLES texC = surfacePoint.xz * 0.8 + m_WindDirection * m_Time* 1.6; mat3 tangentFrame = computeTangentFrame(myNormal, eyeVecNorm, texC); vec3 normal0a = normalize(tangentFrame*(2.0 * texture(m_NormalMap, texC).xyz - 1.0)); @@ -213,12 +320,12 @@ vec4 main_multiSample(int sampleNum){ // gl_FragColor = vec4(color2 + normal*0.0001, 1.0); // return; //} - }else{ + #else normal = myNormal; - } + #endif vec3 refraction = color2; - if (m_UseRefraction){ + #ifdef ENABLE_REFRACTION // texC = texCoord.xy+ m_ReflectionDisplace * normal.x; texC = texCoord.xy; texC += sin(m_Time*1.8 + 3.0 * abs(position.y)) * (refractionScale * min(depth2, 1.0)); @@ -229,7 +336,7 @@ vec4 main_multiSample(int sampleNum){ ivec2 iTexC = ivec2(texC * textureSize(m_Texture, 0)); refraction = texelFetch(m_Texture, iTexC, 0).rgb; #endif - } + #endif vec3 waterPosition = surfacePoint.xyz; waterPosition.y -= (level - m_WaterHeight); @@ -249,8 +356,11 @@ vec4 main_multiSample(int sampleNum){ refraction = mix(mix(refraction, m_WaterColor.rgb * waterCol, saturate(depthN / visibility)), m_DeepWaterColor.rgb * waterCol, saturate(depth2 / m_ColorExtinction)); + + + vec3 foam = vec3(0.0); - if (m_UseFoam){ + #ifdef ENABLE_FOAM texC = (surfacePoint.xz + eyeVecNorm.xz * 0.1) * 0.05 + m_Time * 0.05 * m_WindDirection + sin(m_Time * 0.001 + position.x) * 0.005; vec2 texCoord2 = (surfacePoint.xz + eyeVecNorm.xz * 0.1) * 0.05 + m_Time * 0.1 * m_WindDirection + sin(m_Time * 0.001 + position.z) * 0.005; @@ -267,10 +377,10 @@ vec4 main_multiSample(int sampleNum){ saturate((level - (m_WaterHeight + m_FoamExistence.z)) / (m_MaxAmplitude - m_FoamExistence.z))).rgb; } foam *= m_LightColor.rgb; - } + #endif vec3 specular = vec3(0.0); - if (m_UseSpecular){ + #ifdef ENABLE_SPECULAR vec3 lightDir=normalize(m_LightDir); vec3 mirrorEye = (2.0 * dot(eyeVecNorm, normal) * normal - eyeVecNorm); float dotSpec = saturate(dot(mirrorEye.xyz, -lightDir) * 0.5 + 0.5); @@ -278,7 +388,7 @@ vec4 main_multiSample(int sampleNum){ specular += specular * 25.0 * saturate(m_Shininess - 0.05); //foam does not shine specular=specular * m_LightColor.rgb - (5.0 * foam); - } + #endif color = mix(refraction, reflection, fresnel); color = mix(refraction, color, saturate(depth * m_ShoreHardness)); diff --git a/engine/src/desktop-fx/com/jme3/post/filters/FogFilter.java b/engine/src/desktop-fx/com/jme3/post/filters/FogFilter.java index 1739756d4..b075476b9 100644 --- a/engine/src/desktop-fx/com/jme3/post/filters/FogFilter.java +++ b/engine/src/desktop-fx/com/jme3/post/filters/FogFilter.java @@ -58,6 +58,12 @@ public class FogFilter extends Filter { super("FogFilter"); } + /** + * Create a fog filter + * @param fogColor the color of the fog (default is white) + * @param fogDensity the density of the fog (default is 0.7) + * @param fogDistance the distance of the fog (default is 1000) + */ public FogFilter(ColorRGBA fogColor, float fogDensity, float fogDistance) { this(); this.fogColor = fogColor; diff --git a/engine/src/desktop-fx/com/jme3/post/filters/LightScatteringFilter.java b/engine/src/desktop-fx/com/jme3/post/filters/LightScatteringFilter.java index 1dda176e0..0056506d7 100644 --- a/engine/src/desktop-fx/com/jme3/post/filters/LightScatteringFilter.java +++ b/engine/src/desktop-fx/com/jme3/post/filters/LightScatteringFilter.java @@ -59,7 +59,7 @@ public class LightScatteringFilter extends Filter { private float lightDensity = 1.4f; private boolean adaptative = true; Vector3f viewLightPos = new Vector3f(); - private boolean display; + private boolean display=true; private float innerLightDensity; public LightScatteringFilter() { @@ -101,6 +101,8 @@ public class LightScatteringFilter extends Filter { //System.err.println("screenLightPos "+screenLightPos); if (adaptative) { innerLightDensity = Math.max(lightDensity - Math.max(screenLightPos.x, screenLightPos.y), 0.0f); + }else{ + innerLightDensity=lightDensity; } } diff --git a/engine/src/desktop-fx/com/jme3/water/WaterFilter.java b/engine/src/desktop-fx/com/jme3/water/WaterFilter.java index 01b66ebae..67e2cdda8 100644 --- a/engine/src/desktop-fx/com/jme3/water/WaterFilter.java +++ b/engine/src/desktop-fx/com/jme3/water/WaterFilter.java @@ -67,6 +67,7 @@ public class WaterFilter extends Filter { protected ViewPort reflectionView; private Texture2D normalTexture; private Texture2D foamTexture; + private Texture2D causticsTexture; private Texture2D heightTexture; private Plane plane; private Camera reflectionCam; @@ -102,11 +103,13 @@ public class WaterFilter extends Filter { private boolean useHQShoreline = true; private boolean useSpecular = true; private boolean useFoam = true; + private boolean useCaustics = true; private boolean useRefraction = true; private float time = 0; private float savedTpf = 0; private float reflectionDisplace = 30; private float foamIntensity = 0.5f; + private boolean underWater; /** * Create a Water Filter @@ -126,11 +129,6 @@ public class WaterFilter extends Filter { return true; } - @Override - protected Format getDefaultPassDepthFormat() { - return Format.Depth; - } - @Override public void preFrame(float tpf) { time = time + (tpf * speed); @@ -181,17 +179,23 @@ public class WaterFilter extends Filter { reflectionCam.setAxes(reflectionCam.getLeft().negateLocal(), reflectionCam.getUp(), reflectionCam.getDirection().negateLocal()); } - boolean rtb = true; - if (!renderManager.isHandleTranslucentBucket()) { - renderManager.setHandleTranslucentBucket(true); - rtb = false; - } - renderManager.renderViewPort(reflectionView, savedTpf); - if (!rtb) { - renderManager.setHandleTranslucentBucket(false); + //if we're under water no need to compute reflection + if (sceneCam.getLocation().y >= waterHeight) { + boolean rtb = true; + if (!renderManager.isHandleTranslucentBucket()) { + renderManager.setHandleTranslucentBucket(true); + rtb = false; + } + renderManager.renderViewPort(reflectionView, savedTpf); + if (!rtb) { + renderManager.setHandleTranslucentBucket(false); + } + renderManager.getRenderer().setFrameBuffer(viewPort.getOutputFrameBuffer()); + renderManager.setCamera(sceneCam, false); + underWater=false; + }else{ + underWater=true; } - renderManager.getRenderer().setFrameBuffer(viewPort.getOutputFrameBuffer()); - renderManager.setCamera(sceneCam, false); } @Override @@ -217,14 +221,19 @@ public class WaterFilter extends Filter { if (foamTexture == null) { foamTexture = (Texture2D) manager.loadTexture("Common/MatDefs/Water/Textures/foam.jpg"); } + if (causticsTexture == null) { + causticsTexture = (Texture2D) manager.loadTexture("Common/MatDefs/Water/Textures/caustics.jpg"); + } heightTexture = (Texture2D) manager.loadTexture("Common/MatDefs/Water/Textures/heightmap.jpg"); normalTexture.setWrap(WrapMode.Repeat); foamTexture.setWrap(WrapMode.Repeat); + causticsTexture.setWrap(WrapMode.Repeat); heightTexture.setWrap(WrapMode.Repeat); material = new Material(manager, "Common/MatDefs/Water/Water.j3md"); material.setTexture("HeightMap", heightTexture); + material.setTexture("CausticsMap", causticsTexture); material.setTexture("FoamMap", foamTexture); material.setTexture("NormalMap", normalTexture); material.setTexture("ReflectionMap", reflectionPass.getRenderedTexture()); @@ -250,6 +259,7 @@ public class WaterFilter extends Filter { material.setBoolean("UseHQShoreline", useHQShoreline); material.setBoolean("UseSpecular", useSpecular); material.setBoolean("UseFoam", useFoam); + material.setBoolean("UseCaustics", useCaustics); material.setBoolean("UseRefraction", useRefraction); material.setFloat("ReflectionDisplace", reflectionDisplace); material.setFloat("FoamIntensity", foamIntensity); @@ -293,6 +303,10 @@ public class WaterFilter extends Filter { this.waterHeight = waterHeight; } + /** + * sets the scene to render in the reflection map + * @param reflectionScene + */ public void setReflectionScene(Spatial reflectionScene) { this.reflectionScene = reflectionScene; } @@ -340,6 +354,10 @@ public class WaterFilter extends Filter { } } + /** + * returns the refractoin constant + * @return + */ public float getRefractionConstant() { return refractionConstant; } @@ -360,6 +378,10 @@ public class WaterFilter extends Filter { } } + /** + * return the maximum wave amplitude + * @return + */ public float getMaxAmplitude() { return maxAmplitude; } @@ -437,7 +459,7 @@ public class WaterFilter extends Filter { } /** - * retunrs the foam hardness + * returns the foam hardness * @return */ public float getFoamHardness() { @@ -739,7 +761,37 @@ public class WaterFilter extends Filter { } /** - * + * sets the texture to use to render caustics on the ground underwater + * @param causticsTexture + */ + public void setCausticsTexture(Texture2D causticsTexture) { + this.causticsTexture = causticsTexture; + if (material != null) { + material.setTexture("causticsMap", causticsTexture); + } + } + + /** + * returns true if caustics are rendered + * @return + */ + public boolean isUseCaustics() { + return useCaustics; + } + + /** + * set to true if you want caustics to be rendered on the ground underwater, false otherwise + * @param useCaustics + */ + public void setUseCaustics(boolean useCaustics) { + this.useCaustics = useCaustics; + if (material != null) { + material.setBoolean("UseCaustics", useCaustics); + } + } + + /** + * return true * @return */ public boolean isUseHQShoreline() { @@ -810,7 +862,15 @@ public class WaterFilter extends Filter { if (material != null) { material.setFloat("m_ReflectionDisplace", reflectionDisplace); } + } - + /** + * returns true if the camera is under the water level + * @return + */ + public boolean isUnderWater() { + return underWater; } + + } diff --git a/engine/src/test/jme3test/water/TestPostWater.java b/engine/src/test/jme3test/water/TestPostWater.java index c76174c2b..0ed88ad68 100644 --- a/engine/src/test/jme3test/water/TestPostWater.java +++ b/engine/src/test/jme3test/water/TestPostWater.java @@ -2,6 +2,8 @@ package jme3test.water; import com.jme3.app.SimpleApplication; import com.jme3.audio.AudioNode; +import com.jme3.audio.Filter; +import com.jme3.audio.LowPassFilter; import com.jme3.bounding.BoundingBox; import com.jme3.effect.ParticleEmitter; import com.jme3.effect.ParticleMesh; @@ -16,7 +18,9 @@ import com.jme3.math.Quaternion; import com.jme3.math.Vector3f; import com.jme3.post.FilterPostProcessor; +import com.jme3.post.filters.BloomFilter; import com.jme3.post.filters.DepthOfFieldFilter; +import com.jme3.post.filters.FogFilter; import com.jme3.post.filters.LightScatteringFilter; import com.jme3.post.filters.TranslucentBucketFilter; import com.jme3.renderer.Camera; @@ -34,6 +38,7 @@ import com.jme3.texture.Texture.WrapMode; import com.jme3.texture.Texture2D; import com.jme3.util.SkyFactory; import com.jme3.water.WaterFilter; +import java.awt.image.BufferedImage; import java.util.ArrayList; import java.util.List; import jme3tools.converters.ImageToAwt; @@ -48,6 +53,10 @@ public class TestPostWater extends SimpleApplication { private WaterFilter water; TerrainQuad terrain; Material matRock; + AudioNode waves; + LowPassFilter underWaterAudioFilter = new LowPassFilter(0.5f, 0.1f); + LowPassFilter underWaterReverbFilter = new LowPassFilter(0.5f, 0.1f); + LowPassFilter aboveWaterAudioFilter = new LowPassFilter(1, 1); public static void main(String[] args) { TestPostWater app = new TestPostWater(); @@ -57,6 +66,8 @@ public class TestPostWater extends SimpleApplication { @Override public void simpleInitApp() { + setDisplayFps(false); + setDisplayStatView(false); Node mainScene = new Node("Main Scene"); rootNode.attachChild(mainScene); @@ -72,9 +83,14 @@ public class TestPostWater extends SimpleApplication { l.setColor(ColorRGBA.White.clone().multLocal(0.3f)); mainScene.addLight(l); - flyCam.setMoveSpeed(100); + flyCam.setMoveSpeed(50); - cam.setLocation(new Vector3f(-700, 100, 300)); + //cam.setLocation(new Vector3f(-700, 100, 300)); + //cam.setRotation(new Quaternion().fromAngleAxis(0.5f, Vector3f.UNIT_Z)); + cam.setLocation(new Vector3f(-327.21957f, 61.6459f, 126.884346f)); + cam.setRotation(new Quaternion(0.052168474f, 0.9443102f, -0.18395276f, 0.2678024f)); + + cam.setRotation(new Quaternion().fromAngles(new float[]{FastMath.PI * 0.06f, FastMath.PI * 0.65f, 0})); @@ -83,9 +99,8 @@ public class TestPostWater extends SimpleApplication { mainScene.attachChild(sky); cam.setFrustumFar(4000); //cam.setFrustumNear(100); - AudioNode waves = new AudioNode(audioRenderer, assetManager, "Sound/Environment/Ocean Waves.ogg", false); - waves.setLooping(true); - audioRenderer.playSource(waves); + + //private FilterPostProcessor fpp; @@ -94,15 +109,22 @@ public class TestPostWater extends SimpleApplication { FilterPostProcessor fpp = new FilterPostProcessor(assetManager); fpp.addFilter(water); - + BloomFilter bloom=new BloomFilter(); + bloom.setExposurePower(55); + fpp.addFilter(bloom); + LightScatteringFilter lsf = new LightScatteringFilter(lightDir.mult(-300)); + lsf.setLightDensity(1.0f); + fpp.addFilter(lsf); DepthOfFieldFilter dof=new DepthOfFieldFilter(); dof.setFocusDistance(0); - dof.setFocusRange(100); - fpp.addFilter(new TranslucentBucketFilter()); + dof.setFocusRange(100); fpp.addFilter(dof); +// + // fpp.addFilter(new TranslucentBucketFilter()); + // - // fpp.setNumSamples(4); + // fpp.setNumSamples(4); water.setWaveScale(0.003f); @@ -116,8 +138,17 @@ public class TestPostWater extends SimpleApplication { //water.setFoamHardness(0.6f); water.setWaterHeight(initialWaterHeight); - + uw=cam.getLocation().y