Currently only two uv layers are supported by UVNode(). Using an index other than 0 (uv) or 1 (uv2) will prevent the fragment shader from compiling and will throw an error in the console (undefined value returned by NodeBuilder).
This limitation is acceptable for the standard material system but it would really make sense if the node system could handle any number of layers.
/ping @sunag
Yeah, I will include this in this weekend 馃憤
@sunag I was wondering if you could give me a few tips to make it work as I am at the point in my project where I need the feature.
I started by modifying the generate function from UVNode to get ride of vertexDict and fragmentDict:
UVNode.prototype.generate = function ( builder, output ) {
builder.requires.uv[ this.index ] = true;
var uvIndex = this.index > 0 ? this.index + 1 : '';
var result = builder.isShader( 'vertex' ) ? 'uv' + uvIndex : 'vUv' + uvIndex;
return builder.format( result, this.getType( builder ), output );
};
and then in build function of NodeBuilder I replaced the two if this.requires.uv statements by:
for ( var i = 0; i < this.requires.uv.length; i++ ) {
if ( this.requires.uv[ i ] ) {
var uvIndex = i > 0 ? i + 1 : '';
this.addVaryCode( 'varying vec2 vUv' + uvIndex + ';' );
if ( i > 0 ) {
this.addVertexParsCode( 'attribute vec2 uv' + uvIndex + ';' );
}
this.addVertexFinalCode( 'vUv' + uvIndex + ' = uv' + uvIndex +';' );
}
}
Now it doesn't return any errors but it doesn't work either. Do you know what other part of the code I shall update. If I am successful, I will create a PR for it.
Thanks.
You checked the shader output?
For example: https://github.com/mrdoob/three.js/blob/72ac6e0ca533571f233b612727e5c20a865fb735/examples/jsm/nodes/core/NodeBuilder.js#L508
It should be possible too using VarNode, AttributeNode and BypassNode
https://github.com/mrdoob/three.js/blob/e622cc7890e86663011d12ec405847baa4068515/examples/webgl_materials_nodes.html#L2326
https://github.com/mrdoob/three.js/blob/e622cc7890e86663011d12ec405847baa4068515/examples/webgl_materials_nodes.html#L2166
https://github.com/mrdoob/three.js/blob/e622cc7890e86663011d12ec405847baa4068515/examples/webgl_materials_nodes.html#L2333
I printed this.vertexShader and this.fragmentShader from NodeMaterial build function to check the output. For example if I use UVNode(2) instead of UVNode(1) with my code above, uv2 and vUv2 are accurately replaced by uv3 and vUv3 in both vertex and fragment shaders but for index 2, the texture is not applied (all uv coordinates defaulted to 0,0)
Ok, I have it figured out. The code I wrote above works fine. The issue is with BufferGeometry.fromGeometry() which only converts a maximum of 2 uv layers.
For multiple reasons, I still use Geometry and the automatic conversion process loses all layers other than the first two. To avoid modifying the core version of three.js I manually create geometry._bufferGeometry at load time using a custom function:
function geometryToBufferGeometry(geometry) {
let bufferGeometry = new THREE.BufferGeometry();
bufferGeometry.fromGeometry(geometry);
// Look for extra uv layers
let uvs = {};
let faceVertexUvs = geometry.faceVertexUvs;
for (let i = 2; i < faceVertexUvs.length; i++) {
if (faceVertexUvs[i] && faceVertexUvs[i].length > 0) uvs[i] = [];
}
// Build uv arrays
let faces = geometry.faces;
for (let i = 0; i < faces.length; i++) {
for (let uvLayer in uvs) {
let vertexUvs = faceVertexUvs[uvLayer][i];
if (vertexUvs !== undefined) {
uvs[uvLayer].push(vertexUvs[0], vertexUvs[1], vertexUvs[2]);
} else {
console.warn('Undefined vertexUv' + uvLayer + ' ', i);
uvs[uvLayer].push(new THREE.Vector2(), new THREE.Vector2(), new THREE.Vector2());
}
}
}
// Convert to BufferAttribute
for (let uvLayer in uvs) {
let attribute = new Float32Array(uvs[uvLayer].length * 2);
bufferGeometry.addAttribute('uv' + (parseInt(uvLayer) + 1) , new THREE.BufferAttribute(attribute, 2).copyVector2sArray(uvs[uvLayer]));
}
return bufferGeometry;
}
It would make much more sense to update BufferGeometry.fromDirectGeometry() and DirectGeometry.fromGeometry() directly in the core. I cannot see a reason why only two layers and not all shall be converted.
If you think that this is a good idea, I can create a PR to modify the following files in order to support any number of uv layers:
Most helpful comment
Yeah, I will include this in this weekend 馃憤