## Textural Terrain Generation

posted by on 2016.06.22, under Processing
22:

This post is inspired by some of the stuff Daniel Shiffman has on his YouTube channel.
The idea is based on the use of meshes and textures. So, what’s a mesh and what’s a texture?
A mesh is a collection of vertices, edges and faces that are used in 3D computer graphics to model surfaces or solid objects. If you are familiar with the mathematical notion of a triangulation, you are more or less in business. Even though the faces are not necessarily triangles in general, the analogy works quite well. In Processing there is a nice and quick way to generate a triangular mesh, and it’s via beginShape()/endShape(), which is what I have used in the code below. One starts with a grid (check out some earlier posts for rants about grids), and from the collection of points of the grid Processing will then build a triangular mesh*. This is achieved via the TRIANGLE_STRIP mode: we only need to specify the vertices (though in a precise order), and they will be connected via triangular shapes. Very cool. Ok, we have a tons of little triangles which assemble in a huge square: what do we do with this? Here comes the notion of a texture map. The idea is very simple: we have an image, and we want to “glue” it to a face of the mesh. Once it is glued to such a face, the image will follow the face: for instance, if we rotate such a face, the image which is stuck to that face rotates as well! Now, you should know that mapping textures on a complicated surface is kind of an art, but in our case it is pretty easy, since the surface is just a flat square. To achieve this gluing, we have to define some anchor points in the image. In other words, we have to give information about how points in the original image are associated to vertices in the mesh. The double loop in the code below does exactly this: the last two parameters in the vertex() function specify indeed the gluing.
If we had halted our imagination here, we would end up with something very static: an image attached to a square. Meh. Here comes the simple, but interesting idea. Since we are in 3D geometry, we can modify the z-coordinate of the vertex at point (x,y) with a function of the plane. In this case, the function used is Perlin noise. If we rotate our system of coordinates with respect to the x-axis, you start seeing little mountains appear. Nice! Still, though, there is no movement in place. To achieve such a movement effect, we can increment the y coordinate slightly at each frame, so that the new z-coordinate at point (x, y) will be the value of the z-coordinate at a previous point in the grid, achieving the moving effect. In the code, I’ve furthermore decided to control the offset of the z-coordinate with a variable r whose minimum value is 0, and gets triggered randomly. Notice that I’ve also allowed some “release” time for r, so to achieve some smoothness. In this way you obtain a nice beating feeling. Instead of doing it randomly, what happens if you trigger r via a beat detector listening to some music (using the Sound or Minim library, say)? Yep, you get a nice music visualizer.
Last couple of things I added is to “move along the image”, and using tint() instead of clearing the screen. The first one is achieved via the variable speed: basically, at each new frame, we don’t glue the image to the mesh in the exact same way, but we translate it a bit in the y-direction.
Oh, I’ve also used more than one image, to get a change in the color palette.
Here’s the code

int w = 1900;
int h = 800;
int cols;
int rows;
int scl = 30;

PImage[] img = new PImage[3];
PImage buff;

float speed = 0.01;
int speed2 = 0;
float r = 0;

void setup() {
size(1200, 720, P3D);
background(0);

//Load the images we are using as textures;
img[0] = loadImage("path_to_image1");
img[1] = loadImage("path_to_image2");
img[2] = loadImage("path_to_image3");

for (int i = 0; i < img.length; i++) {
img[i].resize(1900, 2000);
img[i].filter(BLUR, 0.6);
}
buff =img[0];

noStroke();

cols = w / scl;
rows = h / scl;
}

void draw() {

//Triggers a "beat";
if ( (random(0.0, 1.0) < 0.05) & (r < 0.1) ) {
r = 8.0;
}

//This allows for some release time;

if (r > 0.01) {
r *= 0.95;
}

//From time to time, choose another texture image;
if (random(0.0, 1.0) < 0.008) {
int i = int(random(0, 3));
buff = img[i];
}

float yoff = speed;
speed -= 0.03;
if (frameCount%2 == 0) {
speed2 += 1;
}
speed2 = speed2 % 60;

translate(width/2, height/8);
rotateX(PI/3);
translate(-w/2 + sin(frameCount * 0.003)* 20, -h/2, -450); //The sin function allows for some left/right shift

//Building the mesh

for (int y = 0; y < rows; y++) {
beginShape(TRIANGLE_STRIP);
tint(255, 14);
texture(buff); //Using the chosen image as a texture;
float xoff = 0;
for (int x = 0; x < cols + 1; x++) {
vertex(x * scl, y * scl, map(noise(xoff, yoff), 0, 1.0, -60, 60) * (r + 2.9), x * scl, (y + speed2) % 2000 * scl);
vertex(x * scl, (y + 1) * scl, map(noise(xoff, yoff + 0.1), 0, 1.0, -60, 60) * (r + 2.9), x * scl, ((y + 1 + speed2) % 2000) * scl);
xoff += 0.1;
}
endShape();
yoff += 0.1;
}
}

Here’s it how it looks like

Very floaty and cloud-like, no? ðŸ˜‰

Exercise 1: instead of loading images, use the frames of a video as textures.
Exercise 2: instead of controlling the z-coordinate of a point (x,y) via a Perlin noise function, use the brightness at (x,y) of the texture image obtained in Exercise 1.
Exercise 3: Enjoy.

*One should really think of a mesh as an object which stores information about vertices, edges, etc., while we are here concerned only with displaying a deformed grid.

There are no comments.

Current ye@r *

TrackBack URL :

• ## Recent Comments

• This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.