Skip to content
Merged
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
11 changes: 11 additions & 0 deletions examples/src/examples/gaussian-splatting/viewer.controls.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,17 @@ export const controls = ({ observer, ReactPCUI, React, jsx, fragment }) => {
precision: 2
})
),
jsx(
LabelGroup,
{ text: 'midtones' },
jsx(SliderInput, {
binding: new BindingTwoWay(),
link: { observer, path: 'data.colorEnhance.midtones' },
min: -1,
max: 1,
precision: 2
})
),
jsx(
LabelGroup,
{ text: 'vibrance' },
Expand Down
2 changes: 2 additions & 0 deletions examples/src/examples/gaussian-splatting/viewer.example.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ assetListLoader.load(() => {
enabled: false,
shadows: 0,
highlights: 0,
midtones: 0,
vibrance: 0,
dehaze: 0
}
Expand Down Expand Up @@ -210,6 +211,7 @@ assetListLoader.load(() => {
cameraFrame.colorEnhance.enabled = data.get('data.colorEnhance.enabled');
cameraFrame.colorEnhance.shadows = data.get('data.colorEnhance.shadows');
cameraFrame.colorEnhance.highlights = data.get('data.colorEnhance.highlights');
cameraFrame.colorEnhance.midtones = data.get('data.colorEnhance.midtones');
cameraFrame.colorEnhance.vibrance = data.get('data.colorEnhance.vibrance');
cameraFrame.colorEnhance.dehaze = data.get('data.colorEnhance.dehaze');

Expand Down
11 changes: 11 additions & 0 deletions examples/src/examples/graphics/post-processing.controls.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,17 @@ export const controls = ({ observer, ReactPCUI, React, jsx, fragment }) => {
precision: 2
})
),
jsx(
LabelGroup,
{ text: 'midtones' },
jsx(SliderInput, {
binding: new BindingTwoWay(),
link: { observer, path: 'data.colorEnhance.midtones' },
min: -1,
max: 1,
precision: 2
})
),
jsx(
LabelGroup,
{ text: 'vibrance' },
Expand Down
2 changes: 2 additions & 0 deletions examples/src/examples/graphics/post-processing.example.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,7 @@ assetListLoader.load(() => {
if (cameraFrame.colorEnhance.enabled) {
cameraFrame.colorEnhance.shadows = data.get('data.colorEnhance.shadows');
cameraFrame.colorEnhance.highlights = data.get('data.colorEnhance.highlights');
cameraFrame.colorEnhance.midtones = data.get('data.colorEnhance.midtones');
cameraFrame.colorEnhance.vibrance = data.get('data.colorEnhance.vibrance');
cameraFrame.colorEnhance.dehaze = data.get('data.colorEnhance.dehaze');
}
Expand Down Expand Up @@ -320,6 +321,7 @@ assetListLoader.load(() => {
enabled: false,
shadows: 0,
highlights: 0,
midtones: 0,
vibrance: 0,
dehaze: 0
},
Expand Down
11 changes: 11 additions & 0 deletions examples/src/examples/graphics/sky.controls.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,17 @@ export const controls = ({ observer, ReactPCUI, React, jsx, fragment }) => {
precision: 2
})
),
jsx(
LabelGroup,
{ text: 'midtones' },
jsx(SliderInput, {
binding: new BindingTwoWay(),
link: { observer, path: 'data.colorEnhance.midtones' },
min: -1,
max: 1,
precision: 2
})
),
jsx(
LabelGroup,
{ text: 'vibrance' },
Expand Down
2 changes: 2 additions & 0 deletions examples/src/examples/graphics/sky.example.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ assetListLoader.load(() => {
cameraFrame.colorEnhance.enabled = data.get('data.colorEnhance.enabled');
cameraFrame.colorEnhance.shadows = data.get('data.colorEnhance.shadows');
cameraFrame.colorEnhance.highlights = data.get('data.colorEnhance.highlights');
cameraFrame.colorEnhance.midtones = data.get('data.colorEnhance.midtones');
cameraFrame.colorEnhance.vibrance = data.get('data.colorEnhance.vibrance');
cameraFrame.colorEnhance.dehaze = data.get('data.colorEnhance.dehaze');
cameraFrame.update();
Expand All @@ -190,6 +191,7 @@ assetListLoader.load(() => {
enabled: false,
shadows: 0,
highlights: 0,
midtones: 0,
vibrance: 0,
dehaze: 0
});
Expand Down
9 changes: 9 additions & 0 deletions scripts/esm/camera-frame.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,14 @@ class ColorEnhance {
*/
highlights = 0;

/**
* @visibleif {enabled}
* @range [-1, 1]
* @precision 3
* @step 0.01
*/
midtones = 0;

/**
* @visibleif {enabled}
* @range [-1, 1]
Expand Down Expand Up @@ -568,6 +576,7 @@ class CameraFrame extends Script {
if (colorEnhance.enabled) {
dstColorEnhance.shadows = colorEnhance.shadows;
dstColorEnhance.highlights = colorEnhance.highlights;
dstColorEnhance.midtones = colorEnhance.midtones;
dstColorEnhance.vibrance = colorEnhance.vibrance;
dstColorEnhance.dehaze = colorEnhance.dehaze;
}
Expand Down
5 changes: 5 additions & 0 deletions src/extras/render-passes/camera-frame.js
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,9 @@ import { CameraFrameOptions, RenderPassCameraFrame } from './render-pass-camera-
* @property {number} vibrance - The vibrance (smart saturation), -1 to 1 range. Positive values boost
* saturation of less-saturated colors more than already-saturated ones. Negative values desaturate.
* Defaults to 0.
* @property {number} midtones - The midtone adjustment, -1 to 1 range. Positive values brighten
* midtones, negative values darken midtones, with shadows and highlights more strongly preserved
* than by a linear exposure change. Defaults to 0.
* @property {number} dehaze - The dehaze adjustment, -1 to 1 range. Positive values remove atmospheric
* haze, increasing clarity and contrast. Negative values add a haze effect. Defaults to 0.
*/
Expand Down Expand Up @@ -331,6 +334,7 @@ class CameraFrame {
shadows: 0,
highlights: 0,
vibrance: 0,
midtones: 0,
dehaze: 0
};

Expand Down Expand Up @@ -541,6 +545,7 @@ class CameraFrame {
composePass.colorEnhanceShadows = colorEnhance.shadows;
composePass.colorEnhanceHighlights = colorEnhance.highlights;
composePass.colorEnhanceVibrance = colorEnhance.vibrance;
composePass.colorEnhanceMidtones = colorEnhance.midtones;
composePass.colorEnhanceDehaze = colorEnhance.dehaze;
}

Expand Down
4 changes: 4 additions & 0 deletions src/extras/render-passes/render-pass-compose.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ class RenderPassCompose extends RenderPassShaderQuad {

colorEnhanceDehaze = 0;

colorEnhanceMidtones = 0;

_taaEnabled = false;

_sharpness = 0.5;
Expand Down Expand Up @@ -127,6 +129,7 @@ class RenderPassCompose extends RenderPassShaderQuad {
this.colorLUTParams = new Float32Array(4);
this.colorLUTParamsId = scope.resolve('colorLUTParams');
this.colorEnhanceParamsId = scope.resolve('colorEnhanceParams');
this.colorEnhanceMidtonesId = scope.resolve('colorEnhanceMidtones');
}

set debug(value) {
Expand Down Expand Up @@ -385,6 +388,7 @@ class RenderPassCompose extends RenderPassShaderQuad {

if (this._colorEnhanceEnabled) {
this.colorEnhanceParamsId.setValue([this.colorEnhanceShadows, this.colorEnhanceHighlights, this.colorEnhanceVibrance, this.colorEnhanceDehaze]);
this.colorEnhanceMidtonesId.setValue(this.colorEnhanceMidtones);
}

const lutTexture = this._colorLUT;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export default /* glsl */`
#ifdef COLOR_ENHANCE
uniform vec4 colorEnhanceParams; // x=shadows, y=highlights, z=vibrance, w=dehaze
uniform float colorEnhanceMidtones;

vec3 applyColorEnhance(vec3 color) {
float maxChannel = max(color.r, max(color.g, color.b));
Expand All @@ -19,6 +20,20 @@ export default /* glsl */`
color *= pow(2.0, colorEnhanceParams.y * highlightWeight);
}

// Midtones - localized exposure in log-luminance space
if (colorEnhanceMidtones != 0.0) {
const float pivot = 0.18;
const float widthStops = 1.25;
const float maxStops = 2.0;
float y = max(dot(color, vec3(0.2126, 0.7152, 0.0722)), 1e-6);

// 0 at pivot, +/-1 one stop away from pivot
float d = log2(y / pivot);
float w = exp(-(d * d) / (2.0 * widthStops * widthStops));
float stops = colorEnhanceMidtones * maxStops * w;
color *= exp2(stops);
}

// Vibrance - skip if zero (coherent branch)
if (colorEnhanceParams.z != 0.0) {
float minChannel = min(color.r, min(color.g, color.b));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export default /* wgsl */`
#ifdef COLOR_ENHANCE
uniform colorEnhanceParams: vec4f; // x=shadows, y=highlights, z=vibrance, w=dehaze
uniform colorEnhanceMidtones: f32;

fn applyColorEnhance(color: vec3f) -> vec3f {
var colorOut = color;
Expand All @@ -20,6 +21,20 @@ export default /* wgsl */`
colorOut *= pow(2.0, uniform.colorEnhanceParams.y * highlightWeight);
}

// Midtones - localized exposure in log-luminance space
if (uniform.colorEnhanceMidtones != 0.0) {
let pivot = 0.18;
let widthStops = 1.25;
let maxStops = 2.0;
let y = max(dot(colorOut, vec3f(0.2126, 0.7152, 0.0722)), 1e-6);

// 0 at pivot, +/-1 one stop away from pivot
let d = log2(y / pivot);
let w = exp(-(d * d) / (2.0 * widthStops * widthStops));
let stops = uniform.colorEnhanceMidtones * maxStops * w;
colorOut *= exp2(stops);
}

// Vibrance - skip if zero (coherent branch)
if (uniform.colorEnhanceParams.z != 0.0) {
let minChannel = min(colorOut.r, min(colorOut.g, colorOut.b));
Expand Down