* Added tone mapping filter that does not rely on average luminance (unlike HDRProcessor).
This commit is contained in:
parent
97ff9c2949
commit
3838216207
@ -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());
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
@ -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
jme3-examples/src/main/java/jme3test/post/TestToneMapFilter.java
Normal file
141
jme3-examples/src/main/java/jme3test/post/TestToneMapFilter.java
Normal file
@ -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");
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user