-
-
Notifications
You must be signed in to change notification settings - Fork 36.1k
Description
Description
Related to the discussion:
www.https://discourse.threejs.org/t/proposal-post-instancematrix-vertex-transformation-hook-for-instancedmesh-batchmesh/88362/7
In Three.js (TSL / WebGPU) vertex-level transforms like wind/displacement/animations cannot be applied after the instanceMatrix. positionNode and vertexNode run before instanceMatrix, which prevents consistent transformed-space effects across instances.
- instanceMatrix is applied in InstanceNode/BatchNode, but instanceMatrixNode is not exposed.
- positionNode/vertexNode hooks run before instanceMatrix.
- As a consequence, post-instance transforms are awkward/impossible to write reliably.
Otherwise, it would be very practical to also expose instanceMatrixNode in BatchedMesh, with the same color varyingProperty, to facilitate material compatibility between InstancedMesh and BatchedMesh, especially when batching InstanceMesh.
Solution
- Expose builder.instanceMatrixNode in InstanceNode and BatchNode so materials can read it.
- Add optional material hook like instancePositionNode
I implemented in my project a local patch that:
- sets builder.instanceMatrixNode in InstanceNode/BatchNode setup
- calls material.instancePositionNode when defined
Tested ok in my project with lighting, shadows, reflection, refraction, fog, and postProcessing (v180).
Not tested with WEBGL / WEBGL2 fallback yet.
In the THREE.InstanceNode.prototype.setup function, after
this.instanceMatrixNode = instanceMatrixNode
add :
builder.instanceMatrixNode = instanceMatrixNode
At the end of THREE.InstanceNode.prototype.setup and THREE.BatchNode.prototype.setup, add:
if ( builder.material.instancePositionNode ){
positionLocal.assign( builder.material.instancePositionNode )
}
In the THREE.BatchNode.prototype.setup, add:
this.instanceMatrixNode = batchingMatrix
builder.instanceMatrixNode = batchingMatrix
varyingProperty ( 'vec3', 'vInstanceColor' ).assign( color ) // or rename vBatchColor as vInstanceColor ?
Why this helps
Enables proper world-space vertex effects after instance transform
Avoids hacks (double matrix multiply or CPU shadow proxies)
Keeps shadow/light consistency
Example of use:
myMaterial.instancePositionNode = /*#__PURE__*/ Fn( ( builder ) => {
const { object, instanceMatrixNode }=builder
// get the local position transformed by instanceMatrixNode
const pos=positionLocal.toVar()
// some deal with the position in instance space
pos.yz.addAssign( myWindNode.yz )
// some deal with the normal in instance space
normalLocal.assign( normalLocal.mul( myPerturbNormalNode ).normalize() )
// some deal with the color after the instance color has been applyed
varyingProperty( 'vec3', 'vInstanceColor' ).mulAssign( myCustomColor )
// some deal with uv and the instance matrix with stretched scale correction according to instance space
const getInstanceScale = vec3( length( instanceMatrixNode[0].xyz ), length( instanceMatrixNode[1].xyz ), length( instanceMatrixNode[2].xyz ) )
myCustomUV.assign( makeTriplanarUV( positionGeometry, normalGeometry, getInstanceScale ) )
// return the positionLocal
return pos
})()
Request
Would the core team consider adding official instancePositionNode hook and exposing instanceMatrixNode to NodeMaterial pipelines?
Thank you — happy to provide more tests or a PR if the team is interested.
Alternatives
Right now the only alternative is to override InstanceNode.prototype.setup() and BatchNode.prototype.setup() in my local project. This lets me expose instanceMatrixNode and apply post-instance transformations, but it’s fragile and incompatible with upstream changes. It also forces maintaining a custom patched version of THREE.JS.
Additional context
No response