# TRIANGLE\_STRIP lighting problem

**URL:** https://discourse.processing.org/t/triangle-strip-lighting-problem/38348
**Category:** Coding Questions
**Created:** [August 14, 2022, 3:20am UTC](https://discourse.processing.org/t/triangle-strip-lighting-problem/38348 "2022-08-14T03:20:12Z")
**Posts on this page:** 10
**Page:** 1

<div class="post-metadata">

### Author: ![Tsskyx](https://avatars.discourse-cdn.com/v4/letter/t/90ced4/32.png) [@Tsskyx](https://discourse.processing.org/u/Tsskyx)
#### Post date: [August 14, 2022, 3:20am UTC](https://discourse.processing.org/t/triangle-strip-lighting-problem/38348/1 "2022-08-14T03:20:12Z")

</div>

I want to add some simple lighting to my 3D terrain that I’m generating using beginShape with TRIANGLE\_STRIP like this:

```auto
for (int i = 0; i < count - 1; i++) {
  beginShape(TRIANGLE_STRIP);
  for (int j = 0; j < count; j++) {
    vertex(j,i, data[i][j]);
    vertex(j, i + 1, data[i + 1][j]);
  }
  endShape();
}

```

(I simplified the code a lot. The real deal has a lot of scaling and offsetting going on in the vertex calls.)  
The issue is, this generates striped shading, and I suspect that it is possible to make it so that it doesn’t look that way, so that it’s smooth in both directions. I just don’t know how.

 ![NewCanvas1](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/3X/a/b/ab7d0e4c163c501780e098c2a6e8cbddc72f9973.png)

---

<div class="post-metadata">

### Author: ![jafal](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/jafal/32/19112_2.png) [@jafal](https://discourse.processing.org/u/jafal)
#### Post date: [August 14, 2022, 10:32am UTC](https://discourse.processing.org/t/triangle-strip-lighting-problem/38348/2 "2022-08-14T10:32:00Z")

</div>

Hi

What about this ??

 ![Screenshot_2022-08-14-13-30-18-385](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/3X/8/9/89ee1d954e185f30bc04f0a894abe8f40b43f0f2.jpeg)

 ![Screenshot_2022-08-14-15-58-40-492](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/3X/6/f/6fdb01182fa38f29c5e3ca8b417e79ebbcb77b19.jpeg)

---

<div class="post-metadata">

### Author: ![Tsskyx](https://avatars.discourse-cdn.com/v4/letter/t/90ced4/32.png) [@Tsskyx](https://discourse.processing.org/u/Tsskyx)
#### Post date: [August 14, 2022, 1:43pm UTC](https://discourse.processing.org/t/triangle-strip-lighting-problem/38348/3 "2022-08-14T13:43:11Z")

</div>

That’s the same issue. It contains stripes, which I wanna get rid of.

---

<div class="post-metadata">

### Author: ![glv](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/glv/32/18785_2.png) [@glv](https://discourse.processing.org/u/glv)
#### Post date: [August 14, 2022, 2:40pm UTC](https://discourse.processing.org/t/triangle-strip-lighting-problem/38348/4 "2022-08-14T14:40:24Z")

</div>

Hello @Tsskyx,

I was able to achieve this effect _ **without** _ `lights()` and coloring the vertices:

```auto
for (int i = 0; i < gs - 1; i++) 
    {
    beginShape(TRIANGLE_STRIP); // Or use QUAD_STRIP
    for (int j = 0; j < gs; j++) 
      {
      if (fv) fill(255-data[i][j]);
      vertex(j*gsp, i*gsp, data[i][j]);
      if(fv) fill(255-data[i + 1][j]);
      vertex(j*gsp, (i + 1)*gsp, data[i + 1][j]);
      }
    endShape(); 
    }    

```

 ![image](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/3X/4/2/4247db9bb5f561b654d04969023e8b7bfda29293.png)

 ![image](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/3X/c/4/c466b77975baf06ab04824b1c528604c7afeaa6e.png)

I have yet to try experimenting with `normal()` and `lights()`:  
[https://processing.org/reference/normal\_.html](https://processing.org/reference/normal_.html)

Busy days for me so may not be following up anytime soon…

`:)`

---

<div class="post-metadata">

### Author: ![Tsskyx](https://avatars.discourse-cdn.com/v4/letter/t/90ced4/32.png) [@Tsskyx](https://discourse.processing.org/u/Tsskyx)
#### Post date: [August 14, 2022, 2:55pm UTC](https://discourse.processing.org/t/triangle-strip-lighting-problem/38348/5 "2022-08-14T14:55:59Z")

</div>

That’s a nice workaround. I didn’t know you could assign `fill()` to individual vertices. Still, I wanna try it with `normal()`. I’m not sure how to do that though, there seem to be multiple issues with this at once.

---

<div class="post-metadata">

### Author: ![jafal](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/jafal/32/19112_2.png) [@jafal](https://discourse.processing.org/u/jafal)
#### Post date: [August 14, 2022, 11:26pm UTC](https://discourse.processing.org/t/triangle-strip-lighting-problem/38348/6 "2022-08-14T23:26:40Z")

</div>

Hi  
I am not sure what you mean but look at this

> [@Quad\_Strip: Okay, but how to do a line break in a bigger grid with Quad\_Strip?](https://discourse.processing.org/t/quad-strip-okay-but-how-to-do-a-line-break-in-a-bigger-grid-with-quad-strip/28623):
>
> Hello all, the reference has Quad\_Strip, but only for one line of quads. How can I make a bigger grid of QUAD\_STRIP? Actually, the main problem is the line break at the end of one line in the grid. I don’t want the right side to be connected with the left side of the next line in the grid. Part of a MUCH bigger project, but here is an example. As you can see, the values are in MyResults, which is a 2D array (grid) of PVector. I struggle with the vertexPV() command . Or do I have to say en…

> [@Quad\_Strip: Okay, but how to do a line break in a bigger grid with Quad\_Strip?](https://discourse.processing.org/t/quad-strip-okay-but-how-to-do-a-line-break-in-a-bigger-grid-with-quad-strip/28623/2):
>
> Hi @Chrisir , I would just use QUADS. int resolution = 32; PVector[] points = new PVector[resolution \* resolution]; color[] colors = new color[resolution \* resolution]; float scale = 250.0; void setup() { size(720, 405, P3D); int len = points.length; for (int i = 0; i \< len; ++i) { points[i] = new PVector(); } } void draw() { surface.setTitle(nfs(frameRate, 1, 1)); background(#202020); perspective(PI / 3.0, -width / (float)height, 0.001, 1000.0); camera( hei…

---

<div class="post-metadata">

### Author: ![Tsskyx](https://avatars.discourse-cdn.com/v4/letter/t/90ced4/32.png) [@Tsskyx](https://discourse.processing.org/u/Tsskyx)
#### Post date: [August 17, 2022, 3:48pm UTC](https://discourse.processing.org/t/triangle-strip-lighting-problem/38348/7 "2022-08-17T15:48:01Z")

</div>

After implementing the main algorithm from that post, I have obtained the following:

```auto
beginShape(QUADS);
for (int i = 0; i < count - 1; i++) {
  for (int j = 0; j < count - 1; j++) {
    vertex(j + 0, i + 0, data[i + 0][j + 0]);
    vertex(j + 1, i + 0, data[i + 0][j + 1]);
    vertex(j + 1, i + 1, data[i + 1][j + 1]);
    vertex(j + 0, i + 1, data[i + 1][j + 0]);
  }
}
endShape();

```

Once again, I’m leaving out all the scaling and perspective shifting details.

The result now looks like this:

 ![untitled2](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/3X/c/8/c86dc10fdb73abc02c872e0f5cff994eca23147a.png)  
 ![untitled1](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/3X/0/e/0e716db621cbdd2b3e943729000554765885f19a.png)

In my opinion, this looks much better, because the stripes are gone.

However, I am still interested in polishing the lighting, so that the shadows are completely smooth, as they were in each individual stripe from before. If I’m not mistaken, this would require me to set the normal vector for each vertex during each loop iteration, but with neighbor tile averaging of some kind. Has someone created an algorithm for this already?

---

<div class="post-metadata">

### Author: ![Tsskyx](https://avatars.discourse-cdn.com/v4/letter/t/90ced4/32.png) [@Tsskyx](https://discourse.processing.org/u/Tsskyx)
#### Post date: [August 17, 2022, 6:17pm UTC](https://discourse.processing.org/t/triangle-strip-lighting-problem/38348/8 "2022-08-17T18:17:55Z")

</div>

Here’s the full code of my program.

```auto
int count = 200;
float noiseScale = 2;
boolean showMesh = false;
boolean trueLights = true;

float[][] data;
float angle_x, angle_z, draw_x, draw_y, draw_z;
boolean L, R, F, B, U, D;
int[][] coords = {{0, 0}, {1, 0}, {1, 1}, {0, 1}};
int zoomFactor;

void setup() {
  size(800, 800, P3D);
  noStroke();
  noiseDetail(round(log(200) / log(2)), 0.5);
  data = new float[count][count];
  reset();
  reseed();
}

void reset() {
  zoomFactor = 0;
  angle_x = angle_z = draw_x = draw_y = draw_z = 0;
}

void reseed() {
  noiseSeed((int)random(MAX_INT));
  for (int i = 0; i < count; i++)
    for (int j = 0; j < count; j++)
      data[i][j] = noise((float)i / count * noiseScale, (float)j / count * noiseScale);
}

void draw() {
  background(0);

  if (trueLights) lights();

  if (showMesh)
    stroke(0);
  else
    noStroke();

  translate(width / 2, height / 2);
  rotateX(angle_x);
  rotateZ(angle_z);
  translate(draw_x, draw_y, draw_z);
  scale((float)height / (count - 1) * pow(1.1, zoomFactor));
  translate(-(count - 1) * 0.5, -(count - 1) * 0.5, 0);

  beginShape(QUADS);
  for (int i = 0; i < count - 1; i++) {
    for (int j = 0; j < count - 1; j++) {
      for (int k = 0; k < 4; k++) {
        int I = i + coords[k][0];
        int J = j + coords[k][1];
        if (trueLights)
          fill(255);
        else
          fill(255 * data[I][J]);
        vertex(J, I, (data[I][J] - 0.5) * (count - 1) * 0.5);
      }
    }
  }
  endShape();
}

void mouseDragged() {
  int dx = mouseX - pmouseX, dy = mouseY - pmouseY;
  switch (mouseButton) {
  case LEFT:
    angle_z -= dx * 0.005;
    angle_x -= dy * 0.005;
    break;
  case RIGHT:
    draw_x += dx * +cos(angle_z) + dy * sin(angle_z) * cos(angle_x);
    draw_y += dx * -sin(angle_z) + dy * cos(angle_z) * cos(angle_x);
    draw_z += dy * -sin(angle_x);
    break;
  }
}

void mouseWheel(MouseEvent event) {
  zoomFactor -= event.getCount();
}

void keyPressed() {
  switch (key) {
  case ' ':
    reseed();
    break;
  case 'r':
    reset();
    break;
  case 'm':
    showMesh = !showMesh;
    break;
  case 'l':
    trueLights = !trueLights;
    break;
  }
}

```

---

<div class="post-metadata">

### Author: ![scudly](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/scudly/32/5597_2.png) [@scudly](https://discourse.processing.org/u/scudly)
#### Post date: [August 17, 2022, 7:52pm UTC](https://discourse.processing.org/t/triangle-strip-lighting-problem/38348/9 "2022-08-17T19:52:12Z")

</div>

A simple hack that is probably good enough: create vectors between the points on either side of a given vertex, one in the x direction and the other in y, take their cross product and normalize it. Use it as your normal vector. Along the edges, use the vector from your vertex to the existing neighbor.

If you want to do more math, you could fit parabolas through the three points in each directions, compute the derivatives, and take the cross product. But I doubt you could tell the difference between that and the linear approximation especially when your data source is noise() to begin with.

---

<div class="post-metadata">

### Author: ![Tsskyx](https://avatars.discourse-cdn.com/v4/letter/t/90ced4/32.png) [@Tsskyx](https://discourse.processing.org/u/Tsskyx)
#### Post date: [August 21, 2022, 3:31am UTC](https://discourse.processing.org/t/triangle-strip-lighting-problem/38348/10 "2022-08-21T03:31:12Z")

</div>

@scudly After much trial and error and testing of different lighting algorithms, I have determined that yours is one of the best. Here’s my code, it features 4 different types of lighting modes (cycle through them with ‘c’):

```auto
import peasy.*;
PeasyCam cam;

final int points = 200;
final float camMin = .1;
final float camMax = 50;
final float tileScale = 10;
final float noiseScale = 2;
final float noiseFalloff = 0.5;
final float zScale = 1. / noiseScale;
final int noiseOctave = 8;

boolean showNormals = false;
boolean trueLights = true;
boolean showMesh = false;
boolean flatten = false;
int lightingStyle = 0;

float[][] hmap = new float[points][points];
PVector[][] P = new PVector[points][points];
PVector[][][] N = new PVector[points - 1][points - 1][4];

void setup() {
  size(800, 800, P3D);
  cam = new PeasyCam(this, tileScale * sqrt(3) * height / 2);
  cam.setMinimumDistance(camMin * height);
  cam.setMaximumDistance(camMax * height);
  perspective(TAU / 6, (float)width / height, camMin * height, camMax * height);
  noStroke();
  noiseDetail(noiseOctave, noiseFalloff);

  for (int i = 0; i < points; i++)
    for (int j = 0; j < points; j++)
      P[i][j] = new PVector();
  for (int i = 0; i < points - 1; i++)
    for (int j = 0; j < points - 1; j++)
      for (int k = 0; k < 4; k++)
        N[i][j][k] = new PVector();
  updateMap();
  setLights();
}

void updateMap() {
  noiseSeed((int)random(MAX_INT));
  for (int i = 0; i < points; i++) {
    for (int j = 0; j < points; j++) {
      hmap[i][j] = noise(i * noiseScale / points, j * noiseScale / points);
      P[i][j].set(j - 0.5 * (points - 1), i - 0.5 * (points - 1), (hmap[i][j] - 0.5) * points * zScale);
      P[i][j].mult(tileScale * height / (points - 1));
    }
  }
}

void setLights() {
  int $ = points - 1;
  switch (lightingStyle) {
  case 0: // normals of two triangles (processing default)
    for (int i = 0; i < points - 1; i++) {
      for (int j = 0; j < points - 1; j++) {
        PVector s1 = PVector.sub(P[i][j], P[i + 0][j + 1]);
        PVector s2 = PVector.sub(P[i][j], P[i + 1][j + 1]);
        PVector s3 = PVector.sub(P[i][j], P[i + 1][j + 0]);
        PVector n1 = PVector.cross(s1, s2, null).normalize();
        PVector n2 = PVector.cross(s2, s3, null).normalize();
        N[i][j][0].set(n2);
        N[i][j][1].set(n1);
        N[i][j][2].set(n2);
        N[i][j][3].set(n2);
      }
    }
    break;
  case 1: // cross product of tile diagonals (no gradient)
    for (int i = 0; i < points - 1; i++) {
      for (int j = 0; j < points - 1; j++) {
        PVector n = PVector.cross(PVector.sub(P[i][j], P[i + 1][j + 1]), PVector.sub(P[i][j + 1], P[i + 1][j]), null).normalize();
        for (int k = 0; k < 4; k++)
          N[i][j][k].set(n);
      }
    }
    break;
  case 2: // 4-neighbor normal (smooth gradient)
    for (int i = 0; i < points; i++) {
      for (int j = 0; j < points; j++) {
        PVector n = PVector.cross(PVector.sub(P[i][max(j - 1, 0)], P[i][min(j + 1, points - 1)]), PVector.sub(P[max(i - 1, 0)][j], P[min(i + 1, points - 1)][j]), null).normalize();
        if (i != $ && j != $) N[i - 0][j - 0][0].set(n);
        if (i != $ && j != 0) N[i - 0][j - 1][1].set(n);
        if (i != 0 && j != 0) N[i - 1][j - 1][2].set(n);
        if (i != 0 && j != $) N[i - 1][j - 0][3].set(n);
      }
    }
    break;
  case 3: // 6-neighbor normal (even smoother gradient)
    for (int i = 0; i < points; i++) {
      for (int j = 0; j < points; j++) {
        int iL = max(i - 1, 0), iH = min(i + 1, $), jL = max(j - 1, 0), jH = min(j + 1, $);
        PVector A = P[iL][jL], U = P[iL][j], R = P[i][jH], B = P[iH][jH], D = P[iH][j] ,L = P[i][jL];
        PVector nXY = PVector.cross(PVector.sub(R, L), PVector.sub(D, U), null).normalize();
        PVector nXZ = PVector.cross(PVector.sub(R, L), PVector.sub(B, A), null).normalize();
        PVector nYZ = PVector.cross(PVector.sub(B, A), PVector.sub(D, U), null).normalize();
        PVector sum = new PVector().add(nXY).add(nXZ).add(nYZ);
        if (i != $ && j != $) N[i - 0][j - 0][0].set(sum);
        if (i != $ && j != 0) N[i - 0][j - 1][1].set(sum);
        if (i != 0 && j != 0) N[i - 1][j - 1][2].set(sum);
        if (i != 0 && j != $) N[i - 1][j - 0][3].set(sum);
      }
    }
    break;
  }
}

void draw() {
  background(32);
  
  if (showMesh)
    stroke(0);
  else
    noStroke();
  if (trueLights) lights();
  
  beginShape(QUADS);
  for (int i = 0; i < points - 1; i++) {
    for (int j = 0; j < points - 1; j++) {
      for (int k = 0; k < 4; k++) {
        if (trueLights) {
          fill(255);
          normal(N[i][j][k].x, N[i][j][k].y, N[i][j][k].z);
        } else fill(255 * get(hmap, i, j, k));
        vertex(get(P, i, j, k).x, get(P, i, j, k).y, flatten ? 0 : get(P, i, j, k).z);
      }
    }
  }
  endShape();

  if (showNormals) {
    float tileSize = tileScale * (float)height / (points - 1);
    color[] colors = {color(255, 0, 0), color(255, 255, 0), color(0, 255, 0), color(0, 0, 255)};
    for (int i = 0; i < points - 1; i++) {
      for (int j = 0; j < points - 1; j++) {
        for (int k = 0; k < 4; k++) {
          stroke(colors[k]);
          PVector p = get(P, i, j, k), n = N[i][j][k];
          line(p.x, p.y, p.z, p.x + tileSize * n.x, p.y + tileSize * n.y, p.z + tileSize * n.z);
        }
      }
    }
  }
}

void keyPressed() {
  switch (key) {
  case ' ':
    updateMap();
    setLights();
    break;
  case 'm':
    showMesh = !showMesh;
    break;
  case 'l':
    trueLights = !trueLights;
    break;
  case 'c':
    lightingStyle = (lightingStyle + 1) % 4;
    setLights();
    break;
  case 'f':
    flatten = !flatten;
    break;
  case 'n':
    showNormals = !showNormals;
    break;
  }
}

final int[][] coords = {{0, 0}, {0, 1}, {1, 1}, {1, 0}};

float get(float[][] v, int i, int j, int k) {
  return v[i + coords[k][0]][j + coords[k][1]];
}

PVector get(PVector[][] v, int i, int j, int k) {
  return v[i + coords[k][0]][j + coords[k][1]];
}

```
