Procházet zdrojové kódy

* Added tone mapping filter that does not rely on average luminance (unlike HDRProcessor).

shadowislord před 11 roky
rodič
revize
3838216207

+ 125 - 0
jme3-effects/src/main/java/com/jme3/post/filters/ToneMapFilter.java

@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.post.filters;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.material.Material;
+import com.jme3.math.Vector3f;
+import com.jme3.post.Filter;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.ViewPort;
+import java.io.IOException;
+
+/**
+ * Tone-mapping filter that uses filmic curve.
+ * 
+ * @author Kirill Vainer
+ */
+public class ToneMapFilter extends Filter {
+
+    private static final Vector3f DEFAULT_WHITEPOINT = new Vector3f(11.2f, 11.2f, 11.2f);
+    
+    private Vector3f whitePoint = DEFAULT_WHITEPOINT.clone();
+
+    /**
+     * Creates a tone-mapping filter with the default white-point of 11.2.
+     */
+    public ToneMapFilter() {
+        super("ToneMapFilter");
+    }
+
+    /**
+     * Creates a tone-mapping filter with the specified white-point.
+     * 
+     * @param whitePoint The intensity of the brightest part of the scene. 
+     */
+    public ToneMapFilter(Vector3f whitePoint) {
+        this();
+        this.whitePoint = whitePoint.clone();
+    }
+    
+    @Override
+    protected boolean isRequiresDepthTexture() {
+        return false;
+    }
+
+    @Override
+    protected void initFilter(AssetManager manager, RenderManager renderManager, ViewPort vp, int w, int h) {
+        material = new Material(manager, "Common/MatDefs/Post/ToneMap.j3md");
+        material.setVector3("WhitePoint", whitePoint);
+    }
+
+    @Override
+    protected Material getMaterial() {
+        return material;
+    }
+
+    /**
+     * Set the scene white point.
+     * 
+     * @param whitePoint The intensity of the brightest part of the scene. 
+     */
+    public void setWhitePoint(Vector3f whitePoint) {
+        if (material != null) {
+            material.setVector3("WhitePoint", whitePoint);
+        }
+        this.whitePoint = whitePoint;
+    }
+    
+    /**
+     * Get the scene white point.
+     * 
+     * @return The intensity of the brightest part of the scene. 
+     */
+    public Vector3f getWhitePoint() {
+        return whitePoint;
+    }
+
+    @Override
+    public void write(JmeExporter ex) throws IOException {
+        super.write(ex);
+        OutputCapsule oc = ex.getCapsule(this);
+        oc.write(whitePoint, "whitePoint", DEFAULT_WHITEPOINT.clone());
+    }
+
+    @Override
+    public void read(JmeImporter im) throws IOException {
+        super.read(im);
+        InputCapsule ic = im.getCapsule(this);
+        whitePoint = (Vector3f) ic.readSavable("whitePoint", DEFAULT_WHITEPOINT.clone());
+    }
+
+}

+ 38 - 0
jme3-effects/src/main/resources/Common/MatDefs/Post/ToneMap.frag

@@ -0,0 +1,38 @@
+#import "Common/ShaderLib/MultiSample.glsllib"
+
+uniform COLORTEXTURE m_Texture;
+uniform vec3 m_WhitePoint;
+
+#if __VERSION__ >= 150
+in vec2 texCoord;
+#else
+varying vec2 texCoord;
+#endif
+
+vec3 FilmicCurve(in vec3 x)
+{
+    const float A = 0.22;
+    const float B = 0.30;
+    const float C = 0.10;
+    const float D = 0.20;
+    const float E = 0.01;
+    const float F = 0.30;
+
+    return ((x * (A * x + C * B) + D * E) / (x * (A * x + B) + D * F)) - E / F;
+}
+
+// whitePoint should be 11.2
+
+vec3 ToneMap_Filmic(vec3 color, vec3 whitePoint)
+{
+    return FilmicCurve(color) / FilmicCurve(whitePoint);
+}
+
+void main() {
+    // TODO: This is incorrect if multi-sampling is used.
+    // The tone-mapping should be performed for each sample independently.
+
+    vec4 texVal = getColor(m_Texture, texCoord);
+    vec3 toneMapped = ToneMap_Filmic(texVal.rgb, m_WhitePoint);
+    gl_FragColor = vec4(toneMapped, texVal.a);
+}

+ 31 - 0
jme3-effects/src/main/resources/Common/MatDefs/Post/ToneMap.j3md

@@ -0,0 +1,31 @@
+MaterialDef Default GUI {
+
+    MaterialParameters {
+        Int NumSamples
+        Int NumSamplesDepth
+        Texture2D Texture
+        Vector3 WhitePoint
+    }
+
+    Technique {
+        VertexShader GLSL150:   Common/MatDefs/Post/Post15.vert
+        FragmentShader GLSL150: Common/MatDefs/Post/ToneMap.frag
+
+        WorldParameters {
+        }
+
+        Defines {
+            RESOLVE_MS : NumSamples
+        }
+
+    }
+
+    Technique {
+        VertexShader GLSL100:   Common/MatDefs/Post/Post.vert
+        FragmentShader GLSL100: Common/MatDefs/Post/ToneMap.frag
+
+        WorldParameters {  
+        }
+
+    }
+}

+ 141 - 0
jme3-examples/src/main/java/jme3test/post/TestToneMapFilter.java

@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package jme3test.post;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.input.KeyInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.AnalogListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector3f;
+import com.jme3.post.FilterPostProcessor;
+import com.jme3.post.HDRRenderer;
+import com.jme3.post.filters.ColorOverlayFilter;
+import com.jme3.post.filters.RadialBlurFilter;
+import com.jme3.post.filters.ToneMapFilter;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.shape.Box;
+import com.jme3.system.AppSettings;
+import com.jme3.ui.Picture;
+
+public class TestToneMapFilter extends SimpleApplication {
+
+    private boolean enabled = true;
+    private FilterPostProcessor fpp;
+    private ToneMapFilter toneMapFilter;
+
+    public static void main(String[] args){
+        TestToneMapFilter app = new TestToneMapFilter();
+        AppSettings settings = new AppSettings(true);
+        
+        // Must turn on gamma correction, as otherwise it looks too dark.
+        settings.setGammaCorrection(true);
+        
+        app.setSettings(settings);
+        app.start();
+    }
+
+    public Geometry createHDRBox(){
+        Box boxMesh = new Box(1, 1, 1);
+        Geometry box = new Geometry("Box", boxMesh);
+        Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+        mat.setTexture("ColorMap", assetManager.loadTexture("Textures/HdrTest/Memorial.hdr"));
+        box.setMaterial(mat);
+        return box;
+    }
+
+    @Override
+    public void simpleInitApp() {
+        fpp = new FilterPostProcessor(assetManager);
+        toneMapFilter = new ToneMapFilter();
+        fpp.addFilter(toneMapFilter);
+        viewPort.addProcessor(fpp);
+        
+        rootNode.attachChild(createHDRBox());
+        
+        cam.setLocation(new Vector3f(0f,0f,3f));
+        
+        initInputs();
+    }
+    
+    private void initInputs() {
+        inputManager.addMapping("toggle", new KeyTrigger(KeyInput.KEY_SPACE));
+        inputManager.addMapping("WhitePointUp", new KeyTrigger(KeyInput.KEY_Y));
+        inputManager.addMapping("WhitePointDown", new KeyTrigger(KeyInput.KEY_H));
+
+        ActionListener acl = new ActionListener() {
+
+            public void onAction(String name, boolean keyPressed, float tpf) {
+                if (name.equals("toggle") && keyPressed) {
+                    if (enabled) {
+                        enabled = false;
+                        viewPort.removeProcessor(fpp);
+                    } else {
+                        enabled = true;
+                        viewPort.addProcessor(fpp);
+                    }
+                }
+            }
+        };
+
+        AnalogListener anl = new AnalogListener() {
+
+            public void onAnalog(String name, float isPressed, float tpf) {
+                float wp = toneMapFilter.getWhitePoint().x;
+
+                if (name.equals("WhitePointUp")) {
+                    wp += tpf * 1.0;
+                    if (wp > 12f) {
+                        wp = 12f;
+                    }
+                    toneMapFilter.setWhitePoint(new Vector3f(wp, wp, wp));
+                    System.out.println("White point: " + wp);
+                }
+
+                if (name.equals("WhitePointDown")) {
+                    wp -= tpf * 1.0;
+                    if (wp < 0.01f) {
+                        wp = 0.01f;
+                    }
+                    toneMapFilter.setWhitePoint(new Vector3f(wp, wp, wp));
+                    System.out.println("White point: " + wp);
+                }
+            }
+        };
+
+        inputManager.addListener(acl, "toggle");
+        inputManager.addListener(anl, "WhitePointUp", "WhitePointDown");
+    }
+}