Weird light origins in 3d scene

p5.js Web Editor <<this is my project
its a 3d game, wasd to move.

i’m making calls to camera() and perspective() so that the player can move around the scene in first person and some other small tweaks to the view. y is up.

but the lighting behaviours have gone out of whack.
in my scene i have

  • an ambient light, which works fine, is ambient.
  • a point light, which should be centered on the player’s position, but does not seem to be (but still sometimes visible in the scene)
  • a spot light, which should be centered on the player and pointing in the camera direction, but only seems to be doing that when facing certain angles/ when stood far away from what its pointed at
  • a directional light, which should be pointed right at the z side of walls, but doesnt… really seem to be doing that either

ive played with all the parameters but im really not sure what’s going on

There’s a couple of issues.

  1. Because of the way the camera works you need to position your spotlight slightly behind the camera to get the illumination right when you are relatively close to objects.
  2. You need to determine light falloff settings that work well for you as opposed to the default.
  3. You need to set normal coordinates when you are drawing your textured geometry. (see the normal function)

You might also be interested in constructing custom geometry to get better performance for your textured boxes.

Here’s a working example derived from your code just to demonstrate the lighting effects:

Notice how I simplified things and added sliders to make experimentation easier. I recommend you try this sort of thing when debugging.

Note: I had to use cylinders instead of boxes because it seems like the detailX and detailY arguments for box() aren’t working right. Also I added an overhead spotlight because pointLight does a lackluster job of illuminating the floor which I added. Also generally speaking the default lighting model does a poor job with the floor because it is at an acute angle to the direction the camera is pointing.

3 Likes

thanks, paul.

p5.js Web Editor << im convinced that if i had proper normals on my object, it would be lit properly by most kinda lights (but not ambient lights, which i was pretty sure of…) at least at the origin

but i’m getting another problem where if i try to call normal() anywhere in my existing sketch the interpreter complains that normal is not defined. i don’t think its overwritten anywhere and i can see it working in this other sketch… it just doesnt exist there. im worried im gonna have to do the thing where i start with a new sketch and import all the features from the old sketch, which is, idk, it works but i wish it wasnt my solution to each new problem

i’ll have to look into generating custom geometry, thanks

Ah, I failed to notice that you are using a positively ancient version of p5.js. I highly recommend you update to version 1.4.0 which is the version where the normal() function was added. Unfortunately in subsequent versions I’m having trouble getting textures to render in your app which is odd because the work in trivial examples.

Here’s another version of your code with custom geometry implemented: p5.js Web Editor

  // Added to your Box function


  // note: texturing for geometry disregards textureMode
  const textureSize = 1.0;
  box.geometry = 
    new p5.Geometry(
      // detailX and detailY are not used in this implementation
      1, 1,
      function createGeometry() {
        // helper function for making a square plane of triangles
        // The last arg is a callback function to take x, y arguments and
        // turn them into a 3d vector. Thus allowing the plan to be changed
        // to a different orientation.
        this.makePlane = function(size, detail, mk3d) {
          const fSize = size / detail;
          const txSize = textureSize / detail;
          for (let i = 0; i < detail; i++) {
            for (let j = 0; j < detail; j++) {
              const [t, l, b, r] = [
                fSize * i - size / 2,
                fSize * j - size / 2,
                fSize * (i + 1) - size / 2,
                fSize * (j + 1) - size / 2,
              ];
              
              const [txt, txl, txb, txr] = [
                txSize * i,
                txSize * j,
                txSize * (i + 1),
                txSize * (j + 1),
              ];
              
              let vix = this.vertices.length;
              // Draw ◸
              this.vertices.push(mk3d(l, t));
              this.vertices.push(mk3d(r, t));
              this.vertices.push(mk3d(l, b));
              this.uvs.push([txl, txt]);
              this.uvs.push([txr, txt]);
              this.uvs.push([txl, txb]);
              this.faces.push([vix, vix + 1, vix + 2]);
              // Draw ◿
              vix = this.vertices.length;
              this.vertices.push(mk3d(l, b));
              this.vertices.push(mk3d(r, t));
              this.vertices.push(mk3d(r, b));
              this.uvs.push([txl, txb]);
              this.uvs.push([txr, txt]);
              this.uvs.push([txr, txb]);
              this.faces.push([vix, vix + 1, vix + 2]);
            }
          }
        }
        
        // Note: it is important to get the signs right so that the winding
        // direction will be correct. Triangle vertices need to be drawn in
        // clockwise order for computeNormals to work.
        
        // front 
        this.makePlane(size, 10, (x, y) => createVector(x, y, size * 1/2));
        // right 
        this.makePlane(size, 10, (x, y) => createVector(size * 1/2, y, -1 * x));
        // back
        this.makePlane(size, 10, (x, y) => createVector(-1 * x, y, size * -1/2));
        // left 
        this.makePlane(size, 10, (x, y) => createVector(size * -1/2, y, x));
        // top
        this.makePlane(size, 10, (x, y) => createVector(x, size * -1/2, y));
        // bottom
        this.makePlane(size, 10, (x, y) => createVector(x, size * 1/2, -1 * y));

        this.computeNormals();

        this.gid = `custom-box-${size}`;
      }
    );
  
  box.render = function(tex, tintcol) {
    texture(tex);
    tint(tintcol);
    model(this.geometry);
  };
1 Like

that’d do it.

i think what i’m gonna do for geometry for the moment is just call beginShape, draw all my boxes in a batch, then endShape. that’s a little dumber but its basically free performance, and i got it going and it even renders the lights correctly.

but its nice to have a working example for createGeometry because i’ll probably still have to do that later.

thanks for all your help, its been really handy