SSAO RenderPass, unfinished

I will probably not have the time to finish this feature but I post the code to it here so maybe someone wants to continue on it. It's a render state that enables SSAO, by just adding these lines of code to your project:


SSAORenderPass ssaoRenderPass = new SSAORenderPass(cam, 4);
ssaoRenderPass.add(rootNode);
pManager.add(ssaoRenderPass);



Here's the SSAORenderPass:

package ssao;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;

import jmetest.renderer.TestRenderToTexture;
import jmetest.renderer.state.TestGLSLShaderObjectsState;

import com.acarter.scenemonitor.SceneMonitor;
import com.jme.bounding.BoundingBox;
import com.jme.image.Texture;
import com.jme.image.Texture2D;
import com.jme.image.Texture.RenderToTextureType;
import com.jme.image.Texture.WrapMode;
import com.jme.math.Vector2f;
import com.jme.math.Vector3f;
import com.jme.renderer.Camera;
import com.jme.renderer.ColorRGBA;
import com.jme.renderer.Renderer;
import com.jme.renderer.TextureRenderer;
import com.jme.renderer.pass.Pass;
import com.jme.scene.Node;
import com.jme.scene.Spatial;
import com.jme.scene.shape.Box;
import com.jme.scene.shape.Quad;
import com.jme.scene.state.BlendState;
import com.jme.scene.state.GLSLShaderObjectsState;
import com.jme.scene.state.RenderState;
import com.jme.scene.state.TextureState;
import com.jme.scene.state.ZBufferState;
import com.jme.system.DisplaySystem;
import com.jme.system.JmeException;
import com.jme.util.TextureManager;

/**
 * This RenderPass must be added after the normal renderpass of the scene for the effect to be applied correctly.
 * @author Haladria
 *
 */
public class SSAORenderPass extends Pass
{
//   private final TextureRenderer tRendererHalf; // texture renderer with half the screen size
   private final TextureRenderer tRenderer;
   
   private final ArrayList<Texture> textures;
   
   private final Texture2D normalAndDepthTexture;
   private final Texture2D SSAOTexture;
   private final Texture2D SSAOBlurXTexture;
   private final Texture2D SSAOBlurYTexture;
   private final Texture noiseTexture;
   
   private final TextureState fsQuadTextureState;
   
   private final  GLSLShaderObjectsState preRenderShader;
   private final  GLSLShaderObjectsState SSAOShader;
   private final  GLSLShaderObjectsState blurXShader;
   private final  GLSLShaderObjectsState blurYShader;
   private final  GLSLShaderObjectsState texturingShader;
   
   private final Quad fullScreenQuad;
   
   private final int selectedTextureWidth;
   
   private final Camera cam;
   
   private final BlendState blendState;
   
   float blurFallOff = 0.003f,
            blurSharpness = 10110.07f,
            blurRadius = 10.2f;
   
   float ssaoTotalStrength = 1.00f;
   float ssaoRayStrength = 0.07f;
   float ssaoOffset = 18.0f;
   float ssaoRadius = 0.007f;
   float ssaoFallOff =  0.00002f;
   
   private boolean shouldUpdateUniforms = false;
   
   public SSAORenderPass(Camera cam, int renderScale)
   {
      this.cam = cam;
      
      DisplaySystem display = DisplaySystem.getDisplaySystem();
      
      selectedTextureWidth = display.getWidth()/renderScale;
      
      normalAndDepthTexture = new Texture2D();
      normalAndDepthTexture.setRenderToTextureType(RenderToTextureType.RGBA32F);
      normalAndDepthTexture.setWrap(WrapMode.BorderClamp);
      normalAndDepthTexture.setHasBorder(true);
      normalAndDepthTexture.setBorderColor(new ColorRGBA(0.0f,0.0f,0.0f,1.0f));
      normalAndDepthTexture.setMinificationFilter(Texture.MinificationFilter.NearestNeighborNoMipMaps);
      normalAndDepthTexture.setMagnificationFilter(Texture.MagnificationFilter.NearestNeighbor);
      
      SSAOBlurXTexture = new Texture2D();
      SSAOBlurXTexture.setRenderToTextureType(RenderToTextureType.Luminance8);
      SSAOBlurXTexture.setWrap(WrapMode.BorderClamp);
      SSAOBlurXTexture.setHasBorder(true); // not sure about this right now
      SSAOBlurXTexture.setBorderColor(ColorRGBA.white);
      SSAOBlurXTexture.setMinificationFilter(Texture.MinificationFilter.BilinearNearestMipMap);
      SSAOBlurXTexture.setMagnificationFilter(Texture.MagnificationFilter.Bilinear);
      
      SSAOBlurYTexture = new Texture2D();
      SSAOBlurYTexture.setRenderToTextureType(RenderToTextureType.Luminance8);
      SSAOBlurYTexture.setWrap(WrapMode.BorderClamp);
      SSAOBlurYTexture.setHasBorder(true); // not sure about this right now
      SSAOBlurYTexture.setBorderColor(ColorRGBA.white);
      SSAOBlurYTexture.setMinificationFilter(Texture.MinificationFilter.BilinearNearestMipMap);
      SSAOBlurYTexture.setMagnificationFilter(Texture.MagnificationFilter.Bilinear);
      
      SSAOTexture = new Texture2D();
      SSAOTexture.setRenderToTextureType(RenderToTextureType.Luminance8);
      SSAOTexture.setWrap(WrapMode.BorderClamp);
      SSAOTexture.setHasBorder(true); // not sure about this right now
      SSAOTexture.setBorderColor(ColorRGBA.white);
      SSAOTexture.setMinificationFilter(Texture.MinificationFilter.BilinearNearestMipMap);
      SSAOTexture.setMagnificationFilter(Texture.MagnificationFilter.Bilinear);
      
      // load the noise texture for the SSAO shader
      noiseTexture = TextureManager.loadTexture(
              TestRenderToTexture.class.getClassLoader().getResource(
              "Assets/Textures/noise.dds"),
              Texture.MinificationFilter.NearestNeighborNoMipMaps,
              Texture.MagnificationFilter.NearestNeighbor);
      noiseTexture.setWrap(WrapMode.Repeat);
      
      // load the shaders
      preRenderShader = createPreRenderShader(display);
       blurXShader = createBlurXShader(display);
       blurYShader = createBlurYShader(display);
       SSAOShader = createSSAOShader(display);
       texturingShader = createTexturingShader(display);
      
      // Set up the texture renderer
      tRenderer = display.createTextureRenderer(selectedTextureWidth, selectedTextureWidth, TextureRenderer.Target.Texture2D);
      tRenderer.setBackgroundColor(new ColorRGBA(.0f, .0f, .0f, 1f));
      tRenderer.setMultipleTargets(false);
      tRenderer.setupTexture(normalAndDepthTexture);
      tRenderer.setupTexture(SSAOTexture);
      tRenderer.setupTexture(SSAOBlurXTexture);
      tRenderer.setupTexture(SSAOBlurYTexture);
      tRenderer.setCamera(cam);
      
      // create the fullscreen quad
      fullScreenQuad = new Quad("FullScreenQuad", display.getWidth(), display.getHeight());
      fullScreenQuad.getLocalTranslation().set(display.getWidth() / 2, display.getHeight() / 2, 0);
      fullScreenQuad.setRenderQueueMode(Renderer.QUEUE_ORTHO);

      fullScreenQuad.setCullHint(Spatial.CullHint.Never);
      //fullScreenQuad.setTextureCombineMode(Spatial.TextureCombineMode.Replace);
      fullScreenQuad.setLightCombineMode(Spatial.LightCombineMode.Off);
      
      // create the SSAO texture states
      fsQuadTextureState = display.getRenderer().createTextureState();
      fsQuadTextureState.setEnabled(true);

      fsQuadTextureState.setTexture(noiseTexture, 0);
      fsQuadTextureState.setTexture(normalAndDepthTexture, 1);
      fsQuadTextureState.setTexture(SSAOTexture, 2);
      fsQuadTextureState.setTexture(SSAOBlurXTexture, 3);
       fsQuadTextureState.setTexture(SSAOBlurYTexture, 4);
      
       if(fsQuadTextureState == null)
          System.out.println("Could not create TextureState in SSAORenderPass!");
      
       fullScreenQuad.setRenderState(fsQuadTextureState);

       blendState = display.getRenderer().createBlendState();
       blendState.setBlendEnabled(true);
       blendState.setSourceFunction(BlendState.SourceFunction.Zero);
       blendState.setDestinationFunction(BlendState.DestinationFunction.SourceColor);
       blendState.setEnabled(true);
      
       ZBufferState buf = display.getRenderer().createZBufferState();
        buf.setEnabled( true );
        buf.setFunction( ZBufferState.TestFunction.LessThanOrEqualTo );
        fullScreenQuad.setRenderState( buf );
      
        fullScreenQuad.updateGeometricState(0,true);
        fullScreenQuad.updateRenderState();
       
        textures = new ArrayList<Texture>(1);
        textures.add(normalAndDepthTexture);
   }
   
   @Override
   protected void doRender(Renderer r)
   {
      // first render the normal and depths
      
      if(shouldUpdateUniforms)
      {
         blurXShader.setUniform("g_BlurFalloff", blurFallOff);
         blurXShader.setUniform("g_Sharpness", blurSharpness);
         blurXShader.setUniform("g_BlurRadius", blurRadius);
         
         blurYShader.setUniform("g_BlurFalloff", blurFallOff);
         blurYShader.setUniform("g_Sharpness", blurSharpness);
         blurYShader.setUniform("g_BlurRadius", blurRadius);
         
         SSAOShader.setUniform("totStrength", ssaoTotalStrength);
         SSAOShader.setUniform("strength", ssaoRayStrength);
         SSAOShader.setUniform("offset", ssaoOffset);
         SSAOShader.setUniform("rad", ssaoRadius);
           SSAOShader.setUniform("falloff", ssaoFallOff);
          
           shouldUpdateUniforms = false;
      }
      
      // enforce the shader that will render the depth and the normals
      context.enforceState(preRenderShader);
      
      // render to texture
      tRenderer.render(spatials, textures,true);

      // restore context
      context.clearEnforcedState(RenderState.StateType.GLSLShaderObjects);
      
      // second, render SSAO to SSAO texture
       fullScreenQuad.setRenderState(SSAOShader);
       fullScreenQuad.updateRenderState();
      
//       tRendererHalf.render(fullScreenQuad, SSAOTexture,true);
       tRenderer.render(fullScreenQuad, SSAOTexture,false);
      
      // third, render a X-blur to SSAOBlurXTexture

       fullScreenQuad.setRenderState(blurXShader);
       fullScreenQuad.updateRenderState();
      
       tRenderer.render(fullScreenQuad, SSAOBlurXTexture,false);
      
      // fourth, render a Y-blur to the screen (for now)
       fullScreenQuad.setRenderState(blurYShader);
       fullScreenQuad.updateRenderState();
      
       tRenderer.render(fullScreenQuad, SSAOBlurYTexture,false);
      
       fullScreenQuad.setRenderState(texturingShader);
      
      
       // apply the blend state so the result will be blended with the current scene
        fullScreenQuad.setRenderState(blendState);
       fullScreenQuad.updateRenderState();
      
       // we must manually draw this one
       r.draw(fullScreenQuad);
       r.renderQueue();
      
       // remove the blend state
       fullScreenQuad.clearRenderState(RenderState.StateType.Blend);
   }

   private GLSLShaderObjectsState createPreRenderShader(DisplaySystem display)
    {
        GLSLShaderObjectsState so = display.getRenderer().createGLSLShaderObjectsState();

        try
        {
            so .load(
                            TestGLSLShaderObjectsState.class
                                    .getClassLoader()
                                    .getResource(
                                            "Assets/Shaders/SSAO/PreRender_vp.glsl"),
                            TestGLSLShaderObjectsState.class
                                    .getClassLoader()
                                    .getResource(
                                            "Assets/Shaders/SSAO/PreRender_fp.glsl"));
            so.apply();
            DisplaySystem.getDisplaySystem().getRenderer().checkCardError();
        } catch (JmeException e)
        {
           System.out.println(Level.WARNING + "Error loading shader" + e);
        }

        so.setUniform("zFar", cam.getFrustumFar());
       
        so.setEnabled(true);

        return so;
    }
   
   private GLSLShaderObjectsState createTexturingShader(DisplaySystem display)
    {
        GLSLShaderObjectsState so = display.getRenderer().createGLSLShaderObjectsState();

        try
        {
            so.load(TestGLSLShaderObjectsState.class.getClassLoader()
               .getResource("Assets/Shaders/SSAO/Texturing_vp.glsl"),
               TestGLSLShaderObjectsState.class.getClassLoader()
                     .getResource("Assets/Shaders/SSAO/Texturing_fp.glsl"));
         so.apply();
            DisplaySystem.getDisplaySystem().getRenderer().checkCardError();
        } catch (JmeException e)
        {
           System.out.println(Level.WARNING + "Error loading shader" + e);
        }
       
        so.setUniform("texture", 4);
       
        so.setEnabled(true);

        return so;
    }
   
    private GLSLShaderObjectsState createSSAOShader(DisplaySystem display)
    {
        GLSLShaderObjectsState so = display.getRenderer().createGLSLShaderObjectsState();

        try
        {
            so.load(TestGLSLShaderObjectsState.class.getClassLoader()
               .getResource("Assets/Shaders/SSAO/SSAO_vp.glsl"),
               TestGLSLShaderObjectsState.class.getClassLoader()
                     .getResource("Assets/Shaders/SSAO/SSAO_fp.glsl"));
         so.apply();
            DisplaySystem.getDisplaySystem().getRenderer().checkCardError();
        } catch (JmeException e)
        {
           System.out.println(Level.WARNING + "Error loading shader" + e);
        }

        so.setUniform("rnm", 0);
        so.setUniform("normalMap", 1);
       
        so.setUniform("totStrength", ssaoTotalStrength);
        so.setUniform("strength", ssaoRayStrength);
        so.setUniform("offset", ssaoOffset);
        so.setUniform("rad", ssaoRadius); // 0.007
        so.setUniform("falloff", ssaoFallOff); // 0.00002f
       
        so.setEnabled(true);

        return so;
    }
   
    private GLSLShaderObjectsState createBlurXShader(DisplaySystem display)
    {
        GLSLShaderObjectsState so = display.getRenderer().createGLSLShaderObjectsState();

        try
        {
            so.load(TestGLSLShaderObjectsState.class.getClassLoader()
               .getResource("Assets/Shaders/SSAO/BlurX_vp.glsl"),
               TestGLSLShaderObjectsState.class.getClassLoader()
                     .getResource("Assets/Shaders/SSAO/BlurX_fp.glsl"));
         so.apply();
            DisplaySystem.getDisplaySystem().getRenderer().checkCardError();
        } catch (JmeException e)
        {
           System.out.println(Level.WARNING + "Error loading shader" + e);
        }

        so.setUniform("g_BlurFalloff", blurFallOff);
        so.setUniform("g_Sharpness", blurSharpness);
        so.setUniform("g_BlurRadius", blurRadius);
        so.setUniform("g_InvResolutionFull", new Vector2f(1.0f/(float)selectedTextureWidth,1.0f/(float)selectedTextureWidth));

        so.setUniform("AOMap", 2);
        so.setUniform("normalMap", 1);
       
        so.setEnabled(true);

        return so;
    }
   
    private GLSLShaderObjectsState createBlurYShader(DisplaySystem display)
    {
        GLSLShaderObjectsState so = display.getRenderer().createGLSLShaderObjectsState();

        try
        {
            so.load(TestGLSLShaderObjectsState.class.getClassLoader()
               .getResource("Assets/Shaders/SSAO/BlurY_vp.glsl"),
               TestGLSLShaderObjectsState.class.getClassLoader()
                     .getResource("Assets/Shaders/SSAO/BlurY_fp.glsl"));
         so.apply();
            DisplaySystem.getDisplaySystem().getRenderer().checkCardError();
        } catch (JmeException e)
        {
           System.out.println(Level.WARNING + "Error loading shader" + e);
        }

        so.setUniform("g_BlurFalloff", blurFallOff);
        so.setUniform("g_Sharpness", blurSharpness);
        so.setUniform("g_BlurRadius", blurRadius);
        so.setUniform("g_InvResolutionFull", new Vector2f(1.0f/((float)selectedTextureWidth),1.0f/((float)selectedTextureWidth)));
        so.setUniform("normalMap", 1);
        so.setUniform("AOBlurXMap", 3);
       
        so.setEnabled(true);

        return so;
    }
   
    public void cleanup()
    {
       tRenderer.cleanup();
//       tRendererHalf.cleanup();
    }
   
    /**
     * @return
     */
    public boolean isSupported()
    {
       return GLSLShaderObjectsState.isSupported() && tRenderer.isSupported();
    }

   /**
    * @return the blurFallOff
    */
   public float getBlurFallOff()
   {
      return blurFallOff;
   }

   /**
    * @param blurFallOff the blurFallOff to set
    */
   public void setBlurFallOff(float blurFallOff)
   {
      this.blurFallOff = blurFallOff;
      shouldUpdateUniforms = true;
   }

   /**
    * @return the blurSharpness
    */
   public float getBlurSharpness()
   {
      return blurSharpness;
   }

   /**
    * @param blurSharpness the blurSharpness to set
    */
   public void setBlurSharpness(float blurSharpness)
   {
      this.blurSharpness = blurSharpness;
      shouldUpdateUniforms = true;
   }

   /**
    * @return the blurRadius
    */
   public float getBlurRadius()
   {
      return blurRadius;
   }

   /**
    * @param blurRadius the blurRadius to set
    */
   public void setBlurRadius(float blurRadius)
   {
      this.blurRadius = blurRadius;
      shouldUpdateUniforms = true;
   }

   /**
    * @return the ssaoTotalStrength
    */
   public float getSsaoTotalStrength()
   {
      return ssaoTotalStrength;
   }

   /**
    * @param ssaoTotalStrength the ssaoTotalStrength to set
    */
   public void setSsaoTotalStrength(float ssaoTotalStrength)
   {
      this.ssaoTotalStrength = ssaoTotalStrength;
      shouldUpdateUniforms = true;
   }

   /**
    * @return the ssaoRayStrength
    */
   public float getSsaoRayStrength()
   {
      return ssaoRayStrength;
   }

   /**
    * @param ssaoRayStrength the ssaoRayStrength to set
    */
   public void setSsaoRayStrength(float ssaoRayStrength)
   {
      this.ssaoRayStrength = ssaoRayStrength;
      shouldUpdateUniforms = true;
   }

   /**
    * @return the ssaoOffset
    */
   public float getSsaoOffset()
   {
      return ssaoOffset;
   }

   /**
    * @param ssaoOffset the ssaoOffset to set
    */
   public void setSsaoOffset(float ssaoOffset)
   {
      this.ssaoOffset = ssaoOffset;
      shouldUpdateUniforms = true;
   }

   /**
    * @return the ssaoRadius
    */
   public float getSsaoRadius()
   {
      return ssaoRadius;
   }

   /**
    * @param ssaoRadius the ssaoRadius to set
    */
   public void setSsaoRadius(float ssaoRadius)
   {
      this.ssaoRadius = ssaoRadius;
      shouldUpdateUniforms = true;
   }

   /**
    * @return the ssaoFallOff
    */
   public float getSsaoFallOff()
   {
      return ssaoFallOff;
   }

   /**
    * @param ssaoFallOff the ssaoFallOff to set
    */
   public void setSsaoFallOff(float ssaoFallOff)
   {
      this.ssaoFallOff = ssaoFallOff;
      shouldUpdateUniforms = true;
   }
}

Post was to long so I had to split it.



Here's the test program (it's basically a copy of the Bloom test):



package ssao;

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URL;
import java.util.logging.Level;
import java.util.logging.Logger;

import jmetest.renderer.loader.TestMaxJmeWrite;

import com.jme.app.SimplePassGame;
import com.jme.bounding.BoundingBox;
import com.jme.image.Texture;
import com.jme.input.KeyBindingManager;
import com.jme.input.KeyInput;
import com.jme.light.PointLight;
import com.jme.math.FastMath;
import com.jme.math.Quaternion;
import com.jme.math.Vector3f;
import com.jme.renderer.ColorRGBA;
import com.jme.renderer.Renderer;
import com.jme.renderer.pass.RenderPass;
import com.jme.scene.Controller;
import com.jme.scene.Node;
import com.jme.scene.Text;
import com.jme.scene.Spatial.LightCombineMode;
import com.jme.scene.shape.Box;
import com.jme.scene.shape.Torus;
import com.jme.scene.state.TextureState;
import com.jme.util.TextureManager;
import com.jme.util.export.binary.BinaryImporter;
import com.jmex.effects.glsl.BloomRenderPass;
import com.jmex.model.converters.MaxToJme;

/**
 * SSAO effect pass test
 *
 * @author Robert Larsson, rewrite of BloomPassGame (Rikard Herlitz (MrCoder))
 */
public class TestSSAO extends SimplePassGame {
    private static final Logger logger = Logger.getLogger(TestSSAO.class
            .getName());
   
   private SSAORenderPass ssaoRenderPass;
   private int screenshotIndex = 0;

   public static void main(String[] args) {
      TestSSAO app = new TestSSAO();
      app.setConfigShowMode(ConfigShowMode.AlwaysShow);
      app.start();
   }

   protected void cleanup() {
      super.cleanup();
        if (ssaoRenderPass != null)
            ssaoRenderPass.cleanup();
   }

   protected void simpleInitGame() {
      //Setup camera
      cam.setFrustumPerspective(55.0f, (float) display.getWidth() / (float) display.getHeight(), 1, 5000);

      //Setup lights
      PointLight light = new PointLight();
      light.setDiffuse(new ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f));
      light.setAmbient(new ColorRGBA(0.5f, 0.5f, 0.5f, 1.0f));
      light.setLocation(new Vector3f(0, 30, 0));
      light.setEnabled(true);
      lightState.attach(light);

      //Add dummy objects to rootNode
      rootNode.attachChild(createObjects());
      
      try {
            MaxToJme C1=new MaxToJme();
            ByteArrayOutputStream BO=new ByteArrayOutputStream();
            URL maxFile=TestMaxJmeWrite.class.getClassLoader().getResource("jmetest/data/model/char.3ds");
            C1.convert(new BufferedInputStream(maxFile.openStream()),BO);
            Node r = (Node)BinaryImporter.getInstance().load(new ByteArrayInputStream(BO.toByteArray()));
            r.getController(0).setRepeatType(Controller.RT_WRAP);
            r.setLocalScale(.1f);
            if (r.getChild(0).getControllers().size()!=0)
                r.getChild(0).getController(0).setSpeed(20);
            Quaternion temp=new Quaternion();
            temp.fromAngleAxis(FastMath.PI/2,new Vector3f(-1,0,0));
            r.setLocalRotation(temp);
            r.setLocalTranslation(new Vector3f(0,3,0));
            rootNode.attachChild(r);
        } catch (IOException e) {
            logger.log(Level.SEVERE, "Error loading max file", e);
        }

      //Setup renderpasses
      RenderPass rootPass = new RenderPass();
      rootPass.add(rootNode);
      pManager.add(rootPass);

      ssaoRenderPass = new SSAORenderPass(cam, 4);
             
       if(!ssaoRenderPass.isSupported()) {
           Text t = Text.createDefaultTextLabel("Text", "GLSL Not supported on this computer.");
           t.setRenderQueueMode(Renderer.QUEUE_ORTHO);
           t.setLightCombineMode(LightCombineMode.Off);
           t.setLocalTranslation(new Vector3f(0,20,0));
           statNode.attachChild(t);
       } else {
           ssaoRenderPass.add(rootNode);
           pManager.add(ssaoRenderPass);
       }

      RenderPass statPass = new RenderPass();
        statPass.add(statNode);
      pManager.add(statPass);

      //Initialize keybindings
      KeyBindingManager.getKeyBindingManager().set("1", KeyInput.KEY_1);
      KeyBindingManager.getKeyBindingManager().set("2", KeyInput.KEY_2);
      KeyBindingManager.getKeyBindingManager().set("3", KeyInput.KEY_3);
      KeyBindingManager.getKeyBindingManager().set("4", KeyInput.KEY_4);
      KeyBindingManager.getKeyBindingManager().set("5", KeyInput.KEY_5);
      KeyBindingManager.getKeyBindingManager().set("6", KeyInput.KEY_6);
      KeyBindingManager.getKeyBindingManager().set("7", KeyInput.KEY_7);
      KeyBindingManager.getKeyBindingManager().set("8", KeyInput.KEY_8);
      KeyBindingManager.getKeyBindingManager().set("9", KeyInput.KEY_9);

      KeyBindingManager.getKeyBindingManager().set("LControl", KeyInput.KEY_LCONTROL);

      KeyBindingManager.getKeyBindingManager().set("shot", KeyInput.KEY_F4);
    }

   protected void simpleUpdate() {
      
      if(!KeyBindingManager.getKeyBindingManager().isValidCommand("LControl", true))
      {
         if(KeyBindingManager.getKeyBindingManager().isValidCommand("1", false)) {
            ssaoRenderPass.setEnabled(!ssaoRenderPass.isEnabled());
         }
   
         if(KeyBindingManager.getKeyBindingManager().isValidCommand("2", false)) {
            ssaoRenderPass.setBlurFallOff(ssaoRenderPass.getBlurFallOff() - 0.0005f);
         }
         if(KeyBindingManager.getKeyBindingManager().isValidCommand("3", false)) {
            ssaoRenderPass.setBlurFallOff(ssaoRenderPass.getBlurFallOff() + 0.0005f);
         }
   
         if(KeyBindingManager.getKeyBindingManager().isValidCommand("4", false)) {
            ssaoRenderPass.setBlurSharpness(ssaoRenderPass.getBlurSharpness() - 1000.0f);
         }
         if(KeyBindingManager.getKeyBindingManager().isValidCommand("5", false)) {
            ssaoRenderPass.setBlurSharpness(ssaoRenderPass.getBlurSharpness() + 1000.0f);
         }
   
         if(KeyBindingManager.getKeyBindingManager().isValidCommand("6", false)) {
            ssaoRenderPass.setBlurRadius(ssaoRenderPass.getBlurRadius() - 0.1f);
         }
         if(KeyBindingManager.getKeyBindingManager().isValidCommand("7", false)) {
            ssaoRenderPass.setBlurRadius(ssaoRenderPass.getBlurRadius() + 0.1f);
         }
   
         if(KeyBindingManager.getKeyBindingManager().isValidCommand("8", false)) {
            ssaoRenderPass.setSsaoTotalStrength(ssaoRenderPass.getSsaoTotalStrength() - 0.1f);
         }
         if(KeyBindingManager.getKeyBindingManager().isValidCommand("9", false)) {
            ssaoRenderPass.setSsaoTotalStrength(ssaoRenderPass.getSsaoTotalStrength() + 0.1f);
         }
      }else
      {

         if(KeyBindingManager.getKeyBindingManager().isValidCommand("2", false)) {
            ssaoRenderPass.setSsaoRayStrength(ssaoRenderPass.getSsaoRayStrength() - 0.01f);
         }
         if(KeyBindingManager.getKeyBindingManager().isValidCommand("3", false)) {
            ssaoRenderPass.setSsaoRayStrength(ssaoRenderPass.getSsaoRayStrength() + 0.01f);
         }
   
         if(KeyBindingManager.getKeyBindingManager().isValidCommand("4", false)) {
            ssaoRenderPass.setSsaoOffset(ssaoRenderPass.getSsaoOffset() - 1.0f);
         }
         if(KeyBindingManager.getKeyBindingManager().isValidCommand("5", false)) {
            ssaoRenderPass.setSsaoOffset(ssaoRenderPass.getSsaoOffset() + 1.0f);
         }
   
         if(KeyBindingManager.getKeyBindingManager().isValidCommand("6", false)) {
            ssaoRenderPass.setSsaoRadius(ssaoRenderPass.getSsaoRadius() - 0.001f);
         }
         if(KeyBindingManager.getKeyBindingManager().isValidCommand("7", false)) {
            ssaoRenderPass.setSsaoRadius(ssaoRenderPass.getSsaoRadius() + 0.001f);
         }
   
         if(KeyBindingManager.getKeyBindingManager().isValidCommand("8", false)) {
            ssaoRenderPass.setSsaoFallOff(ssaoRenderPass.getSsaoFallOff() - 0.00001f);
         }
         if(KeyBindingManager.getKeyBindingManager().isValidCommand("9", false)) {
            ssaoRenderPass.setSsaoFallOff(ssaoRenderPass.getSsaoFallOff() + 0.00001f);
         }
      }
      
      if(KeyBindingManager.getKeyBindingManager().isValidCommand("shot", false)) {
         display.getRenderer().takeScreenShot("shot" + screenshotIndex++);
      }

   }

   private Node createObjects() {
      Node objects = new Node("objects");

      Torus torus = new Torus("Torus", 50, 50, 10, 20);
      torus.setLocalTranslation(new Vector3f(50, -5, 20));
      TextureState ts = display.getRenderer().createTextureState();
      Texture t0 = TextureManager.loadTexture(
            TestSSAO.class.getClassLoader().getResource(
                  "jmetest/data/images/Monkey.jpg"),
            Texture.MinificationFilter.Trilinear,
            Texture.MagnificationFilter.Bilinear);
      Texture t1 = TextureManager.loadTexture(
            TestSSAO.class.getClassLoader().getResource(
                  "jmetest/data/texture/north.jpg"),
            Texture.MinificationFilter.Trilinear,
            Texture.MagnificationFilter.Bilinear);
      t1.setEnvironmentalMapMode(Texture.EnvironmentalMapMode.SphereMap);
      ts.setTexture(t0, 0);
      ts.setTexture(t1, 1);
      ts.setEnabled(true);
      torus.setRenderState(ts);
      objects.attachChild(torus);

      ts = display.getRenderer().createTextureState();
      t0 = TextureManager.loadTexture(
            TestSSAO.class.getClassLoader().getResource(
                  "jmetest/data/texture/wall.jpg"),
            Texture.MinificationFilter.Trilinear,
            Texture.MagnificationFilter.Bilinear);
      t0.setWrap(Texture.WrapMode.Repeat);
      ts.setTexture(t0);

      Box box = new Box("box1", new Vector3f(-10, -10, -10), new Vector3f(10, 10, 10));
      box.setLocalTranslation(new Vector3f(0, -7, 0));
      box.setRenderState(ts);
      objects.attachChild(box);

      box = new Box("box2", new Vector3f(-5, -5, -5), new Vector3f(5, 5, 5));
      box.setLocalTranslation(new Vector3f(15, 10, 0));
      box.setRenderState(ts);
      objects.attachChild(box);

      box = new Box("box3", new Vector3f(-5, -5, -5), new Vector3f(5, 5, 5));
      box.setLocalTranslation(new Vector3f(0, -10, 15));
      box.setRenderState(ts);
      objects.attachChild(box);

      box = new Box("box4", new Vector3f(-5, -5, -5), new Vector3f(5, 5, 5));
      box.setLocalTranslation(new Vector3f(20, 0, 0));
      box.setRenderState(ts);
      objects.attachChild(box);

      box = new Box("box5", new Vector3f(-50, -2, -50), new Vector3f(50, 2, 50));
      box.setLocalTranslation(new Vector3f(0, -15, 0));
      box.setRenderState(ts);
      box.setModelBound(new BoundingBox());
      box.updateModelBound();
      objects.attachChild(box);

      ts = display.getRenderer().createTextureState();
      t0 = TextureManager.loadTexture(
            TestSSAO.class.getClassLoader().getResource(
                  "jmetest/data/texture/cloud_land.jpg"),
            Texture.MinificationFilter.Trilinear,
            Texture.MagnificationFilter.Bilinear);
      t0.setWrap(Texture.WrapMode.Repeat);
      ts.setTexture(t0);

      box = new Box("floor", new Vector3f(-1000, -10, -1000), new Vector3f(1000, 10, 1000));
      box.setLocalTranslation(new Vector3f(0, -100, 0));
      box.setRenderState(ts);
      box.setModelBound(new BoundingBox());
      box.updateModelBound();
      objects.attachChild(box);

      return objects;
   }
}



Here's a zip with the shaders and the noise image:
http://www.gamerendering.com/Upload/Assets.zip

I am not sure if it's the shaders that are wrong or something in the pass, but at least there is some SSAO and you can adjust it by keys 1-9 with or without left control pressed in the test (see test for more info).

Sorry no images but the shader should be the same as I used in this project:

http://www.gamerendering.com/2009/01/14/ssao/

/Robert

So what is left to be done?

Well, it just don't look as good as it's supposed to do. I have no need of SSAO in the game I'm currently making so therefore I don't want to spend time on finding out what is wrong. Maybe it's just a bad test scene, or maybe some uniforms that are wrong (they can at runtime be changed in the test)… The shaders works fine in a game in Ogre3D (C++) so it should be possible to get some good results out of it in jME too.

looks impressive! great job

I tried to use SSAO,

but it doesn’t work for me…





I use jME 2.0



Code:


package source.bin.effects;

import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;

import source.bin.gamesys.ResourceLocatorLibaryTool;

import com.jme.bounding.BoundingBox;
import com.jme.image.Texture;
import com.jme.image.Texture2D;
import com.jme.image.Texture.RenderToTextureType;
import com.jme.image.Texture.WrapMode;
import com.jme.math.Vector2f;
import com.jme.math.Vector3f;
import com.jme.renderer.Camera;
import com.jme.renderer.ColorRGBA;
import com.jme.renderer.Renderer;
import com.jme.renderer.TextureRenderer;
import com.jme.renderer.pass.Pass;
import com.jme.scene.Node;
import com.jme.scene.Spatial;
import com.jme.scene.shape.Box;
import com.jme.scene.shape.Quad;
import com.jme.scene.state.BlendState;
import com.jme.scene.state.GLSLShaderObjectsState;
import com.jme.scene.state.RenderState;
import com.jme.scene.state.TextureState;
import com.jme.scene.state.ZBufferState;
import com.jme.system.DisplaySystem;
import com.jme.system.JmeException;
import com.jme.util.TextureManager;

/**
 * This RenderPass must be added after the normal renderpass of the scene for the effect to be applied correctly.
 * @author Haladria
 *
 */
public class SSAORenderPass extends Pass
{
//   private final TextureRenderer tRendererHalf; // texture renderer with half the screen size
   private final TextureRenderer tRenderer;
   
   private final ArrayList<Texture> textures;
   
   private final Texture2D normalAndDepthTexture;
   private final Texture2D SSAOTexture;
   private final Texture2D SSAOBlurXTexture;
   private final Texture2D SSAOBlurYTexture;
   private final Texture noiseTexture;
   
   private final TextureState fsQuadTextureState;
   
   private final  GLSLShaderObjectsState preRenderShader;
   private final  GLSLShaderObjectsState SSAOShader;
   private final  GLSLShaderObjectsState blurXShader;
   private final  GLSLShaderObjectsState blurYShader;
   private final  GLSLShaderObjectsState texturingShader;
   
   private final Quad fullScreenQuad;
   
   private final int selectedTextureWidth;
   
   private final Camera cam;
   
   private final BlendState blendState;
   
   float blurFallOff = 0.003f,
            blurSharpness = 10110.07f,
            blurRadius = 10.2f;
   
   float ssaoTotalStrength = 1.00f;
   float ssaoRayStrength = 0.07f;
   float ssaoOffset = 18.0f;
   float ssaoRadius = 0.007f;
   float ssaoFallOff =  0.00002f;
   
   private boolean shouldUpdateUniforms = false;
   
   public SSAORenderPass(Camera cam, int renderScale)
   {
      this.cam = cam;
      
      DisplaySystem display = DisplaySystem.getDisplaySystem();
      
      selectedTextureWidth = display.getWidth()/renderScale;
      
      normalAndDepthTexture = new Texture2D();
      normalAndDepthTexture.setRenderToTextureType(RenderToTextureType.RGBA32F);
      normalAndDepthTexture.setWrap(WrapMode.BorderClamp);
      normalAndDepthTexture.setHasBorder(true);
      normalAndDepthTexture.setBorderColor(new ColorRGBA(0.0f,0.0f,0.0f,1.0f));
      normalAndDepthTexture.setMinificationFilter(Texture.MinificationFilter.NearestNeighborNoMipMaps);
      normalAndDepthTexture.setMagnificationFilter(Texture.MagnificationFilter.NearestNeighbor);
      
      SSAOBlurXTexture = new Texture2D();
      SSAOBlurXTexture.setRenderToTextureType(RenderToTextureType.Luminance8);
      SSAOBlurXTexture.setWrap(WrapMode.BorderClamp);
      SSAOBlurXTexture.setHasBorder(true); // not sure about this right now
      SSAOBlurXTexture.setBorderColor(ColorRGBA.white);
      SSAOBlurXTexture.setMinificationFilter(Texture.MinificationFilter.BilinearNearestMipMap);
      SSAOBlurXTexture.setMagnificationFilter(Texture.MagnificationFilter.Bilinear);
      
      SSAOBlurYTexture = new Texture2D();
      SSAOBlurYTexture.setRenderToTextureType(RenderToTextureType.Luminance8);
      SSAOBlurYTexture.setWrap(WrapMode.BorderClamp);
      SSAOBlurYTexture.setHasBorder(true); // not sure about this right now
      SSAOBlurYTexture.setBorderColor(ColorRGBA.white);
      SSAOBlurYTexture.setMinificationFilter(Texture.MinificationFilter.BilinearNearestMipMap);
      SSAOBlurYTexture.setMagnificationFilter(Texture.MagnificationFilter.Bilinear);
      
      SSAOTexture = new Texture2D();
      SSAOTexture.setRenderToTextureType(RenderToTextureType.Luminance8);
      SSAOTexture.setWrap(WrapMode.BorderClamp);
      SSAOTexture.setHasBorder(true); // not sure about this right now
      SSAOTexture.setBorderColor(ColorRGBA.white);
      SSAOTexture.setMinificationFilter(Texture.MinificationFilter.BilinearNearestMipMap);
      SSAOTexture.setMagnificationFilter(Texture.MagnificationFilter.Bilinear);
      
      // load the noise texture for the SSAO shader
      noiseTexture = TextureManager.loadTexture(
            ResourceLocatorLibaryTool.locateResource(ResourceLocatorLibaryTool.TYPE_EFFECT,"/ssao/noise.dds"),
              Texture.MinificationFilter.NearestNeighborNoMipMaps,
              Texture.MagnificationFilter.NearestNeighbor);
      noiseTexture.setWrap(WrapMode.Repeat);
      
      // load the shaders
      preRenderShader = createPreRenderShader(display);
       blurXShader = createBlurXShader(display);
       blurYShader = createBlurYShader(display);
       SSAOShader = createSSAOShader(display);
       texturingShader = createTexturingShader(display);
       
      // Set up the texture renderer
      tRenderer = display.createTextureRenderer(selectedTextureWidth, selectedTextureWidth, TextureRenderer.Target.Texture2D);
      tRenderer.setBackgroundColor(new ColorRGBA(.0f, .0f, .0f, 1f));
      tRenderer.setMultipleTargets(false);
      tRenderer.setupTexture(normalAndDepthTexture);
      tRenderer.setupTexture(SSAOTexture);
      tRenderer.setupTexture(SSAOBlurXTexture);
      tRenderer.setupTexture(SSAOBlurYTexture);
      tRenderer.setCamera(cam);
      
      // create the fullscreen quad
      fullScreenQuad = new Quad("FullScreenQuad", display.getWidth(), display.getHeight());
      fullScreenQuad.getLocalTranslation().set(display.getWidth() / 2, display.getHeight() / 2, 0);
      fullScreenQuad.setRenderQueueMode(Renderer.QUEUE_ORTHO);

      fullScreenQuad.setCullHint(Spatial.CullHint.Never);
      //fullScreenQuad.setTextureCombineMode(Spatial.TextureCombineMode.Replace);
      fullScreenQuad.setLightCombineMode(Spatial.LightCombineMode.Off);
      
      // create the SSAO texture states
      fsQuadTextureState = display.getRenderer().createTextureState();
      fsQuadTextureState.setEnabled(true);

      fsQuadTextureState.setTexture(noiseTexture, 0);
      fsQuadTextureState.setTexture(normalAndDepthTexture, 1);
      fsQuadTextureState.setTexture(SSAOTexture, 2);
      fsQuadTextureState.setTexture(SSAOBlurXTexture, 3);
       fsQuadTextureState.setTexture(SSAOBlurYTexture, 4);
       
       if(fsQuadTextureState == null)
          System.out.println("Could not create TextureState in SSAORenderPass!");
       
       fullScreenQuad.setRenderState(fsQuadTextureState);

       blendState = display.getRenderer().createBlendState();
       blendState.setBlendEnabled(true);
       blendState.setSourceFunction(BlendState.SourceFunction.Zero);
       blendState.setDestinationFunction(BlendState.DestinationFunction.SourceColor);
       blendState.setEnabled(true);
       
       ZBufferState buf = display.getRenderer().createZBufferState();
        buf.setEnabled( true );
        buf.setFunction( ZBufferState.TestFunction.LessThanOrEqualTo );
        fullScreenQuad.setRenderState( buf );
      
        fullScreenQuad.updateGeometricState(0,true);
        fullScreenQuad.updateRenderState();
        
        textures = new ArrayList<Texture>(1);
        textures.add(normalAndDepthTexture);
   }
   
   @Override
   protected void doRender(Renderer r)
   {
      // first render the normal and depths
      
      if(shouldUpdateUniforms)
      {
         blurXShader.setUniform("g_BlurFalloff", blurFallOff);
         blurXShader.setUniform("g_Sharpness", blurSharpness);
         blurXShader.setUniform("g_BlurRadius", blurRadius);
         
         blurYShader.setUniform("g_BlurFalloff", blurFallOff);
         blurYShader.setUniform("g_Sharpness", blurSharpness);
         blurYShader.setUniform("g_BlurRadius", blurRadius);
         
         SSAOShader.setUniform("totStrength", ssaoTotalStrength);
         SSAOShader.setUniform("strength", ssaoRayStrength);
         SSAOShader.setUniform("offset", ssaoOffset);
         SSAOShader.setUniform("rad", ssaoRadius);
           SSAOShader.setUniform("falloff", ssaoFallOff);
           
           shouldUpdateUniforms = false;
      }
      
      // enforce the shader that will render the depth and the normals
      context.enforceState(preRenderShader);
      
      // render to texture
      tRenderer.render(spatials, textures,true);

      // restore context
      context.clearEnforcedState(RenderState.RS_GLSL_SHADER_OBJECTS);
      
      // second, render SSAO to SSAO texture
       fullScreenQuad.setRenderState(SSAOShader);
       fullScreenQuad.updateRenderState();
      
//       tRendererHalf.render(fullScreenQuad, SSAOTexture,true);
       tRenderer.render(fullScreenQuad, SSAOTexture,false);
       
      // third, render a X-blur to SSAOBlurXTexture

       fullScreenQuad.setRenderState(blurXShader);
       fullScreenQuad.updateRenderState();
       
       tRenderer.render(fullScreenQuad, SSAOBlurXTexture,false);
       
      // fourth, render a Y-blur to the screen (for now)
       fullScreenQuad.setRenderState(blurYShader);
       fullScreenQuad.updateRenderState();
       
       tRenderer.render(fullScreenQuad, SSAOBlurYTexture,false);
       
       fullScreenQuad.setRenderState(texturingShader);
       
       
       // apply the blend state so the result will be blended with the current scene
        fullScreenQuad.setRenderState(blendState);
       fullScreenQuad.updateRenderState();
       
       // we must manually draw this one
       r.draw(fullScreenQuad);
       r.renderQueue();
       
       // remove the blend state
       fullScreenQuad.clearRenderState(RenderState.RS_BLEND);
   }

   private GLSLShaderObjectsState createPreRenderShader(DisplaySystem display)
    {
        GLSLShaderObjectsState so = display.getRenderer().createGLSLShaderObjectsState();

        try{
           so.load(
                 ResourceLocatorLibaryTool.locateResource(ResourceLocatorLibaryTool.TYPE_SHADER, "/ssao/PreRender_vp.glsl"),
                ResourceLocatorLibaryTool.locateResource(ResourceLocatorLibaryTool.TYPE_SHADER, "/ssao/PreRender_fp.glsl")
                );
            so.apply();
            DisplaySystem.getDisplaySystem().getRenderer().checkCardError();
        } catch (JmeException e)
        {
           System.out.println(Level.WARNING + "Error loading shader" + e);
        }

        so.setUniform("zFar", cam.getFrustumFar());
        
        so.setEnabled(true);

        return so;
    }
   
   private GLSLShaderObjectsState createTexturingShader(DisplaySystem display)
    {
        GLSLShaderObjectsState so = display.getRenderer().createGLSLShaderObjectsState();

        try
        {
           so.load(
                 ResourceLocatorLibaryTool.locateResource(ResourceLocatorLibaryTool.TYPE_SHADER, "/ssao/Texturing_vp.glsl"),
                ResourceLocatorLibaryTool.locateResource(ResourceLocatorLibaryTool.TYPE_SHADER, "/ssao/Texturing_fp.glsl")
                );
         so.apply();
            DisplaySystem.getDisplaySystem().getRenderer().checkCardError();
        } catch (JmeException e)
        {
           System.out.println(Level.WARNING + "Error loading shader" + e);
        }
        
        so.setUniform("texture", 4);
        
        so.setEnabled(true);

        return so;
    }
   
    private GLSLShaderObjectsState createSSAOShader(DisplaySystem display)
    {
        GLSLShaderObjectsState so = display.getRenderer().createGLSLShaderObjectsState();

        try
        {
           so.load(
                 ResourceLocatorLibaryTool.locateResource(ResourceLocatorLibaryTool.TYPE_SHADER, "/ssao/SSAO_vp.glsl"),
                ResourceLocatorLibaryTool.locateResource(ResourceLocatorLibaryTool.TYPE_SHADER, "/ssao/SSAO_fp.glsl")
                );
         so.apply();
            DisplaySystem.getDisplaySystem().getRenderer().checkCardError();
        } catch (JmeException e)
        {
           System.out.println(Level.WARNING + "Error loading shader" + e);
        }

        so.setUniform("rnm", 0);
        so.setUniform("normalMap", 1);
        
        so.setUniform("totStrength", ssaoTotalStrength);
        so.setUniform("strength", ssaoRayStrength);
        so.setUniform("offset", ssaoOffset);
        so.setUniform("rad", ssaoRadius); // 0.007
        so.setUniform("falloff", ssaoFallOff); // 0.00002f
        
        so.setEnabled(true);

        return so;
    }
    
    private GLSLShaderObjectsState createBlurXShader(DisplaySystem display)
    {
        GLSLShaderObjectsState so = display.getRenderer().createGLSLShaderObjectsState();

        try
        {
           so.load(
                 ResourceLocatorLibaryTool.locateResource(ResourceLocatorLibaryTool.TYPE_SHADER, "/ssao/BlurX_vp.glsl"),
                ResourceLocatorLibaryTool.locateResource(ResourceLocatorLibaryTool.TYPE_SHADER, "/ssao/BlurX_fp.glsl")
                );
         so.apply();
            DisplaySystem.getDisplaySystem().getRenderer().checkCardError();
        } catch (JmeException e)
        {
           System.out.println(Level.WARNING + "Error loading shader" + e);
        }

        so.setUniform("g_BlurFalloff", blurFallOff);
        so.setUniform("g_Sharpness", blurSharpness);
        so.setUniform("g_BlurRadius", blurRadius);
        so.setUniform("g_InvResolutionFull", new Vector2f(1.0f/(float)selectedTextureWidth,1.0f/(float)selectedTextureWidth));

        so.setUniform("AOMap", 2);
        so.setUniform("normalMap", 1);
        
        so.setEnabled(true);

        return so;
    }
    
    private GLSLShaderObjectsState createBlurYShader(DisplaySystem display)
    {
        GLSLShaderObjectsState so = display.getRenderer().createGLSLShaderObjectsState();

        try
        {
           so.load(
                 ResourceLocatorLibaryTool.locateResource(ResourceLocatorLibaryTool.TYPE_SHADER, "/ssao/BlurY_vp.glsl"),
                ResourceLocatorLibaryTool.locateResource(ResourceLocatorLibaryTool.TYPE_SHADER, "/ssao/BlurY_fp.glsl")
                );
         so.apply();
            DisplaySystem.getDisplaySystem().getRenderer().checkCardError();
        } catch (JmeException e)
        {
           System.out.println(Level.WARNING + "Error loading shader" + e);
        }

        so.setUniform("g_BlurFalloff", blurFallOff);
        so.setUniform("g_Sharpness", blurSharpness);
        so.setUniform("g_BlurRadius", blurRadius);
        so.setUniform("g_InvResolutionFull", new Vector2f(1.0f/((float)selectedTextureWidth),1.0f/((float)selectedTextureWidth)));
        so.setUniform("normalMap", 1);
        so.setUniform("AOBlurXMap", 3);
        
        so.setEnabled(true);

        return so;
    }
    
    public void cleanup()
    {
       tRenderer.cleanup();
//       tRendererHalf.cleanup();
    }
    
    /**
     * @return
     */
    public boolean isSupported()
    {
       return GLSLShaderObjectsState.isSupported() && tRenderer.isSupported();
    }

   /**
    * @return the blurFallOff
    */
   public float getBlurFallOff()
   {
      return blurFallOff;
   }

   /**
    * @param blurFallOff the blurFallOff to set
    */
   public void setBlurFallOff(float blurFallOff)
   {
      this.blurFallOff = blurFallOff;
      shouldUpdateUniforms = true;
   }

   /**
    * @return the blurSharpness
    */
   public float getBlurSharpness()
   {
      return blurSharpness;
   }

   /**
    * @param blurSharpness the blurSharpness to set
    */
   public void setBlurSharpness(float blurSharpness)
   {
      this.blurSharpness = blurSharpness;
      shouldUpdateUniforms = true;
   }

   /**
    * @return the blurRadius
    */
   public float getBlurRadius()
   {
      return blurRadius;
   }

   /**
    * @param blurRadius the blurRadius to set
    */
   public void setBlurRadius(float blurRadius)
   {
      this.blurRadius = blurRadius;
      shouldUpdateUniforms = true;
   }

   /**
    * @return the ssaoTotalStrength
    */
   public float getSsaoTotalStrength()
   {
      return ssaoTotalStrength;
   }

   /**
    * @param ssaoTotalStrength the ssaoTotalStrength to set
    */
   public void setSsaoTotalStrength(float ssaoTotalStrength)
   {
      this.ssaoTotalStrength = ssaoTotalStrength;
      shouldUpdateUniforms = true;
   }

   /**
    * @return the ssaoRayStrength
    */
   public float getSsaoRayStrength()
   {
      return ssaoRayStrength;
   }

   /**
    * @param ssaoRayStrength the ssaoRayStrength to set
    */
   public void setSsaoRayStrength(float ssaoRayStrength)
   {
      this.ssaoRayStrength = ssaoRayStrength;
      shouldUpdateUniforms = true;
   }

   /**
    * @return the ssaoOffset
    */
   public float getSsaoOffset()
   {
      return ssaoOffset;
   }

   /**
    * @param ssaoOffset the ssaoOffset to set
    */
   public void setSsaoOffset(float ssaoOffset)
   {
      this.ssaoOffset = ssaoOffset;
      shouldUpdateUniforms = true;
   }

   /**
    * @return the ssaoRadius
    */
   public float getSsaoRadius()
   {
      return ssaoRadius;
   }

   /**
    * @param ssaoRadius the ssaoRadius to set
    */
   public void setSsaoRadius(float ssaoRadius)
   {
      this.ssaoRadius = ssaoRadius;
      shouldUpdateUniforms = true;
   }

   /**
    * @return the ssaoFallOff
    */
   public float getSsaoFallOff()
   {
      return ssaoFallOff;
   }

   /**
    * @param ssaoFallOff the ssaoFallOff to set
    */
   public void setSsaoFallOff(float ssaoFallOff)
   {
      this.ssaoFallOff = ssaoFallOff;
      shouldUpdateUniforms = true;
   }
}



I haven't changed the test class
DarkPhoenixX said:

I tried to use SSAO,
but it doesn't work for me....


I use jME 2.0

Code:

package source.bin.effects;

import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;

import source.bin.gamesys.ResourceLocatorLibaryTool;

import com.jme.bounding.BoundingBox;
import com.jme.image.Texture;
import com.jme.image.Texture2D;
import com.jme.image.Texture.RenderToTextureType;
import com.jme.image.Texture.WrapMode;
import com.jme.math.Vector2f;
import com.jme.math.Vector3f;
import com.jme.renderer.Camera;
import com.jme.renderer.ColorRGBA;
import com.jme.renderer.Renderer;
import com.jme.renderer.TextureRenderer;
import com.jme.renderer.pass.Pass;
import com.jme.scene.Node;
import com.jme.scene.Spatial;
import com.jme.scene.shape.Box;
import com.jme.scene.shape.Quad;
import com.jme.scene.state.BlendState;
import com.jme.scene.state.GLSLShaderObjectsState;
import com.jme.scene.state.RenderState;
import com.jme.scene.state.TextureState;
import com.jme.scene.state.ZBufferState;
import com.jme.system.DisplaySystem;
import com.jme.system.JmeException;
import com.jme.util.TextureManager;

/**
 * This RenderPass must be added after the normal renderpass of the scene for the effect to be applied correctly.
 * @author Haladria
 *
 */
public class SSAORenderPass extends Pass
{
//   private final TextureRenderer tRendererHalf; // texture renderer with half the screen size
   private final TextureRenderer tRenderer;
   
   private final ArrayList<Texture> textures;
   
   private final Texture2D normalAndDepthTexture;
   private final Texture2D SSAOTexture;
   private final Texture2D SSAOBlurXTexture;
   private final Texture2D SSAOBlurYTexture;
   private final Texture noiseTexture;
   
   private final TextureState fsQuadTextureState;
   
   private final  GLSLShaderObjectsState preRenderShader;
   private final  GLSLShaderObjectsState SSAOShader;
   private final  GLSLShaderObjectsState blurXShader;
   private final  GLSLShaderObjectsState blurYShader;
   private final  GLSLShaderObjectsState texturingShader;
   
   private final Quad fullScreenQuad;
   
   private final int selectedTextureWidth;
   
   private final Camera cam;
   
   private final BlendState blendState;
   
   float blurFallOff = 0.003f,
            blurSharpness = 10110.07f,
            blurRadius = 10.2f;
   
   float ssaoTotalStrength = 1.00f;
   float ssaoRayStrength = 0.07f;
   float ssaoOffset = 18.0f;
   float ssaoRadius = 0.007f;
   float ssaoFallOff =  0.00002f;
   
   private boolean shouldUpdateUniforms = false;
   
   public SSAORenderPass(Camera cam, int renderScale)
   {
      this.cam = cam;
      
      DisplaySystem display = DisplaySystem.getDisplaySystem();
      
      selectedTextureWidth = display.getWidth()/renderScale;
      
      normalAndDepthTexture = new Texture2D();
      normalAndDepthTexture.setRenderToTextureType(RenderToTextureType.RGBA32F);
      normalAndDepthTexture.setWrap(WrapMode.BorderClamp);
      normalAndDepthTexture.setHasBorder(true);
      normalAndDepthTexture.setBorderColor(new ColorRGBA(0.0f,0.0f,0.0f,1.0f));
      normalAndDepthTexture.setMinificationFilter(Texture.MinificationFilter.NearestNeighborNoMipMaps);
      normalAndDepthTexture.setMagnificationFilter(Texture.MagnificationFilter.NearestNeighbor);
      
      SSAOBlurXTexture = new Texture2D();
      SSAOBlurXTexture.setRenderToTextureType(RenderToTextureType.Luminance8);
      SSAOBlurXTexture.setWrap(WrapMode.BorderClamp);
      SSAOBlurXTexture.setHasBorder(true); // not sure about this right now
      SSAOBlurXTexture.setBorderColor(ColorRGBA.white);
      SSAOBlurXTexture.setMinificationFilter(Texture.MinificationFilter.BilinearNearestMipMap);
      SSAOBlurXTexture.setMagnificationFilter(Texture.MagnificationFilter.Bilinear);
      
      SSAOBlurYTexture = new Texture2D();
      SSAOBlurYTexture.setRenderToTextureType(RenderToTextureType.Luminance8);
      SSAOBlurYTexture.setWrap(WrapMode.BorderClamp);
      SSAOBlurYTexture.setHasBorder(true); // not sure about this right now
      SSAOBlurYTexture.setBorderColor(ColorRGBA.white);
      SSAOBlurYTexture.setMinificationFilter(Texture.MinificationFilter.BilinearNearestMipMap);
      SSAOBlurYTexture.setMagnificationFilter(Texture.MagnificationFilter.Bilinear);
      
      SSAOTexture = new Texture2D();
      SSAOTexture.setRenderToTextureType(RenderToTextureType.Luminance8);
      SSAOTexture.setWrap(WrapMode.BorderClamp);
      SSAOTexture.setHasBorder(true); // not sure about this right now
      SSAOTexture.setBorderColor(ColorRGBA.white);
      SSAOTexture.setMinificationFilter(Texture.MinificationFilter.BilinearNearestMipMap);
      SSAOTexture.setMagnificationFilter(Texture.MagnificationFilter.Bilinear);
      
      // load the noise texture for the SSAO shader
      noiseTexture = TextureManager.loadTexture(
            ResourceLocatorLibaryTool.locateResource(ResourceLocatorLibaryTool.TYPE_EFFECT,"/ssao/noise.dds"),
              Texture.MinificationFilter.NearestNeighborNoMipMaps,
              Texture.MagnificationFilter.NearestNeighbor);
      noiseTexture.setWrap(WrapMode.Repeat);
      
      // load the shaders
      preRenderShader = createPreRenderShader(display);
       blurXShader = createBlurXShader(display);
       blurYShader = createBlurYShader(display);
       SSAOShader = createSSAOShader(display);
       texturingShader = createTexturingShader(display);
       
      // Set up the texture renderer
      tRenderer = display.createTextureRenderer(selectedTextureWidth, selectedTextureWidth, TextureRenderer.Target.Texture2D);
      tRenderer.setBackgroundColor(new ColorRGBA(.0f, .0f, .0f, 1f));
      tRenderer.setMultipleTargets(false);
      tRenderer.setupTexture(normalAndDepthTexture);
      tRenderer.setupTexture(SSAOTexture);
      tRenderer.setupTexture(SSAOBlurXTexture);
      tRenderer.setupTexture(SSAOBlurYTexture);
      tRenderer.setCamera(cam);
      
      // create the fullscreen quad
      fullScreenQuad = new Quad("FullScreenQuad", display.getWidth(), display.getHeight());
      fullScreenQuad.getLocalTranslation().set(display.getWidth() / 2, display.getHeight() / 2, 0);
      fullScreenQuad.setRenderQueueMode(Renderer.QUEUE_ORTHO);

      fullScreenQuad.setCullHint(Spatial.CullHint.Never);
      //fullScreenQuad.setTextureCombineMode(Spatial.TextureCombineMode.Replace);
      fullScreenQuad.setLightCombineMode(Spatial.LightCombineMode.Off);
      
      // create the SSAO texture states
      fsQuadTextureState = display.getRenderer().createTextureState();
      fsQuadTextureState.setEnabled(true);

      fsQuadTextureState.setTexture(noiseTexture, 0);
      fsQuadTextureState.setTexture(normalAndDepthTexture, 1);
      fsQuadTextureState.setTexture(SSAOTexture, 2);
      fsQuadTextureState.setTexture(SSAOBlurXTexture, 3);
       fsQuadTextureState.setTexture(SSAOBlurYTexture, 4);
       
       if(fsQuadTextureState == null)
          System.out.println("Could not create TextureState in SSAORenderPass!");
       
       fullScreenQuad.setRenderState(fsQuadTextureState);

       blendState = display.getRenderer().createBlendState();
       blendState.setBlendEnabled(true);
       blendState.setSourceFunction(BlendState.SourceFunction.Zero);
       blendState.setDestinationFunction(BlendState.DestinationFunction.SourceColor);
       blendState.setEnabled(true);
       
       ZBufferState buf = display.getRenderer().createZBufferState();
        buf.setEnabled( true );
        buf.setFunction( ZBufferState.TestFunction.LessThanOrEqualTo );
        fullScreenQuad.setRenderState( buf );
      
        fullScreenQuad.updateGeometricState(0,true);
        fullScreenQuad.updateRenderState();
        
        textures = new ArrayList<Texture>(1);
        textures.add(normalAndDepthTexture);
   }
   
   @Override
   protected void doRender(Renderer r)
   {
      // first render the normal and depths
      
      if(shouldUpdateUniforms)
      {
         blurXShader.setUniform("g_BlurFalloff", blurFallOff);
         blurXShader.setUniform("g_Sharpness", blurSharpness);
         blurXShader.setUniform("g_BlurRadius", blurRadius);
         
         blurYShader.setUniform("g_BlurFalloff", blurFallOff);
         blurYShader.setUniform("g_Sharpness", blurSharpness);
         blurYShader.setUniform("g_BlurRadius", blurRadius);
         
         SSAOShader.setUniform("totStrength", ssaoTotalStrength);
         SSAOShader.setUniform("strength", ssaoRayStrength);
         SSAOShader.setUniform("offset", ssaoOffset);
         SSAOShader.setUniform("rad", ssaoRadius);
           SSAOShader.setUniform("falloff", ssaoFallOff);
           
           shouldUpdateUniforms = false;
      }
      
      // enforce the shader that will render the depth and the normals
      context.enforceState(preRenderShader);
      
      // render to texture
      tRenderer.render(spatials, textures,true);

      // restore context
      context.clearEnforcedState(RenderState.RS_GLSL_SHADER_OBJECTS);
      
      // second, render SSAO to SSAO texture
       fullScreenQuad.setRenderState(SSAOShader);
       fullScreenQuad.updateRenderState();
      
//       tRendererHalf.render(fullScreenQuad, SSAOTexture,true);
       tRenderer.render(fullScreenQuad, SSAOTexture,false);
       
      // third, render a X-blur to SSAOBlurXTexture

       fullScreenQuad.setRenderState(blurXShader);
       fullScreenQuad.updateRenderState();
       
       tRenderer.render(fullScreenQuad, SSAOBlurXTexture,false);
       
      // fourth, render a Y-blur to the screen (for now)
       fullScreenQuad.setRenderState(blurYShader);
       fullScreenQuad.updateRenderState();
       
       tRenderer.render(fullScreenQuad, SSAOBlurYTexture,false);
       
       fullScreenQuad.setRenderState(texturingShader);
       
       
       // apply the blend state so the result will be blended with the current scene
        fullScreenQuad.setRenderState(blendState);
       fullScreenQuad.updateRenderState();
       
       // we must manually draw this one
       r.draw(fullScreenQuad);
       r.renderQueue();
       
       // remove the blend state
       fullScreenQuad.clearRenderState(RenderState.RS_BLEND);
   }

   private GLSLShaderObjectsState createPreRenderShader(DisplaySystem display)
    {
        GLSLShaderObjectsState so = display.getRenderer().createGLSLShaderObjectsState();

        try{
           so.load(
                 ResourceLocatorLibaryTool.locateResource(ResourceLocatorLibaryTool.TYPE_SHADER, "/ssao/PreRender_vp.glsl"),
                ResourceLocatorLibaryTool.locateResource(ResourceLocatorLibaryTool.TYPE_SHADER, "/ssao/PreRender_fp.glsl")
                );
            so.apply();
            DisplaySystem.getDisplaySystem().getRenderer().checkCardError();
        } catch (JmeException e)
        {
           System.out.println(Level.WARNING + "Error loading shader" + e);
        }

        so.setUniform("zFar", cam.getFrustumFar());
        
        so.setEnabled(true);

        return so;
    }
   
   private GLSLShaderObjectsState createTexturingShader(DisplaySystem display)
    {
        GLSLShaderObjectsState so = display.getRenderer().createGLSLShaderObjectsState();

        try
        {
           so.load(
                 ResourceLocatorLibaryTool.locateResource(ResourceLocatorLibaryTool.TYPE_SHADER, "/ssao/Texturing_vp.glsl"),
                ResourceLocatorLibaryTool.locateResource(ResourceLocatorLibaryTool.TYPE_SHADER, "/ssao/Texturing_fp.glsl")
                );
         so.apply();
            DisplaySystem.getDisplaySystem().getRenderer().checkCardError();
        } catch (JmeException e)
        {
           System.out.println(Level.WARNING + "Error loading shader" + e);
        }
        
        so.setUniform("texture", 4);
        
        so.setEnabled(true);

        return so;
    }
   
    private GLSLShaderObjectsState createSSAOShader(DisplaySystem display)
    {
        GLSLShaderObjectsState so = display.getRenderer().createGLSLShaderObjectsState();

        try
        {
           so.load(
                 ResourceLocatorLibaryTool.locateResource(ResourceLocatorLibaryTool.TYPE_SHADER, "/ssao/SSAO_vp.glsl"),
                ResourceLocatorLibaryTool.locateResource(ResourceLocatorLibaryTool.TYPE_SHADER, "/ssao/SSAO_fp.glsl")
                );
         so.apply();
            DisplaySystem.getDisplaySystem().getRenderer().checkCardError();
        } catch (JmeException e)
        {
           System.out.println(Level.WARNING + "Error loading shader" + e);
        }

        so.setUniform("rnm", 0);
        so.setUniform("normalMap", 1);
        
        so.setUniform("totStrength", ssaoTotalStrength);
        so.setUniform("strength", ssaoRayStrength);
        so.setUniform("offset", ssaoOffset);
        so.setUniform("rad", ssaoRadius); // 0.007
        so.setUniform("falloff", ssaoFallOff); // 0.00002f
        
        so.setEnabled(true);

        return so;
    }
    
    private GLSLShaderObjectsState createBlurXShader(DisplaySystem display)
    {
        GLSLShaderObjectsState so = display.getRenderer().createGLSLShaderObjectsState();

        try
        {
           so.load(
                 ResourceLocatorLibaryTool.locateResource(ResourceLocatorLibaryTool.TYPE_SHADER, "/ssao/BlurX_vp.glsl"),
                ResourceLocatorLibaryTool.locateResource(ResourceLocatorLibaryTool.TYPE_SHADER, "/ssao/BlurX_fp.glsl")
                );
         so.apply();
            DisplaySystem.getDisplaySystem().getRenderer().checkCardError();
        } catch (JmeException e)
        {
           System.out.println(Level.WARNING + "Error loading shader" + e);
        }

        so.setUniform("g_BlurFalloff", blurFallOff);
        so.setUniform("g_Sharpness", blurSharpness);
        so.setUniform("g_BlurRadius", blurRadius);
        so.setUniform("g_InvResolutionFull", new Vector2f(1.0f/(float)selectedTextureWidth,1.0f/(float)selectedTextureWidth));

        so.setUniform("AOMap", 2);
        so.setUniform("normalMap", 1);
        
        so.setEnabled(true);

        return so;
    }
    
    private GLSLShaderObjectsState createBlurYShader(DisplaySystem display)
    {
        GLSLShaderObjectsState so = display.getRenderer().createGLSLShaderObjectsState();

        try
        {
           so.load(
                 ResourceLocatorLibaryTool.locateResource(ResourceLocatorLibaryTool.TYPE_SHADER, "/ssao/BlurY_vp.glsl"),
                ResourceLocatorLibaryTool.locateResource(ResourceLocatorLibaryTool.TYPE_SHADER, "/ssao/BlurY_fp.glsl")
                );
         so.apply();
            DisplaySystem.getDisplaySystem().getRenderer().checkCardError();
        } catch (JmeException e)
        {
           System.out.println(Level.WARNING + "Error loading shader" + e);
        }

        so.setUniform("g_BlurFalloff", blurFallOff);
        so.setUniform("g_Sharpness", blurSharpness);
        so.setUniform("g_BlurRadius", blurRadius);
        so.setUniform("g_InvResolutionFull", new Vector2f(1.0f/((float)selectedTextureWidth),1.0f/((float)selectedTextureWidth)));
        so.setUniform("normalMap", 1);
        so.setUniform("AOBlurXMap", 3);
        
        so.setEnabled(true);

        return so;
    }
    
    public void cleanup()
    {
       tRenderer.cleanup();
//       tRendererHalf.cleanup();
    }
    
    /**
     * @return
     */
    public boolean isSupported()
    {
       return GLSLShaderObjectsState.isSupported() && tRenderer.isSupported();
    }

   /**
    * @return the blurFallOff
    */
   public float getBlurFallOff()
   {
      return blurFallOff;
   }

   /**
    * @param blurFallOff the blurFallOff to set
    */
   public void setBlurFallOff(float blurFallOff)
   {
      this.blurFallOff = blurFallOff;
      shouldUpdateUniforms = true;
   }

   /**
    * @return the blurSharpness
    */
   public float getBlurSharpness()
   {
      return blurSharpness;
   }

   /**
    * @param blurSharpness the blurSharpness to set
    */
   public void setBlurSharpness(float blurSharpness)
   {
      this.blurSharpness = blurSharpness;
      shouldUpdateUniforms = true;
   }

   /**
    * @return the blurRadius
    */
   public float getBlurRadius()
   {
      return blurRadius;
   }

   /**
    * @param blurRadius the blurRadius to set
    */
   public void setBlurRadius(float blurRadius)
   {
      this.blurRadius = blurRadius;
      shouldUpdateUniforms = true;
   }

   /**
    * @return the ssaoTotalStrength
    */
   public float getSsaoTotalStrength()
   {
      return ssaoTotalStrength;
   }

   /**
    * @param ssaoTotalStrength the ssaoTotalStrength to set
    */
   public void setSsaoTotalStrength(float ssaoTotalStrength)
   {
      this.ssaoTotalStrength = ssaoTotalStrength;
      shouldUpdateUniforms = true;
   }

   /**
    * @return the ssaoRayStrength
    */
   public float getSsaoRayStrength()
   {
      return ssaoRayStrength;
   }

   /**
    * @param ssaoRayStrength the ssaoRayStrength to set
    */
   public void setSsaoRayStrength(float ssaoRayStrength)
   {
      this.ssaoRayStrength = ssaoRayStrength;
      shouldUpdateUniforms = true;
   }

   /**
    * @return the ssaoOffset
    */
   public float getSsaoOffset()
   {
      return ssaoOffset;
   }

   /**
    * @param ssaoOffset the ssaoOffset to set
    */
   public void setSsaoOffset(float ssaoOffset)
   {
      this.ssaoOffset = ssaoOffset;
      shouldUpdateUniforms = true;
   }

   /**
    * @return the ssaoRadius
    */
   public float getSsaoRadius()
   {
      return ssaoRadius;
   }

   /**
    * @param ssaoRadius the ssaoRadius to set
    */
   public void setSsaoRadius(float ssaoRadius)
   {
      this.ssaoRadius = ssaoRadius;
      shouldUpdateUniforms = true;
   }

   /**
    * @return the ssaoFallOff
    */
   public float getSsaoFallOff()
   {
      return ssaoFallOff;
   }

   /**
    * @param ssaoFallOff the ssaoFallOff to set
    */
   public void setSsaoFallOff(float ssaoFallOff)
   {
      this.ssaoFallOff = ssaoFallOff;
      shouldUpdateUniforms = true;
   }
}



I haven't changed the test class



I didn't see your response until now. Did you change anything in the renderpass? What's the result of running it? Error? Normal scene?

I changed the code since last time. There was a big error inside my code.

Can't tell, if it's work now or not. I will tell you after the weekend.

Would be great, because I like the SSAO Shader

It's not working for me. I've got such exception:



2009-08-20 14:59:56 class com.jme.renderer.lwjgl.LWJGLTextureRenderer render(Spatial, Texture, boolean)

SEVERE: Exception

java.lang.RuntimeException: FrameBuffer: 3, has caused a GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT exception

at com.jme.renderer.lwjgl.LWJGLTextureRenderer.checkFBOComplete(LWJGLTextureRenderer.java:723)

at com.jme.renderer.lwjgl.LWJGLTextureRenderer.setupForSingleTexDraw(LWJGLTextureRenderer.java:681)

at com.jme.renderer.lwjgl.LWJGLTextureRenderer.render(LWJGLTextureRenderer.java:530)

at com.jmetest.ssao.SSAORenderPass.doRender(SSAORenderPass.java:222)

at com.jme.renderer.pass.Pass.renderPass(Pass.java:92)

at com.jme.renderer.pass.BasicPassManager.renderPasses(BasicPassManager.java:90)

at com.jme.app.SimplePassGame.render(SimplePassGame.java:83)

at com.jme.app.BaseGame.start(BaseGame.java:87)

at com.jmetest.ssao.TestSSAO.main(TestSSAO.java:50)

Any ideas why not working on my computer? :confused: I just copied the provided SSAORenderPass nad Test class.

OK I think I fixed that by commenting the lines with setting texture's rtt type. Now it works :slight_smile:

I'll try this one soon. I guess it would deserve to get into jme2 if it's ready for that.

the basic problem with this shader is that it stores depth data in Alpha color. The result texture is bad, further parts blending over the closer parts. I'm modifying this, to use a separate depth texture. It's better, but still i have to figure out some parameters and over all. It renders ugly patches of darkness here and there over my landscape. :-o

@[IR]Radek



I had the same problem over here…

I dunno how to fix the GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT exception, the CheckFBO() throws, and especially i dont know if its a graphics adapter specific problem,<br /> but my geforce 8600GT throws the same exception when using&nbsp; TextureTypes like luminance or intensity with FBO,<br /> but works for me with alpha or depth beside colors;<br /> What you could do solving this problem, is to use PBO instead of FBO.<br /> <br /> take a look at com.jme.system.lwjgl.LWJGLDisplaySystem.createTextureRenderer(int,int,Target)<br /> if your graphics adapter doesnt support FBO … PBO is the FallBackMode.

So you might make some modification there.

What exactly does it look like?



I read the article about SSAO on wikipedia, but it kinda gives me no clue what does it really change the view :frowning:



Can maybee someone post a few screenshots about how it should look?

Good video that shows the effect: http://www.youtube.com/watch?v=MFkF4PcOAug

So the effect is something like diffuse shades in small areas? cause  the video looks kinda good especially when you see at the end when they turn it off, that there seems to be no other shading method at all.

Empire Phoenix said:

So the effect is something like diffuse shades in small areas? cause  the video looks kinda good especially when you see at the end when they turn it off, that there seems to be no other shading method at all.

Diffuse shades in tight areas, right. I think the result is great for a screen-spaced method.

So what it basically does is reducing the ambient lighting value of objects that are near other objects? Or is there something more happening?