0
votes

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;
}
1
You have done more than you reallize :) Try renderer.setClearColor( 0x888888, 1 );WestLangley
Wow, it's like I don't know my left hand from my right hand (systems)! If you want to post an answer, I'll accept it, otherwise just drop another comment here and I'll write up how I goofed.TheJim01
Actually, I'm still a little confused. I get that I was using LHS when I should have used RHS. But why were the normals ignored? Shouldn't an explicit definition override an implicit one? There are lots of reasons why I'd need the normals to point in different directions, so having the handedness overrule my expectations is a little unnerving.TheJim01
I think you are jumping to conclusions... However, from the looks of fiddle #60+, you seem to be getting it. BTW. You do not need to update the fiddle every time your run it...WestLangley
Yeah, I need to get better at using the jsfiddle tools in general... Did some more tinkering, and I think I understand. My early 3D experience was with Blender. If a face was drawing in an unintended direction, it was usually an inverted face normal, and flipping the normal flipped the face. In reality, it was probably flipping the normal AND changing the vertex order for the face. In this instance, I was changing vertex normals, not the face, which lead to GL not handling the light like I expected. Nutshell: vertex/index order always defines the face, normals are only used for lighting.TheJim01

1 Answers

2
votes

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:

indices = [ 0, 1, 2, 0, 2, 3 ];

To draw the faces in the correct direction, this should have been:

indices = [ 0, 2, 1, 0, 3, 2 ];

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.