Marble

How to 3D

Chapter 11: Noise

Marble

Suppose we want to make a model appear to be carved from marble. Real marble is made by building up layers of different minerals over a few millenia. Let's try something similar but faster.

With 2D texturing, we unwrap the triangles onto the texture to assign texture coordinates. With 3D texturing, we don't need to unwrap. The model is 3D and so is the texture. Often we can find some mathematical relationship between the model's vertex positions and the 3D texture coordinates. For example, we could map the model's bounding box to the [0, 1] range expected of texture coordinates. In the renderer below, the model is a sphere in [-1, 1]. In the vertex shader, we rangemap the position to texture coordinates with this assignment:

GLSL
mixTexPosition = position * 0.5 + 0.5;
mixTexPosition = position * 0.5 + 0.5;

The model is colored by interpreting the 3D texture coordinates as RGB values:

We achieve strata by feeding the texture coordinates through the sine function in the fragment shader. This assignment produces perfectly horizontal layers by considering only the y-coordinate:

GLSL
float strataness = sin(mixTexPosition.y) * 0.5 + 0.5;
fragmentColor = vec4(vec3(strataness), 1.0);
float strataness = sin(mixTexPosition.y) * 0.5 + 0.5;
fragmentColor = vec4(vec3(strataness), 1.0);

The sine function yields a value in [-1, 1]. We range-map it to [0, 1] so that we can use it as a color.

Let's tip the strata off the vertical axis by summing the x- and y-coordinates:

GLSL
float d = mixTexPosition.x + mixTexPosition.y;
float strataness = sin(d) * 0.5 + 0.5;
float d = mixTexPosition.x + mixTexPosition.y;
float strataness = sin(d) * 0.5 + 0.5;

The name d is a mnemonic for distance. In the renderer above, check the Marble checkbox to see this tipped oscillating pattern.

Maybe there aren't enough layers. We increase their frequency by scaling the distance, like this:

GLSL
float d = (mixTexPosition.x + mixTexPosition.y) * frequency;
float strataness = sin(d) * 0.5 + 0.5;
float d = (mixTexPosition.x + mixTexPosition.y) * frequency;
float strataness = sin(d) * 0.5 + 0.5;

The frequency is sent as a uniform to the fragment shader. Try changing the frequency in the renderer above.

All that remains is to disrupt the oscillating pattern so it's not so perfect. That's where the noise comes in. We look up the noise value in the 3D texture, scale it, and add it to the distance:

GLSL
float noise = texture(noiseTexture, mixTexPosition).r;
float shift = noise * strength;
float strataness = sin(d + shift) * 0.5 + 0.5;
float noise = texture(noiseTexture, mixTexPosition).r;
float shift = noise * strength;
float strataness = sin(d + shift) * 0.5 + 0.5;

Try changing the strength in the renderer above.

The marbling is neat, but its effect could be improved by using the strataness to look up a color in a colormap that was more than just black and white.

← 3D NoiseWood →