From 0193215eb228cd1b18f0a9454aff2b1be6d5a939 Mon Sep 17 00:00:00 2001 From: "Mr.doob" Date: Sat, 13 Dec 2025 13:50:10 +0700 Subject: [PATCH 1/5] Renderers: Remove premultipliedAlpha requirement for MultiplyBlending/SubtractiveBlending. --- src/renderers/webgl-fallback/utils/WebGLState.js | 4 ++-- src/renderers/webgl/WebGLState.js | 4 ++-- src/renderers/webgpu/utils/WebGPUPipelineUtils.js | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/renderers/webgl-fallback/utils/WebGLState.js b/src/renderers/webgl-fallback/utils/WebGLState.js index d9e5d5bf820c8c..51053c169a6bce 100644 --- a/src/renderers/webgl-fallback/utils/WebGLState.js +++ b/src/renderers/webgl-fallback/utils/WebGLState.js @@ -377,11 +377,11 @@ class WebGLState { break; case SubtractiveBlending: - error( 'WebGLState: SubtractiveBlending requires material.premultipliedAlpha = true' ); + gl.blendFuncSeparate( gl.ZERO, gl.ONE_MINUS_SRC_COLOR, gl.ZERO, gl.ONE ); break; case MultiplyBlending: - error( 'WebGLState: MultiplyBlending requires material.premultipliedAlpha = true' ); + gl.blendFuncSeparate( gl.DST_COLOR, gl.ONE_MINUS_SRC_ALPHA, gl.ZERO, gl.ONE ); break; default: diff --git a/src/renderers/webgl/WebGLState.js b/src/renderers/webgl/WebGLState.js index c49d48eff668be..b856d29aa54212 100644 --- a/src/renderers/webgl/WebGLState.js +++ b/src/renderers/webgl/WebGLState.js @@ -690,11 +690,11 @@ function WebGLState( gl, extensions ) { break; case SubtractiveBlending: - error( 'WebGLState: SubtractiveBlending requires material.premultipliedAlpha = true' ); + gl.blendFuncSeparate( gl.ZERO, gl.ONE_MINUS_SRC_COLOR, gl.ZERO, gl.ONE ); break; case MultiplyBlending: - error( 'WebGLState: MultiplyBlending requires material.premultipliedAlpha = true' ); + gl.blendFuncSeparate( gl.DST_COLOR, gl.ONE_MINUS_SRC_ALPHA, gl.ZERO, gl.ONE ); break; default: diff --git a/src/renderers/webgpu/utils/WebGPUPipelineUtils.js b/src/renderers/webgpu/utils/WebGPUPipelineUtils.js index 5b1a2e4c4da2ad..70e64b28e7f90a 100644 --- a/src/renderers/webgpu/utils/WebGPUPipelineUtils.js +++ b/src/renderers/webgpu/utils/WebGPUPipelineUtils.js @@ -448,11 +448,11 @@ class WebGPUPipelineUtils { break; case SubtractiveBlending: - error( 'WebGPURenderer: SubtractiveBlending requires material.premultipliedAlpha = true' ); + setBlend( GPUBlendFactor.Zero, GPUBlendFactor.OneMinusSrc, GPUBlendFactor.Zero, GPUBlendFactor.One ); break; case MultiplyBlending: - error( 'WebGPURenderer: MultiplyBlending requires material.premultipliedAlpha = true' ); + setBlend( GPUBlendFactor.Dst, GPUBlendFactor.OneMinusSrcAlpha, GPUBlendFactor.Zero, GPUBlendFactor.One ); break; } From 10f45d69dcfc3c1b678d5dfc8289ed2ce97d0c40 Mon Sep 17 00:00:00 2001 From: "Mr.doob" Date: Sat, 13 Dec 2025 14:01:35 +0700 Subject: [PATCH 2/5] Removed premultipliedAlpha from relevant examples. --- examples/misc_exporter_usdz.html | 2 +- examples/webgl_materials_blending.html | 2 -- examples/webgl_materials_car.html | 2 +- examples/webgl_materials_envmaps_groundprojected.html | 2 +- 4 files changed, 3 insertions(+), 5 deletions(-) diff --git a/examples/misc_exporter_usdz.html b/examples/misc_exporter_usdz.html index 68ce134524de91..865fe26ef2a93d 100644 --- a/examples/misc_exporter_usdz.html +++ b/examples/misc_exporter_usdz.html @@ -145,7 +145,7 @@ const geometry = new THREE.PlaneGeometry(); const material = new THREE.MeshBasicMaterial( { - map: shadowTexture, blending: THREE.MultiplyBlending, toneMapped: false, premultipliedAlpha: true + map: shadowTexture, blending: THREE.MultiplyBlending, toneMapped: false } ); const mesh = new THREE.Mesh( geometry, material ); diff --git a/examples/webgl_materials_blending.html b/examples/webgl_materials_blending.html index ca5bf8c956aef3..668b8c9cee17ab 100644 --- a/examples/webgl_materials_blending.html +++ b/examples/webgl_materials_blending.html @@ -102,8 +102,6 @@ material.transparent = true; material.blending = blending.constant; - material.premultipliedAlpha = true; - const x = ( i - blendings.length / 2 ) * 110; const z = 0; diff --git a/examples/webgl_materials_car.html b/examples/webgl_materials_car.html index 938b9cd154b01e..72411dc3df86d9 100644 --- a/examples/webgl_materials_car.html +++ b/examples/webgl_materials_car.html @@ -171,7 +171,7 @@ const mesh = new THREE.Mesh( new THREE.PlaneGeometry( 0.655 * 4, 1.3 * 4 ), new THREE.MeshBasicMaterial( { - map: shadow, blending: THREE.MultiplyBlending, toneMapped: false, transparent: true, premultipliedAlpha: true + map: shadow, blending: THREE.MultiplyBlending, toneMapped: false, transparent: true } ) ); mesh.rotation.x = - Math.PI / 2; diff --git a/examples/webgl_materials_envmaps_groundprojected.html b/examples/webgl_materials_envmaps_groundprojected.html index 5bb296874c31d8..d475c798821b69 100644 --- a/examples/webgl_materials_envmaps_groundprojected.html +++ b/examples/webgl_materials_envmaps_groundprojected.html @@ -112,7 +112,7 @@ const mesh = new THREE.Mesh( new THREE.PlaneGeometry( 0.655 * 4, 1.3 * 4 ), new THREE.MeshBasicMaterial( { - map: shadow, blending: THREE.MultiplyBlending, toneMapped: false, transparent: true, premultipliedAlpha: true + map: shadow, blending: THREE.MultiplyBlending, toneMapped: false, transparent: true } ) ); mesh.rotation.x = - Math.PI / 2; From e2f55ca76250b56e16627f05079146196950870a Mon Sep 17 00:00:00 2001 From: "Mr.doob" Date: Sat, 13 Dec 2025 14:24:39 +0700 Subject: [PATCH 3/5] Fixed webgl_materials_blending. --- examples/webgl_materials_blending.html | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/webgl_materials_blending.html b/examples/webgl_materials_blending.html index 668b8c9cee17ab..9181e2a0d6a306 100644 --- a/examples/webgl_materials_blending.html +++ b/examples/webgl_materials_blending.html @@ -101,6 +101,7 @@ const material = new THREE.MeshBasicMaterial( { map: map } ); material.transparent = true; material.blending = blending.constant; + material.premultipliedAlpha = true; const x = ( i - blendings.length / 2 ) * 110; const z = 0; From 74e50e813de269841bf50e2833d29083bbf69f10 Mon Sep 17 00:00:00 2001 From: "Mr.doob" Date: Sun, 14 Dec 2025 11:01:23 +0700 Subject: [PATCH 4/5] Renderers: Add warnOnce calls to incorrect blending modes. --- src/renderers/webgl-fallback/utils/WebGLState.js | 4 +++- src/renderers/webgl/WebGLState.js | 4 +++- src/renderers/webgpu/utils/WebGPUPipelineUtils.js | 4 +++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/renderers/webgl-fallback/utils/WebGLState.js b/src/renderers/webgl-fallback/utils/WebGLState.js index 51053c169a6bce..1ff7ccead3f126 100644 --- a/src/renderers/webgl-fallback/utils/WebGLState.js +++ b/src/renderers/webgl-fallback/utils/WebGLState.js @@ -7,7 +7,7 @@ import { NeverDepth, AlwaysDepth, LessDepth, LessEqualDepth, EqualDepth, GreaterEqualDepth, GreaterDepth, NotEqualDepth } from '../../../constants.js'; import { Vector4 } from '../../../math/Vector4.js'; -import { error } from '../../../utils.js'; +import { error, warnOnce } from '../../../utils.js'; let equationToGL, factorToGL; @@ -377,10 +377,12 @@ class WebGLState { break; case SubtractiveBlending: + warnOnce( 'WebGLState: SubtractiveBlending works best with material.premultipliedAlpha = true.' ); gl.blendFuncSeparate( gl.ZERO, gl.ONE_MINUS_SRC_COLOR, gl.ZERO, gl.ONE ); break; case MultiplyBlending: + warnOnce( 'WebGLState: MultiplyBlending works best with material.premultipliedAlpha = true.' ); gl.blendFuncSeparate( gl.DST_COLOR, gl.ONE_MINUS_SRC_ALPHA, gl.ZERO, gl.ONE ); break; diff --git a/src/renderers/webgl/WebGLState.js b/src/renderers/webgl/WebGLState.js index b856d29aa54212..eb169f77ef3259 100644 --- a/src/renderers/webgl/WebGLState.js +++ b/src/renderers/webgl/WebGLState.js @@ -1,7 +1,7 @@ import { NotEqualDepth, GreaterDepth, GreaterEqualDepth, EqualDepth, LessEqualDepth, LessDepth, AlwaysDepth, NeverDepth, CullFaceFront, CullFaceBack, CullFaceNone, DoubleSide, BackSide, CustomBlending, MultiplyBlending, SubtractiveBlending, AdditiveBlending, NoBlending, NormalBlending, AddEquation, SubtractEquation, ReverseSubtractEquation, MinEquation, MaxEquation, ZeroFactor, OneFactor, SrcColorFactor, SrcAlphaFactor, SrcAlphaSaturateFactor, DstColorFactor, DstAlphaFactor, OneMinusSrcColorFactor, OneMinusSrcAlphaFactor, OneMinusDstColorFactor, OneMinusDstAlphaFactor, ConstantColorFactor, OneMinusConstantColorFactor, ConstantAlphaFactor, OneMinusConstantAlphaFactor } from '../../constants.js'; import { Color } from '../../math/Color.js'; import { Vector4 } from '../../math/Vector4.js'; -import { error } from '../../utils.js'; +import { error, warnOnce } from '../../utils.js'; const reversedFuncs = { [ NeverDepth ]: AlwaysDepth, @@ -690,10 +690,12 @@ function WebGLState( gl, extensions ) { break; case SubtractiveBlending: + warnOnce( 'WebGLState: SubtractiveBlending works best with material.premultipliedAlpha = true.' ); gl.blendFuncSeparate( gl.ZERO, gl.ONE_MINUS_SRC_COLOR, gl.ZERO, gl.ONE ); break; case MultiplyBlending: + warnOnce( 'WebGLState: MultiplyBlending works best with material.premultipliedAlpha = true.' ); gl.blendFuncSeparate( gl.DST_COLOR, gl.ONE_MINUS_SRC_ALPHA, gl.ZERO, gl.ONE ); break; diff --git a/src/renderers/webgpu/utils/WebGPUPipelineUtils.js b/src/renderers/webgpu/utils/WebGPUPipelineUtils.js index 70e64b28e7f90a..c748d0924ba922 100644 --- a/src/renderers/webgpu/utils/WebGPUPipelineUtils.js +++ b/src/renderers/webgpu/utils/WebGPUPipelineUtils.js @@ -15,7 +15,7 @@ import { NeverStencilFunc, AlwaysStencilFunc, LessStencilFunc, LessEqualStencilFunc, EqualStencilFunc, GreaterEqualStencilFunc, GreaterStencilFunc, NotEqualStencilFunc } from '../../../constants.js'; -import { error } from '../../../utils.js'; +import { error, warnOnce } from '../../../utils.js'; /** * A WebGPU backend utility module for managing pipelines. @@ -448,10 +448,12 @@ class WebGPUPipelineUtils { break; case SubtractiveBlending: + warnOnce( 'WebGPURenderer: SubtractiveBlending works best with material.premultipliedAlpha = true.' ); setBlend( GPUBlendFactor.Zero, GPUBlendFactor.OneMinusSrc, GPUBlendFactor.Zero, GPUBlendFactor.One ); break; case MultiplyBlending: + warnOnce( 'WebGPURenderer: MultiplyBlending works best with material.premultipliedAlpha = true.' ); setBlend( GPUBlendFactor.Dst, GPUBlendFactor.OneMinusSrcAlpha, GPUBlendFactor.Zero, GPUBlendFactor.One ); break; From f02e6c16d9a8c79b5cbacc75eb342b00b4e07487 Mon Sep 17 00:00:00 2001 From: "Mr.doob" Date: Wed, 17 Dec 2025 10:18:09 +0700 Subject: [PATCH 5/5] Renderers: Set premultipliedAlpha to true when using MultiplyBlending or SubtractiveBlending. --- .../webgl-fallback/utils/WebGLState.js | 21 ++++++++++--------- src/renderers/webgl/WebGLState.js | 21 ++++++++++--------- .../webgpu/utils/WebGPUPipelineUtils.js | 21 ++++++++++--------- 3 files changed, 33 insertions(+), 30 deletions(-) diff --git a/src/renderers/webgl-fallback/utils/WebGLState.js b/src/renderers/webgl-fallback/utils/WebGLState.js index 1ff7ccead3f126..f28508b4670d1b 100644 --- a/src/renderers/webgl-fallback/utils/WebGLState.js +++ b/src/renderers/webgl-fallback/utils/WebGLState.js @@ -376,16 +376,6 @@ class WebGLState { gl.blendFuncSeparate( gl.SRC_ALPHA, gl.ONE, gl.ONE, gl.ONE ); break; - case SubtractiveBlending: - warnOnce( 'WebGLState: SubtractiveBlending works best with material.premultipliedAlpha = true.' ); - gl.blendFuncSeparate( gl.ZERO, gl.ONE_MINUS_SRC_COLOR, gl.ZERO, gl.ONE ); - break; - - case MultiplyBlending: - warnOnce( 'WebGLState: MultiplyBlending works best with material.premultipliedAlpha = true.' ); - gl.blendFuncSeparate( gl.DST_COLOR, gl.ONE_MINUS_SRC_ALPHA, gl.ZERO, gl.ONE ); - break; - default: error( 'WebGLState: Invalid blending: ', blending ); break; @@ -751,6 +741,17 @@ class WebGLState { this.setFlipSided( flipSided ); + if ( material.premultipliedAlpha === false ) { + + if ( material.blending === MultiplyBlending || material.blending === SubtractiveBlending ) { + + warnOnce( 'WebGLState: Material premultipliedAlpha was set to true because MultiplyBlending and SubtractiveBlending require it.' ); + material.premultipliedAlpha = true; + + } + + } + ( material.blending === NormalBlending && material.transparent === false ) ? this.setBlending( NoBlending ) : this.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.premultipliedAlpha ); diff --git a/src/renderers/webgl/WebGLState.js b/src/renderers/webgl/WebGLState.js index eb169f77ef3259..78b6f0edd4eeb6 100644 --- a/src/renderers/webgl/WebGLState.js +++ b/src/renderers/webgl/WebGLState.js @@ -689,16 +689,6 @@ function WebGLState( gl, extensions ) { gl.blendFuncSeparate( gl.SRC_ALPHA, gl.ONE, gl.ONE, gl.ONE ); break; - case SubtractiveBlending: - warnOnce( 'WebGLState: SubtractiveBlending works best with material.premultipliedAlpha = true.' ); - gl.blendFuncSeparate( gl.ZERO, gl.ONE_MINUS_SRC_COLOR, gl.ZERO, gl.ONE ); - break; - - case MultiplyBlending: - warnOnce( 'WebGLState: MultiplyBlending works best with material.premultipliedAlpha = true.' ); - gl.blendFuncSeparate( gl.DST_COLOR, gl.ONE_MINUS_SRC_ALPHA, gl.ZERO, gl.ONE ); - break; - default: error( 'WebGLState: Invalid blending: ', blending ); break; @@ -774,6 +764,17 @@ function WebGLState( gl, extensions ) { setFlipSided( flipSided ); + if ( material.premultipliedAlpha === false ) { + + if ( material.blending === MultiplyBlending || material.blending === SubtractiveBlending ) { + + warnOnce( 'WebGLState: Material premultipliedAlpha was set to true because MultiplyBlending and SubtractiveBlending require it.' ); + material.premultipliedAlpha = true; + + } + + } + ( material.blending === NormalBlending && material.transparent === false ) ? setBlending( NoBlending ) : setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.blendColor, material.blendAlpha, material.premultipliedAlpha ); diff --git a/src/renderers/webgpu/utils/WebGPUPipelineUtils.js b/src/renderers/webgpu/utils/WebGPUPipelineUtils.js index c748d0924ba922..a84de1f610532a 100644 --- a/src/renderers/webgpu/utils/WebGPUPipelineUtils.js +++ b/src/renderers/webgpu/utils/WebGPUPipelineUtils.js @@ -120,6 +120,17 @@ class WebGPUPipelineUtils { let blending; + if ( material.premultipliedAlpha === false ) { + + if ( material.blending === MultiplyBlending || material.blending === SubtractiveBlending ) { + + warnOnce( 'WebGPURenderer: Material premultipliedAlpha was set to true because MultiplyBlending and SubtractiveBlending require it.' ); + material.premultipliedAlpha = true; + + } + + } + if ( material.blending !== NoBlending && ( material.blending !== NormalBlending || material.transparent !== false ) ) { blending = this._getBlending( material ); @@ -447,16 +458,6 @@ class WebGPUPipelineUtils { setBlend( GPUBlendFactor.SrcAlpha, GPUBlendFactor.One, GPUBlendFactor.One, GPUBlendFactor.One ); break; - case SubtractiveBlending: - warnOnce( 'WebGPURenderer: SubtractiveBlending works best with material.premultipliedAlpha = true.' ); - setBlend( GPUBlendFactor.Zero, GPUBlendFactor.OneMinusSrc, GPUBlendFactor.Zero, GPUBlendFactor.One ); - break; - - case MultiplyBlending: - warnOnce( 'WebGPURenderer: MultiplyBlending works best with material.premultipliedAlpha = true.' ); - setBlend( GPUBlendFactor.Dst, GPUBlendFactor.OneMinusSrcAlpha, GPUBlendFactor.Zero, GPUBlendFactor.One ); - break; - } }