0

I want to shade a THREE.BoxBufferGeometry using a simple THREE.MeshLambertMaterial. The material is supposed to use a Lambert illumination model to pick the colors for each vertex (and it does), and then use Gouraud shading to produce smooth gradients on each face.

The Gouraud part is not happening. Instead, the cube's faces are each shaded with one single, solid color.

I have tried various other BufferGeometrys, and gotten inconsistent results.

For example, if instead I make an IcosahedronBufferGeometry, I get the same problem: each face is one single, solid color.

geometry = new THREE.IcosahedronBufferGeometry(2, 0); // no Gouraud shading.
geometry = new THREE.IcosahedronBufferGeometry(2, 2); // no Gouraud shading.

On the other hand, if I make a SphereBufferGeometry, the Gouraud is present.

geometry = new THREE.SphereBufferGeometry(2, 3, 2); // yes Gouraud shading.
geometry = new THREE.SphereBufferGeometry(2, 16, 16); // yes Gouraud shading.

But then if I make a cube using a PolyhedronBufferGeometry, the Gouraud shading doesn't appear unless I set the detail to something other than 0.

const verticesOfCube = [
    -1,-1,-1,    1,-1,-1,    1, 1,-1,    -1, 1,-1,
    -1,-1, 1,    1,-1, 1,    1, 1, 1,    -1, 1, 1,
];

const indicesOfFaces = [
    2,1,0,    0,3,2,
    0,4,7,    7,3,0,
    0,1,5,    5,4,0,
    1,2,6,    6,5,1,
    2,3,7,    7,6,2,
    4,5,6,    6,7,4
];

const geometry = new THREE.PolyhedronBufferGeometry(verticesOfCube, indicesOfFaces, 1, 1); // no Gouraud shading
geometry = new THREE.PolyhedronBufferGeometry(verticesOfCube, indicesOfFaces, 1, 1); // yes Gouraud shading

I am aware of the existence of the BufferGeometry methods computeFaceNormals() and computeVertexNormals(). Normals are emphatically important here, as they are used to determine the colors for each face and vertice, respectively. But while they help with the Icosahedron, they have no effect on the Box, no matter whether they are present, only one is present, or both are present in both possible orders.

Here is the code I expect to work:

const geometry = new THREE.BoxBufferGeometry(2, 2, 2);
geometry.computeFaceNormals();
geometry.computeVertexNormals();

const material = new THREE.MeshLambertMaterial({
  color: 0xBE6E37
});

const mesh = new THREE.Mesh(geometry, material);

I should be getting a cube whose faces (the real, triangular ones) are shaded with a gradient. First, the face normals should be computed, and then the vertex normals by averaging the normals of the faces formed by them. Here is a triangular bipyramid on which correct Gouraud shading is being applied:

correct shading

But the code above produces this instead:

incorrect shading

At no point does three.js log any errors or warnings to the console.

So what is it that's going on here? The only explanation I can think of is that the Box is actually comprised of 24 vertices, three at each corner of the cube, and that they form faces such that each vertex's computed normal is an average of at most two faces pointing in the same direction. But I can't find that written down anywhere, and that explanation doesn't fly for the Polyhedron where vertices and faces were explicitly specified in code.

WestLangley
  • 102,557
  • 10
  • 276
  • 276
MadEmperorYuri
  • 298
  • 3
  • 11
  • 1
    Your explanation is correct. There are 3 vertices there each with their own normal. You can weld them, recompute normals. – pailhead Jun 16 '19 at 00:19
  • 1
    trivia: three.js doesn't use gouraud shading. Gouraud shading requires linear interpolation of colors between points but three.js uses perspective correct interpolation between points – gman Jun 16 '19 at 01:56
  • @gman It does? I got my info from the link below, where it says in the second paragraph that Gouraud is used. https://threejs.org/docs/index.html#api/en/materials/MeshLambertMaterial – MadEmperorYuri Jun 16 '19 at 02:18
  • It's a common mis-conception. Most people believe any interpolation between point is Gouraud shading but the actual paper by Gouraud says it uses linear interpolation. three.js does not. It uses perspective correct interpolation. – gman Jun 16 '19 at 02:19
  • @pailhead Your solution worked, although I had to use a plain BoxGeometry and not a BoxBufferGeometry to be able to use the mergeVertices() method. If you want to write up your solution as an answer, I'll stamp it as solved. – MadEmperorYuri Jun 16 '19 at 02:30
  • Yes, you can't merge the vertices of a BoxBufferGeometry since a vertex is the combination of all of its parts. It's another common issue. GPUs call a vertex the sum of it's parts. One vertex = position+uv+color+normal+userdata. Math calls a vertex a position in space and has no concept of the other parts. See https://threejsfundamentals.org/threejs/lessons/threejs-custom-buffergeometry.html – gman Jun 16 '19 at 02:33
  • @gman That is not the point here. Yes - the paper says linear interpolation, but OpenGL fixed function pipeline light model uses perspective correct interpolation. Perspective correct interpolation is nothing else than linear interpolation in clip space relative the [Homogeneous coordinate](https://en.wikipedia.org/wiki/Homogeneous_coordinates) `gl_Positon`. In clip space the depth is linear. In normalized device space after the [Perspective divide](https://www.khronos.org/opengl/wiki/Vertex_Post-Processing#Perspective_divide) it is not. This causes the difference. – Rabbid76 Jun 16 '19 at 10:51
  • @Rabbid76, It's is the point if you're calling it Gouraud shading. The result are different and the look will be different. That cube at the top is definitely not using Gouraud shading. It's got the telltale band to close to the tip of the cube. That it's linear through clip space is irrelevant, gouraud shading interpolation happens linear in screen space. – gman Jun 16 '19 at 12:44
  • I didn't know this only applies to screenspace interpolation. Interesting. Still, welding here works, because you need to discard the two other normals at each corner. Granted, uvs, if not discarded, would be a bit more involved. – pailhead Jun 16 '19 at 23:03
  • 1
    Three's documentation is not always the best source for three's documentation. I think it should be taken with a grain of salt. – pailhead Jun 16 '19 at 23:04

0 Answers0