1 /* 2 * Copyright (c) 2009-2012 jMonkeyEngine 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are 7 * met: 8 * 9 * * Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * * Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * * Neither the name of 'jMonkeyEngine' nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 package com.jme3.post.ssao; 33 34 import com.jme3.asset.AssetManager; 35 import com.jme3.export.InputCapsule; 36 import com.jme3.export.JmeExporter; 37 import com.jme3.export.JmeImporter; 38 import com.jme3.export.OutputCapsule; 39 import com.jme3.material.Material; 40 import com.jme3.math.Vector2f; 41 import com.jme3.math.Vector3f; 42 import com.jme3.post.Filter; 43 import com.jme3.post.Filter.Pass; 44 import com.jme3.renderer.RenderManager; 45 import com.jme3.renderer.Renderer; 46 import com.jme3.renderer.ViewPort; 47 import com.jme3.shader.VarType; 48 import com.jme3.texture.Image.Format; 49 import com.jme3.texture.Texture; 50 import java.io.IOException; 51 import java.util.ArrayList; 52 53 /** 54 * SSAO stands for screen space ambient occlusion 55 * It's a technique that fake ambient lighting by computing shadows that near by objects would casts on each others 56 * under the effect of an ambient light 57 * more info on this in this blog post <a href="http://jmonkeyengine.org/2010/08/16/screen-space-ambient-occlusion-for-jmonkeyengine-3-0/">http://jmonkeyengine.org/2010/08/16/screen-space-ambient-occlusion-for-jmonkeyengine-3-0/</a> 58 * 59 * @author Rmy Bouquet aka Nehon 60 */ 61 public class SSAOFilter extends Filter { 62 63 private Pass normalPass; 64 private Vector3f frustumCorner; 65 private Vector2f frustumNearFar; 66 private Vector2f[] samples = {new Vector2f(1.0f, 0.0f), new Vector2f(-1.0f, 0.0f), new Vector2f(0.0f, 1.0f), new Vector2f(0.0f, -1.0f)}; 67 private float sampleRadius = 5.1f; 68 private float intensity = 1.5f; 69 private float scale = 0.2f; 70 private float bias = 0.1f; 71 private boolean useOnlyAo = false; 72 private boolean useAo = true; 73 private Material ssaoMat; 74 private Pass ssaoPass; 75 // private Material downSampleMat; 76 // private Pass downSamplePass; 77 private float downSampleFactor = 1f; 78 79 /** 80 * Create a Screen Space Ambient Occlusion Filter 81 */ 82 public SSAOFilter() { 83 super("SSAOFilter"); 84 } 85 86 /** 87 * Create a Screen Space Ambient Occlusion Filter 88 * @param sampleRadius The radius of the area where random samples will be picked. default 5.1f 89 * @param intensity intensity of the resulting AO. default 1.2f 90 * @param scale distance between occluders and occludee. default 0.2f 91 * @param bias the width of the occlusion cone considered by the occludee. default 0.1f 92 */ 93 public SSAOFilter(float sampleRadius, float intensity, float scale, float bias) { 94 this(); 95 this.sampleRadius = sampleRadius; 96 this.intensity = intensity; 97 this.scale = scale; 98 this.bias = bias; 99 } 100 101 @Override 102 protected boolean isRequiresDepthTexture() { 103 return true; 104 } 105 106 @Override 107 protected void postQueue(RenderManager renderManager, ViewPort viewPort) { 108 Renderer r = renderManager.getRenderer(); 109 r.setFrameBuffer(normalPass.getRenderFrameBuffer()); 110 renderManager.getRenderer().clearBuffers(true, true, true); 111 renderManager.setForcedTechnique("PreNormalPass"); 112 renderManager.renderViewPortQueues(viewPort, false); 113 renderManager.setForcedTechnique(null); 114 renderManager.getRenderer().setFrameBuffer(viewPort.getOutputFrameBuffer()); 115 } 116 117 @Override 118 protected Material getMaterial() { 119 return material; 120 } 121 122 @Override 123 protected void initFilter(AssetManager manager, RenderManager renderManager, ViewPort vp, int w, int h) { 124 int screenWidth = w; 125 int screenHeight = h; 126 postRenderPasses = new ArrayList<Pass>(); 127 128 normalPass = new Pass(); 129 normalPass.init(renderManager.getRenderer(), (int) (screenWidth / downSampleFactor), (int) (screenHeight / downSampleFactor), Format.RGBA8, Format.Depth); 130 131 132 frustumNearFar = new Vector2f(); 133 134 float farY = (vp.getCamera().getFrustumTop() / vp.getCamera().getFrustumNear()) * vp.getCamera().getFrustumFar(); 135 float farX = farY * ((float) screenWidth / (float) screenHeight); 136 frustumCorner = new Vector3f(farX, farY, vp.getCamera().getFrustumFar()); 137 frustumNearFar.x = vp.getCamera().getFrustumNear(); 138 frustumNearFar.y = vp.getCamera().getFrustumFar(); 139 140 141 142 143 144 //ssao Pass 145 ssaoMat = new Material(manager, "Common/MatDefs/SSAO/ssao.j3md"); 146 ssaoMat.setTexture("Normals", normalPass.getRenderedTexture()); 147 Texture random = manager.loadTexture("Common/MatDefs/SSAO/Textures/random.png"); 148 random.setWrap(Texture.WrapMode.Repeat); 149 ssaoMat.setTexture("RandomMap", random); 150 151 ssaoPass = new Pass() { 152 153 @Override 154 public boolean requiresDepthAsTexture() { 155 return true; 156 } 157 }; 158 159 ssaoPass.init(renderManager.getRenderer(), (int) (screenWidth / downSampleFactor), (int) (screenHeight / downSampleFactor), Format.RGBA8, Format.Depth, 1, ssaoMat); 160 ssaoPass.getRenderedTexture().setMinFilter(Texture.MinFilter.Trilinear); 161 ssaoPass.getRenderedTexture().setMagFilter(Texture.MagFilter.Bilinear); 162 postRenderPasses.add(ssaoPass); 163 material = new Material(manager, "Common/MatDefs/SSAO/ssaoBlur.j3md"); 164 material.setTexture("SSAOMap", ssaoPass.getRenderedTexture()); 165 166 ssaoMat.setVector3("FrustumCorner", frustumCorner); 167 ssaoMat.setFloat("SampleRadius", sampleRadius); 168 ssaoMat.setFloat("Intensity", intensity); 169 ssaoMat.setFloat("Scale", scale); 170 ssaoMat.setFloat("Bias", bias); 171 material.setBoolean("UseAo", useAo); 172 material.setBoolean("UseOnlyAo", useOnlyAo); 173 ssaoMat.setVector2("FrustumNearFar", frustumNearFar); 174 material.setVector2("FrustumNearFar", frustumNearFar); 175 ssaoMat.setParam("Samples", VarType.Vector2Array, samples); 176 177 float xScale = 1.0f / w; 178 float yScale = 1.0f / h; 179 180 float blurScale = 2f; 181 material.setFloat("XScale", blurScale * xScale); 182 material.setFloat("YScale", blurScale * yScale); 183 184 } 185 186 /** 187 * Return the bias<br> 188 * see {@link #setBias(float bias)} 189 * @return 190 */ 191 public float getBias() { 192 return bias; 193 } 194 195 /** 196 * Sets the the width of the occlusion cone considered by the occludee default is 0.1f 197 * @param bias 198 */ 199 public void setBias(float bias) { 200 this.bias = bias; 201 if (ssaoMat != null) { 202 ssaoMat.setFloat("Bias", bias); 203 } 204 } 205 206 /** 207 * returns the ambient occlusion intensity 208 * @return 209 */ 210 public float getIntensity() { 211 return intensity; 212 } 213 214 /** 215 * Sets the Ambient occlusion intensity default is 1.2f 216 * @param intensity 217 */ 218 public void setIntensity(float intensity) { 219 this.intensity = intensity; 220 if (ssaoMat != null) { 221 ssaoMat.setFloat("Intensity", intensity); 222 } 223 224 } 225 226 /** 227 * returns the sample radius<br> 228 * see {link setSampleRadius(float sampleRadius)} 229 * @return 230 */ 231 public float getSampleRadius() { 232 return sampleRadius; 233 } 234 235 /** 236 * Sets the radius of the area where random samples will be picked dafault 5.1f 237 * @param sampleRadius 238 */ 239 public void setSampleRadius(float sampleRadius) { 240 this.sampleRadius = sampleRadius; 241 if (ssaoMat != null) { 242 ssaoMat.setFloat("SampleRadius", sampleRadius); 243 } 244 245 } 246 247 /** 248 * returns the scale<br> 249 * see {@link #setScale(float scale)} 250 * @return 251 */ 252 public float getScale() { 253 return scale; 254 } 255 256 /** 257 * 258 * Returns the distance between occluders and occludee. default 0.2f 259 * @param scale 260 */ 261 public void setScale(float scale) { 262 this.scale = scale; 263 if (ssaoMat != null) { 264 ssaoMat.setFloat("Scale", scale); 265 } 266 } 267 268 /** 269 * debugging only , will be removed 270 * @return 271 */ 272 public boolean isUseAo() { 273 return useAo; 274 } 275 276 /** 277 * debugging only , will be removed 278 */ 279 public void setUseAo(boolean useAo) { 280 this.useAo = useAo; 281 if (material != null) { 282 material.setBoolean("UseAo", useAo); 283 } 284 285 } 286 287 /** 288 * debugging only , will be removed 289 * @return 290 */ 291 public boolean isUseOnlyAo() { 292 return useOnlyAo; 293 } 294 295 /** 296 * debugging only , will be removed 297 */ 298 public void setUseOnlyAo(boolean useOnlyAo) { 299 this.useOnlyAo = useOnlyAo; 300 if (material != null) { 301 material.setBoolean("UseOnlyAo", useOnlyAo); 302 } 303 } 304 305 @Override 306 public void write(JmeExporter ex) throws IOException { 307 super.write(ex); 308 OutputCapsule oc = ex.getCapsule(this); 309 oc.write(sampleRadius, "sampleRadius", 5.1f); 310 oc.write(intensity, "intensity", 1.5f); 311 oc.write(scale, "scale", 0.2f); 312 oc.write(bias, "bias", 0.1f); 313 } 314 315 @Override 316 public void read(JmeImporter im) throws IOException { 317 super.read(im); 318 InputCapsule ic = im.getCapsule(this); 319 sampleRadius = ic.readFloat("sampleRadius", 5.1f); 320 intensity = ic.readFloat("intensity", 1.5f); 321 scale = ic.readFloat("scale", 0.2f); 322 bias = ic.readFloat("bias", 0.1f); 323 } 324 } 325