I've been using BufferGeometry for a while, and I thought I was pretty familiar with it. Now, I'm trying to create a simple square plane, and it's not doing anything--no visible plane, no errors, and no obvious problems as far as I can see. I've seen other posts on here that are similar, but none of the solutions have been successful.
When I inspect the scene, the mesh is there, it has a proper material, and its geometry seems to be set up correctly. But all I get is a black viewport. I have to be missing a painfully obvious/simple step, but right now it's evading me. What am I doing wrong?
Fiddle + code: http://jsfiddle.net/TheJim01/kafybhge/34/
// BufferGeometry Tester
var hostDiv, scene, renderer, camera, root, controls, light;
var WIDTH = 500;//window.innerWidth,
HEIGHT = 500;//window.innerHeight,
FOV = 35,
NEAR = 1,
FAR = 1000;
function createBufferGeometryMesh(){
var geo = new THREE.BufferGeometry();
var vertices =
[
-10., 10., 0., // 0 - top left
10., 10., 0., // 1 - top right
10., -10., 0., // 2 - bottom right
-10., -10., 0. // 3 - bottom left
],
normals =
[
0., 0., 1.,
0., 0., 1.,
0., 0., 1.,
0., 0., 1.
],
indices = [ 0, 1, 2, 0, 2, 3 ];
geo.addAttribute( 'position', new THREE.BufferAttribute( new Float32Array( vertices ), 3 ) );
geo.addAttribute( 'normal', new THREE.BufferAttribute( new Float32Array( normals ), 3 ) );
geo.addAttribute( 'index', new THREE.BufferAttribute( new Uint32Array( indices ), 1 ) );
var mat = new THREE.MeshPhongMaterial( {
color: 0xffffff,
ambient: 0xffffff,
specular: 0xffffff,
shininess: 50,
side: THREE.DoubleSide
} );
var msh = new THREE.Mesh(geo, mat);
return msh;
}
function init() {
hostDiv = document.createElement('div');
document.body.appendChild(hostDiv);
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(WIDTH, HEIGHT);
hostDiv.appendChild(renderer.domElement);
camera = new THREE.PerspectiveCamera(FOV, WIDTH / HEIGHT, NEAR, FAR);
camera.position.z = 50;
controls = new THREE.TrackballControls(camera, renderer.domElement);
light = new THREE.PointLight(0xffffff, 1, 1000);
light.position.copy(camera.position);
scene = new THREE.Scene();
scene.add(camera);
scene.add(light);
var square = createBufferGeometryMesh();
scene.add(square);
animate();
}
function render() {
renderer.render(scene, camera);
}
function animate() {
light.position.copy(camera.position);
requestAnimationFrame(animate);
render();
controls.update();
}
init();
Just to show I'm not new to BufferGeometry, here's something I've done previously, which works if I plug it into my code in place of the createBufferGeometryMesh()
above. I've tried defining the buffers like below (and even explicitly), but it didn't change anything.
function colorCube(scale){
scale = scale || 1;
var geo = new THREE.BufferGeometry();
var positions = new Float32Array( 72 );
var normals = new Float32Array( 72 );
var colors = new Float32Array( 72 );
var indices = new Uint16Array( 36 );
var face = 0, idx = 0, vert = 0;
var x = 0, r = 0, y = 1, g = 1, z = 2, b = 2;
// front face (RED)
positions[vert + x] = 0.5; positions[vert + y] = 0.5; positions[vert + z] = 0.5;
normals[vert + x] = 0.; normals[vert + y] = 0.; normals[vert + z] = 1.;
colors[vert + r] = 1.; colors[vert + g] = 0.; colors[vert + b] = 0.;
vert += 3;
positions[vert + x] = -0.5; positions[vert + y] = 0.5; positions[vert + z] = 0.5;
normals[vert + x] = 0.; normals[vert + y] = 0.; normals[vert + z] = 1.;
colors[vert + r] = 1.; colors[vert + g] = 0.; colors[vert + b] = 0.;
vert += 3;
positions[vert + x] = -0.5; positions[vert + y] = -0.5; positions[vert + z] = 0.5;
normals[vert + x] = 0.; normals[vert + y] = 0.; normals[vert + z] = 1.;
colors[vert + r] = 1.; colors[vert + g] = 0.; colors[vert + b] = 0.;
vert += 3;
positions[vert + x] = 0.5; positions[vert + y] = -0.5; positions[vert + z] = 0.5;
normals[vert + x] = 0.; normals[vert + y] = 0.; normals[vert + z] = 1.;
colors[vert + r] = 1.; colors[vert + g] = 0.; colors[vert + b] = 0.;
vert += 3;
indices[idx + 0] = (face * 4) + 0; indices[idx + 1] = (face * 4) + 1; indices[idx + 2] = (face * 4) + 2;
indices[idx + 3] = (face * 4) + 0; indices[idx + 4] = (face * 4) + 2; indices[idx + 5] = (face * 4) + 3;
idx += 6;
++face;
// back face (BLUE)
positions[vert + x] = -0.5; positions[vert + y] = 0.5; positions[vert + z] = -0.5;
normals[vert + x] = 0.; normals[vert + y] = 0.; normals[vert + z] = -1.;
colors[vert + r] = 0.; colors[vert + g] = 0.; colors[vert + b] = 1.;
vert += 3;
positions[vert + x] = 0.5; positions[vert + y] = 0.5; positions[vert + z] = -0.5;
normals[vert + x] = 0.; normals[vert + y] = 0.; normals[vert + z] = -1.;
colors[vert + r] = 0.; colors[vert + g] = 0.; colors[vert + b] = 1.;
vert += 3;
positions[vert + x] = 0.5; positions[vert + y] = -0.5; positions[vert + z] = -0.5;
normals[vert + x] = 0.; normals[vert + y] = 0.; normals[vert + z] = -1.;
colors[vert + r] = 0.; colors[vert + g] = 0.; colors[vert + b] = 1.;
vert += 3;
positions[vert + x] = -0.5; positions[vert + y] = -0.5; positions[vert + z] = -0.5;
normals[vert + x] = 0.; normals[vert + y] = 0.; normals[vert + z] = -1.;
colors[vert + r] = 0.; colors[vert + g] = 0.; colors[vert + b] = 1.;
vert += 3;
indices[idx + 0] = (face * 4) + 0; indices[idx + 1] = (face * 4) + 1; indices[idx + 2] = (face * 4) + 2;
indices[idx + 3] = (face * 4) + 0; indices[idx + 4] = (face * 4) + 2; indices[idx + 5] = (face * 4) + 3;
idx += 6;
++face;
// right face (GREEN)
positions[vert + x] = 0.5; positions[vert + y] = 0.5; positions[vert + z] = -0.5;
normals[vert + x] = 1.; normals[vert + y] = 0.; normals[vert + z] = 0.;
colors[vert + r] = 0.; colors[vert + g] = 1.; colors[vert + b] = 0.;
vert += 3;
positions[vert + x] = 0.5; positions[vert + y] = 0.5; positions[vert + z] = 0.5;
normals[vert + x] = 1.; normals[vert + y] = 0.; normals[vert + z] = 0.;
colors[vert + r] = 0.; colors[vert + g] = 1.; colors[vert + b] = 0.;
vert += 3;
positions[vert + x] = 0.5; positions[vert + y] = -0.5; positions[vert + z] = 0.5;
normals[vert + x] = 1.; normals[vert + y] = 0.; normals[vert + z] = 0.;
colors[vert + r] = 0.; colors[vert + g] = 1.; colors[vert + b] = 0.;
vert += 3;
positions[vert + x] = 0.5; positions[vert + y] = -0.5; positions[vert + z] = -0.5;
normals[vert + x] = 1.; normals[vert + y] = 0.; normals[vert + z] = 0.;
colors[vert + r] = 0.; colors[vert + g] = 1.; colors[vert + b] = 0.;
vert += 3;
indices[idx + 0] = (face * 4) + 0; indices[idx + 1] = (face * 4) + 1; indices[idx + 2] = (face * 4) + 2;
indices[idx + 3] = (face * 4) + 0; indices[idx + 4] = (face * 4) + 2; indices[idx + 5] = (face * 4) + 3;
idx += 6;
++face;
// left face (MAGENTA)
positions[vert + x] = -0.5; positions[vert + y] = 0.5; positions[vert + z] = 0.5;
normals[vert + x] = -1.; normals[vert + y] = 0.; normals[vert + z] = 0.;
colors[vert + r] = 1.; colors[vert + g] = 0.; colors[vert + b] = 1.;
vert += 3;
positions[vert + x] = -0.5; positions[vert + y] = 0.5; positions[vert + z] = -0.5;
normals[vert + x] = -1.; normals[vert + y] = 0.; normals[vert + z] = 0.;
colors[vert + r] = 1.; colors[vert + g] = 0.; colors[vert + b] = 1.;
vert += 3;
positions[vert + x] = -0.5; positions[vert + y] = -0.5; positions[vert + z] = -0.5;
normals[vert + x] = -1.; normals[vert + y] = 0.; normals[vert + z] = 0.;
colors[vert + r] = 1.; colors[vert + g] = 0.; colors[vert + b] = 1.;
vert += 3;
positions[vert + x] = -0.5; positions[vert + y] = -0.5; positions[vert + z] = 0.5;
normals[vert + x] = -1.; normals[vert + y] = 0.; normals[vert + z] = 0.;
colors[vert + r] = 1.; colors[vert + g] = 0.; colors[vert + b] = 1.;
vert += 3;
indices[idx + 0] = (face * 4) + 0; indices[idx + 1] = (face * 4) + 1; indices[idx + 2] = (face * 4) + 2;
indices[idx + 3] = (face * 4) + 0; indices[idx + 4] = (face * 4) + 2; indices[idx + 5] = (face * 4) + 3;
idx += 6;
++face;
// top face (CYAN)
positions[vert + x] = 0.5; positions[vert + y] = 0.5; positions[vert + z] = -0.5;
normals[vert + x] = 0.; normals[vert + y] = 1.; normals[vert + z] = 0.;
colors[vert + r] = 0.; colors[vert + g] = 1.; colors[vert + b] = 1.;
vert += 3;
positions[vert + x] = -0.5; positions[vert + y] = 0.5; positions[vert + z] = -0.5;
normals[vert + x] = 0.; normals[vert + y] = 1.; normals[vert + z] = 0.;
colors[vert + r] = 0.; colors[vert + g] = 1.; colors[vert + b] = 1.;
vert += 3;
positions[vert + x] = -0.5; positions[vert + y] = 0.5; positions[vert + z] = 0.5;
normals[vert + x] = 0.; normals[vert + y] = 1.; normals[vert + z] = 0.;
colors[vert + r] = 0.; colors[vert + g] = 1.; colors[vert + b] = 1.;
vert += 3;
positions[vert + x] = 0.5; positions[vert + y] = 0.5; positions[vert + z] = 0.5;
normals[vert + x] = 0.; normals[vert + y] = 1.; normals[vert + z] = 0.;
colors[vert + r] = 0.; colors[vert + g] = 1.; colors[vert + b] = 1.;
vert += 3;
indices[idx + 0] = (face * 4) + 0; indices[idx + 1] = (face * 4) + 1; indices[idx + 2] = (face * 4) + 2;
indices[idx + 3] = (face * 4) + 0; indices[idx + 4] = (face * 4) + 2; indices[idx + 5] = (face * 4) + 3;
idx += 6;
++face;
// bottom face (YELLOW)
positions[vert + x] = 0.5; positions[vert + y] = -0.5; positions[vert + z] = 0.5;
normals[vert + x] = 0.; normals[vert + y] = -1.; normals[vert + z] = 0.;
colors[vert + r] = 1.; colors[vert + g] = 1.; colors[vert + b] = 0.;
vert += 3;
positions[vert + x] = -0.5; positions[vert + y] = -0.5; positions[vert + z] = 0.5;
normals[vert + x] = 0.; normals[vert + y] = -1.; normals[vert + z] = 0.;
colors[vert + r] = 1.; colors[vert + g] = 1.; colors[vert + b] = 0.;
vert += 3;
positions[vert + x] = -0.5; positions[vert + y] = -0.5; positions[vert + z] = -0.5;
normals[vert + x] = 0.; normals[vert + y] = -1.; normals[vert + z] = 0.;
colors[vert + r] = 1.; colors[vert + g] = 1.; colors[vert + b] = 0.;
vert += 3;
positions[vert + x] = 0.5; positions[vert + y] = -0.5; positions[vert + z] = -0.5;
normals[vert + x] = 0.; normals[vert + y] = -1.; normals[vert + z] = 0.;
colors[vert + r] = 1.; colors[vert + g] = 1.; colors[vert + b] = 0.;
vert += 3;
indices[idx + 0] = (face * 4) + 0; indices[idx + 1] = (face * 4) + 1; indices[idx + 2] = (face * 4) + 2;
indices[idx + 3] = (face * 4) + 0; indices[idx + 4] = (face * 4) + 2; indices[idx + 5] = (face * 4) + 3;
idx += 6;
++face;
geo.addAttribute( 'index', new THREE.BufferAttribute( indices, 1 ) );
geo.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) );
geo.addAttribute( 'normal', new THREE.BufferAttribute( normals, 3 ) );
geo.addAttribute( 'color', new THREE.BufferAttribute( colors, 3 ) );
var mat = new THREE.MeshPhongMaterial( {
color: 0xffffff,
ambient: 0xffffff,
specular: 0xffffff,
shininess: 50,
side: THREE.DoubleSide,
vertexColors: THREE.VertexColors
} );
var msh = new THREE.Mesh(geo, mat);
msh.scale.multiplyScalar(scale);
return msh;
}
Since WestLangley is being modest, here is what was going wrong in my code above.
My main mental roadblock was that I had a misconception about the function of normals with respect to drawing faces. I came in thinking that vertex normals could define the direction of a face, but that simply isn't true. Vertex normals are used for calculating lighting on the surface, and have nothing to do with defining face direction. It's the vertex order that defines the face direction (face normal). To add to it, I was using the left-hand system when three.js uses the right-hand system.
In my original code, I had:
To draw the faces in the correct direction, this should have been:
The differences are subtle, but in my example they mean the difference between a face normal pointing in the -Z vs. +Z directions, respectively. The faces were facing the wrong way to receive light. The explicit normals didn't even come into play because the surface wasn't reflecting any light anyway. Indexing using the right-hand system fixed the face directions.
With the face direction issue behind me, I did an exercise to solidify my understanding where I flipped my vertex normals to (once again) point in opposite direction of the face normals. As expected (this time), the square turned black. Even though the faces were pointing in the correct direction, the normals essentially told GL to reflect the lighting in toward the object, turning it into a black hole of sorts.
My take-away nutshell from this is: Face direction (face normal) is calculated from the order of the vertices which make up the face, and use RHS. Vertex normals affect the lighting on the face surface, and have nothing to do with defining face direction.