This is a series of blog posts related to WebGL. New post will be available every day

Join mailing list to get new posts right to your inbox

Built with

Hey 👋

Welcome to WebGL month

Today we're going to improve our 3D minecraft terrain scene with fog

Basically we need to "lighten" the color of far cubes (calculate distance between camera and cube vertex)

To calculate relative distance between camera position and some point, we need to multiply position by view and model matrices. Since we also need the same resulting matrix together with projection matrix, let's just extract it to a variable

📄 src/shaders/3d-textured.v.glsl

```
}
void main() {
- gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(position, 1.0);
+ mat4 modelView = viewMatrix * modelMatrix;
+
+ gl_Position = projectionMatrix * modelView * vec4(position, 1.0);
vTexCoord = texCoord;
vColor = encodeObject(index);
```

Since our camera looks in a negative direction of Z axis, we need to get `z`

coordinate of resulting vertex position

📄 src/shaders/3d-textured.v.glsl

```
gl_Position = projectionMatrix * modelView * vec4(position, 1.0);
+ float depth = (modelView * vec4(position, 1.0)).z;
+
vTexCoord = texCoord;
vColor = encodeObject(index);
```

But this value will be negative, while we need a positive value, so let's just negate it

📄 src/shaders/3d-textured.v.glsl

```
gl_Position = projectionMatrix * modelView * vec4(position, 1.0);
- float depth = (modelView * vec4(position, 1.0)).z;
+ float depth = -(modelView * vec4(position, 1.0)).z;
vTexCoord = texCoord;
vColor = encodeObject(index);
```

We can't use `depth`

directly, since we need a value in `[0..1]`

range. Also it'd be nice to have a smooth "gradient" like fog. We can apply glsl smoothstep function to calcuate the final amount of fog. This function interpolates a value in range of `lowerBound`

and `upperBound`

. Max depth of our camera is `142`

```
mat4.perspective(
projectionMatrix,
(Math.PI / 360) * 90,
canvas.width / canvas.height,
0.01,
142 // <- zFar
);
```

So the max value of `depth`

should be < 142 in order to see any fog at all (object farther than 142 won't be visible at all). Let's use `60..100`

range.

One more thing to take into account is that we don't want to see the object *completely* white, so let's multiply the final amount by `0.9`

We'll need the final value of `fogAmount`

in fragment shader, so this should be a `varying`

📄 src/shaders/3d-textured.v.glsl

```
varying vec2 vTexCoord;
varying vec3 vColor;
varying vec4 vColorMultiplier;
+ varying float vFogAmount;
vec3 encodeObject(float id) {
int b = int(mod(id, 255.0));
gl_Position = projectionMatrix * modelView * vec4(position, 1.0);
float depth = -(modelView * vec4(position, 1.0)).z;
+ vFogAmount = smoothstep(60.0, 100.0, depth) * 0.9;
vTexCoord = texCoord;
vColor = encodeObject(index);
```

Let's define this varying in fragment shader

📄 src/shaders/3d-textured.f.glsl

```
uniform float renderIndices;
varying vec4 vColorMultiplier;
+ varying float vFogAmount;
void main() {
gl_FragColor = texture2D(texture, vTexCoord * vec2(1, -1) + vec2(0, 1)) * vColorMultiplier;
```

Now let's define a color of the fog (white). We can also pass this color to a uniform, but let's keep things simple

📄 src/shaders/3d-textured.f.glsl

```
void main() {
gl_FragColor = texture2D(texture, vTexCoord * vec2(1, -1) + vec2(0, 1)) * vColorMultiplier;
+ vec3 fogColor = vec3(1.0, 1.0, 1.0);
+
if (renderIndices == 1.0) {
gl_FragColor.rgb = vColor;
}
```

and finally we need to mix original color of the pixel with the fog. We can use glsl mix

📄 src/shaders/3d-textured.f.glsl

```
gl_FragColor = texture2D(texture, vTexCoord * vec2(1, -1) + vec2(0, 1)) * vColorMultiplier;
vec3 fogColor = vec3(1.0, 1.0, 1.0);
+ gl_FragColor.rgb = mix(gl_FragColor.rgb, fogColor, vFogAmount);
if (renderIndices == 1.0) {
gl_FragColor.rgb = vColor;
```

That's it, our scene is now "foggy". To implement the same effect, but "at night", we just need to change fog color to black.

Thanks for reading!

Join mailing list to get new posts right to your inbox

Built with

## Discussion (0)