# Project approach (height map from image)

**URL:** https://discourse.processing.org/t/project-approach-height-map-from-image/44281
**Category:** Libraries
**Tags:** homework
**Created:** [April 15, 2024, 10:52am UTC](https://discourse.processing.org/t/project-approach-height-map-from-image/44281 "2024-04-15T10:52:33Z")
**Posts on this page:** 8
**Page:** 1

<div class="post-metadata">

### Author: ![Qprocessing](https://avatars.discourse-cdn.com/v4/letter/q/71c47a/32.png) [@Qprocessing](https://discourse.processing.org/u/Qprocessing)
#### Post date: [April 15, 2024, 10:52am UTC](https://discourse.processing.org/t/project-approach-height-map-from-image/44281/1 "2024-04-15T10:52:33Z")

</div>

Hello

I’m figuring out a project to make a topography of a mountain.

I have an example image as a goal.  
 ![eg1](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/3X/1/7/170af15d0fa5b89ef4b30f2f59ea4e821473f214.jpeg)

I did a test with still image (final goals is video)

The result is not really anything near what I want 🥲  
But i’m not really sure how I should approach this project. To create a better outcome.

```auto
// Load the image
PImage img;

void setup() {
  size(800, 600, P3D);
  img = loadImage("mountain.jpg"); 
  img.loadPixels();
}

void draw() {
  background(0);
  
  // Calculate terrain dimensions
  int terrainWidth = img.width;
  int terrainHeight = img.height;
  float terrainScale = 100; 
  
  // Render terrain
  stroke(200);
  noFill();
  translate(width/2, height/2);
  rotateX(PI/3);
  
  for (int y = 0; y < terrainHeight-1; y++) {
    beginShape(TRIANGLE_STRIP);
    for (int x = 0; x < terrainWidth; x++) {
      color c = img.pixels[y * terrainWidth + x];
      float z = brightness(c) / 255.0 * terrainScale; // Use image brightness as height
      vertex(x-terrainWidth/2, y-terrainHeight/2, z);
      vertex(x-terrainWidth/2, (y+1)-terrainHeight/2, brightness(img.pixels[(y+1)*terrainWidth+x]) / 255.0 * terrainScale);
    }
    endShape();
  }
}

```

---

<div class="post-metadata">

### Author: ![Chrisir](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/chrisir/32/45_2.png) [@Chrisir](https://discourse.processing.org/u/Chrisir)
#### Post date: [April 15, 2024, 6:40pm UTC](https://discourse.processing.org/t/project-approach-height-map-from-image/44281/2 "2024-04-15T18:40:58Z")

</div>

> [@Qprocessing](#):
>
> `"mountain.jpg"`

can you upload this image here please?

---

<div class="post-metadata">

### Author: ![Chrisir](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/chrisir/32/45_2.png) [@Chrisir](https://discourse.processing.org/u/Chrisir)
#### Post date: [April 15, 2024, 7:42pm UTC](https://discourse.processing.org/t/project-approach-height-map-from-image/44281/3 "2024-04-15T19:42:45Z")

</div>

here is my idea with test data

maybe one of the gurus can take a look if he usage of QuadStrip is correct?

 ![ssdd2e](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/3X/a/0/a0d0471f8da003a0f408aaff48f95f0d23615fdf.jpeg)

```auto
// use peasy cam

// imports
import peasy.*;

// -------------------------------------------------------------------------------
// consts

// colors
final color RED = color(255, 0, 0);
final color GREEN = color(0, 255, 0);
final color BLUE = color(0, 0, 255);

final color BLUE2 = color(64, 124, 188);
final color GREEN2 = #12E300;

final color BLACK = color(0);
final color WHITE = color(255);
final color LIGHTGRAY = color(185, 180, 180);

final color YELLOW = color(245, 250, 13);
final color YELLOW2 = #E6F523;

final color PINK = #eebbcc;
final color VIOLET = #A146FF;

final color LIGHT_BLUE_COLOR = color(173, 216, 230); //

// -------------------------------------------------------------------------------
// objects, vars, etc.

//peasyCam
PeasyCam peasyCam;

// Tool class for PVectors
ToolsPVector toolsPVector = new ToolsPVector();

// The main list
ArrayList<Track> listTracks = new ArrayList();

float[][] list = { // !!!!! * -1 !!!!!!!!!!!!!!!!!
  { 0, 0, 0, 11, 12, 13, 14, 44, 66, 88, 44, 33, 22, 11, 0, 0, 0 },
  { 0, 0, 0, 11, 12, 13, 14, 44, 66, 90, 44, 33, 22, 11, 0, 0, 0 },
  { 0, 0, 0, 11, 12, 13, 88, 88, 77, 92, 44, 33, 22, 11, 0, 0, 0 },
  { 0, 0, 0, 11, 12, 13, 88, 88, 77, 94, 44, 33, 22, 11, 0, 0, 0 },
  { 0, 0, 0, 11, 12, 13, 14, 44, 66, 98, 44, 33, 22, 11, 0, 0, 0 },
  { 0, 0, 0, 11, 12, 13, 14, 44, 66, 90, 44, 33, 22, 11, 0, 0, 0 },
  { 0, 0, 0, 11, 12, 13, 14, 44, 66, 88, 44, 33, 22, 11, 0, 0, 0 },

  { 0, 0, 0, 11, 12, 13, 14, 44, 66, 88, 44, 33, 22, 11, 0, 0, 0 },
  { 0, 0, 0, 11, 12, 13, 14, 44, 66, 90, 44, 33, 22, 11, 0, 0, 0 },
  { 0, 0, 0, 11, 12, 13, 14, 44, 77, 92, 44, 33, 22, 11, 0, 0, 0 },
  { 0, 0, 0, 11, 12, 13, 14, 44, 77, 94, 44, 33, 22, 11, 0, 0, 0 },
  { 0, 0, 0, 11, 12, 13, 14, 44, 66, 98, 44, 33, 22, 11, 0, 0, 0 },
  { 0, 0, 0, 11, 12, 13, 14, 44, 66, 90, 44, 33, 22, 11, 0, 0, 0 },
  { 0, 0, 0, 11, 12, 13, 14, 44, 66, 88, 44, 33, 22, 11, 0, 0, 0 }
};

// -------------------------------------------------------------------------------------------------
// Two core functions

void setup() {
  size(1300, 990, P3D);

  avoidClipping();

  peasyCam = new PeasyCam(this, width/2, 0, 0, 900);
  //peasyCam.setViewport(0, 0, 400, 400);

  // all elements should have the same length
  int normalLength = list[0].length;

  for (int i=0; i<list.length; i++) {

    if (normalLength!=list[i].length)
      println ("Error 2344");
    println(list[i].length);

    Track tr=new Track();

    for (int i2=0; i2<list[i].length; i2++) {
      tr.listPVectors.add(new PVector( i*8 + width/2, -1 * list [i][i2], i2*8));
    }
    listTracks.add(tr);
  }
}

void draw() {
  background (0);
  lights();

  if (keyPressed) {
    // spheres
    for (Track tr : listTracks) {
      tr.displayAsSpheres();
    }
  } else {
    // full landscape
    stroke(0, 0, 222);
    noStroke();
    beginShape(QUAD_STRIP);
    //
    for (int i=0; i<listTracks.size()-1; i++) {
      Track tr1 = listTracks.get(i);
      Track tr2 = listTracks.get(i+1);
      for (int i2=0; i2<tr1.listPVectors.size()-1; i2++) {

        toolsPVector.vertexPV(GREEN, tr1.listPVectors.get( i2 ));
        toolsPVector.vertexPV(GREEN, tr1.listPVectors.get( i2+1 ));

        toolsPVector.vertexPV(GREEN, tr2.listPVectors.get( i2 ));
        toolsPVector.vertexPV(GREEN, tr2.listPVectors.get( i2+1 ));
      }//for
    }//for

    endShape();

    // red sphere on mouse
    for (Track tr : listTracks) {
      tr.displayAsSpheresWhenMouse();
    }
  }// else
}

// ---------------------------------------------------------------------------------
// Other functions

void avoidClipping() {
  // avoid clipping (at camera):
  // https : //
  // forum.processing.org/two/discussion/4128/quick-q-how-close-is-too-close-why-when-do-3d-objects-disappear
  perspective(PI/3.0, (float) width/height, 1, 1000000);
} //func

// ============================================================================
// classes

class Track {
  // one ridge / column

  ArrayList<PVector> listPVectors = new ArrayList();
  float scx, scy;

  void displayAsSpheres() {
    for ( PVector pv : listPVectors) {
      pushMatrix();
      translate(pv.x, pv.y, pv.z);
      fill(255, 0, 0);
      noStroke();
      sphere( 7 ) ;
      popMatrix();
    }
  }

  void displayAsSpheresWhenMouse() {
    for ( PVector pv : listPVectors) {

      pushMatrix();
      translate(pv.x, pv.y, pv.z);
      scx=screenX(0, 0, 0);
      scy=screenY(0, 0, 0);
      if (dist(mouseX, mouseY, scx, scy) < 17) {
        fill(255, 0, 0);
        noStroke();
        sphere( 3 ) ;
      }
      popMatrix();

      if (dist(mouseX, mouseY, scx, scy) < 17) {
        return;
      }
    }
  }
} //

// ============================================================================

class ToolsPVector {

  // not a class for an object like a car but a class that collects Tools - FOR 3D -----------!!!!

  void linePV ( PVector pv1, PVector pv2) {
    line(pv1.x, pv1.y, pv1.z,
      pv2.x, pv2.y, pv2.z);
  }//func

  void pointPV ( PVector pv) {
    point(pv.x, pv.y, pv.z);
  }//func

  void spherePV (PVector pv) {
    pushMatrix();
    translate(pv.x, pv.y, pv.z);
    noStroke();
    fill(255, 0, 0);
    sphere(7);
    popMatrix();
  }//func

  void myColumn(PVector pv) {
    pushMatrix();
    translate (pv.x, pv.y, pv.z );
    box ( 12, 77, 18); // column
    popMatrix();
  }

  void myText(String txt_, PVector pv) {
    pushMatrix();
    fill(WHITE);
    translate (pv.x, pv.y-44, pv.z );
    // box ( 12, 77, 18); // column
    textMode(SHAPE);
    text(txt_, 0, 0);
    popMatrix();
  }

  void vertexPV(color col_, PVector pv_) {
    fill(col_);
    vertex(pv_.x, pv_.y, pv_.z);
  }
  //
} //class
//

```

---

<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: [April 16, 2024, 12:31am UTC](https://discourse.processing.org/t/project-approach-height-map-from-image/44281/4 "2024-04-16T00:31:36Z")

</div>

Hello @Qprocessing ,

Try creating tiles with just QUADS and then later you can `fill()` or `texture()` them.  
Focus on the QUAD vertices first! Then try `fill()` and then `texture()`.  
Once you master this then consider more advance methods.

Example:

```auto
PImage img;

void setup() 
  {
  size(800, 600, P3D);
  img = loadImage("http://learningprocessing.com/code/assets/sunflower.jpg"); 
  img.resize(500, 500);
  textureMode(NORMAL);
  }

void draw() 
  {
  background(255);

  noFill();
  translate(width/2, height/2-50);
  rotateX(PI/4);
  
  int xo = img.width/2;
  int yo = img.height/2;
  float s = 3;
  
  int iw = img.width;
  
  for (int y = 0; y < img.height-10; y+=10) 
    {
    for (int x = 0; x < img.width-10; x+=10) 
      {   
      PImage im = img.get(x, y, 8, 8); // I made it 8 instead of 10 to see the 8x8!
      image(im, x-xo, y-yo); // Use later in your texture
      
      // You will need to provide the correct vertices.
      //beginShape(QUADS);
      //texture(im);
      //vertex(x, y, x, u, v);
      //vertex(x, y, x, u, v);
      //vertex(x, y, x, u, v);
      //vertex(x, y, x, u, v);
      //endShape();
      }  
    }
  }

```

You will have to complete:

- 4 vertices for the QUAD using x, x+10, y, y+10
- correct u and v coordinates for each vertex; see order in reference
- z is the brightness of each corner of the QUAD

Build on what you have already done!

 ![image](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/3X/f/6/f6b1077565a0abfe4adfbad6923a66f38dda1449.jpeg)

 ![image](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/3X/7/e/7e394b9b73e78b0be9b70660642efccb08c96e74.png)

 ![image](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/3X/0/1/019efdf9e6f497f4eb8bd4723e93b65e40812ac5.jpeg)

You can also` fill()` a QUAD shape with a color! A bit _blocky_.

 ![image](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/3X/3/9/39948ad1143eb757980ccb320cf785ea303e64db.png)

You can also `fill()` each vertex with a color for nice gradients!  
I used the same `fill()` as I used for the z component of the vertex.  
_['Rotate' gradient background - #6 by glv](https://discourse.processing.org/t/rotate-gradient-background/39989/6)_

 ![image](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/3X/a/9/a9d2e70143564b49b20e595b3d066fe1adb63bbe.jpeg)

Reference:

- [texture() / Reference / Processing.org](https://processing.org/reference/texture_.html)
- [textureMode() / Reference / Processing.org](https://processing.org/reference/textureMode_.html)
- [get() / Reference / Processing.org](https://processing.org/reference/PImage_get_.html)

`:)`

---

<div class="post-metadata">

### Author: ![Chrisir](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/chrisir/32/45_2.png) [@Chrisir](https://discourse.processing.org/u/Chrisir)
#### Post date: [April 16, 2024, 8:34am UTC](https://discourse.processing.org/t/project-approach-height-map-from-image/44281/5 "2024-04-16T08:34:59Z")

</div>

Could you please look at my code, is the usage of  
quad\_strip correct?

I am confused that the colors are not smooth on my tiles…

The color gradient is not nice.

Thank you!

---

<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: [April 25, 2024, 5:53pm UTC](https://discourse.processing.org/t/project-approach-height-map-from-image/44281/6 "2024-04-25T17:53:41Z")

</div>

Hello @Qprocessing,

> [@Qprocessing](#):
>
> To create a better outcome.

Take a look at what I did here to space the vertices:

```auto
  int vsp = 10; // vertex spacing
  for (int y = 0; y < terrainHeight-vsp; y+= vsp) 
    {
    beginShape(TRIANGLE_STRIP); // Also works with QUAD_STRIP
    for (int x = 0; x < terrainWidth; x+=vsp) 
      {
      color c = img.pixels[y * terrainWidth + x];
      float z = brightness(c) / 255.0 * terrainScale; // Use image brightness as height
      vertex(x-terrainWidth/2, y-terrainHeight/2, z);
      
      //vertex(x-terrainWidth/2, (y+sp)-terrainHeight/2, brightness(img.pixels[(y+sp)*terrainWidth+x]) / 255.0 * terrainScale);
      // Readable:
      z = brightness(img.pixels[(y+vsp)*terrainWidth+x]) / 255.0 * terrainScale;
      vertex(x-terrainWidth/2, (y+vsp)-terrainHeight/2, z);
      }
    endShape();
    }

```

Outcome:

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

Once you have the mesh you can work with that …  
I already shared some suggestions in previous post

`:)`

---

<div class="post-metadata">

### Author: ![Qprocessing](https://avatars.discourse-cdn.com/v4/letter/q/71c47a/32.png) [@Qprocessing](https://discourse.processing.org/u/Qprocessing)
#### Post date: [May 5, 2024, 2:20pm UTC](https://discourse.processing.org/t/project-approach-height-map-from-image/44281/7 "2024-05-05T14:20:16Z")

</div>

I’ve kind of misunderstood the assignement.

The mountainous image I’ve added is the expected result. But it should come from a sound file.

I’ve started something, but not really near what the image shows.

```auto
//Ladin.wav

import processing.sound.*;

SoundFile soundFile;
FFT fft;
float[][] zValues;
int cols, rows;
int scale = 2;
float angleX = PI / 6;
float angleY = 0;
float zoom = -100;
int bands = 512; // Number of frequency bands
PVector lightPos; // Position of the light source

void setup() {
  size(800, 600, P3D);
  cols = width / scale;
  rows = height / scale;
  zValues = new float[cols][rows];

  // Load and play sound
  soundFile = new SoundFile(this, "Ladin.wav");
  soundFile.loop();
  fft = new FFT(this, bands);
  fft.input(soundFile);
  
  // Initialize light position
  lightPos = new PVector(width / 2, height / 2, 200);
}

void draw() {
  background(0);
  fft.analyze(); // Analyze the sound

  // Update and animate light position with dither
  lightPos.x = width / 2 + map(noise(frameCount * 0.02), 0, 1, -50, 50);
  lightPos.y = height / 2 + map(noise(frameCount * 0.02 + 1000), 0, 1, -50, 50);
  pointLight(255, 255, 255, lightPos.x, lightPos.y, lightPos.z);

  // Apply FFT data to z-coordinates with added noise for individual movement
  for (int i = 0; i < cols; i++) {
    for (int j = 0; j < rows; j++) {
      int bandIndex = (int) random(bands); // Randomly assign a frequency band for each pixel
      float amplitude = fft.spectrum[bandIndex] * 1000; // Scale amplitude
      float noiseFactor = noise(i * 0.1, j * 0.5, frameCount * 0.04); // Perlin noise for more organic movement
      float baseline = 50 * sin(TWO_PI * i / cols); // Sinusoidal baseline for mountain-like curve
      zValues[i][j] = baseline + amplitude * noiseFactor; // Combine all influences for each pixel
    }
  }

  // Transformations based on mouse control
  translate(width / 2, height / 2);
  translate(0, 0, zoom);
  rotateX(angleX);
  rotateY(angleY);

  // Draw points as white pixels
  for (int i = 0; i < cols; i++) {
    for (int j = 0; j < rows; j++) {
      stroke(255);
      point(i * scale, j * scale, zValues[i][j]);
    }
  }
}

void mouseDragged() {
  float rate = 0.005;
  angleY += (mouseX - pmouseX) * rate;
  angleX += (mouseY - pmouseY) * rate;
}

void mouseWheel(MouseEvent event) {
  float e = event.getCount();
  zoom += e * 20;
}

```

---

<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: [July 13, 2024, 2:17pm UTC](https://discourse.processing.org/t/project-approach-height-map-from-image/44281/8 "2024-07-13T14:17:26Z")

</div>

> [@Chrisir](#):
>
> Could you please look at my code, is the usage of  
> quad\_strip correct?

This will work with TRIANGLE\_STRIP or QUAD\_STRIP:

```auto
    stroke(0, 0, 222);
    //noStroke();
    //beginShape(QUAD_STRIP);
    ////beginShape(TRIANGLE_STRIP);
    //
    for (int i=0; i<listTracks.size()-1; i++) {
    //beginShape(QUAD_STRIP);
    beginShape(TRIANGLE_STRIP);
      Track tr1 = listTracks.get(i);
      Track tr2 = listTracks.get(i+1);
      for (int i2=0; i2<tr1.listPVectors.size()-1; i2++) {

        toolsPVector.vertexPV(GREEN, tr1.listPVectors.get( i2 ));
        toolsPVector.vertexPV(GREEN, tr2.listPVectors.get( i2 ));

        //toolsPVector.vertexPV(GREEN, tr2.listPVectors.get( i2+1 ));
        //toolsPVector.vertexPV(GREEN, tr1.listPVectors.get( i2 ));
      endShape();  
      }//for   
    }//for

```

![image](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/3X/f/7/f7f3e3075d640e6b5d6c4fde2be06d2e12bf9309.png)

 ![image](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/3X/5/6/56291c3611e54d97aa7b228673104eb037e401a6.png)

Reference (nice image):

> **[Dasar-Dasar Menggambar dengan OpenGL dan Menggambar Titik](https://zacknov.wordpress.com/2011/09/10/dasar-dasar-menggambar-dengan-opengl-dan-menggambar-titik/)**
>
> Dasar-dasar menggambar dengan OpenGL mempunyai 10 buah bentuk geometris sederhana,biasa disebut 10 Geometris Primitif. Disebut primitif karena bentuk sederhana dan ditampilkan sangat cepat dan bisa…

`:)`
