0
votes

I am currently trying to implement Ambient Occlusion in a simple Voxel Renderer, each block is rendered (unless not visible) with 24 vertices and 12 triangles, so no meshing of any kind.

I followed this tutorial on how ambient occlusion for minecraft-like worlds could work, but it didn't explain how to actually implement that in shaders / graphics programming, so what I've done so far are only my uneducated best guesses.

This is what it looks like currently

The basic idea is, for each vertex to generate a value from the surrounding blocks about how "dark" the ambient occlusion should be. (Nevermind my values being completely wrong currently). These are from 0 (no surroundings) to 3 (vertex completely surrounded) The picture in the tutorial I linked helps to explain that.

So I tried that, but it seems to not darken the area around a vertex, but instead the whole triangle, the vertex is in... How do I make it not do that? I am a total beginner in shaders and graphics programming, so any help is appreciated :D

Here is my vertex shader, the inputs are

  • position of vertex
  • normal of vertex
  • tex_coord in texture atlas
  • tile_uv: position on block (0 or 1 for left/right bottom/top)
  • the ambient_occlusion value
#version 450

layout(location = 0) in vec3 position;
layout(location = 1) in vec3 normal;
layout(location = 2) in vec2 tex_coord;
layout(location = 3) in vec2 tile_uv;
layout(location = 4) in uint ambient_occlusion;

layout(location = 0) out vec3 v_position;
layout(location = 1) out vec3 v_normal;
layout(location = 2) out vec2 v_tex_coord;
layout(location = 3) out vec2 v_tile_uv;
layout(location = 4) out uint v_ambient_occlusion;

layout(set = 0, binding = 0) uniform Data {
    mat4 world;
    mat4 view;
    mat4 proj;
    vec2 tile_size;
} uniforms;

void main() {
    mat4 worldview = uniforms.view * uniforms.world;
    v_normal = mat3(transpose(inverse(uniforms.world))) * normal;
    v_tex_coord = tex_coord;
    v_tile_uv = tile_uv;
    v_position = vec3(uniforms.world * vec4(position, 1.0));
    v_ambient_occlusion = ambient_occlusion;
    gl_Position = uniforms.proj * worldview * vec4(position.x, position.y, position.z, 1.0);
}

And here is my fragment shader:

#version 450

layout(location = 0) in vec3 v_position;
layout(location = 1) in vec3 v_normal;
layout(location = 2) in vec2 v_tex_coord;
layout(location = 3) in vec2 v_tile_uv;
layout(location = 4) in flat uint v_ambient_occlusion;

layout(location = 0) out vec4 f_color;

layout(set = 0, binding = 1) uniform sampler2D block_texture;

void main() {
    vec3 ao_color;
    switch (v_ambient_occlusion) {
        case 0: ao_color = vec3(1.0, 0.0, 0.0); break;
        case 1: ao_color = vec3(0.0, 1.0, 0.0); break;
        case 2: ao_color = vec3(0.0, 0.0, 1.0); break;
        case 3: ao_color = vec3(1.0, 1.0, 1.0); break;
    }

    f_color = texture(block_texture, v_tex_coord);
    f_color.rgb = mix(f_color.rgb, vec3(0.05, 0.05, 0.05), 0.3 * v_ambient_occlusion * distance(v_tile_uv, vec2(0.5)));
}
1

1 Answers

2
votes

The "flat" part of this ...

layout(location = 4) in flat uint v_ambient_occlusion;

... means that the same value is used for the whole triangle (taken from the provoking vertex). If you want interpolation between the 3 per-vertex values then just remove the flat qualifier.

Note that currently this is an integer attribute value, which must be flat shaded, so you'll need to convert the input into a float too.