Browse Source

Added a way to approximate the normals for the SSAO filter instead of rendering an additional geometry pass.

Nehon 9 years ago
parent
commit
e89e0e7c12

+ 22 - 7
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

+ 33 - 14
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);
 }

+ 8 - 1
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
         }
     }
 }

+ 32 - 11
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;

+ 7 - 1
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");
 

+ 2 - 1
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);
 

+ 108 - 0
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) {
+    }
+}