Skip to content

Commit

Permalink
WebGLRenderer: Add support for AgX Tone Mapping (mrdoob#27366)
Browse files Browse the repository at this point in the history
* Add support for AGX tone mapping

* Update tonemapping example

* Update test

* AGX -> AgX

* glsl lint, suffixes fix

* Adjust tone mapping constant values

* Update src/renderers/shaders/ShaderChunk/tonemapping_pars_fragment.glsl.js

Co-authored-by: Don McCurdy <dm@donmccurdy.com>

* Update src/renderers/shaders/ShaderChunk/tonemapping_pars_fragment.glsl.js

Co-authored-by: Don McCurdy <dm@donmccurdy.com>

* Update OutputPass.js

Add `AgX` support.

* Update OutputShader.js

Add `AgX` support.

* Clamp the values to be > 0.0

* Add rec2020 matrices

* Fix rec2020 conversion matrices

* Update implementation to be based on filament using rec 2020 color space

* Spaces -> tabs

* linting

* Code cleanup

* Support WebGL1, rearrange

* Comments update

* Remove redundant clamp

---------

Co-authored-by: Don McCurdy <dm@donmccurdy.com>
Co-authored-by: Michael Herzog <michael.herzog@human-interactive.org>
  • Loading branch information
3 people authored and AdaRoseCannon committed Jan 15, 2024
1 parent cbdafe5 commit e75b461
Show file tree
Hide file tree
Showing 7 changed files with 98 additions and 1 deletion.
2 changes: 2 additions & 0 deletions examples/jsm/postprocessing/OutputPass.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
LinearToneMapping,
ReinhardToneMapping,
CineonToneMapping,
AgXToneMapping,
ACESFilmicToneMapping,
SRGBTransfer
} from 'three';
Expand Down Expand Up @@ -59,6 +60,7 @@ class OutputPass extends Pass {
else if ( this._toneMapping === ReinhardToneMapping ) this.material.defines.REINHARD_TONE_MAPPING = '';
else if ( this._toneMapping === CineonToneMapping ) this.material.defines.CINEON_TONE_MAPPING = '';
else if ( this._toneMapping === ACESFilmicToneMapping ) this.material.defines.ACES_FILMIC_TONE_MAPPING = '';
else if ( this._toneMapping === AgXToneMapping ) this.material.defines.AGX_TONE_MAPPING = '';

this.material.needsUpdate = true;

Expand Down
4 changes: 4 additions & 0 deletions examples/jsm/shaders/OutputShader.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ const OutputShader = {
gl_FragColor.rgb = ACESFilmicToneMapping( gl_FragColor.rgb );
#elif defined( AGX_TONE_MAPPING )
gl_FragColor.rgb = AgXToneMapping( gl_FragColor.rgb );
#endif
// color space
Expand Down
1 change: 1 addition & 0 deletions examples/webgl_tonemapping.html
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
Reinhard: THREE.ReinhardToneMapping,
Cineon: THREE.CineonToneMapping,
ACESFilmic: THREE.ACESFilmicToneMapping,
AgX: THREE.AgXToneMapping,
Custom: THREE.CustomToneMapping
};

Expand Down
1 change: 1 addition & 0 deletions src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ export const ReinhardToneMapping = 2;
export const CineonToneMapping = 3;
export const ACESFilmicToneMapping = 4;
export const CustomToneMapping = 5;
export const AgXToneMapping = 6;
export const AttachedBindMode = 'attached';
export const DetachedBindMode = 'detached';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,5 +73,89 @@ vec3 ACESFilmicToneMapping( vec3 color ) {
}
// Matrices for rec 2020 <> rec 709 color space conversion
// matrix provided in row-major order so it has been transposed
// https://www.itu.int/pub/R-REP-BT.2407-2017
const mat3 LINEAR_REC2020_TO_LINEAR_SRGB = mat3(
vec3( 1.6605, - 0.1246, - 0.0182 ),
vec3( - 0.5876, 1.1329, - 0.1006 ),
vec3( - 0.0728, - 0.0083, 1.1187 )
);
const mat3 LINEAR_SRGB_TO_LINEAR_REC2020 = mat3(
vec3( 0.6274, 0.0691, 0.0164 ),
vec3( 0.3293, 0.9195, 0.0880 ),
vec3( 0.0433, 0.0113, 0.8956 )
);
// https://iolite-engine.com/blog_posts/minimal_agx_implementation
// Mean error^2: 3.6705141e-06
vec3 agxDefaultContrastApprox( vec3 x ) {
vec3 x2 = x * x;
vec3 x4 = x2 * x2;
return + 15.5 * x4 * x2
- 40.14 * x4 * x
+ 31.96 * x4
- 6.868 * x2 * x
+ 0.4298 * x2
+ 0.1191 * x
- 0.00232;
}
// Input and output encoded as Linear-sRGB.
vec3 AgXToneMapping( vec3 color ) {
// AgX constants
const mat3 AgXInsetMatrix = mat3(
vec3( 0.856627153315983, 0.137318972929847, 0.11189821299995 ),
vec3( 0.0951212405381588, 0.761241990602591, 0.0767994186031903 ),
vec3( 0.0482516061458583, 0.101439036467562, 0.811302368396859 )
);
// explicit AgXOutsetMatrix generated from Filaments AgXOutsetMatrixInv
const mat3 AgXOutsetMatrix = mat3(
vec3( 1.1271005818144368, - 0.1413297634984383, - 0.14132976349843826 ),
vec3( - 0.11060664309660323, 1.157823702216272, - 0.11060664309660294 ),
vec3( - 0.016493938717834573, - 0.016493938717834257, 1.2519364065950405 )
);
const float AgxMinEv = - 12.47393; // log2(pow(2, LOG2_MIN) * MIDDLE_GRAY)
const float AgxMaxEv = 4.026069; // log2(pow(2, LOG2_MAX) * MIDDLE_GRAY)
// AGX Tone Mapping implementation based on Filament, which is in turn based
// on Blender's implementation for rec 2020 colors:
// https://github.com/google/filament/pull/7236
color = LINEAR_SRGB_TO_LINEAR_REC2020 * color;
color *= toneMappingExposure;
color = AgXInsetMatrix * color;
// Log2 encoding
color = max( color, 1e-10 ); // avoid 0 or negative numbers for log2
color = log2( color );
color = ( color - AgxMinEv ) / ( AgxMaxEv - AgxMinEv );
color = clamp( color, 0.0, 1.0 );
// Apply sigmoid
color = agxDefaultContrastApprox( color );
// Apply AgX look
// v = agxLook(v, look);
color = AgXOutsetMatrix * color;
// Linearize
color = pow( max( vec3( 0.0 ), color ), vec3( 2.2 ) );
color = LINEAR_REC2020_TO_LINEAR_SRGB * color;
return color;
}
vec3 CustomToneMapping( vec3 color ) { return color; }
`;
6 changes: 5 additions & 1 deletion src/renderers/webgl/WebGLProgram.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { WebGLUniforms } from './WebGLUniforms.js';
import { WebGLShader } from './WebGLShader.js';
import { ShaderChunk } from '../shaders/ShaderChunk.js';
import { NoToneMapping, AddOperation, MixOperation, MultiplyOperation, CubeRefractionMapping, CubeUVReflectionMapping, CubeReflectionMapping, PCFSoftShadowMap, PCFShadowMap, VSMShadowMap, ACESFilmicToneMapping, CineonToneMapping, CustomToneMapping, ReinhardToneMapping, LinearToneMapping, GLSL3, LinearSRGBColorSpace, SRGBColorSpace, LinearDisplayP3ColorSpace, DisplayP3ColorSpace, P3Primaries, Rec709Primaries } from '../../constants.js';
import { NoToneMapping, AddOperation, MixOperation, MultiplyOperation, CubeRefractionMapping, CubeUVReflectionMapping, CubeReflectionMapping, PCFSoftShadowMap, PCFShadowMap, VSMShadowMap, AgXToneMapping, ACESFilmicToneMapping, CineonToneMapping, CustomToneMapping, ReinhardToneMapping, LinearToneMapping, GLSL3, LinearSRGBColorSpace, SRGBColorSpace, LinearDisplayP3ColorSpace, DisplayP3ColorSpace, P3Primaries, Rec709Primaries } from '../../constants.js';
import { ColorManagement } from '../../math/ColorManagement.js';

// From https://www.khronos.org/registry/webgl/extensions/KHR_parallel_shader_compile/
Expand Down Expand Up @@ -120,6 +120,10 @@ function getToneMappingFunction( functionName, toneMapping ) {
toneMappingName = 'ACESFilmic';
break;

case AgXToneMapping:
toneMappingName = 'AgX';
break;

case CustomToneMapping:
toneMappingName = 'Custom';
break;
Expand Down
1 change: 1 addition & 0 deletions test/unit/src/constants.tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ export default QUnit.module( 'Constants', () => {
assert.equal( Constants.CineonToneMapping, 3, 'CineonToneMapping is equal to 3' );
assert.equal( Constants.ACESFilmicToneMapping, 4, 'ACESFilmicToneMapping is equal to 4' );
assert.equal( Constants.CustomToneMapping, 5, 'CustomToneMapping is equal to 5' );
assert.equal( Constants.AgXToneMapping, 6, 'AgXToneMapping is equal to 6' );

assert.equal( Constants.AttachedBindMode, 'attached', 'AttachedBindMode is equal to attached' );
assert.equal( Constants.DetachedBindMode, 'detached', 'DetachedBindMode is equal to detached' );
Expand Down

0 comments on commit e75b461

Please sign in to comment.