InstancedBufferGeometry custom attributes not implemented?
I'm converting a three.js InstanceBufferGeometry vertex and fragment shader to run with this great library, but I'm not seeing how to add custom attributes. It's a grid of tiles that each need to have a unique color that will change over time.
The IndexedAttributeName struct has two entries (morphTarget, morphNormal) and this is the first parameter of BufferGeometry->addAttribute, whereas three.js accepts a string name (and appears to pass this through to the vertex shader).
Is it possible to add custom attribute names that are passed through to the vertex shader? Is there another way that I should be approaching this instead?
Here's both the original working javascript code:
var tileSize = 0.05;
var tileCols = 40;
var tileRows = 50;
var tileCount = cols * rows;
// Create geometry
var tempPlane = new THREE.PlaneBufferGeometry(tileSize, tileSize, 1, 1);
var bGeometry = new THREE.InstancedBufferGeometry();
bGeometry.index = tempPlane.index;
bGeometry.attributes.position = tempPlane.attributes.position;
bGeometry.attributes.uv = tempPlane.attributes.uv;
var quaternion = new THREE.Quaternion()
quaternion.setFromAxisAngle(new THREE.Vector3(1,0,0), degToRad(90));
// Fill data
var position = [];
var orientation = [];
var colors = [];
for (var i = 0; i < tileCount; i++) {
position.push(
((i % tileCols) * tileSize) + tileSize/2,
0,
(Math.floor(i / tileCols) * tileSize) + tileSize/2
);
orientation.push(quaternion.x, quaternion.y, quaternion.z, quaternion.w);
colors.push(0.75, 0.75, 0.75, 1);
}
// Create attributes
var offsetAttribute = new THREE.InstancedBufferAttribute( new Float32Array(position), 3);
var orientationAttribute = new THREE.InstancedBufferAttribute( new Float32Array(orientation), 4);
var colorAttribute = new THREE.InstancedBufferAttribute(new Float32Array(colors), 4);
// Add attributes
bGeometry.addAttribute("offset", offsetAttribute);
bGeometry.addAttribute("orientation", orientationAttribute);
bGeometry.addAttribute("color", colorAttribute);
var material = new THREE.RawShaderMaterial({
side: THREE.DoubleSide,
transparent: true,
opacity: 1,
vertexShader: [
"precision highp float;",
"uniform mat4 modelViewMatrix;",
"uniform mat4 projectionMatrix;",
"attribute vec3 position;",
"attribute vec2 uv;",
"attribute vec3 offset;",
"attribute vec4 orientation;",
"attribute vec4 color;",
"varying vec4 vColor;",
"vec3 applyQuaternionToVector( vec4 q, vec3 v ){",
"return v + 2.0 * cross( q.xyz, cross( q.xyz, v ) + q.w * v );",
"}",
"void main() {",
"vec3 vPosition = applyQuaternionToVector(orientation, position);",
"vColor = color;",
"gl_Position = projectionMatrix * modelViewMatrix * vec4( offset + vPosition, 1.0 );",
"}"
].join("\n"),
fragmentShader: [
"precision highp float;",
"uniform sampler2D map;",
"varying vec2 vUv;",
"varying vec4 vColor;",
"void main() {",
"gl_FragColor = vColor;",
"}",
].join("\n")
});
var bMesh = new THREE.Mesh(bGeometry, material);
bMesh.position.set(0, 0, 0);
bMesh.frustumCulled = false;
scene.add(bMesh);
And the in-progress C++ code:
float tileSize = 0.05;
int tileCols = 40;
int tileRows = 50;
int tileCount = cols * rows;
// Create geometry
geometry::buffer::Plane::Ptr tempPlane = geometry::buffer::Plane::make(tileSize, tileSize, 1, 1);
InstancedBufferGeometry::Ptr bGeometry = InstancedBufferGeometry::make();
bGeometry->setIndex(tempPlane->index());
bGeometry->setPosition(tempPlane->position());
bGeometry->setUV(tempPlane->uv());
math::Quaternion quaternion = math::Quaternion::fromAxisAngle(math::Vector3(1,0,0), degToRad(90));
// Create attributes
BufferAttributeT<float>::Ptr offsetAttribute = attribute::prealloc<float, Vertex>(tileCount, true);
BufferAttributeT<float>::Ptr orientationAttribute = attribute::prealloc<float, math::Vector4>(tileCount, true);
BufferAttributeT<float>::Ptr colorAttribute = attribute::prealloc<float, Color>(tileCount, true);
// Fill data
for (size_t i = 0; i < tileCount; i++) {
offsetAttribute->setXYZ(
i, ((i % tileCols) * tileSize) + tileSize/2,
0, (floor(i / tileCols) * tileSize) + tileSize/2
);
orientationAttribute->setXYZW(i, quaternion.x(), quaternion.y(), quaternion.z(), quaternion.w());
colorAttribute->setXYZW(i, 1.0, 0.0, 0.0, 1.0);
}
// TODO Can we add attributes with custom names?
bGeometry->addAttribute(/* Custom name: offset */, tempPlane->index(), offsetAttribute);
bGeometry->addAttribute(/* Custom name: orientation */, tempPlane->index(), orientationAttribute);
bGeometry->addAttribute(/* Custom name: color */, tempPlane->index(), colorAttribute);
std::stringstream vertexShader;
vertexShader << "in vec3 offset;" << std::endl;
vertexShader << "in vec4 orientation;" << std::endl;
vertexShader << "in vec4 color;" << std::endl;
vertexShader << "out vec4 vColor;" << std::endl;
vertexShader << "void main() {" << std::endl;
vertexShader << " vec3 vPosition = applyQuaternionToVector(orientation, position);" << std::endl;
vertexShader << " vColor = color;" << std::endl;
vertexShader << " gl_Position = projectionMatrix * modelViewMatrix * vec4( offset + vPosition, 1.0 );" << std::endl;
vertexShader << "}" << std::endl;
std::stringstream fragmentShader;
fragmentShader << "in vec4 vColor;" << std::endl;
fragmentShader << "out vec4 fragColor;" << std::endl;
fragmentShader << "void main() {" << std::endl;
fragmentShader << " fragColor = vColor;" << std::endl;
fragmentShader << "}" << std::endl;
ShaderMaterial::Ptr material = ShaderMaterial::make(
gl::UniformValues(),
vertexShader.str().c_str(),
fragmentShader.str().c_str(),
Side::Double
);
DynamicMesh::Ptr bMesh = DynamicMesh::make(bGeometry, material);
bMesh->position().set(0, 0, 0.5);
bMesh->frustumCulled = false;
scene->add(bMesh);
hello david,
there is a facility for setting custom uniforms, but not for attributes at this time. Moreover, I don't recall having used InstancedBufferGeometry ever before (if it's not in the examples, I haven't) so you may be in for surprises there, too.
Christian
BTW, since I'm at it: if someone can tell me why pointlight shadows aren't working - here's another opportunity to gain the ThreePP Special Recognition Award
Am Do., 4. Apr. 2019 um 07:33 Uhr schrieb davidcann < [email protected]>:
I'm converting a three.js InstanceBufferGeometry vertex and fragment shader to run with this great library, but I'm not seeing how to add custom attributes. It's a grid of tiles that each need to have a unique color that will change over time.
The IndexedAttributeName struct has two entries (morphTarget, morphNormal) and this is the first parameter of BufferGeometry->addAttribute, whereas three.js accepts a string name (and appears to pass this through to the vertex shader).
Is it possible to add custom attribute names that are passed through to the vertex shader? Is there another way that I should be approaching this instead?
Here's both the original working javascript code:
var tileSize = 0.05; var tileCols = 40; var tileRows = 50; var tileCount = cols * rows;
// Create geometry var tempPlane = new THREE.PlaneBufferGeometry(tileSize, tileSize, 1, 1); var bGeometry = new THREE.InstancedBufferGeometry(); bGeometry.index = tempPlane.index; bGeometry.attributes.position = tempPlane.attributes.position; bGeometry.attributes.uv = tempPlane.attributes.uv;
var quaternion = new THREE.Quaternion() quaternion.setFromAxisAngle(new THREE.Vector3(1,0,0), degToRad(90));
// Fill data var position = []; var orientation = []; var colors = []; for (var i = 0; i < tileCount; i++) { position.push( ((i % tileCols) * tileSize) + tileSize/2, 0, (Math.floor(i / tileCols) * tileSize) + tileSize/2 ); orientation.push(quaternion.x, quaternion.y, quaternion.z, quaternion.w); colors.push(0.75, 0.75, 0.75, 1); }
// Create attributes var offsetAttribute = new THREE.InstancedBufferAttribute( new Float32Array(position), 3); var orientationAttribute = new THREE.InstancedBufferAttribute( new Float32Array(orientation), 4); var colorAttribute = new THREE.InstancedBufferAttribute(new Float32Array(colors), 4);
// Add attributes bGeometry.addAttribute("offset", offsetAttribute); bGeometry.addAttribute("orientation", orientationAttribute); bGeometry.addAttribute("color", colorAttribute);
var material = new THREE.RawShaderMaterial({ side: THREE.DoubleSide, transparent: true, opacity: 1, vertexShader: [ "precision highp float;", "uniform mat4 modelViewMatrix;", "uniform mat4 projectionMatrix;",
"attribute vec3 position;", "attribute vec2 uv;", "attribute vec3 offset;", "attribute vec4 orientation;", "attribute vec4 color;", "varying vec4 vColor;", "vec3 applyQuaternionToVector( vec4 q, vec3 v ){", "return v + 2.0 * cross( q.xyz, cross( q.xyz, v ) + q.w * v );", "}", "void main() {", "vec3 vPosition = applyQuaternionToVector(orientation, position);", "vColor = color;", "gl_Position = projectionMatrix * modelViewMatrix * vec4( offset + vPosition, 1.0 );", "}" ].join("\n"), fragmentShader: [ "precision highp float;", "uniform sampler2D map;", "varying vec2 vUv;", "varying vec4 vColor;", "void main() {", "gl_FragColor = vColor;", "}", ].join("\n")});
var bMesh = new THREE.Mesh(bGeometry, material); bMesh.position.set(0, 0, 0); bMesh.frustumCulled = false; scene.add(bMesh);
And the in-progress C++ code:
float tileSize = 0.05; int tileCols = 40; int tileRows = 50; int tileCount = cols * rows;
// Create geometry geometry::buffer::Plane::Ptr tempPlane = geometry::buffer::Plane::make(tileSize, tileSize, 1, 1); InstancedBufferGeometry::Ptr bGeometry = InstancedBufferGeometry::make(); bGeometry->setIndex(tempPlane->index()); bGeometry->setPosition(tempPlane->position()); bGeometry->setUV(tempPlane->uv());
math::Quaternion quaternion = math::Quaternion::fromAxisAngle(math::Vector3(1,0,0), degToRad(90));
// Create attributes BufferAttributeT
::Ptr offsetAttribute = attribute::prealloc<float, Vertex>(tileCount, true); BufferAttributeT ::Ptr orientationAttribute = attribute::prealloc<float, math::Vector4>(tileCount, true); BufferAttributeT ::Ptr colorAttribute = attribute::prealloc<float, Color>(tileCount, true); // Fill data for (size_t i = 0; i < tileCount; i++) { offsetAttribute->setXYZ( i, ((i % tileCols) * tileSize) + tileSize/2, 0, (floor(i / tileCols) * tileSize) + tileSize/2 ); orientationAttribute->setXYZW(i, quaternion.x(), quaternion.y(), quaternion.z(), quaternion.w()); colorAttribute->setXYZW(i, 1.0, 0.0, 0.0, 1.0); }
// TODO Can we add attributes with custom names? bGeometry->addAttribute(/* Custom name: offset /, tempPlane->index(), offsetAttribute); bGeometry->addAttribute(/ Custom name: orientation /, tempPlane->index(), orientationAttribute); bGeometry->addAttribute(/ Custom name: color */, tempPlane->index(), colorAttribute);
std::stringstream vertexShader; vertexShader << "in vec3 offset;" << std::endl; vertexShader << "in vec4 orientation;" << std::endl; vertexShader << "in vec4 color;" << std::endl; vertexShader << "out vec4 vColor;" << std::endl; vertexShader << "void main() {" << std::endl; vertexShader << " vec3 vPosition = applyQuaternionToVector(orientation, position);" << std::endl; vertexShader << " vColor = color;" << std::endl; vertexShader << " gl_Position = projectionMatrix * modelViewMatrix * vec4( offset + vPosition, 1.0 );" << std::endl; vertexShader << "}" << std::endl;
std::stringstream fragmentShader; fragmentShader << "in vec4 vColor;" << std::endl; fragmentShader << "out vec4 fragColor;" << std::endl; fragmentShader << "void main() {" << std::endl; fragmentShader << " fragColor = vColor;" << std::endl; fragmentShader << "}" << std::endl;
ShaderMaterial::Ptr material = ShaderMaterial::make( gl::UniformValues(), vertexShader.str().c_str(), fragmentShader.str().c_str(), Side::Double );
DynamicMesh::Ptr bMesh = DynamicMesh::make(bGeometry, material); bMesh->position().set(0, 0, 0.5); bMesh->frustumCulled = false; scene->add(bMesh);
— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/bergstr11/three.cpp/issues/3, or mute the thread https://github.com/notifications/unsubscribe-auth/ABkRptHexysIQLbElwz4vWKbwSVX8_Acks5vdY6ZgaJpZM4cb9sV .
Hi Christian, thanks for the quick reply. I'm investigating this further now – I may not even need to use InstancedBufferGeometry for this use case, although it would be great to have that for the future. The library is fantastic overall.
Hello David, thanks for the kind words. I have looked into the attributes issue, and have found that at least the core infrastructure is in place. Look at Renderer_impl::setupVertexAttributes. In line 775 you can see a call to geometry->getAttribute(name), and if you look into that method, you see a switch over the predefined atributes, which ends in a default case which returns a nullptr. THAT is where you would have to return your custom attribute, but alas, AttributeName is an enum! So maybe that has to be changed..
Thats all I can come up with for now, tough week, need some rest. regards