Yes, you’re right. And we also need to think about WebGL backward compatibility, which was never designed for that.
It’s great to see that there are such dedicated people willing to carefully answer questions from newcomers like me.
I’ll see soon if I can play around with GitHub and suggest a clearer improvement.
Thank you for your clear answers.
In the meantime, for those who might be interested, here’s a less disruptive temporary hack that works according to my latest tests, with lighting, shadows, reflection, refraction, fog, and postProcessing.
Custom InstanceNode, allow to get instanceMatrixNode and after-instance control:
THREE.InstanceNode.prototype.setup=function(builder){
const {instanceMatrix,instanceColor}=this
let {instanceMatrixNode,instanceColorNode}=this
const {count}=instanceMatrix
if(instanceMatrixNode===null){
if(count<=1000){instanceMatrixNode=buffer(instanceMatrix.array,'mat4',Math.max(count,1)).element(instanceIndex)}
else{
const buffer=new THREE.InstancedInterleavedBuffer(instanceMatrix.array,16,1)
this.buffer=buffer
const bufferFn=instanceMatrix.usage===THREE.DynamicDrawUsage?instancedDynamicBufferAttribute:instancedBufferAttribute,
instanceBuffers=[bufferFn(buffer,'vec4',16,0),bufferFn(buffer,'vec4',16,4),bufferFn(buffer,'vec4',16,8),bufferFn(buffer,'vec4',16,12)]
instanceMatrixNode=mat4(...instanceBuffers)}
this.instanceMatrixNode=instanceMatrixNode}
// expose instanceMatrixNode in the builder
builder.instanceMatrixNode=instanceMatrixNode
if(instanceColor&&instanceColorNode===null){
const buffer=new THREE.InstancedBufferAttribute(instanceColor.array,3),
bufferFn=instanceColor.usage===THREE.DynamicDrawUsage?instancedDynamicBufferAttribute:instancedBufferAttribute
this.bufferColor=buffer
instanceColorNode=vec3(bufferFn(buffer,'vec3',3,0))
this.instanceColorNode=instanceColorNode}
const instancePosition=instanceMatrixNode.mul(positionLocal).xyz
positionLocal.assign(instancePosition)
if(builder.hasGeometryAttribute('normal')){
const instanceNormal=transformNormal(normalLocal,instanceMatrixNode)
normalLocal.assign(instanceNormal)
if(this.instanceColorNode!==null){varyingProperty('vec3','vInstanceColor').assign(this.instanceColorNode)}
// add a new stuff after instance work
if(builder.material.instancePositionNode){
positionLocal.assign(builder.material.instancePositionNode)
}
}
Custom BatchNode
THREE.BatchNode.prototype.setup=function(builder){
if(this.batchingIdNode===null){
if(builder.getDrawIndex()===null){this.batchingIdNode=instanceIndex}
else{this.batchingIdNode=drawIndex}}
const getIndirectIndex=Fn(([id])=>{
const size=int(textureSize(textureLoad(this.batchMesh._indirectTexture),0).x),
x=int(id).mod(size),
y=int(id).div(size)
return textureLoad(this.batchMesh._indirectTexture,ivec2(x,y)).x}).setLayout({name:'getIndirectIndex',type:'uint',inputs:[{name:'id',type:'int'}]})
const indirectId=getIndirectIndex(int(this.batchingIdNode)),
matricesTexture=this.batchMesh._matricesTexture,
size=int(textureSize(textureLoad(matricesTexture),0).x),
j=float(indirectId).mul(4).toInt().toVar(),
x=j.mod(size),
y=j.div(size),
batchingMatrix=mat4(
textureLoad(matricesTexture,ivec2(x,y)),
textureLoad(matricesTexture,ivec2(x.add(1),y)),
textureLoad(matricesTexture,ivec2(x.add(2),y)),
textureLoad(matricesTexture,ivec2(x.add(3),y))),
colorsTexture=this.batchMesh._colorsTexture
// expose batchingMatrix as instanceMatrixNode for better compatibility
this.instanceMatrixNode=batchingMatrix
builder.instanceMatrixNode=batchingMatrix
if(colorsTexture!==null){
const getBatchingColor=Fn(([id])=>{
const size=int(textureSize(textureLoad(colorsTexture),0).x),
j=id,
x=j.mod(size),
y=j.div(size)
return textureLoad(colorsTexture,ivec2(x,y)).rgb}).setLayout({name:'getBatchingColor',type:'vec3',inputs:[{ name:'id',type:'int'}]})
const color=getBatchingColor(indirectId)
varyingProperty('vec3','vBatchColor').assign(color)
// add vInstanceColor for the InstancedMesh / BatchedMesh compatibility
varyingProperty('vec3','vInstanceColor').assign(color) // < added
}
const bm=mat3(batchingMatrix)
positionLocal.assign(batchingMatrix.mul(positionLocal))
const transformedNormal=normalLocal.div(vec3(bm[0].dot(bm[0]),bm[1].dot(bm[1]),bm[2].dot(bm[2]))),
batchingNormal=bm.mul(transformedNormal).xyz
normalLocal.assign(batchingNormal)
if(builder.hasGeometryAttribute('tangent')){tangentLocal.mulAssign(bm)}
// add a new stuff after batching work
if(builder.material.instancePositionNode){
positionLocal.assign(builder.material.instancePositionNode)
}
}
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
if(object.isInstancedMesh){
varyingProperty('vec3','vInstanceColor').mulAssign(myCustomColor)
}
else if(object.isBatchedMesh){
varyingProperty('vec3','vBatchColor').mulAssign(myCustomColor)
}
// some deal with uv and the instance matrix
const getInstanceScale=vec3(length(instanceMatrixNode[0].xyz),length(instanceMatrixNode[1].xyz),length(instanceMatrixNode[2].xyz))
myCustomUV.assign(makeTriplanarUV(positionGeometry,normalGeometry,getInstanceScale))
// return the transformed positionLocal
return pos
})()
Hope this helps