diff --git a/build/three.core.js b/build/three.core.js index 95a00ecfe7659c..252e4588832358 100644 --- a/build/three.core.js +++ b/build/three.core.js @@ -3,7 +3,7 @@ * Copyright 2010-2026 Three.js Authors * SPDX-License-Identifier: MIT */ -const REVISION = '185'; +const REVISION = '186dev'; /** * Represents mouse buttons and interaction types in context of controls. @@ -22385,10 +22385,6 @@ const _segCenter = /*@__PURE__*/ new Vector3(); const _segDir = /*@__PURE__*/ new Vector3(); const _diff = /*@__PURE__*/ new Vector3(); -const _edge1 = /*@__PURE__*/ new Vector3(); -const _edge2 = /*@__PURE__*/ new Vector3(); -const _normal$1 = /*@__PURE__*/ new Vector3(); - /** * A ray that emits from an origin in a certain direction. The class is used by * {@link Raycaster} to assist with raycasting. Raycasting is used for @@ -22919,76 +22915,128 @@ class Ray { */ intersectTriangle( a, b, c, backfaceCulling, target ) { - // Compute the offset origin, edges, and normal. + // Watertight ray/triangle intersection. Reference: Woop, Benthin, Wald, + // "Watertight Ray/Triangle Intersection", JCGT vol. 2 no. 1 (2013), Appendix A. + // https://jcgt.org/published/0002/01/05/ + + const origin = this.origin; + const direction = this.direction; + + const dx = direction.x; + const dy = direction.y; + const dz = direction.z; + + // triangle vertices relative to the ray origin + + const aox = a.x - origin.x, aoy = a.y - origin.y, aoz = a.z - origin.z; + const box = b.x - origin.x, boy = b.y - origin.y, boz = b.z - origin.z; + const cox = c.x - origin.x, coy = c.y - origin.y, coz = c.z - origin.z; + + // Use the dimension where the ray direction is maximal as the projection + // axis (kz) and read every component already permuted into (kx, ky, kz). + // kx and ky are swapped when the direction's kz component is negative, to + // preserve the winding order of triangles. + + const adx = Math.abs( dx ), ady = Math.abs( dy ), adz = Math.abs( dz ); + + let dkx, dky, dkz; + let akx, aky, akz, bkx, bky, bkz, ckx, cky, ckz; + + if ( adx >= ady && adx >= adz ) { + + dkz = dx; akz = aox; bkz = box; ckz = cox; + + if ( dx >= 0 ) { - // from https://github.com/pmjoniak/GeometricTools/blob/master/GTEngine/Include/Mathematics/GteIntrRay3Triangle3.h + dkx = dy; dky = dz; + akx = aoy; aky = aoz; bkx = boy; bky = boz; ckx = coy; cky = coz; - _edge1.subVectors( b, a ); - _edge2.subVectors( c, a ); - _normal$1.crossVectors( _edge1, _edge2 ); + } else { + + dkx = dz; dky = dy; + akx = aoz; aky = aoy; bkx = boz; bky = boy; ckx = coz; cky = coy; + + } + + } else if ( ady >= adz ) { + + dkz = dy; akz = aoy; bkz = boy; ckz = coy; - // Solve Q + t*D = b1*E1 + b2*E2 (Q = kDiff, D = ray direction, - // E1 = kEdge1, E2 = kEdge2, N = Cross(E1,E2)) by - // |Dot(D,N)|*b1 = sign(Dot(D,N))*Dot(D,Cross(Q,E2)) - // |Dot(D,N)|*b2 = sign(Dot(D,N))*Dot(D,Cross(E1,Q)) - // |Dot(D,N)|*t = -sign(Dot(D,N))*Dot(Q,N) - let DdN = this.direction.dot( _normal$1 ); - let sign; + if ( dy >= 0 ) { - if ( DdN > 0 ) { + dkx = dz; dky = dx; + akx = aoz; aky = aox; bkx = boz; bky = box; ckx = coz; cky = cox; - if ( backfaceCulling ) return null; - sign = 1; + } else { - } else if ( DdN < 0 ) { + dkx = dx; dky = dz; + akx = aox; aky = aoz; bkx = box; bky = boz; ckx = cox; cky = coz; - sign = -1; - DdN = - DdN; + } } else { - return null; + dkz = dz; akz = aoz; bkz = boz; ckz = coz; - } + if ( dz >= 0 ) { - _diff.subVectors( this.origin, a ); - const DdQxE2 = sign * this.direction.dot( _edge2.crossVectors( _diff, _edge2 ) ); + dkx = dx; dky = dy; + akx = aox; aky = aoy; bkx = box; bky = boy; ckx = cox; cky = coy; - // b1 < 0, no intersection - if ( DdQxE2 < 0 ) { + } else { - return null; + dkx = dy; dky = dx; + akx = aoy; aky = aox; bkx = boy; bky = box; ckx = coy; cky = cox; + + } } - const DdE1xQ = sign * this.direction.dot( _edge1.cross( _diff ) ); + // a zero direction has no maximal axis and cannot intersect - // b2 < 0, no intersection - if ( DdE1xQ < 0 ) { + if ( dkz === 0 ) return null; - return null; + // shear constants that align the ray with the +kz axis - } + const sx = dkx / dkz, sy = dky / dkz, sz = 1 / dkz; - // b1+b2 > 1, no intersection - if ( DdQxE2 + DdE1xQ > DdN ) { + // sheared and scaled vertices - return null; + const ax = akx - sx * akz, ay = aky - sy * akz; + const bx = bkx - sx * bkz, by = bky - sy * bkz; + const cx = ckx - sx * ckz, cy = cky - sy * ckz; - } + // scaled barycentric coordinates (signed edge functions); the shear makes a + // shared edge evaluate identically for both adjacent triangles, so the ray + // can never fall between them - // Line intersects triangle, check if ray does. - const QdN = - sign * _diff.dot( _normal$1 ); + const u = cx * by - cy * bx; + const v = ax * cy - ay * cx; + const w = bx * ay - by * ax; - // t < 0, no intersection - if ( QdN < 0 ) { + if ( backfaceCulling ) { - return null; + if ( u < 0 || v < 0 || w < 0 ) return null; + + } else { + + if ( ( u < 0 || v < 0 || w < 0 ) && ( u > 0 || v > 0 || w > 0 ) ) return null; } - // Ray intersects triangle. - return this.at( QdN / DdN, target ); + const det = u + v + w; + + // ray is co-planar with the triangle + + if ( det === 0 ) return null; + + // scaled hit distance; t = tScaled / det must lie in front of the origin + + const tScaled = sz * ( u * akz + v * bkz + w * ckz ); + + if ( det > 0 ? tScaled < 0 : tScaled > 0 ) return null; + + return this.at( tScaled / det, target ); } @@ -56137,6 +56185,10 @@ class Raycaster { * be detected. To raycast against both faces of an object, you'll want to set {@link Material#side} * to `THREE.DoubleSide`. * + * Note that a ray hitting a triangle mesh exactly along an edge shared by two faces may be + * reported by both faces, resulting in two coincident intersections (identical point and + * distance) in the returned array. + * * @param {Object3D} object - The 3D object to check for intersection with the ray. * @param {boolean} [recursive=true] - If set to `true`, it also checks all descendants. * Otherwise it only checks intersection with the object. @@ -59505,7 +59557,8 @@ class ShapePath { for ( let j = i - 1; j >= 0; j -- ) { const candidate = entries[ j ]; - if ( ! candidate.boundingBox.containsPoint( entry.interiorPoint ) ) continue; + + if ( ! candidate.boundingBox.containsBox( entry.boundingBox ) ) continue; if ( ! pointInPolygon( entry.interiorPoint, candidate.points ) ) continue; entry.container = candidate.exclude ? candidate.container : candidate; @@ -59653,13 +59706,6 @@ class Controls extends EventDispatcher { */ connect( element ) { - if ( element === undefined ) { - - warn( 'Controls: connect() now requires an element.' ); // @deprecated, the warning can be removed with r185 - return; - - } - if ( this.domElement !== null ) this.disconnect(); this.domElement = element; diff --git a/build/three.tsl.js b/build/three.tsl.js index 7f16636cec52b0..c0abc01a87621f 100644 --- a/build/three.tsl.js +++ b/build/three.tsl.js @@ -214,6 +214,7 @@ const vogelDiskSample = TSL.vogelDiskSample; const getParallaxCorrectNormal = TSL.getParallaxCorrectNormal; const getRoughness = TSL.getRoughness; const getScreenPosition = TSL.getScreenPosition; +const getScreenPositionFromClip = TSL.getScreenPositionFromClip; const getShIrradianceAt = TSL.getShIrradianceAt; const getShadowMaterial = TSL.getShadowMaterial; const getShadowRenderObjectFunction = TSL.getShadowRenderObjectFunction; @@ -630,7 +631,6 @@ const viewportDepthTexture = TSL.viewportDepthTexture; const viewportLinearDepth = TSL.viewportLinearDepth; const viewportMipTexture = TSL.viewportMipTexture; const viewportOpaqueMipTexture = TSL.viewportOpaqueMipTexture; -const viewportResolution = TSL.viewportResolution; const viewportSafeUV = TSL.viewportSafeUV; const viewportSharedTexture = TSL.viewportSharedTexture; const viewportSize = TSL.viewportSize; @@ -658,4 +658,4 @@ for ( const key of Object.keys( THREE.TSL ) ) { log( code ); //*/ -export { BRDF_GGX, BRDF_Lambert, BasicPointShadowFilter, BasicShadowFilter, Break, Const, Continue, DFGLUT, D_GGX, Discard, EPSILON, F_Schlick, Fn, HALF_PI, INFINITY, If, Loop, NodeAccess, NodeShaderStage, NodeType, NodeUpdateType, OnBeforeMaterialUpdate, OnBeforeObjectUpdate, OnMaterialUpdate, OnObjectUpdate, PCFShadowFilter, PCFSoftShadowFilter, PI, PI2, PointShadowFilter, Return, Schlick_to_F0, ShaderNode, Stack, Switch, TBNViewMatrix, TWO_PI, VSMShadowFilter, V_GGX_SmithCorrelated, Var, VarIntent, abs, acesFilmicToneMapping, acos, acosh, add, addMethodChaining, addNodeElement, agxToneMapping, all, alphaT, ambientOcclusion, and, anisotropy, anisotropyB, anisotropyT, any, append, array, asin, asinh, assign, atan, atanh, atomicAdd, atomicAnd, atomicFunc, atomicLoad, atomicMax, atomicMin, atomicOr, atomicStore, atomicSub, atomicXor, attenuationColor, attenuationDistance, attribute, attributeArray, backgroundBlurriness, backgroundIntensity, backgroundRotation, batch, bentNormalView, billboarding, bitAnd, bitNot, bitOr, bitXor, bitangentGeometry, bitangentLocal, bitangentView, bitangentWorld, bitcast, blendBurn, blendColor, blendDodge, blendOverlay, blendScreen, blur, bool, buffer, bufferAttribute, builtin, builtinAOContext, builtinShadowContext, bumpMap, bvec2, bvec3, bvec4, bypass, cache, call, cameraFar, cameraIndex, cameraNear, cameraNormalMatrix, cameraPosition, cameraProjectionMatrix, cameraProjectionMatrixInverse, cameraViewMatrix, cameraViewport, cameraWorldMatrix, cbrt, cdl, ceil, checker, cineonToneMapping, clamp, clearcoat, clearcoatNormalView, clearcoatRoughness, clipSpace, code, color, colorSpaceToWorking, colorToDirection, compute, computeKernel, computeSkinning, context, convert, convertColorSpace, convertToTexture, cos, cosh, countLeadingZeros, countOneBits, countTrailingZeros, cross, cubeTexture, cubeTextureBase, dFdx, dFdy, dashSize, debug, decrement, decrementBefore, defaultBuildStages, defaultShaderStages, defined, degrees, deltaTime, densityFog, densityFogFactor, depth, depthPass, determinant, difference, diffuseColor, directPointLight, directionToColor, directionToFaceDirection, dispersion, distance, div, dot, drawIndex, dynamicBufferAttribute, element, emissive, equal, equirectDirection, equirectUV, exp, exp2, exponentialHeightFogFactor, expression, faceDirection, faceForward, faceforward, float, floatBitsToInt, floatBitsToUint, floor, fog, fract, frameGroup, frameId, frontFacing, fwidth, gain, gapSize, getConstNodeType, getCurrentStack, getDirection, getDistanceAttenuation, getGeometryRoughness, getNormalFromDepth, getParallaxCorrectNormal, getRoughness, getScreenPosition, getShIrradianceAt, getShadowMaterial, getShadowRenderObjectFunction, getTextureIndex, getViewPosition, globalId, glsl, glslFn, grayscale, greaterThan, greaterThanEqual, hash, highpModelNormalViewMatrix, highpModelViewMatrix, hue, increment, incrementBefore, instance, instanceIndex, instancedArray, instancedBufferAttribute, instancedDynamicBufferAttribute, instancedMesh, int, intBitsToFloat, interleavedGradientNoise, inverse, inverseSqrt, inversesqrt, invocationLocalIndex, invocationSubgroupIndex, ior, iridescence, iridescenceIOR, iridescenceThickness, ivec2, ivec3, ivec4, js, label, length, lengthSq, lessThan, lessThanEqual, lightPosition, lightProjectionUV, lightShadowMatrix, lightTargetDirection, lightTargetPosition, lightViewPosition, lightingContext, lights, linearDepth, linearToneMapping, localId, log, log2, logarithmicDepthToViewZ, luminance, mat2, mat3, mat4, matcapUV, materialAO, materialAlphaTest, materialAnisotropy, materialAnisotropyVector, materialAttenuationColor, materialAttenuationDistance, materialClearcoat, materialClearcoatNormal, materialClearcoatRoughness, materialColor, materialDispersion, materialEmissive, materialEnvIntensity, materialEnvRotation, materialIOR, materialIridescence, materialIridescenceIOR, materialIridescenceThickness, materialLightMap, materialLineDashOffset, materialLineDashSize, materialLineGapSize, materialLineScale, materialLineWidth, materialMetalness, materialNormal, materialOpacity, materialPointSize, materialReference, materialReflectivity, materialRefractionRatio, materialRotation, materialRoughness, materialSheen, materialSheenRoughness, materialShininess, materialSpecular, materialSpecularColor, materialSpecularIntensity, materialSpecularStrength, materialThickness, materialTransmission, max, maxMipLevel, mediumpModelViewMatrix, metalness, min, mix, mixElement, mod, modelDirection, modelNormalMatrix, modelPosition, modelRadius, modelScale, modelViewMatrix, modelViewPosition, modelViewProjection, modelWorldMatrix, modelWorldMatrixInverse, morphReference, mrt, mul, mx_aastep, mx_add, mx_atan2, mx_cell_noise_float, mx_contrast, mx_divide, mx_fractal_noise_float, mx_fractal_noise_vec2, mx_fractal_noise_vec3, mx_fractal_noise_vec4, mx_frame, mx_heighttonormal, mx_hsvtorgb, mx_ifequal, mx_ifgreater, mx_ifgreatereq, mx_invert, mx_modulo, mx_multiply, mx_noise_float, mx_noise_vec3, mx_noise_vec4, mx_place2d, mx_power, mx_ramp4, mx_ramplr, mx_ramptb, mx_rgbtohsv, mx_rotate2d, mx_rotate3d, mx_safepower, mx_separate, mx_splitlr, mx_splittb, mx_srgb_texture_to_lin_rec709, mx_subtract, mx_timer, mx_transform_uv, mx_unifiednoise2d, mx_unifiednoise3d, mx_worley_noise_float, mx_worley_noise_vec2, mx_worley_noise_vec3, negate, negateOnBackSide, neutralToneMapping, nodeArray, nodeImmutable, nodeObject, nodeObjectIntent, nodeObjects, nodeProxy, nodeProxyIntent, normalFlat, normalGeometry, normalLocal, normalMap, normalView, normalViewGeometry, normalWorld, normalWorldGeometry, normalize, not, notEqual, numWorkgroups, objectDirection, objectGroup, objectPosition, objectRadius, objectScale, objectViewPosition, objectWorldMatrix, oneMinus, or, orthographicDepthToViewZ, oscSawtooth, oscSine, oscSquare, oscTriangle, output, outputStruct, overloadingFn, overrideNode, overrideNodes, packHalf2x16, packNormalToRGB, packSnorm2x16, packUnorm2x16, parabola, parallaxDirection, parallaxUV, parameter, pass, passTexture, pcurve, perspectiveDepthToViewZ, pmremTexture, pointShadow, pointUV, pointWidth, positionGeometry, positionLocal, positionPrevious, positionView, positionViewDirection, positionWorld, positionWorldDirection, posterize, pow, pow2, pow3, pow4, premultiplyAlpha, property, radians, rand, range, rangeFog, rangeFogFactor, reciprocal, reference, referenceBuffer, reflect, reflectVector, reflectView, reflector, refract, refractVector, refractView, reinhardToneMapping, remap, remapClamp, renderGroup, renderOutput, rendererReference, replaceDefaultUV, rotate, rotateUV, roughness, round, rtt, sRGBTransferEOTF, sRGBTransferOETF, sample, sampler, samplerComparison, saturate, saturation, screen, screenCoordinate, screenDPR, screenSize, screenUV, select, setCurrentStack, setName, shaderStages, shadow, shadowPositionWorld, shapeCircle, sharedUniformGroup, sheen, sheenRoughness, shiftLeft, shiftRight, shininess, sign, sin, sinc, sinh, skinning, smoothstep, smoothstepElement, specularColor, specularF90, spherizeUV, split, spritesheetUV, sqrt, stack, step, stepElement, storage, storageBarrier, storageTexture, storageTexture3D, struct, sub, subBuild, subgroupAdd, subgroupAll, subgroupAnd, subgroupAny, subgroupBallot, subgroupBroadcast, subgroupBroadcastFirst, subgroupElect, subgroupExclusiveAdd, subgroupExclusiveMul, subgroupInclusiveAdd, subgroupInclusiveMul, subgroupIndex, subgroupMax, subgroupMin, subgroupMul, subgroupOr, subgroupShuffle, subgroupShuffleDown, subgroupShuffleUp, subgroupShuffleXor, subgroupSize, subgroupXor, tan, tangentGeometry, tangentLocal, tangentView, tangentWorld, tanh, texture, texture3D, textureBarrier, textureBicubic, textureBicubicLevel, textureCubeUV, textureLevel, textureLoad, textureSize, textureStore, thickness, time, toneMapping, toneMappingExposure, toonOutlinePass, transformDirection, transformNormal, transformNormalByInverseViewMatrix, transformNormalByViewMatrix, transformNormalToView, transformedClearcoatNormalView, transformedNormalView, transformedNormalWorld, transmission, transpose, triNoise3D, triplanarTexture, triplanarTextures, trunc, uint, uintBitsToFloat, uniform, uniformArray, uniformCubeTexture, uniformFlow, uniformGroup, uniformTexture, unpackHalf2x16, unpackRGBToNormal, unpackSnorm2x16, unpackUnorm2x16, unpremultiplyAlpha, userData, uv, uvec2, uvec3, uvec4, varying, varyingProperty, vec2, vec3, vec4, vectorComponents, velocity, vertexColor, vertexIndex, vertexStage, vibrance, viewZToLogarithmicDepth, viewZToOrthographicDepth, viewZToPerspectiveDepth, viewZToReversedOrthographicDepth, viewZToReversedPerspectiveDepth, viewport, viewportCoordinate, viewportDepthTexture, viewportLinearDepth, viewportMipTexture, viewportOpaqueMipTexture, viewportResolution, viewportSafeUV, viewportSharedTexture, viewportSize, viewportTexture, viewportUV, vogelDiskSample, wgsl, wgslFn, workgroupArray, workgroupBarrier, workgroupId, workingToColorSpace, xor }; +export { BRDF_GGX, BRDF_Lambert, BasicPointShadowFilter, BasicShadowFilter, Break, Const, Continue, DFGLUT, D_GGX, Discard, EPSILON, F_Schlick, Fn, HALF_PI, INFINITY, If, Loop, NodeAccess, NodeShaderStage, NodeType, NodeUpdateType, OnBeforeMaterialUpdate, OnBeforeObjectUpdate, OnMaterialUpdate, OnObjectUpdate, PCFShadowFilter, PCFSoftShadowFilter, PI, PI2, PointShadowFilter, Return, Schlick_to_F0, ShaderNode, Stack, Switch, TBNViewMatrix, TWO_PI, VSMShadowFilter, V_GGX_SmithCorrelated, Var, VarIntent, abs, acesFilmicToneMapping, acos, acosh, add, addMethodChaining, addNodeElement, agxToneMapping, all, alphaT, ambientOcclusion, and, anisotropy, anisotropyB, anisotropyT, any, append, array, asin, asinh, assign, atan, atanh, atomicAdd, atomicAnd, atomicFunc, atomicLoad, atomicMax, atomicMin, atomicOr, atomicStore, atomicSub, atomicXor, attenuationColor, attenuationDistance, attribute, attributeArray, backgroundBlurriness, backgroundIntensity, backgroundRotation, batch, bentNormalView, billboarding, bitAnd, bitNot, bitOr, bitXor, bitangentGeometry, bitangentLocal, bitangentView, bitangentWorld, bitcast, blendBurn, blendColor, blendDodge, blendOverlay, blendScreen, blur, bool, buffer, bufferAttribute, builtin, builtinAOContext, builtinShadowContext, bumpMap, bvec2, bvec3, bvec4, bypass, cache, call, cameraFar, cameraIndex, cameraNear, cameraNormalMatrix, cameraPosition, cameraProjectionMatrix, cameraProjectionMatrixInverse, cameraViewMatrix, cameraViewport, cameraWorldMatrix, cbrt, cdl, ceil, checker, cineonToneMapping, clamp, clearcoat, clearcoatNormalView, clearcoatRoughness, clipSpace, code, color, colorSpaceToWorking, colorToDirection, compute, computeKernel, computeSkinning, context, convert, convertColorSpace, convertToTexture, cos, cosh, countLeadingZeros, countOneBits, countTrailingZeros, cross, cubeTexture, cubeTextureBase, dFdx, dFdy, dashSize, debug, decrement, decrementBefore, defaultBuildStages, defaultShaderStages, defined, degrees, deltaTime, densityFog, densityFogFactor, depth, depthPass, determinant, difference, diffuseColor, directPointLight, directionToColor, directionToFaceDirection, dispersion, distance, div, dot, drawIndex, dynamicBufferAttribute, element, emissive, equal, equirectDirection, equirectUV, exp, exp2, exponentialHeightFogFactor, expression, faceDirection, faceForward, faceforward, float, floatBitsToInt, floatBitsToUint, floor, fog, fract, frameGroup, frameId, frontFacing, fwidth, gain, gapSize, getConstNodeType, getCurrentStack, getDirection, getDistanceAttenuation, getGeometryRoughness, getNormalFromDepth, getParallaxCorrectNormal, getRoughness, getScreenPosition, getScreenPositionFromClip, getShIrradianceAt, getShadowMaterial, getShadowRenderObjectFunction, getTextureIndex, getViewPosition, globalId, glsl, glslFn, grayscale, greaterThan, greaterThanEqual, hash, highpModelNormalViewMatrix, highpModelViewMatrix, hue, increment, incrementBefore, instance, instanceIndex, instancedArray, instancedBufferAttribute, instancedDynamicBufferAttribute, instancedMesh, int, intBitsToFloat, interleavedGradientNoise, inverse, inverseSqrt, inversesqrt, invocationLocalIndex, invocationSubgroupIndex, ior, iridescence, iridescenceIOR, iridescenceThickness, ivec2, ivec3, ivec4, js, label, length, lengthSq, lessThan, lessThanEqual, lightPosition, lightProjectionUV, lightShadowMatrix, lightTargetDirection, lightTargetPosition, lightViewPosition, lightingContext, lights, linearDepth, linearToneMapping, localId, log, log2, logarithmicDepthToViewZ, luminance, mat2, mat3, mat4, matcapUV, materialAO, materialAlphaTest, materialAnisotropy, materialAnisotropyVector, materialAttenuationColor, materialAttenuationDistance, materialClearcoat, materialClearcoatNormal, materialClearcoatRoughness, materialColor, materialDispersion, materialEmissive, materialEnvIntensity, materialEnvRotation, materialIOR, materialIridescence, materialIridescenceIOR, materialIridescenceThickness, materialLightMap, materialLineDashOffset, materialLineDashSize, materialLineGapSize, materialLineScale, materialLineWidth, materialMetalness, materialNormal, materialOpacity, materialPointSize, materialReference, materialReflectivity, materialRefractionRatio, materialRotation, materialRoughness, materialSheen, materialSheenRoughness, materialShininess, materialSpecular, materialSpecularColor, materialSpecularIntensity, materialSpecularStrength, materialThickness, materialTransmission, max, maxMipLevel, mediumpModelViewMatrix, metalness, min, mix, mixElement, mod, modelDirection, modelNormalMatrix, modelPosition, modelRadius, modelScale, modelViewMatrix, modelViewPosition, modelViewProjection, modelWorldMatrix, modelWorldMatrixInverse, morphReference, mrt, mul, mx_aastep, mx_add, mx_atan2, mx_cell_noise_float, mx_contrast, mx_divide, mx_fractal_noise_float, mx_fractal_noise_vec2, mx_fractal_noise_vec3, mx_fractal_noise_vec4, mx_frame, mx_heighttonormal, mx_hsvtorgb, mx_ifequal, mx_ifgreater, mx_ifgreatereq, mx_invert, mx_modulo, mx_multiply, mx_noise_float, mx_noise_vec3, mx_noise_vec4, mx_place2d, mx_power, mx_ramp4, mx_ramplr, mx_ramptb, mx_rgbtohsv, mx_rotate2d, mx_rotate3d, mx_safepower, mx_separate, mx_splitlr, mx_splittb, mx_srgb_texture_to_lin_rec709, mx_subtract, mx_timer, mx_transform_uv, mx_unifiednoise2d, mx_unifiednoise3d, mx_worley_noise_float, mx_worley_noise_vec2, mx_worley_noise_vec3, negate, negateOnBackSide, neutralToneMapping, nodeArray, nodeImmutable, nodeObject, nodeObjectIntent, nodeObjects, nodeProxy, nodeProxyIntent, normalFlat, normalGeometry, normalLocal, normalMap, normalView, normalViewGeometry, normalWorld, normalWorldGeometry, normalize, not, notEqual, numWorkgroups, objectDirection, objectGroup, objectPosition, objectRadius, objectScale, objectViewPosition, objectWorldMatrix, oneMinus, or, orthographicDepthToViewZ, oscSawtooth, oscSine, oscSquare, oscTriangle, output, outputStruct, overloadingFn, overrideNode, overrideNodes, packHalf2x16, packNormalToRGB, packSnorm2x16, packUnorm2x16, parabola, parallaxDirection, parallaxUV, parameter, pass, passTexture, pcurve, perspectiveDepthToViewZ, pmremTexture, pointShadow, pointUV, pointWidth, positionGeometry, positionLocal, positionPrevious, positionView, positionViewDirection, positionWorld, positionWorldDirection, posterize, pow, pow2, pow3, pow4, premultiplyAlpha, property, radians, rand, range, rangeFog, rangeFogFactor, reciprocal, reference, referenceBuffer, reflect, reflectVector, reflectView, reflector, refract, refractVector, refractView, reinhardToneMapping, remap, remapClamp, renderGroup, renderOutput, rendererReference, replaceDefaultUV, rotate, rotateUV, roughness, round, rtt, sRGBTransferEOTF, sRGBTransferOETF, sample, sampler, samplerComparison, saturate, saturation, screen, screenCoordinate, screenDPR, screenSize, screenUV, select, setCurrentStack, setName, shaderStages, shadow, shadowPositionWorld, shapeCircle, sharedUniformGroup, sheen, sheenRoughness, shiftLeft, shiftRight, shininess, sign, sin, sinc, sinh, skinning, smoothstep, smoothstepElement, specularColor, specularF90, spherizeUV, split, spritesheetUV, sqrt, stack, step, stepElement, storage, storageBarrier, storageTexture, storageTexture3D, struct, sub, subBuild, subgroupAdd, subgroupAll, subgroupAnd, subgroupAny, subgroupBallot, subgroupBroadcast, subgroupBroadcastFirst, subgroupElect, subgroupExclusiveAdd, subgroupExclusiveMul, subgroupInclusiveAdd, subgroupInclusiveMul, subgroupIndex, subgroupMax, subgroupMin, subgroupMul, subgroupOr, subgroupShuffle, subgroupShuffleDown, subgroupShuffleUp, subgroupShuffleXor, subgroupSize, subgroupXor, tan, tangentGeometry, tangentLocal, tangentView, tangentWorld, tanh, texture, texture3D, textureBarrier, textureBicubic, textureBicubicLevel, textureCubeUV, textureLevel, textureLoad, textureSize, textureStore, thickness, time, toneMapping, toneMappingExposure, toonOutlinePass, transformDirection, transformNormal, transformNormalByInverseViewMatrix, transformNormalByViewMatrix, transformNormalToView, transformedClearcoatNormalView, transformedNormalView, transformedNormalWorld, transmission, transpose, triNoise3D, triplanarTexture, triplanarTextures, trunc, uint, uintBitsToFloat, uniform, uniformArray, uniformCubeTexture, uniformFlow, uniformGroup, uniformTexture, unpackHalf2x16, unpackRGBToNormal, unpackSnorm2x16, unpackUnorm2x16, unpremultiplyAlpha, userData, uv, uvec2, uvec3, uvec4, varying, varyingProperty, vec2, vec3, vec4, vectorComponents, velocity, vertexColor, vertexIndex, vertexStage, vibrance, viewZToLogarithmicDepth, viewZToOrthographicDepth, viewZToPerspectiveDepth, viewZToReversedOrthographicDepth, viewZToReversedPerspectiveDepth, viewport, viewportCoordinate, viewportDepthTexture, viewportLinearDepth, viewportMipTexture, viewportOpaqueMipTexture, viewportSafeUV, viewportSharedTexture, viewportSize, viewportTexture, viewportUV, vogelDiskSample, wgsl, wgslFn, workgroupArray, workgroupBarrier, workgroupId, workingToColorSpace, xor }; diff --git a/build/three.webgpu.js b/build/three.webgpu.js index 9f83e322a167c3..b3af7b15cebc50 100644 --- a/build/three.webgpu.js +++ b/build/three.webgpu.js @@ -4908,30 +4908,6 @@ const split = ( node, channels ) => new SplitNode( nodeObject( node ), channels addMethodChaining( 'element', element ); addMethodChaining( 'convert', convert ); -// deprecated - -/** - * @tsl - * @function - * @deprecated since r176. Use {@link Stack} instead. - * - * @param {Node} node - The node to add. - * @returns {Function} - */ -const append = ( node ) => { // @deprecated, r176 - - warn( 'TSL: append() has been renamed to Stack().', new StackTrace() ); - return Stack( node ); - -}; - -addMethodChaining( 'append', ( node ) => { // @deprecated, r176 - - warn( 'TSL: .append() has been renamed to .toStack().', new StackTrace() ); - return Stack( node ); - -} ); - /** * This class represents a shader property. It can be used * to explicitly define a property and assign a value to it. @@ -14170,19 +14146,6 @@ const viewportCoordinate = /*@__PURE__*/ screenCoordinate.sub( viewport.xy ); */ const viewportUV = /*@__PURE__*/ viewportCoordinate.div( viewportSize ); -// Deprecated - -/** - * @deprecated since r169. Use {@link screenSize} instead. - */ -const viewportResolution = /*@__PURE__*/ ( Fn( () => { // @deprecated, r169 - - warn( 'TSL: "viewportResolution" is deprecated. Use "screenSize" instead.', new StackTrace() ); - - return screenSize; - -}, 'vec2' ).once() )(); - // Cache node uniforms let _cameraProjectionMatrixBase = null; @@ -15519,8 +15482,8 @@ const materialEnvIntensity = /*@__PURE__*/ uniform( 1 ).onReference( ( { materia /** * TSL object that represents the rotation of environment maps. - * When `material.envMap` is set, the value is `material.envMapRotation`. `scene.environmentRotation` controls the - * rotation of `scene.environment` instead. + * When `material.envMap` is set, the value is `material.envMapRotation`. + * `scene.environmentRotation` controls the rotation of `scene.environment` or `scene.environmentNode` instead. * * @tsl * @type {Node} @@ -15531,7 +15494,8 @@ const materialEnvRotation = /*@__PURE__*/ uniform( new Matrix4() ).onReference( } ).onObjectUpdate( function ( { material, scene } ) { - const rotation = ( scene.environment !== null && material.envMap === null ) ? scene.environmentRotation : material.envMapRotation; + const hasSceneEnvironment = ( scene.environment !== null ) || ( scene.environmentNode && scene.environmentNode.isNode ); + const rotation = ( hasSceneEnvironment && material.envMap === null ) ? scene.environmentRotation : material.envMapRotation; if ( rotation ) { @@ -18302,26 +18266,26 @@ const _previousInstanceMatrices = /*@__PURE__*/ new WeakMap(); * * @param {NodeBuilder} builder - The current node builder. * @param {InstancedBufferAttribute|StorageInstancedBufferAttribute} instanceMatrix - The matrix buffer attribute. - * @param {number} count - The instance count. * @returns {Node} The matrix node. */ -function createInstanceMatrixNode( builder, instanceMatrix, count ) { +function createInstanceMatrixNode( builder, instanceMatrix ) { let instanceMatrixNode; + const matrixCount = Math.max( instanceMatrix.count, 1 ); const isStorageMatrix = instanceMatrix.isStorageInstancedBufferAttribute === true; if ( isStorageMatrix ) { - instanceMatrixNode = storage( instanceMatrix, 'mat4', Math.max( count, 1 ) ).element( instanceIndex ); + instanceMatrixNode = storage( instanceMatrix, 'mat4', matrixCount ).element( instanceIndex ); } else { - const uniformBufferSize = count * 16 * 4; + const uniformBufferSize = matrixCount * 16 * 4; if ( uniformBufferSize <= builder.getUniformBufferLimit() ) { - instanceMatrixNode = buffer( instanceMatrix.array, 'mat4', Math.max( count, 1 ) ).element( instanceIndex ); + instanceMatrixNode = buffer( instanceMatrix.array, 'mat4', matrixCount ).element( instanceIndex ); } else { @@ -18360,10 +18324,9 @@ function createInstanceMatrixNode( builder, instanceMatrix, count ) { * @param {InstancedMesh} instancedMesh - The instanced mesh object. * @param {InstancedBufferAttribute|StorageInstancedBufferAttribute} instanceMatrix - The current matrix buffer attribute. * @param {NodeBuilder} builder - The current node builder. - * @param {number} count - The instance count. * @returns {Node} The previous frame instance matrix node. */ -function getPreviousInstance( instancedMesh, instanceMatrix, builder, count ) { +function getPreviousInstance( instancedMesh, instanceMatrix, builder ) { let data = _previousInstanceMatrices.get( instancedMesh ); @@ -18373,7 +18336,7 @@ function getPreviousInstance( instancedMesh, instanceMatrix, builder, count ) { data = { previousInstanceMatrix, - node: createInstanceMatrixNode( builder, previousInstanceMatrix, count ) + node: createInstanceMatrixNode( builder, previousInstanceMatrix ) }; _previousInstanceMatrices.set( instancedMesh, data ); @@ -18397,26 +18360,22 @@ const instanceColor = /*@__PURE__*/ varyingProperty( 'vec3', 'vInstanceColor' ); * * @tsl * @function - * @param {number} count - The instance count. * @param {InstancedBufferAttribute|StorageInstancedBufferAttribute} matrices - The instanced transformation matrices. * @param {?InstancedBufferAttribute|StorageInstancedBufferAttribute} [colors=null] - The optional instanced colors. */ -const instance = /*@__PURE__*/ Fn( ( [ count, matrices, colors = null ], builder ) => { - - // get numeric value (non-node) - count = count.value; +const instance = /*@__PURE__*/ Fn( ( [ matrices, colors = null ], builder ) => { const isStorageMatrix = matrices.isStorageInstancedBufferAttribute === true; const isStorageColor = colors && colors.isStorageInstancedBufferAttribute === true; - const instanceMatrixNode = createInstanceMatrixNode( builder, matrices, count ); + const instanceMatrixNode = createInstanceMatrixNode( builder, matrices ); // interleaved buffer tracking for matrix let interleavedMatrix = null; if ( ! isStorageMatrix ) { - const uniformBufferSize = count * 16 * 4; + const uniformBufferSize = Math.max( matrices.count, 1 ) * 16 * 4; if ( uniformBufferSize > builder.getUniformBufferLimit() ) { @@ -18508,7 +18467,7 @@ const instance = /*@__PURE__*/ Fn( ( [ count, matrices, colors = null ], builder } ); - const previousInstanceMatrixNode = getPreviousInstance( instancedMesh, matrices, builder, count ); + const previousInstanceMatrixNode = getPreviousInstance( instancedMesh, matrices, builder ); positionPrevious.assign( previousInstanceMatrixNode.mul( positionPrevious ).xyz ); } @@ -18541,9 +18500,9 @@ const instance = /*@__PURE__*/ Fn( ( [ count, matrices, colors = null ], builder */ const instancedMesh = /*@__PURE__*/ Fn( ( [ instancedMesh ] ) => { - const { count, instanceMatrix, instanceColor } = instancedMesh; + const { instanceMatrix, instanceColor } = instancedMesh; - instance( count, instanceMatrix, instanceColor ); + instance( instanceMatrix, instanceColor ); }, 'void' ); @@ -27221,7 +27180,7 @@ class PMREMNode extends TempNode { // PMREMGenerator renders into a render target with inverted Y, so its output needs the Y // flip on sampling. Externally authored PMREMs follow the standard convention and don't. - uvNode = this._pmrem.isRenderTargetTexture + uvNode = this._pmrem === null || this._pmrem.isRenderTargetTexture ? materialEnvRotation.mul( vec3( uvNode.x, uvNode.y.negate(), uvNode.z ) ) : materialEnvRotation.mul( uvNode ); @@ -29221,9 +29180,9 @@ class VolumetricLightingModel extends LightingModel { direct( { lightNode, lightColor }, builder ) { - // Ignore lights with infinite distance + // Ignore non-analytical lights and lights with infinite distance - if ( lightNode.light.distance === undefined ) return; + if ( lightNode.isAnalyticLightNode !== true || lightNode.light.distance === undefined ) return; // TODO: We need a viewportOpaque*() ( output, depth ) to fit with modern rendering approaches @@ -36054,6 +36013,10 @@ class MRTNode extends OutputStructNode { for ( const name in outputNodes ) { const index = getTextureIndex( textures, name ); + + // Ignore if the output exists in the MRT but has never been used. + if ( index === -1 ) continue; + const type = builder.getOutputType( index ); members[ index ] = outputNodes[ name ].convert( type ); @@ -38516,6 +38479,27 @@ const getScreenPosition = /*@__PURE__*/ Fn( ( [ viewPosition, projectionMatrix ] } ); +/** + * Converts a clip-space position into a screen position expressed as uv coordinates. + * + * @tsl + * @function + * @param {Node} clipPosition - The position in clip space. + * @return {Node} The screen position expressed as uv coordinates. + */ +const getScreenPositionFromClip = /*@__PURE__*/ Fn( ( [ clipPosition ] ) => { + + const screen = clipPosition.xy.div( clipPosition.w ).mul( 0.5 ).add( 0.5 ).toVar(); + return vec2( screen.x, screen.y.oneMinus() ); + +} ).setLayout( { + name: 'getScreenPositionFromClip', + type: 'vec2', + inputs: [ + { name: 'clipPosition', type: 'vec4' } + ] +} ); + /** * Computes a normal vector based on depth data. Can be used as a fallback when no normal render * target is available or if flat surface normals are required. @@ -38930,7 +38914,7 @@ const backgroundRotation = /*@__PURE__*/ uniform( new Matrix4() ).setGroup( rend const background = scene.background; - if ( background !== null && background.isTexture && background.mapping !== UVMapping ) { + if ( ( background !== null && background.isTexture && background.mapping !== UVMapping ) || ( scene.backgroundNode && scene.backgroundNode.isNode ) ) { // note: since the matrix is orthonormal, we can use the more-efficient transpose() in lieu of invert() _m1.makeRotationFromEuler( scene.backgroundRotation ).transpose(); @@ -48248,7 +48232,6 @@ var TSL = /*#__PURE__*/Object.freeze({ anisotropyB: anisotropyB, anisotropyT: anisotropyT, any: any, - append: append, array: array, asin: asin, asinh: asinh, @@ -48404,6 +48387,7 @@ var TSL = /*#__PURE__*/Object.freeze({ getParallaxCorrectNormal: getParallaxCorrectNormal, getRoughness: getRoughness, getScreenPosition: getScreenPosition, + getScreenPositionFromClip: getScreenPositionFromClip, getShIrradianceAt: getShIrradianceAt, getShadowMaterial: getShadowMaterial, getShadowRenderObjectFunction: getShadowRenderObjectFunction, @@ -48828,7 +48812,6 @@ var TSL = /*#__PURE__*/Object.freeze({ viewportLinearDepth: viewportLinearDepth, viewportMipTexture: viewportMipTexture, viewportOpaqueMipTexture: viewportOpaqueMipTexture, - viewportResolution: viewportResolution, viewportSafeUV: viewportSafeUV, viewportSharedTexture: viewportSharedTexture, viewportSize: viewportSize, @@ -61334,6 +61317,22 @@ class Renderer { } + /** + * Resets the backend's internal state cache. Useful when the rendering context is shared with + * other libraries that change the state. A no-op for the WebGPU backend. + */ + resetState() { + + if ( this._initialized === false ) { + + throw new Error( 'THREE.Renderer: .resetState() called before the backend is initialized. Use "await renderer.init();" before using this method.' ); + + } + + this.backend.resetState(); + + } + /** * Returns the viewport definition. * @@ -66756,6 +66755,13 @@ class Backend { */ setScissorTest( /*boolean*/ ) { } + /** + * Resets the backend's internal state. A no-op for backends without a state cache (e.g. WebGPU). + * + * @abstract + */ + resetState() { } + /** * Returns the clear color and alpha into a single * color object. @@ -67299,6 +67305,8 @@ class WebGLState { this.currentProgram = null; this.currentBlendingEnabled = false; this.currentBlending = null; + this.currentBlendEquation = null; + this.currentBlendEquationAlpha = null; this.currentBlendSrc = null; this.currentBlendDst = null; this.currentBlendSrcAlpha = null; @@ -68599,6 +68607,128 @@ class WebGLState { } + /** + * Restores the WebGL state to its default and clears the cache so subsequent renderings + * re-apply the required state. Useful when the WebGL context is shared with other libraries. + */ + reset() { + + const { gl } = this; + + // reset WebGL state + + gl.disable( gl.BLEND ); + gl.disable( gl.CULL_FACE ); + gl.disable( gl.DEPTH_TEST ); + gl.disable( gl.POLYGON_OFFSET_FILL ); + gl.disable( gl.SCISSOR_TEST ); + gl.disable( gl.STENCIL_TEST ); + gl.disable( gl.SAMPLE_ALPHA_TO_COVERAGE ); + + gl.blendEquation( gl.FUNC_ADD ); + gl.blendFunc( gl.ONE, gl.ZERO ); + gl.blendFuncSeparate( gl.ONE, gl.ZERO, gl.ONE, gl.ZERO ); + gl.blendColor( 0, 0, 0, 0 ); + + gl.colorMask( true, true, true, true ); + gl.clearColor( 0, 0, 0, 0 ); + + gl.depthMask( true ); + gl.depthFunc( gl.LESS ); + gl.clearDepth( 1 ); + + gl.stencilMask( 0xffffffff ); + gl.stencilFunc( gl.ALWAYS, 0, 0xffffffff ); + gl.stencilOp( gl.KEEP, gl.KEEP, gl.KEEP ); + gl.clearStencil( 0 ); + + gl.cullFace( gl.BACK ); + gl.frontFace( gl.CCW ); + + gl.polygonOffset( 0, 0 ); + + gl.activeTexture( gl.TEXTURE0 ); + + gl.bindFramebuffer( gl.FRAMEBUFFER, null ); + gl.bindFramebuffer( gl.DRAW_FRAMEBUFFER, null ); + gl.bindFramebuffer( gl.READ_FRAMEBUFFER, null ); + + gl.useProgram( null ); + + gl.lineWidth( 1 ); + + gl.scissor( 0, 0, gl.canvas.width, gl.canvas.height ); + gl.viewport( 0, 0, gl.canvas.width, gl.canvas.height ); + + gl.pixelStorei( gl.PACK_ALIGNMENT, 4 ); + gl.pixelStorei( gl.UNPACK_ALIGNMENT, 4 ); + gl.pixelStorei( gl.UNPACK_FLIP_Y_WEBGL, false ); + gl.pixelStorei( gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false ); + gl.pixelStorei( gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.BROWSER_DEFAULT_WEBGL ); + gl.pixelStorei( gl.PACK_ROW_LENGTH, 0 ); + gl.pixelStorei( gl.PACK_SKIP_PIXELS, 0 ); + gl.pixelStorei( gl.PACK_SKIP_ROWS, 0 ); + gl.pixelStorei( gl.UNPACK_ROW_LENGTH, 0 ); + gl.pixelStorei( gl.UNPACK_IMAGE_HEIGHT, 0 ); + gl.pixelStorei( gl.UNPACK_SKIP_PIXELS, 0 ); + gl.pixelStorei( gl.UNPACK_SKIP_ROWS, 0 ); + gl.pixelStorei( gl.UNPACK_SKIP_IMAGES, 0 ); + + this.resetVertexState(); + + // reset internal cache + + this.enabled = {}; + this.parameters = {}; + this.currentFlipSided = null; + this.currentCullFace = null; + this.currentProgram = null; + this.currentBlendingEnabled = false; + this.currentBlending = null; + this.currentBlendEquation = null; + this.currentBlendEquationAlpha = null; + this.currentBlendSrc = null; + this.currentBlendDst = null; + this.currentBlendSrcAlpha = null; + this.currentBlendDstAlpha = null; + this.currentPremultipledAlpha = null; + this.currentPolygonOffsetFactor = null; + this.currentPolygonOffsetUnits = null; + this.currentColorMask = null; + this.currentDepthFunc = null; + this.currentDepthMask = null; + this.currentStencilFunc = null; + this.currentStencilRef = null; + this.currentStencilFuncMask = null; + this.currentStencilFail = null; + this.currentStencilZFail = null; + this.currentStencilZPass = null; + this.currentStencilMask = null; + this.currentLineWidth = null; + this.currentClippingPlanes = 0; + + this.currentBoundFramebuffers = {}; + this.currentDrawbuffers = new WeakMap(); + + this.currentTextureSlot = null; + this.currentBoundTextures = {}; + this.currentBoundBufferBases = {}; + + this.currentScissor.set( 0, 0, gl.canvas.width, gl.canvas.height ); + this.currentViewport.set( 0, 0, gl.canvas.width, gl.canvas.height ); + + // re-apply reversed depth if used by the renderer + + this.currentDepthReversed = false; + + if ( this.backend.renderer.reversedDepthBuffer === true ) { + + this.setReversedDepth( true ); + + } + + } + } /** @@ -71809,6 +71939,15 @@ class WebGLBackend extends Backend { } + /** + * Restores the WebGL state to its default and invalidates the internal state cache. + */ + resetState() { + + this.state.reset(); + + } + /** * Returns the clear color and alpha into a single * color object. diff --git a/build/three.webgpu.nodes.js b/build/three.webgpu.nodes.js index 23100dfbb9743c..4beb227f7618e2 100644 --- a/build/three.webgpu.nodes.js +++ b/build/three.webgpu.nodes.js @@ -4908,30 +4908,6 @@ const split = ( node, channels ) => new SplitNode( nodeObject( node ), channels addMethodChaining( 'element', element ); addMethodChaining( 'convert', convert ); -// deprecated - -/** - * @tsl - * @function - * @deprecated since r176. Use {@link Stack} instead. - * - * @param {Node} node - The node to add. - * @returns {Function} - */ -const append = ( node ) => { // @deprecated, r176 - - warn( 'TSL: append() has been renamed to Stack().', new StackTrace() ); - return Stack( node ); - -}; - -addMethodChaining( 'append', ( node ) => { // @deprecated, r176 - - warn( 'TSL: .append() has been renamed to .toStack().', new StackTrace() ); - return Stack( node ); - -} ); - /** * This class represents a shader property. It can be used * to explicitly define a property and assign a value to it. @@ -14170,19 +14146,6 @@ const viewportCoordinate = /*@__PURE__*/ screenCoordinate.sub( viewport.xy ); */ const viewportUV = /*@__PURE__*/ viewportCoordinate.div( viewportSize ); -// Deprecated - -/** - * @deprecated since r169. Use {@link screenSize} instead. - */ -const viewportResolution = /*@__PURE__*/ ( Fn( () => { // @deprecated, r169 - - warn( 'TSL: "viewportResolution" is deprecated. Use "screenSize" instead.', new StackTrace() ); - - return screenSize; - -}, 'vec2' ).once() )(); - // Cache node uniforms let _cameraProjectionMatrixBase = null; @@ -15519,8 +15482,8 @@ const materialEnvIntensity = /*@__PURE__*/ uniform( 1 ).onReference( ( { materia /** * TSL object that represents the rotation of environment maps. - * When `material.envMap` is set, the value is `material.envMapRotation`. `scene.environmentRotation` controls the - * rotation of `scene.environment` instead. + * When `material.envMap` is set, the value is `material.envMapRotation`. + * `scene.environmentRotation` controls the rotation of `scene.environment` or `scene.environmentNode` instead. * * @tsl * @type {Node} @@ -15531,7 +15494,8 @@ const materialEnvRotation = /*@__PURE__*/ uniform( new Matrix4() ).onReference( } ).onObjectUpdate( function ( { material, scene } ) { - const rotation = ( scene.environment !== null && material.envMap === null ) ? scene.environmentRotation : material.envMapRotation; + const hasSceneEnvironment = ( scene.environment !== null ) || ( scene.environmentNode && scene.environmentNode.isNode ); + const rotation = ( hasSceneEnvironment && material.envMap === null ) ? scene.environmentRotation : material.envMapRotation; if ( rotation ) { @@ -18302,26 +18266,26 @@ const _previousInstanceMatrices = /*@__PURE__*/ new WeakMap(); * * @param {NodeBuilder} builder - The current node builder. * @param {InstancedBufferAttribute|StorageInstancedBufferAttribute} instanceMatrix - The matrix buffer attribute. - * @param {number} count - The instance count. * @returns {Node} The matrix node. */ -function createInstanceMatrixNode( builder, instanceMatrix, count ) { +function createInstanceMatrixNode( builder, instanceMatrix ) { let instanceMatrixNode; + const matrixCount = Math.max( instanceMatrix.count, 1 ); const isStorageMatrix = instanceMatrix.isStorageInstancedBufferAttribute === true; if ( isStorageMatrix ) { - instanceMatrixNode = storage( instanceMatrix, 'mat4', Math.max( count, 1 ) ).element( instanceIndex ); + instanceMatrixNode = storage( instanceMatrix, 'mat4', matrixCount ).element( instanceIndex ); } else { - const uniformBufferSize = count * 16 * 4; + const uniformBufferSize = matrixCount * 16 * 4; if ( uniformBufferSize <= builder.getUniformBufferLimit() ) { - instanceMatrixNode = buffer( instanceMatrix.array, 'mat4', Math.max( count, 1 ) ).element( instanceIndex ); + instanceMatrixNode = buffer( instanceMatrix.array, 'mat4', matrixCount ).element( instanceIndex ); } else { @@ -18360,10 +18324,9 @@ function createInstanceMatrixNode( builder, instanceMatrix, count ) { * @param {InstancedMesh} instancedMesh - The instanced mesh object. * @param {InstancedBufferAttribute|StorageInstancedBufferAttribute} instanceMatrix - The current matrix buffer attribute. * @param {NodeBuilder} builder - The current node builder. - * @param {number} count - The instance count. * @returns {Node} The previous frame instance matrix node. */ -function getPreviousInstance( instancedMesh, instanceMatrix, builder, count ) { +function getPreviousInstance( instancedMesh, instanceMatrix, builder ) { let data = _previousInstanceMatrices.get( instancedMesh ); @@ -18373,7 +18336,7 @@ function getPreviousInstance( instancedMesh, instanceMatrix, builder, count ) { data = { previousInstanceMatrix, - node: createInstanceMatrixNode( builder, previousInstanceMatrix, count ) + node: createInstanceMatrixNode( builder, previousInstanceMatrix ) }; _previousInstanceMatrices.set( instancedMesh, data ); @@ -18397,26 +18360,22 @@ const instanceColor = /*@__PURE__*/ varyingProperty( 'vec3', 'vInstanceColor' ); * * @tsl * @function - * @param {number} count - The instance count. * @param {InstancedBufferAttribute|StorageInstancedBufferAttribute} matrices - The instanced transformation matrices. * @param {?InstancedBufferAttribute|StorageInstancedBufferAttribute} [colors=null] - The optional instanced colors. */ -const instance = /*@__PURE__*/ Fn( ( [ count, matrices, colors = null ], builder ) => { - - // get numeric value (non-node) - count = count.value; +const instance = /*@__PURE__*/ Fn( ( [ matrices, colors = null ], builder ) => { const isStorageMatrix = matrices.isStorageInstancedBufferAttribute === true; const isStorageColor = colors && colors.isStorageInstancedBufferAttribute === true; - const instanceMatrixNode = createInstanceMatrixNode( builder, matrices, count ); + const instanceMatrixNode = createInstanceMatrixNode( builder, matrices ); // interleaved buffer tracking for matrix let interleavedMatrix = null; if ( ! isStorageMatrix ) { - const uniformBufferSize = count * 16 * 4; + const uniformBufferSize = Math.max( matrices.count, 1 ) * 16 * 4; if ( uniformBufferSize > builder.getUniformBufferLimit() ) { @@ -18508,7 +18467,7 @@ const instance = /*@__PURE__*/ Fn( ( [ count, matrices, colors = null ], builder } ); - const previousInstanceMatrixNode = getPreviousInstance( instancedMesh, matrices, builder, count ); + const previousInstanceMatrixNode = getPreviousInstance( instancedMesh, matrices, builder ); positionPrevious.assign( previousInstanceMatrixNode.mul( positionPrevious ).xyz ); } @@ -18541,9 +18500,9 @@ const instance = /*@__PURE__*/ Fn( ( [ count, matrices, colors = null ], builder */ const instancedMesh = /*@__PURE__*/ Fn( ( [ instancedMesh ] ) => { - const { count, instanceMatrix, instanceColor } = instancedMesh; + const { instanceMatrix, instanceColor } = instancedMesh; - instance( count, instanceMatrix, instanceColor ); + instance( instanceMatrix, instanceColor ); }, 'void' ); @@ -27221,7 +27180,7 @@ class PMREMNode extends TempNode { // PMREMGenerator renders into a render target with inverted Y, so its output needs the Y // flip on sampling. Externally authored PMREMs follow the standard convention and don't. - uvNode = this._pmrem.isRenderTargetTexture + uvNode = this._pmrem === null || this._pmrem.isRenderTargetTexture ? materialEnvRotation.mul( vec3( uvNode.x, uvNode.y.negate(), uvNode.z ) ) : materialEnvRotation.mul( uvNode ); @@ -29221,9 +29180,9 @@ class VolumetricLightingModel extends LightingModel { direct( { lightNode, lightColor }, builder ) { - // Ignore lights with infinite distance + // Ignore non-analytical lights and lights with infinite distance - if ( lightNode.light.distance === undefined ) return; + if ( lightNode.isAnalyticLightNode !== true || lightNode.light.distance === undefined ) return; // TODO: We need a viewportOpaque*() ( output, depth ) to fit with modern rendering approaches @@ -36054,6 +36013,10 @@ class MRTNode extends OutputStructNode { for ( const name in outputNodes ) { const index = getTextureIndex( textures, name ); + + // Ignore if the output exists in the MRT but has never been used. + if ( index === -1 ) continue; + const type = builder.getOutputType( index ); members[ index ] = outputNodes[ name ].convert( type ); @@ -38516,6 +38479,27 @@ const getScreenPosition = /*@__PURE__*/ Fn( ( [ viewPosition, projectionMatrix ] } ); +/** + * Converts a clip-space position into a screen position expressed as uv coordinates. + * + * @tsl + * @function + * @param {Node} clipPosition - The position in clip space. + * @return {Node} The screen position expressed as uv coordinates. + */ +const getScreenPositionFromClip = /*@__PURE__*/ Fn( ( [ clipPosition ] ) => { + + const screen = clipPosition.xy.div( clipPosition.w ).mul( 0.5 ).add( 0.5 ).toVar(); + return vec2( screen.x, screen.y.oneMinus() ); + +} ).setLayout( { + name: 'getScreenPositionFromClip', + type: 'vec2', + inputs: [ + { name: 'clipPosition', type: 'vec4' } + ] +} ); + /** * Computes a normal vector based on depth data. Can be used as a fallback when no normal render * target is available or if flat surface normals are required. @@ -38930,7 +38914,7 @@ const backgroundRotation = /*@__PURE__*/ uniform( new Matrix4() ).setGroup( rend const background = scene.background; - if ( background !== null && background.isTexture && background.mapping !== UVMapping ) { + if ( ( background !== null && background.isTexture && background.mapping !== UVMapping ) || ( scene.backgroundNode && scene.backgroundNode.isNode ) ) { // note: since the matrix is orthonormal, we can use the more-efficient transpose() in lieu of invert() _m1.makeRotationFromEuler( scene.backgroundRotation ).transpose(); @@ -48248,7 +48232,6 @@ var TSL = /*#__PURE__*/Object.freeze({ anisotropyB: anisotropyB, anisotropyT: anisotropyT, any: any, - append: append, array: array, asin: asin, asinh: asinh, @@ -48404,6 +48387,7 @@ var TSL = /*#__PURE__*/Object.freeze({ getParallaxCorrectNormal: getParallaxCorrectNormal, getRoughness: getRoughness, getScreenPosition: getScreenPosition, + getScreenPositionFromClip: getScreenPositionFromClip, getShIrradianceAt: getShIrradianceAt, getShadowMaterial: getShadowMaterial, getShadowRenderObjectFunction: getShadowRenderObjectFunction, @@ -48828,7 +48812,6 @@ var TSL = /*#__PURE__*/Object.freeze({ viewportLinearDepth: viewportLinearDepth, viewportMipTexture: viewportMipTexture, viewportOpaqueMipTexture: viewportOpaqueMipTexture, - viewportResolution: viewportResolution, viewportSafeUV: viewportSafeUV, viewportSharedTexture: viewportSharedTexture, viewportSize: viewportSize, @@ -61334,6 +61317,22 @@ class Renderer { } + /** + * Resets the backend's internal state cache. Useful when the rendering context is shared with + * other libraries that change the state. A no-op for the WebGPU backend. + */ + resetState() { + + if ( this._initialized === false ) { + + throw new Error( 'THREE.Renderer: .resetState() called before the backend is initialized. Use "await renderer.init();" before using this method.' ); + + } + + this.backend.resetState(); + + } + /** * Returns the viewport definition. * @@ -66756,6 +66755,13 @@ class Backend { */ setScissorTest( /*boolean*/ ) { } + /** + * Resets the backend's internal state. A no-op for backends without a state cache (e.g. WebGPU). + * + * @abstract + */ + resetState() { } + /** * Returns the clear color and alpha into a single * color object. @@ -67299,6 +67305,8 @@ class WebGLState { this.currentProgram = null; this.currentBlendingEnabled = false; this.currentBlending = null; + this.currentBlendEquation = null; + this.currentBlendEquationAlpha = null; this.currentBlendSrc = null; this.currentBlendDst = null; this.currentBlendSrcAlpha = null; @@ -68599,6 +68607,128 @@ class WebGLState { } + /** + * Restores the WebGL state to its default and clears the cache so subsequent renderings + * re-apply the required state. Useful when the WebGL context is shared with other libraries. + */ + reset() { + + const { gl } = this; + + // reset WebGL state + + gl.disable( gl.BLEND ); + gl.disable( gl.CULL_FACE ); + gl.disable( gl.DEPTH_TEST ); + gl.disable( gl.POLYGON_OFFSET_FILL ); + gl.disable( gl.SCISSOR_TEST ); + gl.disable( gl.STENCIL_TEST ); + gl.disable( gl.SAMPLE_ALPHA_TO_COVERAGE ); + + gl.blendEquation( gl.FUNC_ADD ); + gl.blendFunc( gl.ONE, gl.ZERO ); + gl.blendFuncSeparate( gl.ONE, gl.ZERO, gl.ONE, gl.ZERO ); + gl.blendColor( 0, 0, 0, 0 ); + + gl.colorMask( true, true, true, true ); + gl.clearColor( 0, 0, 0, 0 ); + + gl.depthMask( true ); + gl.depthFunc( gl.LESS ); + gl.clearDepth( 1 ); + + gl.stencilMask( 0xffffffff ); + gl.stencilFunc( gl.ALWAYS, 0, 0xffffffff ); + gl.stencilOp( gl.KEEP, gl.KEEP, gl.KEEP ); + gl.clearStencil( 0 ); + + gl.cullFace( gl.BACK ); + gl.frontFace( gl.CCW ); + + gl.polygonOffset( 0, 0 ); + + gl.activeTexture( gl.TEXTURE0 ); + + gl.bindFramebuffer( gl.FRAMEBUFFER, null ); + gl.bindFramebuffer( gl.DRAW_FRAMEBUFFER, null ); + gl.bindFramebuffer( gl.READ_FRAMEBUFFER, null ); + + gl.useProgram( null ); + + gl.lineWidth( 1 ); + + gl.scissor( 0, 0, gl.canvas.width, gl.canvas.height ); + gl.viewport( 0, 0, gl.canvas.width, gl.canvas.height ); + + gl.pixelStorei( gl.PACK_ALIGNMENT, 4 ); + gl.pixelStorei( gl.UNPACK_ALIGNMENT, 4 ); + gl.pixelStorei( gl.UNPACK_FLIP_Y_WEBGL, false ); + gl.pixelStorei( gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false ); + gl.pixelStorei( gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.BROWSER_DEFAULT_WEBGL ); + gl.pixelStorei( gl.PACK_ROW_LENGTH, 0 ); + gl.pixelStorei( gl.PACK_SKIP_PIXELS, 0 ); + gl.pixelStorei( gl.PACK_SKIP_ROWS, 0 ); + gl.pixelStorei( gl.UNPACK_ROW_LENGTH, 0 ); + gl.pixelStorei( gl.UNPACK_IMAGE_HEIGHT, 0 ); + gl.pixelStorei( gl.UNPACK_SKIP_PIXELS, 0 ); + gl.pixelStorei( gl.UNPACK_SKIP_ROWS, 0 ); + gl.pixelStorei( gl.UNPACK_SKIP_IMAGES, 0 ); + + this.resetVertexState(); + + // reset internal cache + + this.enabled = {}; + this.parameters = {}; + this.currentFlipSided = null; + this.currentCullFace = null; + this.currentProgram = null; + this.currentBlendingEnabled = false; + this.currentBlending = null; + this.currentBlendEquation = null; + this.currentBlendEquationAlpha = null; + this.currentBlendSrc = null; + this.currentBlendDst = null; + this.currentBlendSrcAlpha = null; + this.currentBlendDstAlpha = null; + this.currentPremultipledAlpha = null; + this.currentPolygonOffsetFactor = null; + this.currentPolygonOffsetUnits = null; + this.currentColorMask = null; + this.currentDepthFunc = null; + this.currentDepthMask = null; + this.currentStencilFunc = null; + this.currentStencilRef = null; + this.currentStencilFuncMask = null; + this.currentStencilFail = null; + this.currentStencilZFail = null; + this.currentStencilZPass = null; + this.currentStencilMask = null; + this.currentLineWidth = null; + this.currentClippingPlanes = 0; + + this.currentBoundFramebuffers = {}; + this.currentDrawbuffers = new WeakMap(); + + this.currentTextureSlot = null; + this.currentBoundTextures = {}; + this.currentBoundBufferBases = {}; + + this.currentScissor.set( 0, 0, gl.canvas.width, gl.canvas.height ); + this.currentViewport.set( 0, 0, gl.canvas.width, gl.canvas.height ); + + // re-apply reversed depth if used by the renderer + + this.currentDepthReversed = false; + + if ( this.backend.renderer.reversedDepthBuffer === true ) { + + this.setReversedDepth( true ); + + } + + } + } /** @@ -71809,6 +71939,15 @@ class WebGLBackend extends Backend { } + /** + * Restores the WebGL state to its default and invalidates the internal state cache. + */ + resetState() { + + this.state.reset(); + + } + /** * Returns the clear color and alpha into a single * color object. diff --git a/examples/jsm/tsl/display/GTAONode.js b/examples/jsm/tsl/display/GTAONode.js index b37941ed68b5f4..9d53ead4f37b5e 100644 --- a/examples/jsm/tsl/display/GTAONode.js +++ b/examples/jsm/tsl/display/GTAONode.js @@ -1,5 +1,5 @@ import { DataTexture, RenderTarget, RepeatWrapping, Vector2, Vector3, TempNode, QuadMesh, NodeMaterial, RendererUtils, RedFormat } from 'three/webgpu'; -import { reference, logarithmicDepthToViewZ, viewZToPerspectiveDepth, getNormalFromDepth, getScreenPosition, getViewPosition, nodeObject, Fn, float, NodeUpdateType, uv, uniform, Loop, vec2, vec3, int, dot, max, pow, abs, If, textureSize, sin, cos, PI, texture, passTexture, mat3, add, normalize, cross, mix, acos, clamp, interleavedGradientNoise, screenCoordinate, fract, rand } from 'three/tsl'; +import { reference, logarithmicDepthToViewZ, viewZToPerspectiveDepth, getNormalFromDepth, getViewPosition, getScreenPositionFromClip, nodeObject, Fn, float, NodeUpdateType, uv, uniform, Loop, vec2, vec3, vec4, int, dot, max, min, pow, abs, If, textureSize, sin, cos, PI, texture, passTexture, mat3, add, normalize, cross, mix, acos, clamp, interleavedGradientNoise, screenCoordinate, rand } from 'three/tsl'; const _quadMesh = /*@__PURE__*/ new QuadMesh(); const _size = /*@__PURE__*/ new Vector2(); @@ -369,7 +369,9 @@ class GTAONode extends TempNode { const viewPosition = getViewPosition( uvNode, depth, this._cameraProjectionMatrixInverse ).toVar(); const viewNormal = sampleNormal( uvNode ).toVar(); - const radiusToUse = this.radius; + const radius = this.radius; + const viewDir = normalize( viewPosition.xyz.negate() ).toVar(); + const clipPosition = this._cameraProjectionMatrix.mul( vec4( viewPosition, 1.0 ) ).toVar(); const noiseResolution = textureSize( this._noiseNode, 0 ); let noiseUv = vec2( uvNode.x, uvNode.y.oneMinus() ); @@ -390,15 +392,14 @@ class GTAONode extends TempNode { // Per-step phase jitter for spatio-temporal decorrelation. const noiseJitterIdx = this._temporalDirection.mul( 0.02 ); - const stepJitter = fract( interleavedGradientNoise( screenCoordinate.add( this._temporalOffset ) ) ).add( rand( uvNode.add( noiseJitterIdx ).mul( 2 ).sub( 1 ) ) ); + const stepJitter = interleavedGradientNoise( screenCoordinate.add( this._temporalOffset ) ).add( rand( uvNode.add( noiseJitterIdx ).mul( 2 ).sub( 1 ) ) ); Loop( { start: int( 0 ), end: DIRECTIONS, type: 'int', condition: '<' }, ( { i } ) => { const angle = float( i ).div( float( DIRECTIONS ) ).mul( PI ).add( this._temporalDirection ).toVar(); - const sampleDir = vec3( cos( angle ), sin( angle ), 0 ).toVar(); - sampleDir.assign( normalize( kernelMatrix.mul( sampleDir ) ) ); + const sampleDir = kernelMatrix.mul( vec3( cos( angle ), sin( angle ), 0 ) ).toVar(); + const clipDirRadius = this._cameraProjectionMatrix.mul( vec4( sampleDir, 0.0 ) ).mul( radius ).toVar(); - const viewDir = normalize( viewPosition.xyz.negate() ).toVar(); const sliceBitangent = normalize( cross( sampleDir, viewDir ) ).toVar(); const sliceTangent = cross( sliceBitangent, viewDir ).toVar(); @@ -416,7 +417,8 @@ class GTAONode extends TempNode { const angleN = signNSin.mul( acos( nCos ) ).toVar(); const tangentToNormalInSlice = cross( projN, sliceBitangent ).toVar(); - const cosHorizons = vec2( dot( viewDir, tangentToNormalInSlice ), dot( viewDir, tangentToNormalInSlice.negate() ) ).toVar(); + const cosHorizon = dot( viewDir, tangentToNormalInSlice ).toVar(); + const cosHorizons = vec2( cosHorizon, cosHorizon.negate() ).toVar(); // For each slice, the inner loop performs ray marching to find the horizons. @@ -426,13 +428,13 @@ class GTAONode extends TempNode { // near-field. (Blender's Eevee adaptation) const t = float( j ).add( 1.0 ).add( stepJitter ).div( STEPS ).toVar(); const sampleDist = t.mul( t ); - const sampleViewOffset = sampleDir.mul( radiusToUse ).mul( sampleDist ); + const clipOffset = clipDirRadius.mul( sampleDist ).toVar(); // The loop marches in two opposite directions (x and y) along the slice's line to find the horizon on both sides. // x - const sampleScreenPositionX = getScreenPosition( viewPosition.add( sampleViewOffset ), this._cameraProjectionMatrix ).toVar(); + const sampleScreenPositionX = getScreenPositionFromClip( clipPosition.add( clipOffset ) ).toVar(); const sampleDepthX = sampleDepth( sampleScreenPositionX ).toVar(); const sampleSceneViewPositionX = getViewPosition( sampleScreenPositionX, sampleDepthX, this._cameraProjectionMatrixInverse ).toVar(); const viewDeltaX = sampleSceneViewPositionX.sub( viewPosition ).toVar(); @@ -445,7 +447,7 @@ class GTAONode extends TempNode { // back toward the prior horizon as it approaches the radius boundary. // (squared variant of the paper's near-field attenuation; // Activision GTAO paper, Section 4.3 "Bounding the sampling area") - const distFacX = clamp( lenX.div( radiusToUse ), 0, 1 ); + const distFacX = min( lenX.div( radius ), 1 ); const distFacSqX = distFacX.mul( distFacX ); If( abs( viewDeltaX.z ).lessThan( this.thickness ), () => { @@ -456,7 +458,7 @@ class GTAONode extends TempNode { // y - const sampleScreenPositionY = getScreenPosition( viewPosition.sub( sampleViewOffset ), this._cameraProjectionMatrix ).toVar(); + const sampleScreenPositionY = getScreenPositionFromClip( clipPosition.sub( clipOffset ) ).toVar(); const sampleDepthY = sampleDepth( sampleScreenPositionY ).toVar(); const sampleSceneViewPositionY = getViewPosition( sampleScreenPositionY, sampleDepthY, this._cameraProjectionMatrixInverse ).toVar(); const viewDeltaY = sampleSceneViewPositionY.sub( viewPosition ).toVar(); @@ -464,7 +466,7 @@ class GTAONode extends TempNode { const sHY = dot( viewDir, viewDeltaY.div( max( lenY, float( 0.0001 ) ) ) ); - const distFacY = clamp( lenY.div( radiusToUse ), 0, 1 ); + const distFacY = min( lenY.div( radius ), 1 ); const distFacSqY = distFacY.mul( distFacY ); If( abs( viewDeltaY.z ).lessThan( this.thickness ), () => { diff --git a/examples/webgpu_tsl_editor.html b/examples/webgpu_tsl_editor.html index ba820dcaaaea3c..535445ce94caca 100644 --- a/examples/webgpu_tsl_editor.html +++ b/examples/webgpu_tsl_editor.html @@ -81,6 +81,7 @@ const renderer = new THREE.WebGPURenderer( { antialias: true } ); renderer.setPixelRatio( window.devicePixelRatio ); renderer.setSize( 200, 200 ); + renderer.debug.diagnostics.keywords = true; rendererDOM.appendChild( renderer.domElement ); const material = new THREE.NodeMaterial(); @@ -169,6 +170,7 @@ }; const webGLRenderer = new THREE.WebGPURenderer( { forceWebGL: true } ); + webGLRenderer.debug.diagnostics.keywords = true; const build = async () => { diff --git a/src/Three.TSL.js b/src/Three.TSL.js index f1d0d6456b055d..a6ca7a5b66a108 100644 --- a/src/Three.TSL.js +++ b/src/Three.TSL.js @@ -209,6 +209,7 @@ export const vogelDiskSample = TSL.vogelDiskSample; export const getParallaxCorrectNormal = TSL.getParallaxCorrectNormal; export const getRoughness = TSL.getRoughness; export const getScreenPosition = TSL.getScreenPosition; +export const getScreenPositionFromClip = TSL.getScreenPositionFromClip; export const getShIrradianceAt = TSL.getShIrradianceAt; export const getShadowMaterial = TSL.getShadowMaterial; export const getShadowRenderObjectFunction = TSL.getShadowRenderObjectFunction; diff --git a/src/nodes/code/FunctionNode.js b/src/nodes/code/FunctionNode.js index 3914f3e9a4efeb..6598431f58a898 100644 --- a/src/nodes/code/FunctionNode.js +++ b/src/nodes/code/FunctionNode.js @@ -126,9 +126,19 @@ class FunctionNode extends CodeNode { if ( name !== '' ) { - // use a custom property name + const nodeData = builder.getDataFromNode( this ); - nodeCode.name = name; + if ( nodeData.declarationRegistered !== true ) { + + // use a custom property name + + nodeCode.name = name; + + builder.registerDeclaration( nodeCode ); + + nodeData.declarationRegistered = true; + + } } diff --git a/src/nodes/core/NodeBuilder.js b/src/nodes/core/NodeBuilder.js index be00b81ef36e1a..50ef6dafef541c 100644 --- a/src/nodes/core/NodeBuilder.js +++ b/src/nodes/core/NodeBuilder.js @@ -1542,6 +1542,20 @@ class NodeBuilder { } + /** + * Returns whether the given name is a reserved keyword of the backend's + * shading language. Backends override this method to provide their + * language-specific keywords. + * + * @param {string} name - The name to test. + * @return {boolean} Whether the name is a reserved keyword or not. + */ + isReservedKeyword( /* name */ ) { + + return false; + + } + /** * Whether the given type is a vector type or not. * @@ -2267,6 +2281,7 @@ class NodeBuilder { const shaderStage = this.shaderStage; const declarations = this.declarations[ shaderStage ] || ( this.declarations[ shaderStage ] = {} ); + const checkKeywords = this.renderer.debug.diagnostics.keywords; const baseName = node.name; @@ -2274,9 +2289,9 @@ class NodeBuilder { let property = this.getPropertyName( node ); let index = 1; - // Automatically renames the property if the name is already in use. + // Automatically renames the property if the name is already in use or reserved. - while ( declarations[ property ] !== undefined ) { + while ( ( checkKeywords && this.isReservedKeyword( name ) ) || declarations[ property ] !== undefined ) { name = baseName + '_' + index ++; node.name = name; @@ -2286,7 +2301,7 @@ class NodeBuilder { if ( name !== baseName ) { - warn( `TSL: Declaration name '${ baseName }' of '${ node.type }' already in use. Renamed to '${ name }'.` ); + warn( `TSL: Declaration name '${ baseName }' of '${ node.type }' is a reserved keyword or already in use. Renamed to '${ name }'.` ); } diff --git a/src/nodes/utils/PostProcessingUtils.js b/src/nodes/utils/PostProcessingUtils.js index ac731929191351..823c8d27ea15ed 100644 --- a/src/nodes/utils/PostProcessingUtils.js +++ b/src/nodes/utils/PostProcessingUtils.js @@ -54,6 +54,27 @@ export const getScreenPosition = /*@__PURE__*/ Fn( ( [ viewPosition, projectionM } ); +/** + * Converts a clip-space position into a screen position expressed as uv coordinates. + * + * @tsl + * @function + * @param {Node} clipPosition - The position in clip space. + * @return {Node} The screen position expressed as uv coordinates. + */ +export const getScreenPositionFromClip = /*@__PURE__*/ Fn( ( [ clipPosition ] ) => { + + const screen = clipPosition.xy.div( clipPosition.w ).mul( 0.5 ).add( 0.5 ).toVar(); + return vec2( screen.x, screen.y.oneMinus() ); + +} ).setLayout( { + name: 'getScreenPositionFromClip', + type: 'vec2', + inputs: [ + { name: 'clipPosition', type: 'vec4' } + ] +} ); + /** * Computes a normal vector based on depth data. Can be used as a fallback when no normal render * target is available or if flat surface normals are required. diff --git a/src/renderers/WebGLRenderer.js b/src/renderers/WebGLRenderer.js index cc2f156939863f..dfc294b8c207f9 100644 --- a/src/renderers/WebGLRenderer.js +++ b/src/renderers/WebGLRenderer.js @@ -180,6 +180,14 @@ class WebGLRenderer { * @type {boolean} */ checkShaderErrors: true, + /** + * Diagnostics configuration for the shader generation. Only relevant for TSL. + * @type {Object} + * @property {boolean} keywords - Whether declaration names that collide with reserved keywords of the shading language should be renamed or not. + */ + diagnostics: { + keywords: false + }, /** * Callback for custom error reporting. * @type {?Function} diff --git a/src/renderers/common/Renderer.js b/src/renderers/common/Renderer.js index 41f9ab0c87198c..a65cd1342b0673 100644 --- a/src/renderers/common/Renderer.js +++ b/src/renderers/common/Renderer.js @@ -723,6 +723,8 @@ class Renderer { * Debug configuration. * @typedef {Object} DebugConfig * @property {boolean} checkShaderErrors - Whether shader errors should be checked or not. + * @property {Object} diagnostics - Diagnostics configuration for the shader generation. + * @property {boolean} diagnostics.keywords - Whether declaration names that collide with reserved keywords should be renamed or not. * @property {?Function} onShaderError - A callback function that is executed when a shader error happens. Only supported with WebGL 2 right now. * @property {Function} getShaderAsync - Allows the get the raw shader code for the given scene, camera and 3D object. */ @@ -734,6 +736,9 @@ class Renderer { */ this.debug = { checkShaderErrors: true, + diagnostics: { + keywords: false + }, onShaderError: null, getShaderAsync: async ( scene, camera, object ) => { diff --git a/src/renderers/webgl-fallback/nodes/GLSLNodeBuilder.js b/src/renderers/webgl-fallback/nodes/GLSLNodeBuilder.js index e35c9dc8ae3ba1..fda792306f2533 100644 --- a/src/renderers/webgl-fallback/nodes/GLSLNodeBuilder.js +++ b/src/renderers/webgl-fallback/nodes/GLSLNodeBuilder.js @@ -134,6 +134,30 @@ precision highp sampler2DArrayShadow; precision highp samplerCubeShadow; `; +const glslReservedKeywords = new Set( [ + // keywords + 'const', 'uniform', 'buffer', 'shared', 'attribute', 'varying', 'coherent', 'volatile', 'restrict', + 'readonly', 'writeonly', 'atomic_uint', 'layout', 'centroid', 'flat', 'smooth', 'noperspective', + 'patch', 'sample', 'invariant', 'precise', 'break', 'continue', 'do', 'for', 'while', 'switch', + 'case', 'default', 'if', 'else', 'subroutine', 'in', 'out', 'inout', 'int', 'void', 'bool', 'true', + 'false', 'float', 'double', 'discard', 'return', 'vec2', 'vec3', 'vec4', 'ivec2', 'ivec3', 'ivec4', + 'bvec2', 'bvec3', 'bvec4', 'uint', 'uvec2', 'uvec3', 'uvec4', 'dvec2', 'dvec3', 'dvec4', 'mat2', + 'mat3', 'mat4', 'mat2x2', 'mat2x3', 'mat2x4', 'mat3x2', 'mat3x3', 'mat3x4', 'mat4x2', 'mat4x3', + 'mat4x4', 'dmat2', 'dmat3', 'dmat4', 'dmat2x2', 'dmat2x3', 'dmat2x4', 'dmat3x2', 'dmat3x3', + 'dmat3x4', 'dmat4x2', 'dmat4x3', 'dmat4x4', 'lowp', 'mediump', 'highp', 'precision', 'sampler2D', + 'sampler3D', 'samplerCube', 'sampler2DShadow', 'samplerCubeShadow', 'sampler2DArray', + 'sampler2DArrayShadow', 'isampler2D', 'isampler3D', 'isamplerCube', 'isampler2DArray', 'usampler2D', + 'usampler3D', 'usamplerCube', 'usampler2DArray', 'struct', + // reserved for future use + 'common', 'partition', 'active', 'asm', 'class', 'union', 'enum', 'typedef', 'template', 'this', + 'resource', 'goto', 'inline', 'noinline', 'public', 'static', 'extern', 'external', 'interface', + 'long', 'short', 'half', 'fixed', 'unsigned', 'superp', 'input', 'output', 'hvec2', 'hvec3', + 'hvec4', 'fvec2', 'fvec3', 'fvec4', 'sampler3DRect', 'filter', 'sizeof', 'cast', 'namespace', + 'using', + // generated entry points + 'main' +] ); + /** * A node builder targeting GLSL. * @@ -428,6 +452,18 @@ ${ flowData.code } } + /** + * Returns whether the given name is a reserved keyword of GLSL. + * + * @param {string} name - The name to test. + * @return {boolean} Whether the name is a reserved keyword or not. + */ + isReservedKeyword( name ) { + + return glslReservedKeywords.has( name ); + + } + /** * Setups the Pixel Buffer Object (PBO) for the given storage * buffer node. diff --git a/src/renderers/webgpu/nodes/WGSLNodeBuilder.js b/src/renderers/webgpu/nodes/WGSLNodeBuilder.js index be07536301cd47..2ee436fee8e160 100644 --- a/src/renderers/webgpu/nodes/WGSLNodeBuilder.js +++ b/src/renderers/webgpu/nodes/WGSLNodeBuilder.js @@ -240,6 +240,35 @@ const wgslMethods = { floatunpack_float16_2x16: 'unpack2x16float' }; +// See: https://www.w3.org/TR/WGSL/#keyword-summary and #reserved-words-section + +const wgslReservedKeywords = new Set( [ + // keywords + 'alias', 'break', 'case', 'const', 'const_assert', 'continue', 'continuing', 'default', 'diagnostic', + 'discard', 'else', 'enable', 'false', 'fn', 'for', 'if', 'let', 'loop', 'override', 'requires', + 'return', 'struct', 'switch', 'true', 'var', 'while', + // reserved words + 'NULL', 'Self', 'abstract', 'active', 'alignas', 'alignof', 'as', 'asm', 'asm_fragment', 'async', + 'attribute', 'auto', 'await', 'become', 'binding_array', 'cast', 'catch', 'class', 'co_await', + 'co_return', 'co_yield', 'coherent', 'column_major', 'common', 'compile', 'compile_fragment', + 'concept', 'const_cast', 'consteval', 'constexpr', 'constinit', 'crate', 'debugger', 'decltype', + 'delete', 'demote', 'demote_to_helper', 'do', 'dynamic_cast', 'enum', 'explicit', 'export', + 'extends', 'extern', 'external', 'fallthrough', 'filter', 'final', 'finally', 'friend', 'from', + 'fxgroup', 'get', 'goto', 'groupshared', 'highp', 'impl', 'implements', 'import', 'inline', + 'instanceof', 'interface', 'layout', 'lowp', 'macro', 'macro_rules', 'match', 'mediump', 'meta', + 'mod', 'module', 'move', 'mut', 'mutable', 'namespace', 'new', 'nil', 'noexcept', 'noinline', + 'nointerpolation', 'non_coherent', 'noncoherent', 'noperspective', 'null', 'nullptr', 'of', + 'operator', 'package', 'packoffset', 'partition', 'pass', 'patch', 'pixelfragment', 'precise', + 'precision', 'premerge', 'priv', 'protected', 'pub', 'public', 'readonly', 'ref', 'regardless', + 'register', 'reinterpret_cast', 'require', 'resource', 'restrict', 'self', 'set', 'shared', + 'sizeof', 'smooth', 'snorm', 'static', 'static_assert', 'static_cast', 'std', 'subroutine', + 'super', 'target', 'template', 'this', 'thread_local', 'throw', 'trait', 'try', 'type', 'typedef', + 'typeid', 'typename', 'typeof', 'union', 'unless', 'unorm', 'unsafe', 'unsized', 'use', 'using', + 'varying', 'virtual', 'volatile', 'wgsl', 'where', 'with', 'writeonly', 'yield', + // generated entry points + 'main' +] ); + // let diagnostics = ''; @@ -1148,6 +1177,18 @@ class WGSLNodeBuilder extends NodeBuilder { } + /** + * Returns whether the given name is a reserved keyword of WGSL. + * + * @param {string} name - The name to test. + * @return {boolean} Whether the name is a reserved keyword or not. + */ + isReservedKeyword( name ) { + + return wgslReservedKeywords.has( name ); + + } + /** * Returns the output struct name. *