From e89e0e7c12fee154181d710cecb18fcb6b62d60b Mon Sep 17 00:00:00 2001 From: Nehon Date: Sat, 6 Aug 2016 15:16:20 +0200 Subject: [PATCH] Added a way to approximate the normals for the SSAO filter instead of rendering an additional geometry pass. --- .../java/com/jme3/post/ssao/SSAOFilter.java | 29 +++-- .../resources/Common/MatDefs/SSAO/ssao.frag | 47 +++++--- .../resources/Common/MatDefs/SSAO/ssao.j3md | 9 +- .../resources/Common/MatDefs/SSAO/ssao15.frag | 43 +++++-- .../src/main/java/jme3test/post/SSAOUI.java | 8 +- .../src/main/java/jme3test/post/TestSSAO.java | 3 +- .../main/java/jme3test/post/TestSSAO2.java | 108 ++++++++++++++++++ 7 files changed, 212 insertions(+), 35 deletions(-) create mode 100644 jme3-examples/src/main/java/jme3test/post/TestSSAO2.java diff --git a/jme3-effects/src/main/java/com/jme3/post/ssao/SSAOFilter.java b/jme3-effects/src/main/java/com/jme3/post/ssao/SSAOFilter.java index 7e9012b6d..860b834df 100644 --- a/jme3-effects/src/main/java/com/jme3/post/ssao/SSAOFilter.java +++ b/jme3-effects/src/main/java/com/jme3/post/ssao/SSAOFilter.java @@ -78,6 +78,7 @@ public class SSAOFilter extends Filter { private float downSampleFactor = 1f; private RenderManager renderManager; private ViewPort viewPort; + private boolean approximateNormals = false; /** * Create a Screen Space Ambient Occlusion Filter @@ -108,13 +109,15 @@ public class SSAOFilter extends Filter { @Override protected void postQueue(RenderQueue queue) { - Renderer r = renderManager.getRenderer(); - r.setFrameBuffer(normalPass.getRenderFrameBuffer()); - renderManager.getRenderer().clearBuffers(true, true, true); - renderManager.setForcedTechnique("PreNormalPass"); - renderManager.renderViewPortQueues(viewPort, false); - renderManager.setForcedTechnique(null); - renderManager.getRenderer().setFrameBuffer(viewPort.getOutputFrameBuffer()); + if(!approximateNormals) { + Renderer r = renderManager.getRenderer(); + r.setFrameBuffer(normalPass.getRenderFrameBuffer()); + renderManager.getRenderer().clearBuffers(true, true, true); + renderManager.setForcedTechnique("PreNormalPass"); + renderManager.renderViewPortQueues(viewPort, false); + renderManager.setForcedTechnique(null); + renderManager.getRenderer().setFrameBuffer(viewPort.getOutputFrameBuffer()); + } } @Override @@ -178,6 +181,7 @@ public class SSAOFilter extends Filter { ssaoMat.setVector2("FrustumNearFar", frustumNearFar); material.setVector2("FrustumNearFar", frustumNearFar); ssaoMat.setParam("Samples", VarType.Vector2Array, samples); + ssaoMat.setBoolean("ApproximateNormals", approximateNormals); float xScale = 1.0f / w; float yScale = 1.0f / h; @@ -294,6 +298,17 @@ public class SSAOFilter extends Filter { } + public void setApproximateNormals(boolean approximateNormals) { + this.approximateNormals = approximateNormals; + if (ssaoMat != null) { + ssaoMat.setBoolean("ApproximateNormals", approximateNormals); + } + } + + public boolean isApproximateNormals() { + return approximateNormals; + } + /** * debugging only , will be removed * @return useOnlyAo diff --git a/jme3-effects/src/main/resources/Common/MatDefs/SSAO/ssao.frag b/jme3-effects/src/main/resources/Common/MatDefs/SSAO/ssao.frag index c9a9c0ff2..f74015b34 100644 --- a/jme3-effects/src/main/resources/Common/MatDefs/SSAO/ssao.frag +++ b/jme3-effects/src/main/resources/Common/MatDefs/SSAO/ssao.frag @@ -1,4 +1,5 @@ uniform vec2 g_Resolution; +uniform vec2 g_ResolutionInverse; uniform vec2 m_FrustumNearFar; uniform sampler2D m_Texture; uniform sampler2D m_Normals; @@ -15,12 +16,9 @@ uniform vec2[4] m_Samples; varying vec2 texCoord; -float depthv; - -vec3 getPosition(in vec2 uv){ +vec3 getPosition(float depthv, in vec2 uv){ //Reconstruction from depth - depthv =texture2D(m_DepthTexture,uv).r; - float depth= (2.0 * m_FrustumNearFar.x) / (m_FrustumNearFar.y + m_FrustumNearFar.x - depthv* (m_FrustumNearFar.y-m_FrustumNearFar.x)); + float depth = (2.0 * m_FrustumNearFar.x) / (m_FrustumNearFar.y + m_FrustumNearFar.x - depthv * (m_FrustumNearFar.y-m_FrustumNearFar.x)); //one frustum corner method float x = mix(-m_FrustumCorner.x, m_FrustumCorner.x, uv.x); @@ -29,6 +27,19 @@ vec3 getPosition(in vec2 uv){ return depth* vec3(x, y, m_FrustumCorner.z); } +vec3 approximateNormal(in vec3 pos,in vec2 texCoord){ + float step = g_ResolutionInverse.x ; + float stepy = g_ResolutionInverse.y ; + float depth2 = texture2D(m_DepthTexture,texCoord + vec2(step,-stepy)).r; + float depth3 = texture2D(m_DepthTexture,texCoord + vec2(-step,-stepy)).r; + vec3 pos2 = vec3(getPosition(depth2,texCoord + vec2(step,-stepy))); + vec3 pos3 = vec3(getPosition(depth3,texCoord + vec2(-step,-stepy))); + + vec3 v1 = (pos - pos2).xyz; + vec3 v2 = (pos3 - pos2).xyz; + return normalize(cross(-v1, v2)); +} + vec3 getNormal(in vec2 uv){ return normalize(texture2D(m_Normals, uv).xyz * 2.0 - 1.0); } @@ -39,7 +50,8 @@ vec2 getRandom(in vec2 uv){ } float doAmbientOcclusion(in vec2 tc, in vec3 pos, in vec3 norm){ - vec3 diff = getPosition(tc)- pos; + float depthv = texture2D(m_DepthTexture, tc).r; + vec3 diff = getPosition(depthv, tc)- pos; vec3 v = normalize(diff); float d = length(diff) * m_Scale; @@ -58,14 +70,21 @@ void main(){ float result; - //vec2 vec[4] = { vec2(1.0, 0.0), vec2(-1.0, 0.0), vec2(0.0, 1.0), vec2(0.0, -1.0) }; - vec3 position = getPosition(texCoord); - //optimization, do not calculate AO if depth is 1 - if(depthv==1.0){ - gl_FragColor=vec4(1.0); - return; + + float depthv = texture2D(m_DepthTexture, texCoord).r; + //optimization, do not calculate AO if depth is 1 + if(depthv == 1.0){ + gl_FragColor = vec4(1.0); + return; } - vec3 normal = getNormal(texCoord); + vec3 position = getPosition(depthv, texCoord); + + #ifdef APPROXIMATE_NORMALS + vec3 normal = approximateNormal(position, texCoord); + #else + vec3 normal = getNormal(texCoord); + #endif + vec2 rand = getRandom(texCoord); float ao = 0.0; @@ -85,5 +104,5 @@ void main(){ ao /= float(iterations) * 4.0; result = 1.0 - ao; - gl_FragColor=vec4(result); + gl_FragColor = vec4(result); } \ No newline at end of file diff --git a/jme3-effects/src/main/resources/Common/MatDefs/SSAO/ssao.j3md b/jme3-effects/src/main/resources/Common/MatDefs/SSAO/ssao.j3md index d19d98a3b..b2e03c697 100644 --- a/jme3-effects/src/main/resources/Common/MatDefs/SSAO/ssao.j3md +++ b/jme3-effects/src/main/resources/Common/MatDefs/SSAO/ssao.j3md @@ -14,20 +14,23 @@ MaterialDef SSAO { Float Bias Vector2 FrustumNearFar Vector2Array Samples + Boolean ApproximateNormals } Technique { VertexShader GLSL150: Common/MatDefs/Post/Post15.vert FragmentShader GLSL150: Common/MatDefs/SSAO/ssao15.frag - WorldParameters { + WorldParameters { WorldViewMatrix Resolution + ResolutionInverse } Defines { RESOLVE_MS : NumSamples RESOLVE_DEPTH_MS : NumSamplesDepth + APPROXIMATE_NORMALS : ApproximateNormals } } @@ -38,6 +41,10 @@ MaterialDef SSAO { WorldParameters { WorldViewMatrix Resolution + ResolutionInverse + } + Defines { + APPROXIMATE_NORMALS : ApproximateNormals } } } \ No newline at end of file diff --git a/jme3-effects/src/main/resources/Common/MatDefs/SSAO/ssao15.frag b/jme3-effects/src/main/resources/Common/MatDefs/SSAO/ssao15.frag index 707af7335..7a59f1f36 100644 --- a/jme3-effects/src/main/resources/Common/MatDefs/SSAO/ssao15.frag +++ b/jme3-effects/src/main/resources/Common/MatDefs/SSAO/ssao15.frag @@ -3,6 +3,7 @@ uniform COLORTEXTURE m_Texture; uniform DEPTHTEXTURE m_DepthTexture; +uniform vec2 g_ResolutionInverse; uniform vec2 g_Resolution; uniform vec2 m_FrustumNearFar; uniform sampler2D m_Normals; @@ -18,12 +19,11 @@ in vec2 texCoord; out vec4 fragColor; -float depthv; -vec3 getPosition(in vec2 uv){ + +vec3 getPosition(float depthv, in vec2 uv){ //Reconstruction from depth - depthv =getDepth(m_DepthTexture,uv).r; - float depth= (2.0 * m_FrustumNearFar.x) / (m_FrustumNearFar.y + m_FrustumNearFar.x - depthv* (m_FrustumNearFar.y-m_FrustumNearFar.x)); + float depth = (2.0 * m_FrustumNearFar.x) / (m_FrustumNearFar.y + m_FrustumNearFar.x - depthv * (m_FrustumNearFar.y-m_FrustumNearFar.x)); //one frustum corner method float x = mix(-m_FrustumCorner.x, m_FrustumCorner.x, uv.x); @@ -36,6 +36,19 @@ vec3 getNormal(in vec2 uv){ return normalize(texture2D(m_Normals, uv).xyz * 2.0 - 1.0); } +vec3 approximateNormal(in vec3 pos,in vec2 texCoord){ + float step = g_ResolutionInverse.x ; + float stepy = g_ResolutionInverse.y ; + float depth2 = getDepth(m_DepthTexture,texCoord + vec2(step,-stepy)).r; + float depth3 = getDepth(m_DepthTexture,texCoord + vec2(-step,-stepy)).r; + vec3 pos2 = vec3(getPosition(depth2,texCoord + vec2(step,-stepy))); + vec3 pos3 = vec3(getPosition(depth3,texCoord + vec2(-step,-stepy))); + + vec3 v1 = (pos - pos2).xyz; + vec3 v2 = (pos3 - pos2).xyz; + return normalize(cross(-v1, v2)); +} + vec2 getRandom(in vec2 uv){ //float rand=(fract(uv.x*(g_Resolution.x/2.0))*0.25)+(fract(uv.y*(g_Resolution.y/2.0))*0.5); vec4 rand=texture2D(m_RandomMap, g_Resolution * uv / 128.0 * 3.0)*2.0 -1.0; @@ -44,7 +57,8 @@ vec2 getRandom(in vec2 uv){ } float doAmbientOcclusion(in vec2 tc, in vec3 pos, in vec3 norm){ - vec3 diff = getPosition(tc)- pos; + float depthv = getDepth(m_DepthTexture, tc).r; + vec3 diff = getPosition(depthv, tc)- pos; vec3 v = normalize(diff); float d = length(diff) * m_Scale; @@ -63,13 +77,20 @@ void main(){ float result; - vec3 position = getPosition(texCoord); - //optimization, do not calculate AO if depth is 1 - if(depthv==1.0){ - fragColor = vec4(1.0); - return; + float depthv = getDepth(m_DepthTexture, texCoord).r; + //optimization, do not calculate AO if depth is 1 + if(depthv == 1.0){ + fragColor = vec4(1.0); + return; } - vec3 normal = getNormal(texCoord); + vec3 position = getPosition(depthv, texCoord); + + #ifdef APPROXIMATE_NORMALS + vec3 normal = approximateNormal(position, texCoord); + #else + vec3 normal = getNormal(texCoord); + #endif + vec2 rand = getRandom(texCoord); float ao = 0.0; diff --git a/jme3-examples/src/main/java/jme3test/post/SSAOUI.java b/jme3-examples/src/main/java/jme3test/post/SSAOUI.java index a5b7ba8e8..3314d2c7b 100644 --- a/jme3-examples/src/main/java/jme3test/post/SSAOUI.java +++ b/jme3-examples/src/main/java/jme3test/post/SSAOUI.java @@ -73,6 +73,7 @@ public class SSAOUI { inputManager.addMapping("outputConfig", new KeyTrigger(KeyInput.KEY_P)); inputManager.addMapping("toggleUseAO", new KeyTrigger(KeyInput.KEY_SPACE)); inputManager.addMapping("toggleUseOnlyAo", new KeyTrigger(KeyInput.KEY_NUMPAD0)); + inputManager.addMapping("toggleApprox", new KeyTrigger(KeyInput.KEY_NUMPAD5)); ActionListener acl = new ActionListener() { @@ -83,6 +84,11 @@ public class SSAOUI { // filter.setUseAo(!filter.isUseAo()); System.out.println("use AO : " + filter.isEnabled()); } + if (name.equals("toggleApprox") && keyPressed) { + filter.setApproximateNormals(!filter.isApproximateNormals()); + System.out.println("Approximate Normals : " + filter.isApproximateNormals()); + + } if (name.equals("toggleUseOnlyAo") && keyPressed) { filter.setUseOnlyAo(!filter.isUseOnlyAo()); System.out.println("use Only AO : " + filter.isUseOnlyAo()); @@ -132,7 +138,7 @@ public class SSAOUI { } }; - inputManager.addListener(acl, "toggleUseAO", "toggleUseOnlyAo", "outputConfig"); + inputManager.addListener(acl, "toggleUseAO", "toggleApprox", "toggleUseOnlyAo", "outputConfig"); inputManager.addListener(anl, "sampleRadiusUp", "sampleRadiusDown", "intensityUp", "intensityDown", "scaleUp", "scaleDown", "biasUp", "biasDown"); diff --git a/jme3-examples/src/main/java/jme3test/post/TestSSAO.java b/jme3-examples/src/main/java/jme3test/post/TestSSAO.java index 3cd508a5d..29ad9913b 100644 --- a/jme3-examples/src/main/java/jme3test/post/TestSSAO.java +++ b/jme3-examples/src/main/java/jme3test/post/TestSSAO.java @@ -83,7 +83,8 @@ public class TestSSAO extends SimpleApplication { rootNode.attachChild(model); FilterPostProcessor fpp = new FilterPostProcessor(assetManager); - SSAOFilter ssaoFilter = new SSAOFilter(12.940201f, 43.928635f, 0.32999992f, 0.6059958f); + SSAOFilter ssaoFilter = new SSAOFilter(2.9299974f,32.920483f,5.8100376f,0.091000035f);; + ssaoFilter.setApproximateNormals(true); fpp.addFilter(ssaoFilter); SSAOUI ui = new SSAOUI(inputManager, ssaoFilter); diff --git a/jme3-examples/src/main/java/jme3test/post/TestSSAO2.java b/jme3-examples/src/main/java/jme3test/post/TestSSAO2.java new file mode 100644 index 000000000..3219a17ae --- /dev/null +++ b/jme3-examples/src/main/java/jme3test/post/TestSSAO2.java @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2009-2012 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package jme3test.post; + +import com.jme3.app.SimpleApplication; +import com.jme3.light.*; +import com.jme3.material.Material; +import com.jme3.math.*; +import com.jme3.post.FilterPostProcessor; +import com.jme3.post.ssao.SSAOFilter; +import com.jme3.scene.*; +import com.jme3.scene.control.LodControl; +import com.jme3.scene.shape.Box; +import com.jme3.texture.Texture; + +public class TestSSAO2 extends SimpleApplication { + + Geometry model; + + public static void main(String[] args) { + TestSSAO2 app = new TestSSAO2(); + app.start(); + } + + @Override + public void simpleInitApp() { + DirectionalLight dl = new DirectionalLight(); + dl.setDirection(new Vector3f(-1,-1,-1).normalizeLocal()); + rootNode.addLight(dl); + + Material mat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md"); + mat.setFloat("Shininess", 16f); + //mat.setBoolean("VertexLighting", true); + + + Geometry floor = new Geometry("floor", new Box(1000,0.1f,1000)); + floor.setMaterial(mat); + rootNode.attachChild(floor); + + Node teapotNode = (Node) assetManager.loadModel("Models/Teapot/Teapot.mesh.xml"); + Geometry teapot = (Geometry) teapotNode.getChild(0); + teapot.setMaterial(mat); +// Sphere sph = new Sphere(16, 16, 4); +// Geometry teapot = new Geometry("teapot", sph); + + + + // show normals as material + //Material mat = new Material(assetManager, "Common/MatDefs/Misc/ShowNormals.j3md"); + for (int f = 10; f > 3; f--) { + for (int y = -f; y < f; y++) { + for (int x = -f; x < f; x++) { + Geometry clonePot = teapot.clone(); + + //clonePot.setMaterial(mat); + clonePot.setLocalTranslation(x * .5f, 10 - f, y * .5f); + clonePot.setLocalScale(.15f); + + rootNode.attachChild(clonePot); + } + } + } + + cam.setLocation(new Vector3f(10.247649f, 8.275992f, 10.405156f)); + cam.setRotation(new Quaternion(-0.083419204f, 0.90370524f, -0.20599906f, -0.36595422f)); + + + FilterPostProcessor fpp = new FilterPostProcessor(assetManager); + SSAOFilter ssaoFilter = new SSAOFilter(2.9299974f,25f,5.8100376f,0.091000035f); + fpp.addFilter(ssaoFilter); + SSAOUI ui = new SSAOUI(inputManager, ssaoFilter); + + viewPort.addProcessor(fpp); + } + + @Override + public void simpleUpdate(float tpf) { + } +}