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?
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);
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);