Project approach (height map from image)

Hello

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

I have an example image as a goal.
eg1

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

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

// 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();
  }
}

can you upload this image here please?

1 Like

here is my idea with test data

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

// 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
//

2 Likes

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:

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!

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

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

Reference:

:)

3 Likes

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!

2 Likes

Hello @Qprocessing,

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

  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:

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

:)

2 Likes

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.

//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;
}

1 Like