From cfbf86624351a9401674b2f093d3e4137107cd24 Mon Sep 17 00:00:00 2001 From: Michael Herzog Date: Tue, 21 Jul 2026 09:42:25 +0200 Subject: [PATCH 1/5] TSL: Fix angle bug in `hashBlur()`. (#34077) --- examples/jsm/tsl/display/hashBlur.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/jsm/tsl/display/hashBlur.js b/examples/jsm/tsl/display/hashBlur.js index f781569d845ac9..540ccc560fc184 100644 --- a/examples/jsm/tsl/display/hashBlur.js +++ b/examples/jsm/tsl/display/hashBlur.js @@ -1,4 +1,4 @@ -import { float, Fn, vec2, uv, sin, rand, degrees, cos, Loop, vec4, premultiplyAlpha, unpremultiplyAlpha, convertToTexture, nodeObject } from 'three/tsl'; +import { float, Fn, vec2, uv, sin, rand, TWO_PI, cos, Loop, vec4, premultiplyAlpha, unpremultiplyAlpha, convertToTexture, nodeObject } from 'three/tsl'; /** * Applies a hash blur effect to the given texture node. @@ -41,7 +41,8 @@ export const hashBlur = /*#__PURE__*/ Fn( ( [ textureNode, bluramount = float( 0 Loop( { start: 0., end: repeats, type: 'float' }, ( { i } ) => { - const q = vec2( vec2( cos( degrees( i.div( repeats ).mul( 360. ) ) ), sin( degrees( i.div( repeats ).mul( 360. ) ) ) ).mul( rand( vec2( i, targetUV.x.add( targetUV.y ) ) ).add( bluramount ) ) ); + const angle = i.div( repeats ).mul( TWO_PI ).toConst(); + const q = vec2( cos( angle ), sin( angle ) ).mul( rand( vec2( i, targetUV.x.add( targetUV.y ) ) ).add( bluramount ) ); const uv2 = vec2( targetUV.add( q.mul( bluramount ) ) ); blurred_image.addAssign( tap( uv2 ) ); From 3fce968195edcfa64b22577c7ab310c5d87f8ab8 Mon Sep 17 00:00:00 2001 From: WestLangley Date: Tue, 21 Jul 2026 03:43:22 -0400 Subject: [PATCH 2/5] Src: Improve retroreflection nomenclature (#34079) --- examples/files.json | 2 +- .../webgpu_materials_retroreflection.jpg | Bin 0 -> 16767 bytes .../webgpu_materials_retroreflective.jpg | Bin 16903 -> 0 bytes ... => webgpu_materials_retroreflection.html} | 19 +++++++------- src/Three.TSL.js | 4 +-- src/materials/Material.js | 4 +-- src/materials/MeshPhysicalMaterial.js | 14 +++++----- .../nodes/MeshPhysicalNodeMaterial.js | 24 +++++++++--------- .../nodes/manager/NodeMaterialObserver.js | 2 +- src/nodes/accessors/MaterialNode.js | 4 +-- src/nodes/core/PropertyNode.js | 4 +-- src/nodes/functions/PhysicalLightingModel.js | 14 +++++----- .../lights_physical_fragment.glsl.js | 4 +-- .../lights_physical_pars_fragment.glsl.js | 12 ++++----- src/renderers/shaders/ShaderLib.js | 2 +- .../shaders/ShaderLib/meshphysical.glsl.js | 4 +-- src/renderers/webgl/WebGLMaterials.js | 4 +-- src/renderers/webgl/WebGLProgram.js | 2 +- src/renderers/webgl/WebGLPrograms.js | 6 ++--- .../materials/MeshPhysicalMaterial.tests.js | 22 ++++++++-------- 20 files changed, 74 insertions(+), 73 deletions(-) create mode 100644 examples/screenshots/webgpu_materials_retroreflection.jpg delete mode 100644 examples/screenshots/webgpu_materials_retroreflective.jpg rename examples/{webgpu_materials_retroreflective.html => webgpu_materials_retroreflection.html} (89%) diff --git a/examples/files.json b/examples/files.json index 070955f2d4a3f3..f9c3ae92ef2431 100644 --- a/examples/files.json +++ b/examples/files.json @@ -397,7 +397,7 @@ "webgpu_materials_envmaps", "webgpu_materials_lightmap", "webgpu_materials_matcap", - "webgpu_materials_retroreflective", + "webgpu_materials_retroreflection", "webgpu_materials_sss", "webgpu_materials_texture_html", "webgpu_materials_texture_manualmipmap", diff --git a/examples/screenshots/webgpu_materials_retroreflection.jpg b/examples/screenshots/webgpu_materials_retroreflection.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e515892acee3180621839f37bc5792090befbeab GIT binary patch literal 16767 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 - three.js webgpu - materials - retroreflective + three.js webgpu - materials - retroreflection - + - + + @@ -46,8 +47,8 @@ let frontLight, bandMaterial; const params = { - retroreflectivity: true, - retroreflective: 1.0, + retroreflective: true, + retroreflectivity: 1.0, frontLightIntensity: 5.0 }; @@ -95,8 +96,8 @@ controls.update(); const gui = new GUI(); - gui.add( params, 'retroreflectivity' ).name( 'retroreflectivity' ).onChange( updateMaterial ); - gui.add( params, 'retroreflective', 0, 1, 0.01 ).name( 'retroreflective' ).onChange( updateMaterial ); + gui.add( params, 'retroreflective' ).name( 'retroreflective' ).onChange( updateMaterial ); + gui.add( params, 'retroreflectivity', 0, 1, 0.01 ).name( 'retroreflectivity' ).onChange( updateMaterial ); gui.add( params, 'frontLightIntensity', 0, 30, 0.1 ).name( 'front light intensity' ).onChange( updateMaterial ); window.addEventListener( 'resize', onWindowResize ); @@ -120,7 +121,7 @@ color: 0xf7f0d6, roughness: 0.22, metalness: 0.0, - retroreflective: params.retroreflective + retroreflectivity: params.retroreflectivity } ); const base = new THREE.Mesh( @@ -167,7 +168,7 @@ function updateMaterial() { - bandMaterial.retroreflective = params.retroreflectivity ? params.retroreflective : 0; + bandMaterial.retroreflectivity = params.retroreflective ? params.retroreflectivity : 0; frontLight.intensity = params.frontLightIntensity; } diff --git a/src/Three.TSL.js b/src/Three.TSL.js index b2e4f301fdab08..76a0c83656e7d4 100644 --- a/src/Three.TSL.js +++ b/src/Three.TSL.js @@ -300,7 +300,7 @@ export const materialPointSize = TSL.materialPointSize; export const materialReference = TSL.materialReference; export const materialReflectivity = TSL.materialReflectivity; export const materialRefractionRatio = TSL.materialRefractionRatio; -export const materialRetroreflective = TSL.materialRetroreflective; +export const materialRetroreflectivity = TSL.materialRetroreflectivity; export const materialRotation = TSL.materialRotation; export const materialRoughness = TSL.materialRoughness; export const materialSheen = TSL.materialSheen; @@ -475,7 +475,7 @@ export const refract = TSL.refract; export const refractVector = TSL.refractVector; export const refractView = TSL.refractView; export const reinhardToneMapping = TSL.reinhardToneMapping; -export const retroreflective = TSL.retroreflective; +export const retroreflectivity = TSL.retroreflectivity; export const remap = TSL.remap; export const remapClamp = TSL.remapClamp; export const renderGroup = TSL.renderGroup; diff --git a/src/materials/Material.js b/src/materials/Material.js index b21e1658e91293..b45f318a6ac7ab 100644 --- a/src/materials/Material.js +++ b/src/materials/Material.js @@ -683,7 +683,7 @@ class Material extends EventDispatcher { } if ( this.dispersion !== undefined ) data.dispersion = this.dispersion; - if ( this.retroreflective !== undefined ) data.retroreflective = this.retroreflective; + if ( this.retroreflectivity !== undefined ) data.retroreflectivity = this.retroreflectivity; if ( this.iridescence !== undefined ) data.iridescence = this.iridescence; if ( this.iridescenceIOR !== undefined ) data.iridescenceIOR = this.iridescenceIOR; @@ -912,7 +912,7 @@ class Material extends EventDispatcher { if ( json.clearcoat !== undefined ) this.clearcoat = json.clearcoat; if ( json.clearcoatRoughness !== undefined ) this.clearcoatRoughness = json.clearcoatRoughness; if ( json.dispersion !== undefined ) this.dispersion = json.dispersion; - if ( json.retroreflective !== undefined ) this.retroreflective = json.retroreflective; + if ( json.retroreflectivity !== undefined ) this.retroreflectivity = json.retroreflectivity; if ( json.iridescence !== undefined ) this.iridescence = json.iridescence; if ( json.iridescenceIOR !== undefined ) this.iridescenceIOR = json.iridescenceIOR; if ( json.iridescenceThicknessRange !== undefined ) this.iridescenceThicknessRange = json.iridescenceThicknessRange; diff --git a/src/materials/MeshPhysicalMaterial.js b/src/materials/MeshPhysicalMaterial.js index 3d785961f506bd..d2523561152163 100644 --- a/src/materials/MeshPhysicalMaterial.js +++ b/src/materials/MeshPhysicalMaterial.js @@ -356,7 +356,7 @@ class MeshPhysicalMaterial extends MeshStandardMaterial { this._clearcoat = 0; this._dispersion = 0; this._iridescence = 0; - this._retroreflective = 0; + this._retroreflectivity = 0; this._sheen = 0.0; this._transmission = 0; @@ -473,21 +473,21 @@ class MeshPhysicalMaterial extends MeshStandardMaterial { * @type {number} * @default 0 */ - get retroreflective() { + get retroreflectivity() { - return this._retroreflective; + return this._retroreflectivity; } - set retroreflective( value ) { + set retroreflectivity( value ) { - if ( this._retroreflective > 0 !== value > 0 ) { + if ( this._retroreflectivity > 0 !== value > 0 ) { this.version ++; } - this._retroreflective = value; + this._retroreflectivity = value; } @@ -576,7 +576,7 @@ class MeshPhysicalMaterial extends MeshStandardMaterial { this.iridescenceThicknessRange = [ ...source.iridescenceThicknessRange ]; this.iridescenceThicknessMap = source.iridescenceThicknessMap; - this.retroreflective = source.retroreflective; + this.retroreflectivity = source.retroreflectivity; this.sheen = source.sheen; this.sheenColor.copy( source.sheenColor ); diff --git a/src/materials/nodes/MeshPhysicalNodeMaterial.js b/src/materials/nodes/MeshPhysicalNodeMaterial.js index 5accbc99db72cc..bd0b92411317ea 100644 --- a/src/materials/nodes/MeshPhysicalNodeMaterial.js +++ b/src/materials/nodes/MeshPhysicalNodeMaterial.js @@ -1,5 +1,5 @@ -import { clearcoat, clearcoatRoughness, sheen, sheenRoughness, iridescence, iridescenceIOR, iridescenceThickness, specularColor, specularColorBlended, specularF90, diffuseColor, metalness, roughness, anisotropy, alphaT, anisotropyT, anisotropyB, ior, transmission, thickness, attenuationDistance, attenuationColor, dispersion, retroreflective } from '../../nodes/core/PropertyNode.js'; -import { materialClearcoat, materialClearcoatRoughness, materialClearcoatNormal, materialSheen, materialSheenRoughness, materialIridescence, materialIridescenceIOR, materialIridescenceThickness, materialSpecularIntensity, materialSpecularColor, materialAnisotropy, materialIOR, materialTransmission, materialThickness, materialAttenuationDistance, materialAttenuationColor, materialDispersion, materialRetroreflective } from '../../nodes/accessors/MaterialNode.js'; +import { clearcoat, clearcoatRoughness, sheen, sheenRoughness, iridescence, iridescenceIOR, iridescenceThickness, specularColor, specularColorBlended, specularF90, diffuseColor, metalness, roughness, anisotropy, alphaT, anisotropyT, anisotropyB, ior, transmission, thickness, attenuationDistance, attenuationColor, dispersion, retroreflectivity } from '../../nodes/core/PropertyNode.js'; +import { materialClearcoat, materialClearcoatRoughness, materialClearcoatNormal, materialSheen, materialSheenRoughness, materialIridescence, materialIridescenceIOR, materialIridescenceThickness, materialSpecularIntensity, materialSpecularColor, materialAnisotropy, materialIOR, materialTransmission, materialThickness, materialAttenuationDistance, materialAttenuationColor, materialDispersion, materialRetroreflectivity } from '../../nodes/accessors/MaterialNode.js'; import { float, vec2, vec3, If } from '../../nodes/tsl/TSLBase.js'; import getRoughness from '../../nodes/functions/material/getRoughness.js'; import { TBNViewMatrix } from '../../nodes/accessors/AccessorsUtils.js'; @@ -253,16 +253,16 @@ class MeshPhysicalNodeMaterial extends MeshStandardNodeMaterial { /** * The retroreflective strength of physical materials is by default inferred from the - * `retroreflective` property. This node property allows to overwrite the default + * `retroreflectivity` property. This node property allows to overwrite the default * and define the retroreflective strength with a node instead. * * If you don't want to overwrite the retroreflective strength but modify the existing - * value instead, use {@link materialRetroreflective}. + * value instead, use {@link materialRetroreflectivity}. * * @type {?Node} * @default null */ - this.retroreflectiveNode = null; + this.retroreflectivityNode = null; /** * The anisotropy of physical materials is by default inferred from the @@ -361,9 +361,9 @@ class MeshPhysicalNodeMaterial extends MeshStandardNodeMaterial { * @type {boolean} * @default true */ - get useRetroreflective() { + get useRetroreflection() { - return this.retroreflective > 0 || this.retroreflectiveNode !== null; + return this.retroreflectivity > 0 || this.retroreflectivityNode !== null; } @@ -388,7 +388,7 @@ class MeshPhysicalNodeMaterial extends MeshStandardNodeMaterial { */ setupLightingModel( /*builder*/ ) { - return new PhysicalLightingModel( this.useClearcoat, this.useSheen, this.useIridescence, this.useAnisotropy, this.useTransmission, this.useDispersion, this.useRetroreflective ); + return new PhysicalLightingModel( this.useClearcoat, this.useSheen, this.useIridescence, this.useAnisotropy, this.useTransmission, this.useDispersion, this.useRetroreflection ); } @@ -425,13 +425,13 @@ class MeshPhysicalNodeMaterial extends MeshStandardNodeMaterial { } - // RETROREFLECTIVE + // RETROREFLECTION - if ( this.useRetroreflective ) { + if ( this.useRetroreflection ) { - const retroreflectiveNode = this.retroreflectiveNode ? float( this.retroreflectiveNode ) : materialRetroreflective; + const retroreflectivityNode = this.retroreflectivityNode ? float( this.retroreflectivityNode ) : materialRetroreflectivity; - retroreflective.assign( retroreflectiveNode ); + retroreflectivity.assign( retroreflectivityNode ); } diff --git a/src/materials/nodes/manager/NodeMaterialObserver.js b/src/materials/nodes/manager/NodeMaterialObserver.js index c64cd090139690..755d51b0e463af 100644 --- a/src/materials/nodes/manager/NodeMaterialObserver.js +++ b/src/materials/nodes/manager/NodeMaterialObserver.js @@ -38,7 +38,7 @@ const refreshUniforms = [ 'normalMap', 'normalScale', 'opacity', - 'retroreflective', + 'retroreflectivity', 'roughness', 'roughnessMap', 'sheen', diff --git a/src/nodes/accessors/MaterialNode.js b/src/nodes/accessors/MaterialNode.js index a02f8c579a2695..a813b85b81fff0 100644 --- a/src/nodes/accessors/MaterialNode.js +++ b/src/nodes/accessors/MaterialNode.js @@ -463,7 +463,7 @@ MaterialNode.LINE_WIDTH = 'linewidth'; MaterialNode.LINE_DASH_OFFSET = 'dashOffset'; MaterialNode.POINT_SIZE = 'size'; MaterialNode.DISPERSION = 'dispersion'; -MaterialNode.RETROREFLECTIVE = 'retroreflective'; +MaterialNode.RETROREFLECTIVITY = 'retroreflectivity'; MaterialNode.LIGHT_MAP = 'light'; MaterialNode.AO = 'ao'; @@ -771,7 +771,7 @@ export const materialDispersion = /*@__PURE__*/ nodeImmutable( MaterialNode, Mat * @tsl * @type {Node} */ -export const materialRetroreflective = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.RETROREFLECTIVE ); +export const materialRetroreflectivity = /*@__PURE__*/ nodeImmutable( MaterialNode, MaterialNode.RETROREFLECTIVITY ); /** * TSL object that represents the light map of the current material. diff --git a/src/nodes/core/PropertyNode.js b/src/nodes/core/PropertyNode.js index b256399267e1ee..870782636336b6 100644 --- a/src/nodes/core/PropertyNode.js +++ b/src/nodes/core/PropertyNode.js @@ -404,12 +404,12 @@ export const attenuationColor = /*@__PURE__*/ nodeImmutable( PropertyNode, 'colo export const dispersion = /*@__PURE__*/ nodeImmutable( PropertyNode, 'float', 'Dispersion' ); /** - * TSL object that represents the shader variable `Retroreflective`. + * TSL object that represents the shader variable `Retroreflectivity`. * * @tsl * @type {PropertyNode} */ -export const retroreflective = /*@__PURE__*/ nodeImmutable( PropertyNode, 'float', 'Retroreflective' ); +export const retroreflectivity = /*@__PURE__*/ nodeImmutable( PropertyNode, 'float', 'Retroreflectivity' ); /** * TSL object that represents the shader variable `AmbientOcclusion`. diff --git a/src/nodes/functions/PhysicalLightingModel.js b/src/nodes/functions/PhysicalLightingModel.js index 55a6922a93514e..4e2567f4b300cb 100644 --- a/src/nodes/functions/PhysicalLightingModel.js +++ b/src/nodes/functions/PhysicalLightingModel.js @@ -7,7 +7,7 @@ import Schlick_to_F0 from './BSDF/Schlick_to_F0.js'; import BRDF_Sheen from './BSDF/BRDF_Sheen.js'; import { LTC_Evaluate, LTC_Uv } from './BSDF/LTC.js'; import LightingModel from '../core/LightingModel.js'; -import { diffuseColor, diffuseContribution, specularColor, specularColorBlended, specularF90, roughness, metalness, clearcoat, clearcoatRoughness, sheen, sheenRoughness, iridescence, iridescenceIOR, iridescenceThickness, ior, thickness, transmission, attenuationDistance, attenuationColor, dispersion, retroreflective } from '../core/PropertyNode.js'; +import { diffuseColor, diffuseContribution, specularColor, specularColorBlended, specularF90, roughness, metalness, clearcoat, clearcoatRoughness, sheen, sheenRoughness, iridescence, iridescenceIOR, iridescenceThickness, ior, thickness, transmission, attenuationDistance, attenuationColor, dispersion, retroreflectivity } from '../core/PropertyNode.js'; import { normalView, clearcoatNormalView, normalWorld } from '../accessors/Normal.js'; import { positionViewDirection, positionView, positionWorld } from '../accessors/Position.js'; import { Fn, float, vec2, vec3, vec4, mat3, If } from '../tsl/TSLBase.js'; @@ -347,9 +347,9 @@ class PhysicalLightingModel extends LightingModel { * @param {boolean} [anisotropy=false] - Whether anisotropy is supported or not. * @param {boolean} [transmission=false] - Whether transmission is supported or not. * @param {boolean} [dispersion=false] - Whether dispersion is supported or not. - * @param {boolean} [retroreflective=false] - Whether retroreflection is supported or not. + * @param {boolean} [retroreflection=false] - Whether retroreflection is supported or not. */ - constructor( clearcoat = false, sheen = false, iridescence = false, anisotropy = false, transmission = false, dispersion = false, retroreflective = false ) { + constructor( clearcoat = false, sheen = false, iridescence = false, anisotropy = false, transmission = false, dispersion = false, retroreflection = false ) { super(); @@ -407,7 +407,7 @@ class PhysicalLightingModel extends LightingModel { * @type {boolean} * @default false */ - this.retroreflective = retroreflective; + this.retroreflection = retroreflection; /** * The clear coat radiance. @@ -655,7 +655,7 @@ class PhysicalLightingModel extends LightingModel { let specularBRDF = BRDF_GGX( { lightDirection, f0: specularColorBlended, f90: 1, roughness, f: this.iridescenceFresnel, USE_IRIDESCENCE: this.iridescence, USE_ANISOTROPY: this.anisotropy } ); - if ( this.retroreflective === true ) { + if ( this.retroreflection === true ) { // Minimal Retroreflective Microfacet Model: // https://jcgt.org/published/0015/01/04/ @@ -665,8 +665,8 @@ class PhysicalLightingModel extends LightingModel { const retroF = F_Schlick( { f0: specularColor, f90: specularF90, dotVH: dotRetroVH } ); const retroSpecularBRDF = BRDF_GGX( { lightDirection, viewDirection: retroViewDirection, f0: specularColorBlended, f90: 1, roughness, f: this.iridescenceFresnel, USE_IRIDESCENCE: this.iridescence, USE_ANISOTROPY: this.anisotropy } ); - F = mix( F, retroF, retroreflective.clamp() ); - specularBRDF = mix( specularBRDF, retroSpecularBRDF, retroreflective.clamp() ); + F = mix( F, retroF, retroreflectivity.clamp() ); + specularBRDF = mix( specularBRDF, retroSpecularBRDF, retroreflectivity.clamp() ); } diff --git a/src/renderers/shaders/ShaderChunk/lights_physical_fragment.glsl.js b/src/renderers/shaders/ShaderChunk/lights_physical_fragment.glsl.js index dbc1abdfd3acac..8ce14943dc980c 100644 --- a/src/renderers/shaders/ShaderChunk/lights_physical_fragment.glsl.js +++ b/src/renderers/shaders/ShaderChunk/lights_physical_fragment.glsl.js @@ -85,9 +85,9 @@ material.roughness = min( material.roughness, 1.0 ); #endif -#ifdef USE_RETROREFLECTIVE +#ifdef USE_RETROREFLECTION - material.retroreflective = retroreflective; + material.retroreflectivity = retroreflectivity; #endif diff --git a/src/renderers/shaders/ShaderChunk/lights_physical_pars_fragment.glsl.js b/src/renderers/shaders/ShaderChunk/lights_physical_pars_fragment.glsl.js index 374e17ef5392f4..6855001c80723d 100644 --- a/src/renderers/shaders/ShaderChunk/lights_physical_pars_fragment.glsl.js +++ b/src/renderers/shaders/ShaderChunk/lights_physical_pars_fragment.glsl.js @@ -16,8 +16,8 @@ struct PhysicalMaterial { vec2 dfg; vec3 multiScatteringCompensation; - #ifdef USE_RETROREFLECTIVE - float retroreflective; + #ifdef USE_RETROREFLECTION + float retroreflectivity; #endif #ifdef USE_CLEARCOAT @@ -515,14 +515,14 @@ void RE_Direct_Physical( const in IncidentLight directLight, const in vec3 geome vec3 specularBRDF = BRDF_GGX( directLight.direction, geometryViewDir, geometryNormal, material ); - #ifdef USE_RETROREFLECTIVE + #ifdef USE_RETROREFLECTION // Minimal Retroreflective Microfacet Model: // https://jcgt.org/published/0015/01/04/ vec3 retroViewDir = reflect( - geometryViewDir, geometryNormal ); vec3 retroSpecularBRDF = BRDF_GGX( directLight.direction, retroViewDir, geometryNormal, material ); - specularBRDF = mix( specularBRDF, retroSpecularBRDF, saturate( material.retroreflective ) ); + specularBRDF = mix( specularBRDF, retroSpecularBRDF, saturate( material.retroreflectivity ) ); #endif @@ -533,13 +533,13 @@ void RE_Direct_Physical( const in IncidentLight directLight, const in vec3 geome float dotVH = saturate( dot( geometryViewDir, halfDir ) ); vec3 F = F_Schlick( material.specularColor, material.specularF90, dotVH ); - #ifdef USE_RETROREFLECTIVE + #ifdef USE_RETROREFLECTION vec3 retroHalfDir = normalize( directLight.direction + retroViewDir ); float dotRetroVH = saturate( dot( retroViewDir, retroHalfDir ) ); vec3 retroF = F_Schlick( material.specularColor, material.specularF90, dotRetroVH ); - F = mix( F, retroF, saturate( material.retroreflective ) ); + F = mix( F, retroF, saturate( material.retroreflectivity ) ); #endif diff --git a/src/renderers/shaders/ShaderLib.js b/src/renderers/shaders/ShaderLib.js index ff768bc3fb8544..295581fa69c95b 100644 --- a/src/renderers/shaders/ShaderLib.js +++ b/src/renderers/shaders/ShaderLib.js @@ -316,7 +316,7 @@ ShaderLib.physical = { clearcoatRoughnessMap: { value: null }, clearcoatRoughnessMapTransform: { value: /*@__PURE__*/ new Matrix3() }, dispersion: { value: 0 }, - retroreflective: { value: 0 }, + retroreflectivity: { value: 0 }, iridescence: { value: 0 }, iridescenceMap: { value: null }, iridescenceMapTransform: { value: /*@__PURE__*/ new Matrix3() }, diff --git a/src/renderers/shaders/ShaderLib/meshphysical.glsl.js b/src/renderers/shaders/ShaderLib/meshphysical.glsl.js index c4a87fb4baec20..21403cce485bc0 100644 --- a/src/renderers/shaders/ShaderLib/meshphysical.glsl.js +++ b/src/renderers/shaders/ShaderLib/meshphysical.glsl.js @@ -99,8 +99,8 @@ uniform float opacity; uniform float dispersion; #endif -#ifdef USE_RETROREFLECTIVE - uniform float retroreflective; +#ifdef USE_RETROREFLECTION + uniform float retroreflectivity; #endif #ifdef USE_IRIDESCENCE diff --git a/src/renderers/webgl/WebGLMaterials.js b/src/renderers/webgl/WebGLMaterials.js index ba1de8caf4e367..c41c1984d215e2 100644 --- a/src/renderers/webgl/WebGLMaterials.js +++ b/src/renderers/webgl/WebGLMaterials.js @@ -483,9 +483,9 @@ function WebGLMaterials( renderer, properties ) { } - if ( material.retroreflective > 0 ) { + if ( material.retroreflectivity > 0 ) { - uniforms.retroreflective.value = material.retroreflective; + uniforms.retroreflectivity.value = material.retroreflectivity; } diff --git a/src/renderers/webgl/WebGLProgram.js b/src/renderers/webgl/WebGLProgram.js index 50a582235c339a..4d64b338ba5ee4 100644 --- a/src/renderers/webgl/WebGLProgram.js +++ b/src/renderers/webgl/WebGLProgram.js @@ -709,7 +709,7 @@ function WebGLProgram( renderer, cacheKey, parameters, bindingStates ) { parameters.clearcoatNormalMap ? '#define USE_CLEARCOAT_NORMALMAP' : '', parameters.dispersion ? '#define USE_DISPERSION' : '', - parameters.retroreflective ? '#define USE_RETROREFLECTIVE' : '', + parameters.retroreflection ? '#define USE_RETROREFLECTION' : '', parameters.iridescence ? '#define USE_IRIDESCENCE' : '', parameters.iridescenceMap ? '#define USE_IRIDESCENCEMAP' : '', diff --git a/src/renderers/webgl/WebGLPrograms.js b/src/renderers/webgl/WebGLPrograms.js index 773a691f6d6c2b..b6a0fceaf49724 100644 --- a/src/renderers/webgl/WebGLPrograms.js +++ b/src/renderers/webgl/WebGLPrograms.js @@ -140,7 +140,7 @@ function WebGLPrograms( renderer, environments, extensions, capabilities, bindin const HAS_ANISOTROPY = material.anisotropy > 0; const HAS_CLEARCOAT = material.clearcoat > 0; const HAS_DISPERSION = material.dispersion > 0; - const HAS_RETROREFLECTIVE = material.retroreflective > 0; + const HAS_RETROREFLECTION = material.retroreflectivity > 0; const HAS_IRIDESCENCE = material.iridescence > 0; const HAS_SHEEN = material.sheen > 0; const HAS_TRANSMISSION = material.transmission > 0; @@ -241,7 +241,7 @@ function WebGLPrograms( renderer, environments, extensions, capabilities, bindin clearcoatRoughnessMap: HAS_CLEARCOAT_ROUGHNESSMAP, dispersion: HAS_DISPERSION, - retroreflective: HAS_RETROREFLECTIVE, + retroreflection: HAS_RETROREFLECTION, iridescence: HAS_IRIDESCENCE, iridescenceMap: HAS_IRIDESCENCEMAP, @@ -534,7 +534,7 @@ function WebGLPrograms( renderer, environments, extensions, capabilities, bindin _programLayers.enable( 18 ); if ( parameters.dispersion ) _programLayers.enable( 19 ); - if ( parameters.retroreflective ) + if ( parameters.retroreflection ) _programLayers.enable( 24 ); if ( parameters.batchingColor ) _programLayers.enable( 20 ); diff --git a/test/unit/src/materials/MeshPhysicalMaterial.tests.js b/test/unit/src/materials/MeshPhysicalMaterial.tests.js index 81267b8c276d92..8a822a45a8d020 100644 --- a/test/unit/src/materials/MeshPhysicalMaterial.tests.js +++ b/test/unit/src/materials/MeshPhysicalMaterial.tests.js @@ -37,37 +37,37 @@ export default QUnit.module( 'Materials', () => { } ); - QUnit.test( 'retroreflective', ( assert ) => { + QUnit.test( 'retroreflectivity', ( assert ) => { const object = new MeshPhysicalMaterial(); - assert.strictEqual( object.retroreflective, 0, 'retroreflective defaults to 0.' ); + assert.strictEqual( object.retroreflectivity, 0, 'retroreflectivity defaults to 0.' ); - object.retroreflective = 0.75; - assert.strictEqual( object.retroreflective, 0.75, 'Can set retroreflective.' ); + object.retroreflectivity = 0.75; + assert.strictEqual( object.retroreflectivity, 0.75, 'Can set retroreflectivity.' ); } ); - QUnit.test( 'copy copies retroreflective', ( assert ) => { + QUnit.test( 'copy copies retroreflectivity', ( assert ) => { - const source = new MeshPhysicalMaterial( { retroreflective: 0.5 } ); + const source = new MeshPhysicalMaterial( { retroreflectivity: 0.5 } ); const object = new MeshPhysicalMaterial(); object.copy( source ); - assert.strictEqual( object.retroreflective, 0.5, 'copy() preserves retroreflective.' ); + assert.strictEqual( object.retroreflectivity, 0.5, 'copy() preserves retroreflectivity.' ); } ); - QUnit.test( 'fromJSON restores retroreflective', ( assert ) => { + QUnit.test( 'fromJSON restores retroreflectivity', ( assert ) => { - const source = new MeshPhysicalMaterial( { retroreflective: 0.25 } ); + const source = new MeshPhysicalMaterial( { retroreflectivity: 0.25 } ); const json = source.toJSON(); const object = new MeshPhysicalMaterial(); object.fromJSON( json ); - assert.strictEqual( json.retroreflective, 0.25, 'toJSON() serializes retroreflective.' ); - assert.strictEqual( object.retroreflective, 0.25, 'fromJSON() restores retroreflective.' ); + assert.strictEqual( json.retroreflectivity, 0.25, 'toJSON() serializes retroreflectivity.' ); + assert.strictEqual( object.retroreflectivity, 0.25, 'fromJSON() restores retroreflectivity.' ); } ); From 74b9ec04297f44e3b390ca528d789744f8e3afd1 Mon Sep 17 00:00:00 2001 From: Rik Cabanier Date: Tue, 21 Jul 2026 00:45:23 -0700 Subject: [PATCH 3/5] WebGLXRFallback: Detect WebGPU XR support upfront (#34080) --- examples/jsm/webxr/WebGLXRFallback.js | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/examples/jsm/webxr/WebGLXRFallback.js b/examples/jsm/webxr/WebGLXRFallback.js index 16df25631cfabb..c02cac6d829dc3 100644 --- a/examples/jsm/webxr/WebGLXRFallback.js +++ b/examples/jsm/webxr/WebGLXRFallback.js @@ -26,27 +26,13 @@ function setupWebGLXRFallback( renderer, createFallbackRenderer, onFallback = () } - if ( session !== null && renderer.backend.isWebGPUBackend === true && session.enabledFeatures.includes( 'webgpu' ) === false ) { + if ( session !== null && renderer.backend.isWebGPUBackend === true && typeof globalThis.XRGPUBinding === 'undefined' ) { return switchToFallbackRenderer( session, renderer ); } - try { - - return await setSession( session ); - - } catch ( error ) { - - if ( session === null || renderer.backend.isWebGPUBackend !== true ) { - - throw error; - - } - - return switchToFallbackRenderer( session, renderer ); - - } + return setSession( session ); }; From d238476c0cd723c89f900f322b376d39370a87c9 Mon Sep 17 00:00:00 2001 From: Michael Herzog Date: Tue, 21 Jul 2026 10:30:33 +0200 Subject: [PATCH 4/5] Core: Fix some `copy()` and `toJSON()` implementations. (#34083) --- src/lights/LightShadow.js | 1 + src/lights/SpotLightShadow.js | 18 ++++++++++++++++++ src/loaders/ObjectLoader.js | 3 +++ src/materials/Material.js | 10 +++++++++- src/renderers/common/Storage3DTexture.js | 16 ++++++++++++++++ src/textures/CompressedArrayTexture.js | 16 ++++++++++++++++ src/textures/Data3DTexture.js | 16 ++++++++++++++++ src/textures/DataArrayTexture.js | 16 ++++++++++++++++ 8 files changed, 95 insertions(+), 1 deletion(-) diff --git a/src/lights/LightShadow.js b/src/lights/LightShadow.js index 98fab479e3fea7..88b7d3b22bc844 100644 --- a/src/lights/LightShadow.js +++ b/src/lights/LightShadow.js @@ -333,6 +333,7 @@ class LightShadow { if ( this.bias !== 0 ) object.bias = this.bias; if ( this.normalBias !== 0 ) object.normalBias = this.normalBias; if ( this.radius !== 1 ) object.radius = this.radius; + if ( this.blurSamples !== 8 ) object.blurSamples = this.blurSamples; if ( this.mapSize.x !== 512 || this.mapSize.y !== 512 ) object.mapSize = this.mapSize.toArray(); object.camera = this.camera.toJSON( false ).object; diff --git a/src/lights/SpotLightShadow.js b/src/lights/SpotLightShadow.js index ba68595261d391..d7a5801b0f1fb8 100644 --- a/src/lights/SpotLightShadow.js +++ b/src/lights/SpotLightShadow.js @@ -70,11 +70,29 @@ class SpotLightShadow extends LightShadow { super.copy( source ); this.focus = source.focus; + this.aspect = source.aspect; return this; } + /** + * Serializes the light shadow into JSON. + * + * @return {Object} A JSON object representing the serialized light shadow. + * @see {@link ObjectLoader#parse} + */ + toJSON() { + + const object = super.toJSON(); + + if ( this.focus !== 1 ) object.focus = this.focus; + if ( this.aspect !== 1 ) object.aspect = this.aspect; + + return object; + + } + } export { SpotLightShadow }; diff --git a/src/loaders/ObjectLoader.js b/src/loaders/ObjectLoader.js index 318df4e55bea06..6a0bd5b0769c57 100644 --- a/src/loaders/ObjectLoader.js +++ b/src/loaders/ObjectLoader.js @@ -1162,6 +1162,9 @@ class ObjectLoader extends Loader { if ( data.shadow.bias !== undefined ) object.shadow.bias = data.shadow.bias; if ( data.shadow.normalBias !== undefined ) object.shadow.normalBias = data.shadow.normalBias; if ( data.shadow.radius !== undefined ) object.shadow.radius = data.shadow.radius; + if ( data.shadow.blurSamples !== undefined ) object.shadow.blurSamples = data.shadow.blurSamples; + if ( data.shadow.focus !== undefined ) object.shadow.focus = data.shadow.focus; + if ( data.shadow.aspect !== undefined ) object.shadow.aspect = data.shadow.aspect; if ( data.shadow.mapSize !== undefined ) object.shadow.mapSize.fromArray( data.shadow.mapSize ); if ( data.shadow.camera !== undefined ) object.shadow.camera = this.parseObject( data.shadow.camera ); diff --git a/src/materials/Material.js b/src/materials/Material.js index b45f318a6ac7ab..8f227ef84347d3 100644 --- a/src/materials/Material.js +++ b/src/materials/Material.js @@ -1,6 +1,6 @@ import { Color } from '../math/Color.js'; import { EventDispatcher } from '../core/EventDispatcher.js'; -import { FrontSide, NormalBlending, LessEqualDepth, AddEquation, OneMinusSrcAlphaFactor, SrcAlphaFactor, AlwaysStencilFunc, KeepStencilOp } from '../constants.js'; +import { FrontSide, NormalBlending, LessEqualDepth, AddEquation, OneMinusSrcAlphaFactor, SrcAlphaFactor, AlwaysStencilFunc, KeepStencilOp, BasicDepthPacking } from '../constants.js'; import { generateUUID } from '../math/MathUtils.js'; import { warn } from '../utils.js'; import { Vector2 } from '../math/Vector2.js'; @@ -822,11 +822,16 @@ class Material extends EventDispatcher { // rotation (SpriteMaterial) if ( this.rotation !== undefined && this.rotation !== 0 ) data.rotation = this.rotation; + // depthPacking (MeshDepthMaterial) + if ( this.depthPacking !== undefined && this.depthPacking !== BasicDepthPacking ) data.depthPacking = this.depthPacking; + if ( this.polygonOffset === true ) data.polygonOffset = true; if ( this.polygonOffsetFactor !== 0 ) data.polygonOffsetFactor = this.polygonOffsetFactor; if ( this.polygonOffsetUnits !== 0 ) data.polygonOffsetUnits = this.polygonOffsetUnits; if ( this.linewidth !== undefined && this.linewidth !== 1 ) data.linewidth = this.linewidth; + if ( this.linecap !== undefined && this.linecap !== 'round' ) data.linecap = this.linecap; + if ( this.linejoin !== undefined && this.linejoin !== 'round' ) data.linejoin = this.linejoin; if ( this.dashSize !== undefined ) data.dashSize = this.dashSize; if ( this.gapSize !== undefined ) data.gapSize = this.gapSize; if ( this.scale !== undefined ) data.scale = this.scale; @@ -936,6 +941,7 @@ class Material extends EventDispatcher { if ( json.depthTest !== undefined ) this.depthTest = json.depthTest; if ( json.depthWrite !== undefined ) this.depthWrite = json.depthWrite; if ( json.colorWrite !== undefined ) this.colorWrite = json.colorWrite; + if ( json.depthPacking !== undefined ) this.depthPacking = json.depthPacking; if ( json.blendSrc !== undefined ) this.blendSrc = json.blendSrc; if ( json.blendDst !== undefined ) this.blendDst = json.blendDst; if ( json.blendEquation !== undefined ) this.blendEquation = json.blendEquation; @@ -961,6 +967,8 @@ class Material extends EventDispatcher { if ( json.rotation !== undefined ) this.rotation = json.rotation; if ( json.linewidth !== undefined ) this.linewidth = json.linewidth; + if ( json.linecap !== undefined ) this.linecap = json.linecap; + if ( json.linejoin !== undefined ) this.linejoin = json.linejoin; if ( json.dashSize !== undefined ) this.dashSize = json.dashSize; if ( json.gapSize !== undefined ) this.gapSize = json.gapSize; if ( json.scale !== undefined ) this.scale = json.scale; diff --git a/src/renderers/common/Storage3DTexture.js b/src/renderers/common/Storage3DTexture.js index f17b4cdd150e65..c985ac36a38026 100644 --- a/src/renderers/common/Storage3DTexture.js +++ b/src/renderers/common/Storage3DTexture.js @@ -74,6 +74,22 @@ class Storage3DTexture extends Texture { } + /** + * Copies the values of the given texture to this instance. + * + * @param {Storage3DTexture} source - The texture to copy. + * @return {Storage3DTexture} A reference to this instance. + */ + copy( source ) { + + super.copy( source ); + + this.wrapR = source.wrapR; + + return this; + + } + /** * Sets the size of the storage 3d texture. * diff --git a/src/textures/CompressedArrayTexture.js b/src/textures/CompressedArrayTexture.js index 56205e36c8bc19..6a7c30e4a1ba17 100644 --- a/src/textures/CompressedArrayTexture.js +++ b/src/textures/CompressedArrayTexture.js @@ -60,6 +60,22 @@ class CompressedArrayTexture extends CompressedTexture { } + /** + * Copies the values of the given texture to this instance. + * + * @param {CompressedArrayTexture} source - The texture to copy. + * @return {CompressedArrayTexture} A reference to this instance. + */ + copy( source ) { + + super.copy( source ); + + this.wrapR = source.wrapR; + + return this; + + } + /** * Describes that a specific layer of the texture needs to be updated. * Normally when {@link Texture#needsUpdate} is set to `true`, the diff --git a/src/textures/Data3DTexture.js b/src/textures/Data3DTexture.js index e826b5261f5818..72fcd167afee0e 100644 --- a/src/textures/Data3DTexture.js +++ b/src/textures/Data3DTexture.js @@ -107,6 +107,22 @@ class Data3DTexture extends Texture { } + /** + * Copies the values of the given texture to this instance. + * + * @param {Data3DTexture} source - The texture to copy. + * @return {Data3DTexture} A reference to this instance. + */ + copy( source ) { + + super.copy( source ); + + this.wrapR = source.wrapR; + + return this; + + } + } export { Data3DTexture }; diff --git a/src/textures/DataArrayTexture.js b/src/textures/DataArrayTexture.js index 1e80e055e1eeed..1b17ac3a2a7dae 100644 --- a/src/textures/DataArrayTexture.js +++ b/src/textures/DataArrayTexture.js @@ -105,6 +105,22 @@ class DataArrayTexture extends Texture { } + /** + * Copies the values of the given texture to this instance. + * + * @param {DataArrayTexture} source - The texture to copy. + * @return {DataArrayTexture} A reference to this instance. + */ + copy( source ) { + + super.copy( source ); + + this.wrapR = source.wrapR; + + return this; + + } + /** * Describes that a specific layer of the texture needs to be updated. * Normally when {@link Texture#needsUpdate} is set to `true`, the From fa268ead472d5a2f8dbab227b422874c80702ab2 Mon Sep 17 00:00:00 2001 From: Michael Herzog Date: Tue, 21 Jul 2026 10:55:35 +0200 Subject: [PATCH 5/5] Material: Add serialization/deserialization support for clipping planes. (#34084) --- src/materials/Material.js | 13 +++++++++++++ src/math/Plane.js | 29 +++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/src/materials/Material.js b/src/materials/Material.js index 8f227ef84347d3..060a4e67fb26b4 100644 --- a/src/materials/Material.js +++ b/src/materials/Material.js @@ -4,6 +4,7 @@ import { FrontSide, NormalBlending, LessEqualDepth, AddEquation, OneMinusSrcAlph import { generateUUID } from '../math/MathUtils.js'; import { warn } from '../utils.js'; import { Vector2 } from '../math/Vector2.js'; +import { Plane } from '../math/Plane.js'; let _materialId = 0; @@ -810,6 +811,15 @@ class Material extends EventDispatcher { if ( this.depthWrite === false ) data.depthWrite = this.depthWrite; if ( this.colorWrite === false ) data.colorWrite = this.colorWrite; + if ( Array.isArray( this.clippingPlanes ) && this.clippingPlanes.length > 0 ) { + + data.clippingPlanes = this.clippingPlanes.map( plane => plane.toJSON() ); + + } + + if ( this.clipIntersection === true ) data.clipIntersection = true; + if ( this.clipShadows === true ) data.clipShadows = true; + if ( this.stencilWriteMask !== 0xff ) data.stencilWriteMask = this.stencilWriteMask; if ( this.stencilFunc !== AlwaysStencilFunc ) data.stencilFunc = this.stencilFunc; if ( this.stencilRef !== 0 ) data.stencilRef = this.stencilRef; @@ -941,6 +951,9 @@ class Material extends EventDispatcher { if ( json.depthTest !== undefined ) this.depthTest = json.depthTest; if ( json.depthWrite !== undefined ) this.depthWrite = json.depthWrite; if ( json.colorWrite !== undefined ) this.colorWrite = json.colorWrite; + if ( json.clippingPlanes !== undefined ) this.clippingPlanes = json.clippingPlanes.map( plane => new Plane().fromJSON( plane ) ); + if ( json.clipIntersection !== undefined ) this.clipIntersection = json.clipIntersection; + if ( json.clipShadows !== undefined ) this.clipShadows = json.clipShadows; if ( json.depthPacking !== undefined ) this.depthPacking = json.depthPacking; if ( json.blendSrc !== undefined ) this.blendSrc = json.blendSrc; if ( json.blendDst !== undefined ) this.blendDst = json.blendDst; diff --git a/src/math/Plane.js b/src/math/Plane.js index 95287da599362e..1cfe3ee8861352 100644 --- a/src/math/Plane.js +++ b/src/math/Plane.js @@ -363,6 +363,35 @@ class Plane { } + /** + * Returns a serialized structure of the plane. + * + * @return {Object} Serialized structure with fields representing the object state. + */ + toJSON() { + + return { + normal: this.normal.toArray(), + constant: this.constant + }; + + } + + /** + * Sets the plane properties from the given JSON. + * + * @param {Object} json - The serialized json to set the plane from. + * @return {Plane} A reference to this plane. + */ + fromJSON( json ) { + + this.normal.fromArray( json.normal ); + this.constant = json.constant; + + return this; + + } + } export { Plane };