Home | History | Annotate | Download | only in ssao
      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