Skip to content
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
5476d1b
ContrastAdjustmentFilter
pavly-gerges Nov 18, 2021
f916528
ContrastAdjustmentFilter test
pavly-gerges Nov 18, 2021
326314f
brightness code fix
pavly-gerges Nov 18, 2021
8cc4887
Fixed material reading problems, added gles glsl support, added final…
pavly-gerges Nov 19, 2021
918feee
update the testcase to meet the new changes
pavly-gerges Nov 19, 2021
43a5622
corrected instances namings in the testcase
pavly-gerges Nov 19, 2021
60b0378
Changed the default exp value to 2.2 and documenting other default va…
pavly-gerges Nov 20, 2021
b8e99ca
Fixed default constructor javadocs
pavly-gerges Nov 20, 2021
b68eecb
Corrected the brightness docs
pavly-gerges Nov 20, 2021
935d5bb
Cleaning up
pavly-gerges Nov 20, 2021
6f876f6
Cleaning up
pavly-gerges Nov 20, 2021
1eba06e
Java naming convention, code design simplifications, docs improvements
pavly-gerges Nov 23, 2021
135f541
Java naming conventions
pavly-gerges Nov 23, 2021
3be45cd
Minor language fix
pavly-gerges Nov 23, 2021
5f46638
Removed keyword 'brightness' from all files
pavly-gerges Nov 23, 2021
05b06e2
don't limit the output scaling to between 0 and 1
stephengold Dec 5, 2021
d0dbd77
ContrastAdjustmentFilter: simplify the class description
stephengold Dec 5, 2021
719134a
ContrastAdjustmentFilter: clarify what the input range does
stephengold Dec 5, 2021
745a85f
frags: use max() instead of abs() to avoid unintended color inversion
stephengold Dec 5, 2021
b09cc62
ContrastAdjustmentFilter: remove the null check from getMaterial()
stephengold Dec 5, 2021
3cd9675
ContrastAdjustmentFilter: add setters to set each parameter by itself
stephengold Dec 5, 2021
cd5d5cc
ContrastAdjustmentFilter: add a toString() method
stephengold Dec 5, 2021
b33772d
TestContrastAdjustmentFilter: add a user interface for thorough testing
stephengold Dec 5, 2021
d7562c1
ContrastAdjustmentFilter: set the filter name in both constructors
stephengold Dec 5, 2021
79fb8fd
ContrastAdjustmentFilter: remove the extra "material" field
stephengold Dec 5, 2021
3d9b332
ContrastAdjustmentFilter: don't use overridable method in constructor
stephengold Dec 5, 2021
f0b8fe4
ContrastAdjustmentFilter: consistent naming of fields, methods, and args
stephengold Dec 6, 2021
9adf407
ContrastAdjustmentFilter: privatize 8 fields for better encapsulation
stephengold Dec 6, 2021
6a9ae27
TCAF: include current year in the copyright notice
stephengold Dec 6, 2021
4888ff6
TCAF: display the filter status using a BitmapText
stephengold Dec 6, 2021
765e438
TCAF: scale change rates so they don't depend on the frames per second
stephengold Dec 6, 2021
86cc01d
TCAF: make the globe smaller, orient with the North Pole on top
stephengold Dec 6, 2021
0f35685
TCAF: unshaded scene doesn't need any lights
stephengold Dec 6, 2021
7d01835
TCAF: press Enter to reset the filter to defaults
stephengold Dec 6, 2021
b2936fb
TCAF: configure multisampling in the FilterPostProcessor
stephengold Dec 6, 2021
19b0af1
TCAF: improve the class javadoc
stephengold Dec 6, 2021
949defa
ContrastAdjustmentFilter: improve the javadoc
stephengold Dec 6, 2021
c8984ea
ContrastAdjustmentFilter: delete some unnecessary checks
stephengold Dec 6, 2021
bcdf7dc
TCAF: avoid crash when numSamples=0
stephengold Dec 6, 2021
d1ac65b
ContrastAdjustmentFilter: more javadoc
stephengold Dec 6, 2021
62eb06a
TCAF: rename to TestContrastAdjustment
stephengold Dec 8, 2021
43de4a6
rename ColorContrast->ContrastAdjustment consistent with other filters
stephengold Dec 8, 2021
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,296 @@
/*
* Copyright (c) 2009-2021 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.post.Filter;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
import java.io.IOException;

/**
* A color filter used to adjust the contrast for color channels of the rendered scene by using a
* simple transfer function which involves adjusting the input range of the color channels based on
* an upper and a lower limit, adjusting the exponent of different color channels and scaling the output values
* before being processed by the fragment shader.
*
* @author pavl_g.
*/
public class ContrastAdjustmentFilter extends Filter {

protected float redChannelExponent;
protected float greenChannelExponent;
protected float blueChannelExponent;
//the lower value and the upper value of the inputRange
protected float lowerLimit;
protected float upperLimit;
//the final pass scale factor
protected float redChannelScale;
protected float greenChannelScale;
protected float blueChannelScale;
protected Material material;

/**
* Instantiates a default color contrast filter, default input range and default scale.
* Default values :
* - Exponents = 1.0f on all channels.
* - Input Range Lower Limit = 0f.
* - Input Range Upper Limit = 1f.
* - Scale = 1.0f on all channels.
*/
public ContrastAdjustmentFilter() {
this(1f);
}

/**
* Instantiates a color contrast filter with a specific exponent, default scale and default input range.
*
* @param exponent an exponent to apply on all channels.
*/
public ContrastAdjustmentFilter(float exponent) {
setExponents(exponent, exponent, exponent);
Copy link
Contributor

Choose a reason for hiding this comment

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

You shouldn't call public methods in the constructor because methods can be overridden.
Use the filters that already exist as a reference.
Can you change it like this?

public ContrastAdjustmentFilter(float exponent) {
    this();
    this.redChannelExponent = exponent;
    this.greenChannelExponent = exponent;
    this.blueChannelExponent = exponent;
}

this(); is used to invoke the default constructor that sets the name of the filter.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I completely agree, calling an overridable method in a constructor is not right, I will apply these changes.

Copy link
Member

Choose a reason for hiding this comment

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

This conversation should be resolved now.

}


/**
* Sets the exponents used to adjust the contrast of the color channels.
* Default values are 1f.
*
* @param redChannelExponent the red channel exponent.
* @param greenChannelExponent the green channel exponent.
* @param blueChannelExponent the blue channel exponent.
* @return this filter instance for a chain call.
*/
public ContrastAdjustmentFilter setExponents(float redChannelExponent, float greenChannelExponent, float blueChannelExponent) {
this.redChannelExponent = redChannelExponent;
this.greenChannelExponent = greenChannelExponent;
this.blueChannelExponent = blueChannelExponent;

if (material == null) {
return this;
}
//different channels exp for different transfer functions
material.setFloat("redChannelExponent", redChannelExponent);
material.setFloat("greenChannelExponent", greenChannelExponent);
material.setFloat("blueChannelExponent", blueChannelExponent);
return this;
}

/**
* Retrieves the red channel exponent.
* Default value = 1.0f
*
* @return the red channel exponent.
*/
public float getRedChannelExponent() {
return redChannelExponent;
}

/**
* Retrieves the green channel exponent.
* Default value = 1.0f.
*
* @return the green channel exponent.
*/
public float getGreenChannelExponent() {
return greenChannelExponent;
}

/**
* Retrieves the blue channel exponent.
* Default value = 1.0f
*
* @return the blue channel exponent.
*/
public float getBlueChannelExponent() {
return blueChannelExponent;
}

/**
* Sets the color channels input range using a lowerLimit and an upperLimit based on this equation :
* color.rgb = (color.rgb - lowerLimit) / (upperLimit - lowerLimit)
* where; increasing the lowerLimit value and increasing the difference between upperLimit and lowerLimit would decrease the input range,
* while decreasing the lowerLimit value and increasing the difference between upperLimit and lowerLimit would increase the input range.
* The final input range value is always above 0.0.
*
* @param lowerLimit the lower value of the color channels input range, default is 0f.
* @param upperLimit the higher value of the color channels input range, default is 1f.
* @return this filter instance for a chain call.
*/
public ContrastAdjustmentFilter setInputRange(float lowerLimit, float upperLimit) {
this.lowerLimit = lowerLimit;
this.upperLimit = upperLimit;

if (material == null) {
return this;
Copy link
Member

Choose a reason for hiding this comment

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

The code would be clearer if there were a single return statement in each setter.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Although I feel the code is very clear, like 'if the material is not initialized' then skip setting the material data until we initialize it via the FilterProcessor, but I will try to simplify it more.

}

//inputRange values
material.setFloat("lowerLimit", lowerLimit);
material.setFloat("upperLimit", upperLimit);
return this;
}

/**
* Retrieves the channels input range lower limit.
* Default value = 0.0.
*
* @return the lower limit of the channels input range.
*/
public float getInputRangeLowerLimit() {
return lowerLimit;
}

/**
* Retrieves the channels input range upper limit.
* Default value = 1.0.
*
* @return the upper limit of the channels input range.
*/
public float getInputRangeUpperLimit() {
return upperLimit;
}

/**
* Adjusts the scales of different channels.
* Default values = 1.0.
*
* @param redChannelScale the red channel scale.
* @param greenChannelScale the green channel scale.
* @param blueChannelScale the blue channel scale.
* @return this filter instance for a chain call.
*/
public ContrastAdjustmentFilter setScales(float redChannelScale, float greenChannelScale, float blueChannelScale) {
this.redChannelScale = redChannelScale;
this.greenChannelScale = greenChannelScale;
this.blueChannelScale = blueChannelScale;

if (material == null) {
return this;
}

//adjust the scales of different channels through the material file
material.setFloat("redChannelScale", redChannelScale);
material.setFloat("greenChannelScale", greenChannelScale);
material.setFloat("blueChannelScale", blueChannelScale);
return this;
}

/**
* Retrieves the value of the red channel scale that's applied on the final pass.
* Default value = 1.0.
*
* @return the scale of the red channel.
*/
public float getRedChannelScale() {
return redChannelScale;
}

/**
* Retrieves the value of the green channel scale that's applied on the final pass.
* Default value = 1.0.
*
* @return the scale of the green channel.
*/
public float getGreenChannelScale() {
return greenChannelScale;
}

/**
* Retrieves the value of the blue channel scale that's applied on the final pass.
* Default value = 1.0.
*
* @return the scale of the blue channel.
*/
public float getBlueChannelScale() {
return blueChannelScale;
}

@Override
protected void initFilter(AssetManager manager, RenderManager renderManager, ViewPort vp, int w, int h) {
//validate app
if (manager == null || renderManager == null || vp == null || w == 0 || h == 0) {
return;
Copy link
Member

Choose a reason for hiding this comment

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

Throw an InvalidArgumentException here instead of failing silently.

Copy link
Contributor

@capdevon capdevon Nov 24, 2021

Choose a reason for hiding this comment

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

These checks are not necessary. Looking at the FilterPostProcessor class, the assetManager, renderManager and viewPort variables are never null and width and height are always greater than 0.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I will remove all the null checks.
@capdevon @stephengold Thanks for the review, keep reviewing other parts.

}
material = new Material(manager, "Common/MatDefs/Post/ColorContrast.j3md");

//different channels exp for different transfer functions
setExponents(redChannelExponent, greenChannelExponent, blueChannelExponent);

//input range
setInputRange(lowerLimit, upperLimit);

//final pass scales
setScales(redChannelScale, greenChannelScale, blueChannelScale);
}

@Override
protected Material getMaterial() {
if (material == null) {
throw new IllegalStateException("Cannot create a color filter from a null reference !");
}
return material;
}

@Override
public void read(JmeImporter im) throws IOException {
super.read(im);
final InputCapsule inputCapsule = im.getCapsule(this);
redChannelExponent = inputCapsule.readFloat("redChannelExponent", 1f);
greenChannelExponent = inputCapsule.readFloat("greenChannelExponent", 1f);
blueChannelExponent = inputCapsule.readFloat("blueChannelExponent", 1f);
lowerLimit = inputCapsule.readFloat("lowerLimit", 0f);
upperLimit = inputCapsule.readFloat("upperLimit", 1f);
redChannelScale = inputCapsule.readFloat("redChannelScale", 1f);
greenChannelScale = inputCapsule.readFloat("greenChannelScale", 1f);
blueChannelScale = inputCapsule.readFloat("blueChannelScale", 1f);
}

@Override
public void write(JmeExporter ex) throws IOException {
super.write(ex);
final OutputCapsule outputCapsule = ex.getCapsule(this);
outputCapsule.write(redChannelExponent, "redChannelExponent", 1f);
outputCapsule.write(greenChannelExponent, "greenChannelExponent", 1f);
outputCapsule.write(blueChannelExponent, "blueChannelExponent", 1f);
outputCapsule.write(lowerLimit, "lowerLimit", 0f);
outputCapsule.write(upperLimit, "upperLimit", 1f);
outputCapsule.write(redChannelScale, "redChannelScale", 1f);
outputCapsule.write(greenChannelScale, "greenChannelScale", 1f);
outputCapsule.write(blueChannelScale, "blueChannelScale", 1f);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* Copyright (c) 2009-2021 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.
*/

/**
* Utilized by the ColorContrast.j3md to adjust the color channels contrast by adjusting the input range of the color channels based on
* an upper and a lower limit, adjusting the exponent of different color channels and scaling the output values.
*
* Supports GLSL100 GLSL110 GLSL120 GLSL130.
*/

//constant inputs from java source
uniform float m_redChannelExponent;
uniform float m_greenChannelExponent;
uniform float m_blueChannelExponent;

//final scale values
uniform float m_redChannelScale;
uniform float m_greenChannelScale;
uniform float m_blueChannelScale;

//input range
uniform float m_lowerLimit;
uniform float m_upperLimit;

//container for the input from post.vert
uniform sampler2D m_Texture;

//varying input from post.vert vertex shader
varying vec2 texCoord;

void main() {

//get the color from a 2d sampler.
vec4 color = texture2D(m_Texture, texCoord);

//apply the color transfer function.

//1) adjust the channels input range
color.rgb = (color.rgb - vec3(m_lowerLimit)) / (vec3(m_upperLimit) - vec3(m_lowerLimit));
// limit the chromaticity space into the +ve quadrant
color.rgb = abs(color.rgb);

//2) apply transfer functions on different channels.
color.r = pow(color.r, m_redChannelExponent);
color.g = pow(color.g, m_greenChannelExponent);
color.b = pow(color.b, m_blueChannelExponent);

//3) apply a final scale factor, between 0.0 and 1.0.
color.r = color.r * min(max(m_redChannelScale, 0.0), 1.0);
color.b = color.b * min(max(m_blueChannelScale, 0.0), 1.0);
color.g = color.g * min(max(m_greenChannelScale, 0.0), 1.0);

//4) process the textures colors.
gl_FragColor = color;
}
Loading