• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 Rémy 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      */
SSAOFilter()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      */
SSAOFilter(float sampleRadius, float intensity, float scale, float bias)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
isRequiresDepthTexture()102     protected boolean isRequiresDepthTexture() {
103         return true;
104     }
105 
106     @Override
postQueue(RenderManager renderManager, ViewPort viewPort)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
getMaterial()118     protected Material getMaterial() {
119         return material;
120     }
121 
122     @Override
initFilter(AssetManager manager, RenderManager renderManager, ViewPort vp, int w, int h)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      */
getBias()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      */
setBias(float bias)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      */
getIntensity()210     public float getIntensity() {
211         return intensity;
212     }
213 
214     /**
215      * Sets the Ambient occlusion intensity default is 1.2f
216      * @param intensity
217      */
setIntensity(float intensity)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      */
getSampleRadius()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      */
setSampleRadius(float sampleRadius)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      */
getScale()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      */
setScale(float scale)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      */
isUseAo()272     public boolean isUseAo() {
273         return useAo;
274     }
275 
276     /**
277      * debugging only , will be removed
278      */
setUseAo(boolean useAo)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      */
isUseOnlyAo()291     public boolean isUseOnlyAo() {
292         return useOnlyAo;
293     }
294 
295     /**
296      * debugging only , will be removed
297      */
setUseOnlyAo(boolean useOnlyAo)298     public void setUseOnlyAo(boolean useOnlyAo) {
299         this.useOnlyAo = useOnlyAo;
300         if (material != null) {
301             material.setBoolean("UseOnlyAo", useOnlyAo);
302         }
303     }
304 
305     @Override
write(JmeExporter ex)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
read(JmeImporter im)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