diff --git a/src/SparkRenderer.ts b/src/SparkRenderer.ts index 3b6368b..9fb91fa 100644 --- a/src/SparkRenderer.ts +++ b/src/SparkRenderer.ts @@ -399,6 +399,8 @@ export class SparkRenderer extends THREE.Mesh { time: { value: 0 }, // Delta time in seconds since last frame deltaTime: { value: 0 }, + // Whether to use three.js's built-in linearToOutputTexel function instead of encodeLinear. + useLinearToOutputTexel: { value: false }, // Whether to encode Gsplat with linear RGB (for environment mapping) encodeLinear: { value: false }, // Debug flag that alternates each frame @@ -536,6 +538,7 @@ export class SparkRenderer extends THREE.Mesh { | THREE.OrthographicCamera; this.uniforms.near.value = typedCamera.near; this.uniforms.far.value = typedCamera.far; + this.uniforms.useLinearToOutputTexel.value = viewpoint.useLinearToOutputTexel; this.uniforms.encodeLinear.value = viewpoint.encodeLinear; this.uniforms.maxStdDev.value = this.maxStdDev; this.uniforms.minPixelRadius.value = this.minPixelRadius; diff --git a/src/SparkViewpoint.ts b/src/SparkViewpoint.ts index 43d960c..4460307 100644 --- a/src/SparkViewpoint.ts +++ b/src/SparkViewpoint.ts @@ -150,6 +150,7 @@ export class SparkViewpoint { target?: THREE.WebGLRenderTarget; private back?: THREE.WebGLRenderTarget; onTextureUpdated?: (texture: THREE.Texture) => void; + useLinearToOutputTexel = false; encodeLinear = false; superXY = 1; private superPixels?: Uint8Array; diff --git a/src/shaders/splatDefines.glsl b/src/shaders/splatDefines.glsl index 79e10c1..d56d048 100644 --- a/src/shaders/splatDefines.glsl +++ b/src/shaders/splatDefines.glsl @@ -35,11 +35,17 @@ float pow8(float x) { } vec3 srgbToLinear(vec3 rgb) { - return pow(rgb, vec3(2.2)); + // See https://github.com/mrdoob/three.js/blob/e04b9f7bd7f5b17103339d343168bfab2d6e0ace/src/math/ColorManagement.js#L205 + vec3 linearLow = rgb * 0.0773993808; + vec3 linearHigh = pow(rgb * 0.9478672986 + 0.0521327014, vec3(2.4)); + return mix(linearLow, linearHigh, greaterThanEqual(rgb, vec3(0.04045))); } vec3 linearToSrgb(vec3 rgb) { - return pow(rgb, vec3(1.0 / 2.2)); + // See https://github.com/mrdoob/three.js/blob/e04b9f7bd7f5b17103339d343168bfab2d6e0ace/src/math/ColorManagement.js#L211 + vec3 srgbLow = rgb * 12.92; + vec3 srgbHigh = 1.055 * (pow(rgb, vec3(0.41666)) - 0.055); + return mix(srgbLow, srgbHigh, greaterThanEqual(rgb, vec3(0.0031308))); } // uint encodeQuatXyz888(vec4 q) { diff --git a/src/shaders/splatFragment.glsl b/src/shaders/splatFragment.glsl index 0112ef3..e18f360 100644 --- a/src/shaders/splatFragment.glsl +++ b/src/shaders/splatFragment.glsl @@ -7,6 +7,7 @@ precision highp int; uniform float near; uniform float far; +uniform bool useLinearToOutputTexel; uniform bool encodeLinear; uniform float time; uniform bool debugFlag; @@ -69,7 +70,7 @@ void main() { if (rgba.a < minAlpha) { discard; } - if (encodeLinear) { + if (useLinearToOutputTexel || encodeLinear) { rgba.rgb = srgbToLinear(rgba.rgb); } @@ -88,6 +89,9 @@ void main() { discard; } } else { + if (useLinearToOutputTexel) { + rgba = linearToOutputTexel( rgba ); + } #ifdef PREMULTIPLIED_ALPHA fragColor = vec4(rgba.rgb * rgba.a, rgba.a); #else