From 05fc79cd52b79e8c3e8dec1e7dca72c5c39983a4 Mon Sep 17 00:00:00 2001 From: Michael Herzog Date: Tue, 4 Jan 2022 16:18:12 +0100 Subject: [PATCH] WebGLRenderer: Remove inline sRGB decode. (#23129) * WebGLRenderer: Remove inline sRGB decode. * Update WebGLTextures.js * VideoTexture: Use inline sRGB decode. * WebGLTexture: Fix video textures with WebGL 1. --- examples/jsm/loaders/GLTFLoader.js | 1 - examples/jsm/nodes/utils/ColorSpaceNode.js | 17 +---- .../renderers/nodes/display/ColorSpaceNode.js | 27 ++----- examples/jsm/utils/RoughnessMipmapper.js | 2 - examples/webgl_lightprobe_cubecamera.html | 1 - examples/webgl_materials_nodes.html | 19 ++--- src/constants.js | 3 + src/extras/ImageUtils.js | 63 +++++++++++++++ src/extras/PMREMGenerator.js | 76 ++----------------- src/math/Color.js | 2 +- .../ShaderChunk/emissivemap_fragment.glsl.js | 2 - .../encodings_pars_fragment.glsl.js | 4 - .../ShaderChunk/envmap_fragment.glsl.js | 2 - .../ShaderChunk/lightmap_fragment.glsl.js | 2 +- .../ShaderChunk/lights_fragment_maps.glsl.js | 2 +- .../lights_physical_fragment.glsl.js | 4 +- .../shaders/ShaderChunk/map_fragment.glsl.js | 13 +++- .../ShaderChunk/map_particle_fragment.glsl.js | 3 +- .../shaders/ShaderLib/background.glsl.js | 4 +- .../shaders/ShaderLib/equirect.glsl.js | 4 +- .../shaders/ShaderLib/meshbasic.glsl.js | 2 +- .../shaders/ShaderLib/meshmatcap.glsl.js | 1 - src/renderers/webgl/WebGLProgram.js | 16 +--- src/renderers/webgl/WebGLPrograms.js | 57 ++------------ src/renderers/webgl/WebGLTextures.js | 74 ++++++++++++++++-- src/renderers/webgl/WebGLUtils.js | 21 ++++- 26 files changed, 208 insertions(+), 214 deletions(-) diff --git a/examples/jsm/loaders/GLTFLoader.js b/examples/jsm/loaders/GLTFLoader.js index 1f6f2ea294b74f..82e7b012bff6c0 100644 --- a/examples/jsm/loaders/GLTFLoader.js +++ b/examples/jsm/loaders/GLTFLoader.js @@ -1478,7 +1478,6 @@ class GLTFMeshStandardSGMaterial extends MeshStandardMaterial { 'vec3 specularFactor = specular;', '#ifdef USE_SPECULARMAP', ' vec4 texelSpecular = texture2D( specularMap, vUv );', - ' texelSpecular = sRGBToLinear( texelSpecular );', ' // reads channel RGB, compatible with a glTF Specular-Glossiness (RGBA) texture', ' specularFactor *= texelSpecular.rgb;', '#endif' diff --git a/examples/jsm/nodes/utils/ColorSpaceNode.js b/examples/jsm/nodes/utils/ColorSpaceNode.js index 35a9691b324ae6..d9cea7de466359 100644 --- a/examples/jsm/nodes/utils/ColorSpaceNode.js +++ b/examples/jsm/nodes/utils/ColorSpaceNode.js @@ -57,9 +57,11 @@ class ColorSpaceNode extends TempNode { } - fromDecoding( encoding ) { + fromDecoding() { - const components = ColorSpaceNode.getEncodingComponents( encoding ); + // TODO: Remove fromDecoding() + + const components = ColorSpaceNode.getEncodingComponents( LinearEncoding ); this.method = components[ 0 ] + 'ToLinear'; this.factor = components[ 1 ]; @@ -106,14 +108,6 @@ ColorSpaceNode.Nodes = ( function () { }` ); - const sRGBToLinear = new FunctionNode( /* glsl */` - vec4 sRGBToLinear( in vec4 value ) { - - return vec4( mix( pow( value.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) ), value.rgb * 0.0773993808, vec3( lessThanEqual( value.rgb, vec3( 0.04045 ) ) ) ), value.w ); - - }` - ); - const LinearTosRGB = new FunctionNode( /* glsl */` vec4 LinearTosRGB( in vec4 value ) { @@ -124,15 +118,12 @@ ColorSpaceNode.Nodes = ( function () { return { LinearToLinear: LinearToLinear, - sRGBToLinear: sRGBToLinear, LinearTosRGB: LinearTosRGB }; } )(); ColorSpaceNode.LINEAR_TO_LINEAR = 'LinearToLinear'; - -ColorSpaceNode.SRGB_TO_LINEAR = 'sRGBToLinear'; ColorSpaceNode.LINEAR_TO_SRGB = 'LinearTosRGB'; ColorSpaceNode.getEncodingComponents = function ( encoding ) { diff --git a/examples/jsm/renderers/nodes/display/ColorSpaceNode.js b/examples/jsm/renderers/nodes/display/ColorSpaceNode.js index dcaee7c88d6c68..adc07d87963741 100644 --- a/examples/jsm/renderers/nodes/display/ColorSpaceNode.js +++ b/examples/jsm/renderers/nodes/display/ColorSpaceNode.js @@ -1,7 +1,7 @@ import TempNode from '../core/Node.js'; import { ShaderNode, vec3, - pow, mul, add, sub, mix, join, + pow, mul, sub, mix, join, lessThanEqual } from '../ShaderNode.js'; import { LinearEncoding, sRGBEncoding } from '../../../../../build/three.module.js'; @@ -12,22 +12,6 @@ export const LinearToLinear = new ShaderNode( ( inputs ) => { } ); -export const sRGBToLinear = new ShaderNode( ( inputs ) => { - - const { value } = inputs; - - const rgb = value.rgb; - - const a = pow( add( mul( rgb, 0.9478672986 ), vec3( 0.0521327014 ) ), vec3( 2.4 ) ); - const b = mul( rgb, 0.0773993808 ); - const factor = vec3( lessThanEqual( rgb, vec3( 0.04045 ) ) ); - - const rgbResult = mix( a, b, factor ); - - return join( rgbResult.r, rgbResult.g, rgbResult.b, value.a ); - -} ); - export const LinearTosRGB = new ShaderNode( ( inputs ) => { const { value } = inputs; @@ -46,7 +30,6 @@ export const LinearTosRGB = new ShaderNode( ( inputs ) => { const EncodingLib = { LinearToLinear, - sRGBToLinear, LinearTosRGB }; @@ -66,8 +49,6 @@ function getEncodingComponents( encoding ) { class ColorSpaceNode extends TempNode { static LINEAR_TO_LINEAR = 'LinearToLinear'; - - static SRGB_TO_LINEAR = 'sRGBToLinear'; static LINEAR_TO_SRGB = 'LinearTosRGB'; constructor( method, node ) { @@ -92,9 +73,11 @@ class ColorSpaceNode extends TempNode { } - fromDecoding( encoding ) { + fromDecoding() { - const components = getEncodingComponents( encoding ); + // TODO: Remove fromDecoding() + + const components = getEncodingComponents( LinearEncoding ); this.method = components[ 0 ] + 'ToLinear'; this.factor = components[ 1 ]; diff --git a/examples/jsm/utils/RoughnessMipmapper.js b/examples/jsm/utils/RoughnessMipmapper.js index 9fc441f655419a..9d3e5da929a6c1 100644 --- a/examples/jsm/utils/RoughnessMipmapper.js +++ b/examples/jsm/utils/RoughnessMipmapper.js @@ -184,8 +184,6 @@ function _getMipmapMaterial() { #define ENVMAP_TYPE_CUBE_UV - vec4 envMapTexelToLinear( vec4 a ) { return a; } - #include float roughnessToVariance( float roughness ) { diff --git a/examples/webgl_lightprobe_cubecamera.html b/examples/webgl_lightprobe_cubecamera.html index b6d9dd8344fa0c..4de9d5a61cff75 100644 --- a/examples/webgl_lightprobe_cubecamera.html +++ b/examples/webgl_lightprobe_cubecamera.html @@ -43,7 +43,6 @@ camera.position.set( 0, 0, 30 ); const cubeRenderTarget = new THREE.WebGLCubeRenderTarget( 256, { - encoding: THREE.sRGBEncoding, // since gamma is applied during rendering, the cubeCamera renderTarget texture encoding must be sRGBEncoding format: THREE.RGBAFormat } ); diff --git a/examples/webgl_materials_nodes.html b/examples/webgl_materials_nodes.html index 398354cc7624d9..373540d8363244 100644 --- a/examples/webgl_materials_nodes.html +++ b/examples/webgl_materials_nodes.html @@ -40,13 +40,13 @@ const library = {}; let serialized = false; const textures = { - brick: { url: 'textures/brick_diffuse.jpg' }, - grass: { url: 'textures/terrain/grasslight-big.jpg' }, - grassNormal: { url: 'textures/terrain/grasslight-big-nm.jpg' }, - decalDiffuse: { url: 'textures/decal/decal-diffuse.png' }, - decalNormal: { url: 'textures/decal/decal-normal.jpg' }, - cloud: { url: 'textures/lava/cloud.png' }, - spherical: { url: 'textures/envmap.png' } + brick: { url: 'textures/brick_diffuse.jpg', encoding: THREE.sRGBEncoding }, + grass: { url: 'textures/terrain/grasslight-big.jpg', encoding: THREE.sRGBEncoding }, + grassNormal: { url: 'textures/terrain/grasslight-big-nm.jpg', encoding: THREE.LinearEncoding }, + decalDiffuse: { url: 'textures/decal/decal-diffuse.png', encoding: THREE.sRGBEncoding }, + decalNormal: { url: 'textures/decal/decal-normal.jpg', encoding: THREE.LinearEncoding }, + cloud: { url: 'textures/lava/cloud.png', encoding: THREE.sRGBEncoding }, + spherical: { url: 'textures/envmap.png', encoding: THREE.sRGBEncoding } }; const param = { example: new URL( window.location.href ).searchParams.get( 'e' ) || 'mesh-standard' }; @@ -59,6 +59,7 @@ texture = textures[ name ].texture = new THREE.TextureLoader().load( textures[ name ].url ); texture.wrapS = texture.wrapT = THREE.RepeatWrapping; + texture.encoding = textures[ name ].encoding; library[ texture.uuid ] = texture; @@ -187,7 +188,7 @@ 'basic / spherical-reflection': 'spherical-reflection', 'basic / standard': 'standard', 'basic / uv-transform': 'uv-transform', - + 'adv / bias': 'bias', 'adv / camera-depth': 'camera-depth', 'adv / caustic': 'caustic', @@ -215,7 +216,7 @@ 'node / normal': 'node-normal', 'node / position': 'node-position', 'node / reflect': 'node-reflect', - + 'misc / basic-material': 'basic-material', 'misc / custom-attribute': 'custom-attribute', 'misc / firefly': 'firefly', diff --git a/src/constants.js b/src/constants.js index 352e5808e1ac1e..f0cb11ebbff83a 100644 --- a/src/constants.js +++ b/src/constants.js @@ -177,3 +177,6 @@ export const StreamCopyUsage = 35042; export const GLSL1 = '100'; export const GLSL3 = '300 es'; + +export const _SRGBFormat = 1034; // fallback for WebGL 1 +export const _SRGBAFormat = 1035; // fallback for WebGL 1 diff --git a/src/extras/ImageUtils.js b/src/extras/ImageUtils.js index 8ec60408c389c7..e7fcfef9b51fa0 100644 --- a/src/extras/ImageUtils.js +++ b/src/extras/ImageUtils.js @@ -1,4 +1,5 @@ import { createElementNS } from '../utils.js'; +import { SRGBToLinear } from '../math/Color.js'; let _canvas; @@ -61,6 +62,68 @@ class ImageUtils { } + static sRGBToLinear( image ) { + + if ( ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement ) || + ( typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement ) || + ( typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap ) ) { + + const canvas = createElementNS( 'canvas' ); + + canvas.width = image.width; + canvas.height = image.height; + + const context = canvas.getContext( '2d' ); + context.drawImage( image, 0, 0, image.width, image.height ); + + const imageData = context.getImageData( 0, 0, image.width, image.height ); + const data = imageData.data; + + for ( let i = 0; i < data.length; i ++ ) { + + data[ i ] = SRGBToLinear( data[ i ] / 255 ) * 255; + + } + + context.putImageData( imageData, 0, 0 ); + + return canvas; + + } else if ( image.data ) { + + const data = image.data.slice( 0 ); + + for ( let i = 0; i < data.length; i ++ ) { + + if ( data instanceof Uint8Array || data instanceof Uint8ClampedArray ) { + + data[ i ] = Math.floor( SRGBToLinear( data[ i ] / 255 ) * 255 ); + + } else { + + // assuming float + + data[ i ] = SRGBToLinear( data[ i ] ); + + } + + } + + return { + data: data, + width: image.width, + height: image.height + }; + + } else { + + console.warn( 'THREE.ImageUtils.sRGBToLinear(): Unsupported image type. No color space conversion applied.' ); + return image; + + } + + } + } export { ImageUtils }; diff --git a/src/extras/PMREMGenerator.js b/src/extras/PMREMGenerator.js index 7ccb4ec1ebde78..b85cf5d97fa64d 100644 --- a/src/extras/PMREMGenerator.js +++ b/src/extras/PMREMGenerator.js @@ -7,8 +7,6 @@ import { NoToneMapping, NoBlending, RGBAFormat, - UnsignedByteType, - sRGBEncoding, HalfFloatType } from '../constants.js'; @@ -42,11 +40,6 @@ const TOTAL_LODS = LOD_MAX - LOD_MIN + 1 + EXTRA_LOD_SIGMA.length; // samples and exit early, but not recompile the shader. const MAX_SAMPLES = 20; -const ENCODINGS = { - [ LinearEncoding ]: 0, - [ sRGBEncoding ]: 1 -}; - const _flatCamera = /*@__PURE__*/ new OrthographicCamera(); const { _lodPlanes, _sizeLods, _sigmas } = /*@__PURE__*/ _createPlanes(); const _clearColor = /*@__PURE__*/ new Color(); @@ -335,20 +328,6 @@ class PMREMGenerator { } - _setEncoding( uniform, texture ) { - - if ( this._renderer.capabilities.isWebGL2 === true && texture.format === RGBAFormat && texture.type === UnsignedByteType && texture.encoding === sRGBEncoding ) { - - uniform.value = ENCODINGS[ LinearEncoding ]; - - } else { - - uniform.value = ENCODINGS[ texture.encoding ]; - - } - - } - _textureToCubeUV( texture, cubeUVRenderTarget ) { const renderer = this._renderer; @@ -386,8 +365,6 @@ class PMREMGenerator { } - this._setEncoding( uniforms[ 'inputEncoding' ], texture ); - _setViewport( cubeUVRenderTarget, 0, 0, 3 * SIZE_MAX, 2 * SIZE_MAX ); renderer.setRenderTarget( cubeUVRenderTarget ); @@ -662,8 +639,6 @@ function _getBlurShader( maxSamples ) { uniform float mipInt; uniform vec3 poleAxis; - ${ _getEncodings() } - #define ENVMAP_TYPE_CUBE_UV #include @@ -730,8 +705,7 @@ function _getEquirectShader() { uniforms: { 'envMap': { value: null }, - 'texelSize': { value: texelSize }, - 'inputEncoding': { value: ENCODINGS[ LinearEncoding ] } + 'texelSize': { value: texelSize } }, vertexShader: _getCommonVertexShader(), @@ -746,8 +720,6 @@ function _getEquirectShader() { uniform sampler2D envMap; uniform vec2 texelSize; - ${ _getEncodings() } - #include void main() { @@ -759,13 +731,13 @@ function _getEquirectShader() { vec2 f = fract( uv / texelSize - 0.5 ); uv -= f * texelSize; - vec3 tl = envMapTexelToLinear( texture2D ( envMap, uv ) ).rgb; + vec3 tl = texture2D ( envMap, uv ).rgb; uv.x += texelSize.x; - vec3 tr = envMapTexelToLinear( texture2D ( envMap, uv ) ).rgb; + vec3 tr = texture2D ( envMap, uv ).rgb; uv.y += texelSize.y; - vec3 br = envMapTexelToLinear( texture2D ( envMap, uv ) ).rgb; + vec3 br = texture2D ( envMap, uv ).rgb; uv.x -= texelSize.x; - vec3 bl = envMapTexelToLinear( texture2D ( envMap, uv ) ).rgb; + vec3 bl = texture2D ( envMap, uv ).rgb; vec3 tm = mix( tl, tr, f.x ); vec3 bm = mix( bl, br, f.x ); @@ -791,8 +763,7 @@ function _getCubemapShader() { name: 'CubemapToCubeUV', uniforms: { - 'envMap': { value: null }, - 'inputEncoding': { value: ENCODINGS[ LinearEncoding ] } + 'envMap': { value: null } }, vertexShader: _getCommonVertexShader(), @@ -806,11 +777,9 @@ function _getCubemapShader() { uniform samplerCube envMap; - ${ _getEncodings() } - void main() { - gl_FragColor = envMapTexelToLinear( textureCube( envMap, vec3( - vOutputDirection.x, vOutputDirection.yz ) ) ); + gl_FragColor = textureCube( envMap, vec3( - vOutputDirection.x, vOutputDirection.yz ) ); } `, @@ -888,35 +857,4 @@ function _getCommonVertexShader() { } -function _getEncodings() { - - return /* glsl */` - - uniform int inputEncoding; - - #include - - vec4 inputTexelToLinear( vec4 value ) { - - if ( inputEncoding == 0 ) { - - return value; - - } else { - - return sRGBToLinear( value ); - - } - - } - - vec4 envMapTexelToLinear( vec4 color ) { - - return inputTexelToLinear( color ); - - } - `; - -} - export { PMREMGenerator }; diff --git a/src/math/Color.js b/src/math/Color.js index 96541f8983556b..42e77d471dbc30 100644 --- a/src/math/Color.js +++ b/src/math/Color.js @@ -563,4 +563,4 @@ Color.prototype.r = 1; Color.prototype.g = 1; Color.prototype.b = 1; -export { Color }; +export { Color, SRGBToLinear }; diff --git a/src/renderers/shaders/ShaderChunk/emissivemap_fragment.glsl.js b/src/renderers/shaders/ShaderChunk/emissivemap_fragment.glsl.js index 3c2855627971fd..68c4a3c7df38aa 100644 --- a/src/renderers/shaders/ShaderChunk/emissivemap_fragment.glsl.js +++ b/src/renderers/shaders/ShaderChunk/emissivemap_fragment.glsl.js @@ -3,8 +3,6 @@ export default /* glsl */` vec4 emissiveColor = texture2D( emissiveMap, vUv ); - emissiveColor.rgb = emissiveMapTexelToLinear( emissiveColor ).rgb; - totalEmissiveRadiance *= emissiveColor.rgb; #endif diff --git a/src/renderers/shaders/ShaderChunk/encodings_pars_fragment.glsl.js b/src/renderers/shaders/ShaderChunk/encodings_pars_fragment.glsl.js index 67d34ea063a13c..26371675293d38 100644 --- a/src/renderers/shaders/ShaderChunk/encodings_pars_fragment.glsl.js +++ b/src/renderers/shaders/ShaderChunk/encodings_pars_fragment.glsl.js @@ -4,10 +4,6 @@ vec4 LinearToLinear( in vec4 value ) { return value; } -vec4 sRGBToLinear( in vec4 value ) { - return vec4( mix( pow( value.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) ), value.rgb * 0.0773993808, vec3( lessThanEqual( value.rgb, vec3( 0.04045 ) ) ) ), value.a ); -} - vec4 LinearTosRGB( in vec4 value ) { return vec4( mix( pow( value.rgb, vec3( 0.41666 ) ) * 1.055 - vec3( 0.055 ), value.rgb * 12.92, vec3( lessThanEqual( value.rgb, vec3( 0.0031308 ) ) ) ), value.a ); } diff --git a/src/renderers/shaders/ShaderChunk/envmap_fragment.glsl.js b/src/renderers/shaders/ShaderChunk/envmap_fragment.glsl.js index 2ecf3c48d8f7c1..79454ff567aab8 100644 --- a/src/renderers/shaders/ShaderChunk/envmap_fragment.glsl.js +++ b/src/renderers/shaders/ShaderChunk/envmap_fragment.glsl.js @@ -38,8 +38,6 @@ export default /* glsl */` vec4 envColor = textureCube( envMap, vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) ); - envColor = envMapTexelToLinear( envColor ); - #elif defined( ENVMAP_TYPE_CUBE_UV ) vec4 envColor = textureCubeUV( envMap, reflectVec, 0.0 ); diff --git a/src/renderers/shaders/ShaderChunk/lightmap_fragment.glsl.js b/src/renderers/shaders/ShaderChunk/lightmap_fragment.glsl.js index 2a8a907cfc26cf..e9542e54d11c6d 100644 --- a/src/renderers/shaders/ShaderChunk/lightmap_fragment.glsl.js +++ b/src/renderers/shaders/ShaderChunk/lightmap_fragment.glsl.js @@ -2,7 +2,7 @@ export default /* glsl */` #ifdef USE_LIGHTMAP vec4 lightMapTexel = texture2D( lightMap, vUv2 ); - vec3 lightMapIrradiance = lightMapTexelToLinear( lightMapTexel ).rgb * lightMapIntensity; + vec3 lightMapIrradiance = lightMapTexel.rgb * lightMapIntensity; #ifndef PHYSICALLY_CORRECT_LIGHTS diff --git a/src/renderers/shaders/ShaderChunk/lights_fragment_maps.glsl.js b/src/renderers/shaders/ShaderChunk/lights_fragment_maps.glsl.js index 37132de884a11e..b72838bcbb771c 100644 --- a/src/renderers/shaders/ShaderChunk/lights_fragment_maps.glsl.js +++ b/src/renderers/shaders/ShaderChunk/lights_fragment_maps.glsl.js @@ -4,7 +4,7 @@ export default /* glsl */` #ifdef USE_LIGHTMAP vec4 lightMapTexel = texture2D( lightMap, vUv2 ); - vec3 lightMapIrradiance = lightMapTexelToLinear( lightMapTexel ).rgb * lightMapIntensity; + vec3 lightMapIrradiance = lightMapTexel.rgb * lightMapIntensity; #ifndef PHYSICALLY_CORRECT_LIGHTS diff --git a/src/renderers/shaders/ShaderChunk/lights_physical_fragment.glsl.js b/src/renderers/shaders/ShaderChunk/lights_physical_fragment.glsl.js index 4a0a6f7d7d1316..1e47d88909f3cd 100644 --- a/src/renderers/shaders/ShaderChunk/lights_physical_fragment.glsl.js +++ b/src/renderers/shaders/ShaderChunk/lights_physical_fragment.glsl.js @@ -24,7 +24,7 @@ material.roughness = min( material.roughness, 1.0 ); #ifdef USE_SPECULARCOLORMAP - specularColorFactor *= specularColorMapTexelToLinear( texture2D( specularColorMap, vUv ) ).rgb; + specularColorFactor *= texture2D( specularColorMap, vUv ).rgb; #endif @@ -79,7 +79,7 @@ material.roughness = min( material.roughness, 1.0 ); #ifdef USE_SHEENCOLORMAP - material.sheenColor *= sheenColorMapTexelToLinear( texture2D( sheenColorMap, vUv ) ).rgb; + material.sheenColor *= texture2D( sheenColorMap, vUv ).rgb; #endif diff --git a/src/renderers/shaders/ShaderChunk/map_fragment.glsl.js b/src/renderers/shaders/ShaderChunk/map_fragment.glsl.js index 5938668d59c7c7..89ebc6f8b34ec0 100644 --- a/src/renderers/shaders/ShaderChunk/map_fragment.glsl.js +++ b/src/renderers/shaders/ShaderChunk/map_fragment.glsl.js @@ -1,10 +1,17 @@ export default /* glsl */` #ifdef USE_MAP - vec4 texelColor = texture2D( map, vUv ); + vec4 sampledDiffuseColor = texture2D( map, vUv ); - texelColor = mapTexelToLinear( texelColor ); - diffuseColor *= texelColor; + #ifdef DECODE_VIDEO_TEXTURE + + // inline sRGB decode (TODO: Remove this code when https://crbug.com/1256340 is solved) + + sampledDiffuseColor = vec4( mix( pow( sampledDiffuseColor.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) ), sampledDiffuseColor.rgb * 0.0773993808, vec3( lessThanEqual( sampledDiffuseColor.rgb, vec3( 0.04045 ) ) ) ), sampledDiffuseColor.w ); + + #endif + + diffuseColor *= sampledDiffuseColor; #endif `; diff --git a/src/renderers/shaders/ShaderChunk/map_particle_fragment.glsl.js b/src/renderers/shaders/ShaderChunk/map_particle_fragment.glsl.js index 6bbc896acb9314..e1c7259cc1952d 100644 --- a/src/renderers/shaders/ShaderChunk/map_particle_fragment.glsl.js +++ b/src/renderers/shaders/ShaderChunk/map_particle_fragment.glsl.js @@ -7,8 +7,7 @@ export default /* glsl */` #ifdef USE_MAP - vec4 mapTexel = texture2D( map, uv ); - diffuseColor *= mapTexelToLinear( mapTexel ); + diffuseColor *= texture2D( map, uv ); #endif diff --git a/src/renderers/shaders/ShaderLib/background.glsl.js b/src/renderers/shaders/ShaderLib/background.glsl.js index a269da94a149e7..0ffff22930bb1e 100644 --- a/src/renderers/shaders/ShaderLib/background.glsl.js +++ b/src/renderers/shaders/ShaderLib/background.glsl.js @@ -18,9 +18,7 @@ varying vec2 vUv; void main() { - vec4 texColor = texture2D( t2D, vUv ); - - gl_FragColor = mapTexelToLinear( texColor ); + gl_FragColor = texture2D( t2D, vUv ); #include #include diff --git a/src/renderers/shaders/ShaderLib/equirect.glsl.js b/src/renderers/shaders/ShaderLib/equirect.glsl.js index b567619bae9a7a..f866aecd2aba26 100644 --- a/src/renderers/shaders/ShaderLib/equirect.glsl.js +++ b/src/renderers/shaders/ShaderLib/equirect.glsl.js @@ -26,9 +26,7 @@ void main() { vec2 sampleUV = equirectUv( direction ); - vec4 texColor = texture2D( tEquirect, sampleUV ); - - gl_FragColor = mapTexelToLinear( texColor ); + gl_FragColor = texture2D( tEquirect, sampleUV ); #include #include diff --git a/src/renderers/shaders/ShaderLib/meshbasic.glsl.js b/src/renderers/shaders/ShaderLib/meshbasic.glsl.js index d93243ede76ac7..3cd936dafe63b2 100644 --- a/src/renderers/shaders/ShaderLib/meshbasic.glsl.js +++ b/src/renderers/shaders/ShaderLib/meshbasic.glsl.js @@ -87,7 +87,7 @@ void main() { #ifdef USE_LIGHTMAP vec4 lightMapTexel= texture2D( lightMap, vUv2 ); - reflectedLight.indirectDiffuse += lightMapTexelToLinear( lightMapTexel ).rgb * lightMapIntensity; + reflectedLight.indirectDiffuse += lightMapTexel.rgb * lightMapIntensity; #else diff --git a/src/renderers/shaders/ShaderLib/meshmatcap.glsl.js b/src/renderers/shaders/ShaderLib/meshmatcap.glsl.js index 05a953139a7525..4c7fd4896dc473 100644 --- a/src/renderers/shaders/ShaderLib/meshmatcap.glsl.js +++ b/src/renderers/shaders/ShaderLib/meshmatcap.glsl.js @@ -86,7 +86,6 @@ void main() { #ifdef USE_MATCAP vec4 matcapColor = texture2D( matcap, uv ); - matcapColor = matcapTexelToLinear( matcapColor ); #else diff --git a/src/renderers/webgl/WebGLProgram.js b/src/renderers/webgl/WebGLProgram.js index a4461c692e9077..58acc0b9e5a701 100644 --- a/src/renderers/webgl/WebGLProgram.js +++ b/src/renderers/webgl/WebGLProgram.js @@ -49,13 +49,6 @@ function getShaderErrors( gl, shader, type ) { } -function getTexelDecodingFunction( functionName, encoding ) { - - const components = getEncodingComponents( encoding ); - return 'vec4 ' + functionName + '( vec4 value ) { return ' + components[ 0 ] + 'ToLinear' + components[ 1 ] + '; }'; - -} - function getTexelEncodingFunction( functionName, encoding ) { const components = getEncodingComponents( encoding ); @@ -625,6 +618,8 @@ function WebGLProgram( renderer, cacheKey, parameters, bindingStates ) { parameters.transmissionMap ? '#define USE_TRANSMISSIONMAP' : '', parameters.thicknessMap ? '#define USE_THICKNESSMAP' : '', + parameters.decodeVideoTexture ? '#define DECODE_VIDEO_TEXTURE' : '', + parameters.vertexTangents ? '#define USE_TANGENT' : '', parameters.vertexColors || parameters.instancingColor ? '#define USE_COLOR' : '', parameters.vertexAlphas ? '#define USE_COLOR_ALPHA' : '', @@ -662,13 +657,6 @@ function WebGLProgram( renderer, cacheKey, parameters, bindingStates ) { parameters.format === RGBFormat ? '#define OPAQUE' : '', ShaderChunk[ 'encodings_pars_fragment' ], // this code is required here because it is used by the various encoding/decoding function defined below - parameters.map ? getTexelDecodingFunction( 'mapTexelToLinear', parameters.mapEncoding ) : '', - parameters.matcap ? getTexelDecodingFunction( 'matcapTexelToLinear', parameters.matcapEncoding ) : '', - parameters.envMap ? getTexelDecodingFunction( 'envMapTexelToLinear', parameters.envMapEncoding ) : '', - parameters.emissiveMap ? getTexelDecodingFunction( 'emissiveMapTexelToLinear', parameters.emissiveMapEncoding ) : '', - parameters.specularColorMap ? getTexelDecodingFunction( 'specularColorMapTexelToLinear', parameters.specularColorMapEncoding ) : '', - parameters.sheenColorMap ? getTexelDecodingFunction( 'sheenColorMapTexelToLinear', parameters.sheenColorMapEncoding ) : '', - parameters.lightMap ? getTexelDecodingFunction( 'lightMapTexelToLinear', parameters.lightMapEncoding ) : '', getTexelEncodingFunction( 'linearToOutputTexel', parameters.outputEncoding ), parameters.depthPacking ? '#define DEPTH_PACKING ' + parameters.depthPacking : '', diff --git a/src/renderers/webgl/WebGLPrograms.js b/src/renderers/webgl/WebGLPrograms.js index 8474aaa1c81d43..9fc487df050a99 100644 --- a/src/renderers/webgl/WebGLPrograms.js +++ b/src/renderers/webgl/WebGLPrograms.js @@ -1,4 +1,4 @@ -import { BackSide, DoubleSide, CubeUVRefractionMapping, CubeUVReflectionMapping, LinearEncoding, sRGBEncoding, ObjectSpaceNormalMap, TangentSpaceNormalMap, NoToneMapping, RGBAFormat, UnsignedByteType } from '../../constants.js'; +import { BackSide, DoubleSide, CubeUVRefractionMapping, CubeUVReflectionMapping, ObjectSpaceNormalMap, TangentSpaceNormalMap, NoToneMapping, sRGBEncoding } from '../../constants.js'; import { Layers } from '../../core/Layers.js'; import { WebGLProgram } from './WebGLProgram.js'; import { WebGLShaderCache } from './WebGLShaderCache.js'; @@ -72,41 +72,6 @@ function WebGLPrograms( renderer, cubemaps, cubeuvmaps, extensions, capabilities } - function getTextureEncodingFromMap( map ) { - - let encoding; - - if ( map && map.isTexture ) { - - encoding = map.encoding; - - } else if ( map && map.isWebGLRenderTarget ) { - - console.warn( 'THREE.WebGLPrograms.getTextureEncodingFromMap: don\'t use render targets as textures. Use their .texture property instead.' ); - encoding = map.texture.encoding; - - } else { - - encoding = LinearEncoding; - - } - - if ( isWebGL2 && map && map.isTexture && map.format === RGBAFormat && map.type === UnsignedByteType && map.encoding === sRGBEncoding ) { - - encoding = LinearEncoding; // disable inline decode for sRGB textures in WebGL 2 - - } - - if ( map && map.isCompressedTexture ) { - - encoding = LinearEncoding; // disable inline decode for sRGB compressed textures - - } - - return encoding; - - } - function getParameters( material, lights, shadows, scene, object ) { const fog = scene.fog; @@ -183,25 +148,22 @@ function WebGLPrograms( renderer, cubemaps, cubeuvmaps, extensions, capabilities instancingColor: object.isInstancedMesh === true && object.instanceColor !== null, supportsVertexTextures: vertexTextures, - outputEncoding: ( currentRenderTarget !== null ) ? getTextureEncodingFromMap( currentRenderTarget.texture ) : renderer.outputEncoding, + outputEncoding: ( currentRenderTarget !== null ) ? currentRenderTarget.texture.encoding : renderer.outputEncoding, map: !! material.map, - mapEncoding: getTextureEncodingFromMap( material.map ), matcap: !! material.matcap, - matcapEncoding: getTextureEncodingFromMap( material.matcap ), envMap: !! envMap, envMapMode: envMap && envMap.mapping, - envMapEncoding: getTextureEncodingFromMap( envMap ), envMapCubeUV: ( !! envMap ) && ( ( envMap.mapping === CubeUVReflectionMapping ) || ( envMap.mapping === CubeUVRefractionMapping ) ), lightMap: !! material.lightMap, - lightMapEncoding: getTextureEncodingFromMap( material.lightMap ), aoMap: !! material.aoMap, emissiveMap: !! material.emissiveMap, - emissiveMapEncoding: getTextureEncodingFromMap( material.emissiveMap ), bumpMap: !! material.bumpMap, normalMap: !! material.normalMap, objectSpaceNormalMap: material.normalMapType === ObjectSpaceNormalMap, tangentSpaceNormalMap: material.normalMapType === TangentSpaceNormalMap, + decodeVideoTexture: !! material.map && ( material.map.isVideoTexture === true ) && ( material.map.encoding === sRGBEncoding ), + clearcoat: useClearcoat, clearcoatMap: useClearcoat && !! material.clearcoatMap, clearcoatRoughnessMap: useClearcoat && !! material.clearcoatRoughnessMap, @@ -213,7 +175,6 @@ function WebGLPrograms( renderer, cubemaps, cubeuvmaps, extensions, capabilities specularMap: !! material.specularMap, specularIntensityMap: !! material.specularIntensityMap, specularColorMap: !! material.specularColorMap, - specularColorMapEncoding: getTextureEncodingFromMap( material.specularColorMap ), alphaMap: !! material.alphaMap, alphaTest: useAlphaTest, @@ -222,7 +183,6 @@ function WebGLPrograms( renderer, cubemaps, cubeuvmaps, extensions, capabilities sheen: material.sheen > 0, sheenColorMap: !! material.sheenColorMap, - sheenColorMapEncoding: getTextureEncodingFromMap( material.sheenColorMap ), sheenRoughnessMap: !! material.sheenRoughnessMap, transmission: material.transmission > 0, @@ -346,12 +306,7 @@ function WebGLPrograms( renderer, cubemaps, cubeuvmaps, extensions, capabilities array.push( parameters.precision ); array.push( parameters.outputEncoding ); - array.push( parameters.mapEncoding ); - array.push( parameters.matcapEncoding ); array.push( parameters.envMapMode ); - array.push( parameters.envMapEncoding ); - array.push( parameters.lightMapEncoding ); - array.push( parameters.emissiveMapEncoding ); array.push( parameters.combine ); array.push( parameters.vertexUvs ); array.push( parameters.fogExp2 ); @@ -371,8 +326,6 @@ function WebGLPrograms( renderer, cubemaps, cubeuvmaps, extensions, capabilities array.push( parameters.numClippingPlanes ); array.push( parameters.numClipIntersection ); array.push( parameters.format ); - array.push( parameters.specularColorMapEncoding ); - array.push( parameters.sheenColorMapEncoding ); } @@ -492,6 +445,8 @@ function WebGLPrograms( renderer, cubemaps, cubeuvmaps, extensions, capabilities _programLayers.enable( 20 ); if ( parameters.sheenRoughnessMap ) _programLayers.enable( 21 ); + if ( parameters.decodeVideoTexture ) + _programLayers.enable( 22 ); array.push( _programLayers.mask ); diff --git a/src/renderers/webgl/WebGLTextures.js b/src/renderers/webgl/WebGLTextures.js index a4c316d46e852b..18a4b4301b30b3 100644 --- a/src/renderers/webgl/WebGLTextures.js +++ b/src/renderers/webgl/WebGLTextures.js @@ -1,5 +1,6 @@ -import { LinearFilter, LinearMipmapLinearFilter, LinearMipmapNearestFilter, NearestFilter, NearestMipmapLinearFilter, NearestMipmapNearestFilter, RGBFormat, RGBAFormat, DepthFormat, DepthStencilFormat, UnsignedShortType, UnsignedIntType, UnsignedInt248Type, FloatType, HalfFloatType, MirroredRepeatWrapping, ClampToEdgeWrapping, RepeatWrapping, sRGBEncoding } from '../../constants.js'; +import { LinearFilter, LinearMipmapLinearFilter, LinearMipmapNearestFilter, NearestFilter, NearestMipmapLinearFilter, NearestMipmapNearestFilter, RGBFormat, RGBAFormat, DepthFormat, DepthStencilFormat, UnsignedShortType, UnsignedIntType, UnsignedInt248Type, FloatType, HalfFloatType, MirroredRepeatWrapping, ClampToEdgeWrapping, RepeatWrapping, sRGBEncoding, LinearEncoding, UnsignedByteType, _SRGBFormat, _SRGBAFormat } from '../../constants.js'; import * as MathUtils from '../../math/MathUtils.js'; +import { ImageUtils } from '../../extras/ImageUtils.js'; import { createElementNS } from '../../utils.js'; function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info ) { @@ -130,7 +131,7 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, } - function getInternalFormat( internalFormatName, glFormat, glType, encoding ) { + function getInternalFormat( internalFormatName, glFormat, glType, encoding, isVideoTexture = false ) { if ( isWebGL2 === false ) return glFormat; @@ -164,7 +165,7 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, if ( glType === _gl.FLOAT ) internalFormat = _gl.RGBA32F; if ( glType === _gl.HALF_FLOAT ) internalFormat = _gl.RGBA16F; - if ( glType === _gl.UNSIGNED_BYTE ) internalFormat = ( encoding === sRGBEncoding ) ? _gl.SRGB8_ALPHA8 : _gl.RGBA8; + if ( glType === _gl.UNSIGNED_BYTE ) internalFormat = ( encoding === sRGBEncoding && isVideoTexture === false ) ? _gl.SRGB8_ALPHA8 : _gl.RGBA8; } @@ -550,13 +551,14 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, _gl.pixelStorei( _gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, _gl.NONE ); const needsPowerOfTwo = textureNeedsPowerOfTwo( texture ) && isPowerOfTwo( texture.image ) === false; - const image = resizeImage( texture.image, needsPowerOfTwo, false, maxTextureSize ); + let image = resizeImage( texture.image, needsPowerOfTwo, false, maxTextureSize ); + image = verifyColorSpace( texture, image ); const supportsMips = isPowerOfTwo( image ) || isWebGL2, glFormat = utils.convert( texture.format, texture.encoding ); let glType = utils.convert( texture.type ), - glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType, texture.encoding ); + glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType, texture.encoding, texture.isVideoTexture ); setTextureParameters( textureType, texture, supportsMips ); @@ -900,6 +902,8 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, } + cubeImage[ i ] = verifyColorSpace( texture, cubeImage[ i ] ); + } const image = cubeImage[ 0 ], @@ -1631,6 +1635,66 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, } + function verifyColorSpace( texture, image ) { + + const encoding = texture.encoding; + const format = texture.format; + const type = texture.type; + + if ( texture.isCompressedTexture === true || texture.format === _SRGBFormat || texture.format === _SRGBAFormat ) return image; + + if ( encoding !== LinearEncoding ) { + + // sRGB + + if ( encoding === sRGBEncoding && texture.isVideoTexture !== true ) { + + if ( isWebGL2 === false ) { + + // in WebGL 1, try to use EXT_sRGB extension and unsized formats + + if ( extensions.has( 'EXT_sRGB' ) === true && ( format === RGBFormat || format === RGBAFormat ) ) { + + if ( format === RGBFormat ) texture.format = _SRGBFormat; + if ( format === RGBAFormat ) texture.format = _SRGBAFormat; + + // it's not possible to generate mips in WebGL 1 with the above formats + + texture.minFilter = LinearFilter; + texture.generateMipmaps = false; + + } else { + + // slow fallback (CPU decode) + + image = ImageUtils.sRGBToLinear( image ); + + } + + } else { + + // in WebGL 2 uncompressed textures can only be sRGB encoded if they have the RGBA8 format + + if ( format !== RGBAFormat || type !== UnsignedByteType ) { + + console.warn( 'THREE.WebGLTextures: sRGB encoded textures have to use RGBAFormat and UnsignedByteType.' ); + + } + + } + + } else { + + console.error( 'THREE.WebGLTextures: Unsupported texture encoding:', encoding ); + + } + + } + + return image; + + } + // backwards compatibility let warnedTexture2D = false; diff --git a/src/renderers/webgl/WebGLUtils.js b/src/renderers/webgl/WebGLUtils.js index 7abb3bdae62de7..b1d9ca8b11d616 100644 --- a/src/renderers/webgl/WebGLUtils.js +++ b/src/renderers/webgl/WebGLUtils.js @@ -1,4 +1,4 @@ -import { RGBA_ASTC_4x4_Format, RGBA_ASTC_5x4_Format, RGBA_ASTC_5x5_Format, RGBA_ASTC_6x5_Format, RGBA_ASTC_6x6_Format, RGBA_ASTC_8x5_Format, RGBA_ASTC_8x6_Format, RGBA_ASTC_8x8_Format, RGBA_ASTC_10x5_Format, RGBA_ASTC_10x6_Format, RGBA_ASTC_10x8_Format, RGBA_ASTC_10x10_Format, RGBA_ASTC_12x10_Format, RGBA_ASTC_12x12_Format, RGB_ETC1_Format, RGB_ETC2_Format, RGBA_ETC2_EAC_Format, RGBA_PVRTC_2BPPV1_Format, RGBA_PVRTC_4BPPV1_Format, RGB_PVRTC_2BPPV1_Format, RGB_PVRTC_4BPPV1_Format, RGBA_S3TC_DXT5_Format, RGBA_S3TC_DXT3_Format, RGBA_S3TC_DXT1_Format, RGB_S3TC_DXT1_Format, DepthFormat, DepthStencilFormat, LuminanceAlphaFormat, LuminanceFormat, RedFormat, RGBAFormat, RGBFormat, AlphaFormat, RedIntegerFormat, RGFormat, RGIntegerFormat, RGBIntegerFormat, RGBAIntegerFormat, HalfFloatType, FloatType, UnsignedIntType, IntType, UnsignedShortType, ShortType, ByteType, UnsignedInt248Type, UnsignedShort565Type, UnsignedShort5551Type, UnsignedShort4444Type, UnsignedByteType, RGBA_BPTC_Format, sRGBEncoding } from '../../constants.js'; +import { RGBA_ASTC_4x4_Format, RGBA_ASTC_5x4_Format, RGBA_ASTC_5x5_Format, RGBA_ASTC_6x5_Format, RGBA_ASTC_6x6_Format, RGBA_ASTC_8x5_Format, RGBA_ASTC_8x6_Format, RGBA_ASTC_8x8_Format, RGBA_ASTC_10x5_Format, RGBA_ASTC_10x6_Format, RGBA_ASTC_10x8_Format, RGBA_ASTC_10x10_Format, RGBA_ASTC_12x10_Format, RGBA_ASTC_12x12_Format, RGB_ETC1_Format, RGB_ETC2_Format, RGBA_ETC2_EAC_Format, RGBA_PVRTC_2BPPV1_Format, RGBA_PVRTC_4BPPV1_Format, RGB_PVRTC_2BPPV1_Format, RGB_PVRTC_4BPPV1_Format, RGBA_S3TC_DXT5_Format, RGBA_S3TC_DXT3_Format, RGBA_S3TC_DXT1_Format, RGB_S3TC_DXT1_Format, DepthFormat, DepthStencilFormat, LuminanceAlphaFormat, LuminanceFormat, RedFormat, RGBAFormat, RGBFormat, AlphaFormat, RedIntegerFormat, RGFormat, RGIntegerFormat, RGBIntegerFormat, RGBAIntegerFormat, HalfFloatType, FloatType, UnsignedIntType, IntType, UnsignedShortType, ShortType, ByteType, UnsignedInt248Type, UnsignedShort565Type, UnsignedShort5551Type, UnsignedShort4444Type, UnsignedByteType, RGBA_BPTC_Format, sRGBEncoding, _SRGBFormat, _SRGBAFormat } from '../../constants.js'; function WebGLUtils( gl, extensions, capabilities ) { @@ -47,6 +47,25 @@ function WebGLUtils( gl, extensions, capabilities ) { if ( p === DepthStencilFormat ) return gl.DEPTH_STENCIL; if ( p === RedFormat ) return gl.RED; + // WebGL 1 sRGB fallback + + if ( p === _SRGBFormat || p === _SRGBAFormat ) { + + extension = extensions.get( 'EXT_sRGB' ); + + if ( extension !== null ) { + + if ( p === _SRGBFormat ) return extension.SRGB_EXT; + if ( p === _SRGBAFormat ) return extension.SRGB_ALPHA_EXT; + + } else { + + return null; + + } + + } + // WebGL2 formats. if ( p === RedIntegerFormat ) return gl.RED_INTEGER;