1 /*
2  * Copyright (c) 2009-2010 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.filters;
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.ColorRGBA;
41 import com.jme3.post.Filter;
42 import com.jme3.renderer.RenderManager;
43 import com.jme3.renderer.ViewPort;
44 import com.jme3.texture.Image.Format;
45 import java.io.IOException;
46 import java.util.ArrayList;
47 
48 /**
49  * BloomFilter is used to make objects in the scene have a glow effect.<br>
50  * There are 2 mode : Scene and Objects.<br>
51  * Scene mode extracts the bright parts of the scene to make them glow<br>
52  * Object mode make objects glow according to their material's glowMap or their GlowColor<br>
53  * @see <a href="http://jmonkeyengine.org/wiki/doku.php/jme3:advanced:bloom_and_glow">advanced:bloom_and_glow</a> for more details
54  *
55  * @author Rémy Bouquet aka Nehon
56  */
57 public class BloomFilter extends Filter {
58 
59     /**
60      * GlowMode specifies if the glow will be applied to the whole scene,or to objects that have aglow color or a glow map
61      */
62     public enum GlowMode {
63 
64         /**
65          * Apply bloom filter to bright areas in the scene.
66          */
67         Scene,
68         /**
69          * Apply bloom only to objects that have a glow map or a glow color.
70          */
71         Objects,
72         /**
73          * Apply bloom to both bright parts of the scene and objects with glow map.
74          */
75         SceneAndObjects;
76     }
77 
78     private GlowMode glowMode = GlowMode.Scene;
79     //Bloom parameters
80     private float blurScale = 1.5f;
81     private float exposurePower = 5.0f;
82     private float exposureCutOff = 0.0f;
83     private float bloomIntensity = 2.0f;
84     private float downSamplingFactor = 1;
85     private Pass preGlowPass;
86     private Pass extractPass;
87     private Pass horizontalBlur = new Pass();
88     private Pass verticalalBlur = new Pass();
89     private Material extractMat;
90     private Material vBlurMat;
91     private Material hBlurMat;
92     private int screenWidth;
93     private int screenHeight;
94 
95     /**
96      * Creates a Bloom filter
97      */
BloomFilter()98     public BloomFilter() {
99         super("BloomFilter");
100     }
101 
102     /**
103      * Creates the bloom filter with the specific glow mode
104      * @param glowMode
105      */
BloomFilter(GlowMode glowMode)106     public BloomFilter(GlowMode glowMode) {
107         this();
108         this.glowMode = glowMode;
109     }
110 
111     @Override
initFilter(AssetManager manager, RenderManager renderManager, ViewPort vp, int w, int h)112     protected void initFilter(AssetManager manager, RenderManager renderManager, ViewPort vp, int w, int h) {
113         screenWidth = (int) Math.max(1, (w / downSamplingFactor));
114         screenHeight = (int) Math.max(1, (h / downSamplingFactor));
115         //    System.out.println(screenWidth + " " + screenHeight);
116         if (glowMode != GlowMode.Scene) {
117             preGlowPass = new Pass();
118             preGlowPass.init(renderManager.getRenderer(), screenWidth, screenHeight, Format.RGBA8, Format.Depth);
119         }
120 
121         postRenderPasses = new ArrayList<Pass>();
122         //configuring extractPass
123         extractMat = new Material(manager, "Common/MatDefs/Post/BloomExtract.j3md");
124         extractPass = new Pass() {
125 
126             @Override
127             public boolean requiresSceneAsTexture() {
128                 return true;
129             }
130 
131             @Override
132             public void beforeRender() {
133                 extractMat.setFloat("ExposurePow", exposurePower);
134                 extractMat.setFloat("ExposureCutoff", exposureCutOff);
135                 if (glowMode != GlowMode.Scene) {
136                     extractMat.setTexture("GlowMap", preGlowPass.getRenderedTexture());
137                 }
138                 extractMat.setBoolean("Extract", glowMode != GlowMode.Objects);
139             }
140         };
141 
142         extractPass.init(renderManager.getRenderer(), screenWidth, screenHeight, Format.RGBA8, Format.Depth, 1, extractMat);
143         postRenderPasses.add(extractPass);
144 
145         //configuring horizontal blur pass
146         hBlurMat = new Material(manager, "Common/MatDefs/Blur/HGaussianBlur.j3md");
147         horizontalBlur = new Pass() {
148 
149             @Override
150             public void beforeRender() {
151                 hBlurMat.setTexture("Texture", extractPass.getRenderedTexture());
152                 hBlurMat.setFloat("Size", screenWidth);
153                 hBlurMat.setFloat("Scale", blurScale);
154             }
155         };
156 
157         horizontalBlur.init(renderManager.getRenderer(), screenWidth, screenHeight, Format.RGBA8, Format.Depth, 1, hBlurMat);
158         postRenderPasses.add(horizontalBlur);
159 
160         //configuring vertical blur pass
161         vBlurMat = new Material(manager, "Common/MatDefs/Blur/VGaussianBlur.j3md");
162         verticalalBlur = new Pass() {
163 
164             @Override
165             public void beforeRender() {
166                 vBlurMat.setTexture("Texture", horizontalBlur.getRenderedTexture());
167                 vBlurMat.setFloat("Size", screenHeight);
168                 vBlurMat.setFloat("Scale", blurScale);
169             }
170         };
171 
172         verticalalBlur.init(renderManager.getRenderer(), screenWidth, screenHeight, Format.RGBA8, Format.Depth, 1, vBlurMat);
173         postRenderPasses.add(verticalalBlur);
174 
175 
176         //final material
177         material = new Material(manager, "Common/MatDefs/Post/BloomFinal.j3md");
178         material.setTexture("BloomTex", verticalalBlur.getRenderedTexture());
179     }
180 
181 
182     @Override
getMaterial()183     protected Material getMaterial() {
184         material.setFloat("BloomIntensity", bloomIntensity);
185         return material;
186     }
187 
188     @Override
postQueue(RenderManager renderManager, ViewPort viewPort)189     protected void postQueue(RenderManager renderManager, ViewPort viewPort) {
190         if (glowMode != GlowMode.Scene) {
191             renderManager.getRenderer().setBackgroundColor(ColorRGBA.BlackNoAlpha);
192             renderManager.getRenderer().setFrameBuffer(preGlowPass.getRenderFrameBuffer());
193             renderManager.getRenderer().clearBuffers(true, true, true);
194             renderManager.setForcedTechnique("Glow");
195             renderManager.renderViewPortQueues(viewPort, false);
196             renderManager.setForcedTechnique(null);
197             renderManager.getRenderer().setFrameBuffer(viewPort.getOutputFrameBuffer());
198         }
199     }
200 
201     /**
202      * returns the bloom intensity
203      * @return
204      */
getBloomIntensity()205     public float getBloomIntensity() {
206         return bloomIntensity;
207     }
208 
209     /**
210      * intensity of the bloom effect default is 2.0
211      * @param bloomIntensity
212      */
setBloomIntensity(float bloomIntensity)213     public void setBloomIntensity(float bloomIntensity) {
214         this.bloomIntensity = bloomIntensity;
215     }
216 
217     /**
218      * returns the blur scale
219      * @return
220      */
getBlurScale()221     public float getBlurScale() {
222         return blurScale;
223     }
224 
225     /**
226      * sets The spread of the bloom default is 1.5f
227      * @param blurScale
228      */
setBlurScale(float blurScale)229     public void setBlurScale(float blurScale) {
230         this.blurScale = blurScale;
231     }
232 
233     /**
234      * returns the exposure cutoff<br>
235      * for more details see {@link setExposureCutOff(float exposureCutOff)}
236      * @return
237      */
getExposureCutOff()238     public float getExposureCutOff() {
239         return exposureCutOff;
240     }
241 
242     /**
243      * Define the color threshold on which the bloom will be applied (0.0 to 1.0)
244      * @param exposureCutOff
245      */
setExposureCutOff(float exposureCutOff)246     public void setExposureCutOff(float exposureCutOff) {
247         this.exposureCutOff = exposureCutOff;
248     }
249 
250     /**
251      * returns the exposure power<br>
252      * form more details see {@link setExposurePower(float exposurePower)}
253      * @return
254      */
getExposurePower()255     public float getExposurePower() {
256         return exposurePower;
257     }
258 
259     /**
260      * defines how many time the bloom extracted color will be multiplied by itself. default id 5.0<br>
261      * a high value will reduce rough edges in the bloom and somhow the range of the bloom area     *
262      * @param exposurePower
263      */
setExposurePower(float exposurePower)264     public void setExposurePower(float exposurePower) {
265         this.exposurePower = exposurePower;
266     }
267 
268     /**
269      * returns the downSampling factor<br>
270      * form more details see {@link setDownSamplingFactor(float downSamplingFactor)}
271      * @return
272      */
getDownSamplingFactor()273     public float getDownSamplingFactor() {
274         return downSamplingFactor;
275     }
276 
277     /**
278      * Sets the downSampling factor : the size of the computed texture will be divided by this factor. default is 1 for no downsampling
279      * A 2 value is a good way of widening the blur
280      * @param downSamplingFactor
281      */
setDownSamplingFactor(float downSamplingFactor)282     public void setDownSamplingFactor(float downSamplingFactor) {
283         this.downSamplingFactor = downSamplingFactor;
284     }
285 
286     @Override
write(JmeExporter ex)287     public void write(JmeExporter ex) throws IOException {
288         super.write(ex);
289         OutputCapsule oc = ex.getCapsule(this);
290         oc.write(glowMode, "glowMode", GlowMode.Scene);
291         oc.write(blurScale, "blurScale", 1.5f);
292         oc.write(exposurePower, "exposurePower", 5.0f);
293         oc.write(exposureCutOff, "exposureCutOff", 0.0f);
294         oc.write(bloomIntensity, "bloomIntensity", 2.0f);
295         oc.write(downSamplingFactor, "downSamplingFactor", 1);
296     }
297 
298     @Override
read(JmeImporter im)299     public void read(JmeImporter im) throws IOException {
300         super.read(im);
301         InputCapsule ic = im.getCapsule(this);
302         glowMode = ic.readEnum("glowMode", GlowMode.class, GlowMode.Scene);
303         blurScale = ic.readFloat("blurScale", 1.5f);
304         exposurePower = ic.readFloat("exposurePower", 5.0f);
305         exposureCutOff = ic.readFloat("exposureCutOff", 0.0f);
306         bloomIntensity = ic.readFloat("bloomIntensity", 2.0f);
307         downSamplingFactor = ic.readFloat("downSamplingFactor", 1);
308     }
309 }
310