Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions jme3-core/src/main/resources/Common/ShaderLib/Hdr.glsllib
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,30 @@ vec3 HDR_ToneMap(in vec3 color, in float lumAvg, in float a, in float white){
vec3 HDR_ToneMap2(in vec3 color, in float lumAvg, in float a, in float white){
float scale = a / (lumAvg + 0.001);
return (vec3(scale) * color) / (color + vec3(1.0));
}

// Based on https://github.com/KhronosGroup/ToneMapping/blob/main/PBR_Neutral/pbrNeutral.glsl
// Input color is non-negative and resides in the Linear Rec. 709 color space.
// Output color is also Linear Rec. 709, but in the [0, 1] range.
vec3 HDR_KHRToneMap(in vec3 color, in vec3 exposure, in vec3 gamma) {
color *= pow(vec3(2.0), exposure);

const float startCompression = 0.8 - 0.04;
const float desaturation = 0.15;

float x = min(color.r, min(color.g, color.b));
float offset = x < 0.08 ? x - 6.25 * x * x : 0.04;
color -= offset;

float peak = max(color.r, max(color.g, color.b));
if (peak < startCompression) return color;

const float d = 1. - startCompression;
float newPeak = 1. - d * d / (peak + d - startCompression);
color *= newPeak / peak;

float g = 1. - 1. / (desaturation * (peak - newPeak) + 1.);
color = mix(color, newPeak * vec3(1, 1, 1), g);
color = pow(color, gamma);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

afaik this is the order used in blender to apply gamma and exposure, but i am not 100% sure on this

return color;
}
135 changes: 135 additions & 0 deletions jme3-effects/src/main/java/com/jme3/post/filters/KHRToneMapFilter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
/*
* 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 khronos neutral pbr tone mapping curve.
*/
public class KHRToneMapFilter extends Filter {

private static final float DEFAULT_EXPOSURE = 0.0f;
private static final float DEFAULT_GAMMA = 1.0f;

private final Vector3f exposure = new Vector3f(DEFAULT_EXPOSURE, DEFAULT_EXPOSURE, DEFAULT_EXPOSURE);
private final Vector3f gamma = new Vector3f(DEFAULT_GAMMA, DEFAULT_GAMMA, DEFAULT_GAMMA);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i've used vectors here so it gives more artistic control, kinda doubling as color correction


/**
* Creates a tone-mapping filter with the default exposure and gamma.
*/
public KHRToneMapFilter() {
super("KHRToneMapFilter");
}


@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/KHRToneMap.j3md");
material.setVector3("Exposure", exposure);
material.setVector3("Gamma", gamma);
}

@Override
protected Material getMaterial() {
return material;
}

/**
* Set the exposure for the tone mapping.
*
* @param whitePoint The exposure vector.
*/
public void setExposure(Vector3f whitePoint) {
this.exposure.set(whitePoint);
}

/**
* Get the exposure for the tone mapping.
*
* @return The exposure vector.
*/
public Vector3f getExposure() {
return exposure;
}


/**
* Set the gamma for the tone mapping.
*
* @param gamma The gamma vector.
*/
public void setGamma(Vector3f gamma) {
this.gamma.set(gamma);
}

/**
* Get the gamma for the tone mapping.
*
* @return The gamma vector.
*/
public Vector3f getGamma() {
return gamma;
}

@Override
public void write(JmeExporter ex) throws IOException {
super.write(ex);
OutputCapsule oc = ex.getCapsule(this);
oc.write(exposure, "exposure", new Vector3f(DEFAULT_EXPOSURE, DEFAULT_EXPOSURE, DEFAULT_EXPOSURE));
oc.write(gamma, "gamma", new Vector3f(DEFAULT_GAMMA, DEFAULT_GAMMA, DEFAULT_GAMMA));
}

@Override
public void read(JmeImporter im) throws IOException {
super.read(im);
InputCapsule ic = im.getCapsule(this);
exposure.set((Vector3f)ic.readSavable("exposure", new Vector3f(DEFAULT_EXPOSURE, DEFAULT_EXPOSURE, DEFAULT_EXPOSURE)));
gamma.set((Vector3f)ic.readSavable("gamma", new Vector3f(DEFAULT_GAMMA, DEFAULT_GAMMA, DEFAULT_GAMMA)));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#extension GL_ARB_texture_multisample : enable
#import "Common/ShaderLib/GLSLCompat.glsllib"
#import "Common/ShaderLib/Hdr.glsllib"


uniform vec3 m_Exposure;
uniform vec3 m_Gamma;
varying vec2 texCoord;

vec3 applyCurve(in vec3 x) {
return HDR_KHRToneMap(x, m_Exposure, m_Gamma);
}


#ifdef NUM_SAMPLES

uniform sampler2DMS m_Texture;

vec4 applyToneMap() {
ivec2 iTexC = ivec2(texCoord * vec2(textureSize(m_Texture)));
vec4 color = vec4(0.0);
for (int i = 0; i < NUM_SAMPLES; i++) {
vec4 hdrColor = texelFetch(m_Texture, iTexC, i);
vec3 ldrColor = applyCurve(hdrColor.rgb);
color += vec4(ldrColor, hdrColor.a);
}
return color / float(NUM_SAMPLES);
}

#else

uniform sampler2D m_Texture;

vec4 applyToneMap() {
vec4 texVal = texture2D(m_Texture, texCoord);
return vec4(applyCurve(texVal.rgb) , texVal.a);
}

#endif

void main() {
gl_FragColor = applyToneMap();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
MaterialDef KHRToneMap {

MaterialParameters {
Int BoundDrawBuffer
Int NumSamples
Int NumSamplesDepth
Texture2D Texture
Vector3 Gamma
Vector3 Exposure
}

Technique {
VertexShader GLSL300 GLSL150 GLSL100: Common/MatDefs/Post/Post.vert
FragmentShader GLSL300 GLSL150 GLSL100: Common/MatDefs/Post/KHRToneMap.frag

WorldParameters {
}

Defines {
BOUND_DRAW_BUFFER: BoundDrawBuffer
NUM_SAMPLES : NumSamples
}
}
}
Loading