Loading...
Logo
Processing Forum
Hello there,

I'm trying to make a simple 3d landscape, and it seems a good way to go is using greyscale vertex displacement.  however, despite looking all over the various processing pages, I can't find a straightfoward description of how to achieve this.  My understanding is something like:  make a grid of quads, with the z position of the vertexes controlled by an underlying greyscale image's grey values.  is this close?  i can't seem to figure out how to make this happen. 

thanks in advance, and I apologize if this question is somewhere on the forum and I'm missing it. 

btw, why the forum change?  the old one was easier to use and find info. 

Replies(11)

this is exactly what it is about, you already got it... i have a working code of exactly this. but i prefer helping.
where is the problem. ? making the grid? getting the color out if the image ?
> make a grid of quads, with the z position of the vertexes controlled by an underlying greyscale image's gery values.  is this close?

pretty much spot on.

> i can't seem to figure out how to make this happen.

i can't tell you anything you don't already know.

break the problem down.

start with a float[][]. fill it full of 0s or random numbers of a certain range.

you'll then need two loops, nested, one for x, one for y.

draw the quad for each point...

post what you have...
ok, well, good to know I'm at least on the right track. 

here's what i have so far: 

Copy code
  1. import processing.opengl.*;

    int x, y, d=50;
    float noiseScale=0.02, z;

    void setup(){
      size(720, 480, OPENGL);
     
    }

    void draw(){
      background(0);
      lights();
      stroke(0, 255, 255);
      fill(200);
           /*float z1 = random(-100, 100);
             float z2 = random(-100, 100);
             float z3 = random(-100, 100);
             float z4 = random(-100, 100);*/
         

     

      translate(0, height/3, -400);
      rotateX(70);
      translate(0, 0, 0);
      for (int x=0; x<width; x+=d){
        for(int y=0; y<height; y+=d){
        z = noise((mouseX+x)*noiseScale);
      //quad(x, y, x+10, y, x+10, y+10, x, y+10);
        beginShape(QUADS);
        vertex(x, y, z*80);
        vertex( x+d, y, 0);
        vertex(x+d, y+d, 0);
        vertex(x, y+d, 0);
      endShape(CLOSE);
          
    }
    }

     
    }

I can get a grid of quads, ok.  everything i have tried to get variety in the z-axis is not working. right now, it gets variety on one corner  it seems to me the problem is that I need to assign each vertex a variable, and have that vertex controlled by the variable for all four adjacent quads, but i really don't know the syntax to do this. 




alright, this was already a good start. but i have changed several things.

first the jumping of your quads is caused by the getting new random values with every frame.
you can stop this by simply using a randomSeed().

you assumed right that you need different values for each corner, but if you think of it again you may realize that the lower left corner of the first quad in row 1 is the upper left corner of the quad in row2 so you can not just randomize it like the way you did.
What makes it much easier in this case is if you store all your z values in an array you already fill in a loop you do before the actual drawing loops.

you can now access all the previous or later values as well and can draw your quads.

btw, its much easier if you always count loops one by one.
 y++ instead of y+=d;
i just added *d to the vertex in the end. it makes it much easier to use the array.

Hope this code helps.
shouldnt be to hard to add the image heightmap now.  If you need some more help, just ask again.

Copy code
  1. import processing.opengl.*;
    int x, y, d=20;
    float[][] val ;
    float xSize,ySize;
    void setup(){
      size(720, 480, OPENGL);
      val = new float[width/d][height/d];
      xSize = width/d;
      ySize = height/d;
    }


    void draw(){
      for(int x = 0; x<xSize;  x++){
        for(int y =0; y<ySize;  y++){
          val[x][y] = random(-1,1)*mouseX/10;
        }
      }
      randomSeed(9); // add seed to stop jumping
      background(0);
      lights();
      stroke(0, 255, 255);
      noFill();

      translate(0, height/3, -400);
      rotateX(70);
      translate(0, 0, 0);

      for (int x=0; x<xSize-1; x++){
        for(int y=0; y<ySize-1; y++){


          beginShape();
          vertex(x*d, y*d, val[x][y]);
          vertex( x*d+d, y*d, val[x+1][y]);
          vertex(x*d+d, y*d+d, val[x+1][y+1]);
          vertex(x*d, y*d+d,val[x][y+1]);
          endShape(CLOSE);

        }
      }
    }




hey thanks, i appreciate it.  now I'm trying to add the greyscale reference, and that part is working, but for some reason, the variable d now controls the width of the entire grid image, rather than just the width ofthe subdividing quads.  like, the quads get bigger, but there's still 720 x 480 of them, no matter what d is set to. 

Copy code
  1. import processing.opengl.*;
    PImage extrude;
    int[][] val;
    int x, y, d=40;
    int xSize, ySize;

    float angle;


    void setup(){
      size(720, 480, OPENGL);


      extrude = loadImage("grey.jpg"); 
      extrude.loadPixels();
     
      xSize = extrude.width;
      ySize = extrude.height;
     
      val = new int[xSize][ySize];
      for (int y = 0; y < ySize-1; y++) {
        for (int x = 0; x < xSize-1; x++) {
          color pixel = extrude.get(x, y);
          val[x][y] = int(brightness(pixel));
        }
      }

    }

    void draw(){
     background(0);
      lights();
      stroke(0, 255, 255);
      noFill();
     camera(mouseX, mouseY, (height/2.0) / tan(PI*60.0 / 360.0), // eyeX, eyeY, eyeZ
             width/2, height/2, 0.0, // centerX, centerY, centerZ
             1.0, 1.0, 0.0); // upX, upY, upZ

     
      // Rotate around the center axis
      translate(0, height/3, -400);
      rotateX(70);
      translate(0, 0, 0);

      for (int y = 0; y < ySize-1; y++) {
        for (int x = 0; x < xSize-1; x++) {

          beginShape();
          vertex(x*d, y*d, val[x][y]);
          vertex( x*d+d, y*d, val[x+1][y]);
          vertex(x*d+d, y*d+d, val[x+1][y+1]);
          vertex(x*d, y*d+d,val[x][y+1]);
          endShape(CLOSE);
               
    }
    }

    }
> the variable d now controls the width of the entire grid image, rather than just the width ofthe subdividing quads

that's not quite true

> there's still 720 x 480

yes. you're taking number of elements in the grid from the size of extrude and drawing each at width d whereas before you were dividing the number of elements by d before doing the drawing, only drawing a fraction of them.

Copy code
  1.   size(720, 480, OPENGL);
  2.   val = new float[width/d][height/d];
  3.   xSize = width/d;
  4.   ySize = height/d;
vs

Copy code
  1.   extrude = loadImage("grey.jpg");
  2.   extrude.loadPixels();
  3.   xSize = extrude.width;
  4.   ySize = extrude.height;

you need to make a choice: do you draw every value in your image - this means you have a huge grid. or take samples and only draw every 40th one, in which case you are throwing away 39/40ths of the data (and you have an image 40 times bigger than it needs to be)
bump.  please help.  I'm having real trouble solving this last part. 

thanks. 
here we go.
now you can load every image size you want but have to decide as well what resolution your mesh should have.
i changed the variable to meshSize not to use "d" again.
Try to understand what i did. i didnt change much.

hope it makes sense.


Copy code
  1. import processing.opengl.*;
    PImage extrude;
    int[][] val;
    int x, y;
    int meshSize = 10; //resolution of your mesh
    int xSize, ySize;

    float angle;


    void setup(){
      size(720, 480, OPENGL);


      extrude = loadImage("grey.jpg");
      extrude.loadPixels();
     
      xSize = extrude.width/meshSize;
      ySize = extrude.height/meshSize;
     
      val = new int[xSize][ySize];
      for (int y = 0; y < ySize-1; y++) {
        for (int x = 0; x < xSize-1; x++) {
          color pixel = extrude.get(x*meshSize, y*meshSize);
          val[x][y] = int(brightness(pixel));
        }
      }

    }

    void draw(){
     background(0);
      lights();
      stroke(0, 255, 255);
      noFill();
     camera(mouseX, mouseY, (height/2.0) / tan(PI*60.0 / 360.0), // eyeX, eyeY, eyeZ
             width/2, height/2, 0.0, // centerX, centerY, centerZ
             1.0, 1.0, 0.0); // upX, upY, upZ

     
      // Rotate around the center axis
      translate(0, height/3, -400);
      rotateX(70);
      translate(0, 0, 0);

      for (int y = 0; y < ySize-1; y++) {
        for (int x = 0; x < xSize-1; x++) {

          beginShape();
          vertex(x*meshSize, y*meshSize, val[x][y]);
          vertex( x*meshSize+meshSize, y*meshSize, val[x+1][y]);
          vertex(x*meshSize+meshSize, y*meshSize+meshSize, val[x+1][y+1]);
          vertex(x*meshSize, y*meshSize+meshSize,val[x][y+1]);
          endShape(CLOSE);
              
    }
    }

    }
got it figured out.  now generated by Perlin Noise.  thanks for the help, here's the "final" 

Copy code
  1. import processing.opengl.*;
    int  d=8;

    float[][] val ;
    float xSize,ySize;
    float increment = 0.03;
    float zoff = 0.0; 
    float zincrement = 0.01;


    void setup(){
      //smooth();
      size(720, 480, OPENGL);
      val = new float[width/d][height/d];
      xSize = width/d;
      ySize = height/d;   
    }


    void draw(){
      background(0);
      lights();
      fill(255, 0, 175);
      //stroke(0, 150, 150);
      noStroke();
      camera(mouseX*1.5, mouseY*2.5, (height/2.0) / tan(PI*60.0 / 360.0),
             width/2.0, height/2.0, 0, // centerX, centerY, centerZ
             0.0, 1.0, 0.0); // upX, upY, upZ
      translate(0, height/2, -300);
      rotateX(70);
     
     noiseSeed(12);
      float xoff = 0.0; // Start xoff at 0
      noiseDetail(8,0.3);
      // For every x,y coordinate in a 2D space, calculate a noise value and produce a brightness value
      for (int x = 0; x < width/d; x++) {
        xoff += increment;   // Increment xoff
        float yoff = 0.0;   // For every xoff, start yoff at 0
        for (int y = 0; y < height/d; y++) {
          yoff += increment; // Increment yoff
          float z = noise(xoff,yoff,zoff)*600;
          val[x][y] = z;
        }
      }

     for (int x=0; x<xSize-1; x++){
        for(int y=0; y<ySize-1; y++){
          fill((val[x][y])-50, 0, 200);
          beginShape();
          vertex(x*d, y*d, val[x][y]);
          vertex( x*d+d, y*d, val[x+1][y]);
          vertex(x*d+d, y*d+d, val[x+1][y+1]);
          vertex(x*d, y*d+d,val[x][y+1]);
          endShape(CLOSE);
        }
      }
       zoff += zincrement; // Increment zoff

    }

just for comparison.
here is me old code i was talking about in the first post. also with perlin noise.
it uses peasy cam for camera control and the stl export library.


Copy code
  1. //demo creating a simple mesh using perlin noise as height information
    // change noise resolution by moving the mouse and export pressing a key

    import peasy.*;
    import unlekker.data.*;
    boolean record;
    PeasyCam cam;

    float noiseScale= .12;


    int meshSize = 10;
    int resX = 50;
    int resY = 50;
    float[][] val = new float[resX][resY];

    void setup() {
      size(900,600,P3D);
        smooth();
      background(255);
     

      cam = new PeasyCam(this, 0,0,0,600);



    }

    void draw() {
     
      translate(-resX/2*meshSize,-resY/2*meshSize);
      noiseScale= mouseX*.0002;

      float xoff = 0.0;
      for(int x =0; x<resX; x++){
        xoff +=noiseScale;
        float yoff = 0.0;
        for(int y =0; y<resY; y++){

          yoff +=noiseScale;

          val[x][y] = noise(xoff,yoff)*255;
        }
      }

      background(0);
      if (record) {
        beginRaw("unlekker.data.STL",millis()+".stl");
      }


      for(int x =0; x<resX-1; x++){
        for(int y =0; y<resY-1; y++){
          beginShape();
     colorMode(HSB,255);
      fill( val[x][y],255,255);

          vertex(x*meshSize,y*meshSize,val[x][y] );
          vertex((x+1)*meshSize,y*meshSize,val[x+1][y] );
          vertex((x+1)*meshSize,(y+1)*meshSize,val[x+1][y+1] );
          vertex(x*meshSize,(y+1)*meshSize,val[x][y+1] );


          endShape(CLOSE);
        }

      }


      if (record) {
        endRaw();
        record = false;
      }

    }


    void keyPressed() {
      record = true;
    }








nice.  I like the peasy cam control, I'll have to try that in the future.  thanks for the help, btw