简体   繁体   中英

GLSL: Fade 2D grid based on distance from camera

I am currently trying to draw a 2D grid on a single quad using only shaders. I am using SFML as the graphics library and sf::View to control the camera. So far I have been able to draw an anti-aliased multi level grid. The first level (blue) outlines a chunk and the second level (grey) outlines the tiles within a chunk.

在此处输入图片说明

I would now like to fade grid levels based on the distance from the camera. For example, the chunk grid should fade in as the camera zooms in. The same should be done for the tile grid after the chunk grid has been completely faded in.

I am not sure how this could be implemented as I am still new to OpenGL and GLSL. If anybody has any pointers on how this functionality can be implemented, please let me know.

Vertex Shader

#version 130
out vec2 texCoords;

void main() {
    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
    texCoords = (gl_TextureMatrix[0] * gl_MultiTexCoord0).xy;
}

Fragment Shader

#version 130

uniform vec2 chunkSize = vec2(64.0, 64.0);
uniform vec2 tileSize = vec2(16.0, 16.0);

uniform vec3 chunkBorderColor = vec3(0.0, 0.0, 1.0);
uniform vec3 tileBorderColor = vec3(0.5, 0.5, 0.5);

uniform bool drawGrid = true;

in vec2 texCoords;

void main() {
    vec2 uv = texCoords.xy * chunkSize;
    
    vec3 color = vec3(1.0, 1.0, 1.0);

    if(drawGrid) {
        float aa = length(fwidth(uv));

        vec2 halfChunkSize = chunkSize / 2.0;
        vec2 halfTileSize =  tileSize / 2.0;
        vec2 a = abs(mod(uv - halfChunkSize, chunkSize) - halfChunkSize);
        vec2 b = abs(mod(uv - halfTileSize, tileSize) - halfTileSize);

        color = mix(
            color, 
            tileBorderColor, 
            smoothstep(aa, .0, min(b.x, b.y))
        );

        color = mix(
            color, 
            chunkBorderColor, 
            smoothstep(aa, .0, min(a.x, a.y))
        );
    }

    gl_FragColor.rgb = color;
    gl_FragColor.a = 1.0;
}   

You need to split your multiplication in the vertex shader to two parts:

// have a variable to be interpolated per fragment
out vec2 vertex_coordinate;
...
{
  // this will store the coordinates of the vertex
  // before its projected (i.e. its "world" coordinates)
  vertex_coordinate = gl_ModelViewMatrix * gl_Vertex;

  // get your projected vertex position as before
  gl_Position = gl_ProjectionMatrix * vertex_coordinate;
  ...
}

Then in the fragment shader you change the color based on the world vertex coordinate and the camera position:

in vec2 vertex_coordinate;
// have to update this value, every time your camera changes its position
uniform vec2 camera_world_position = vec2(64.0, 64.0);
...
{
...
  // calculate the distance from the fragment in world coordinates to the camera
  float fade_factor = length(camera_world_position - vertex_coordinate);

  // make it to be 1 near the camera and 0 if its more then 100 units.
  fade_factor = clamp(1.0 - fade_factor / 100.0, 0.0, 1.0);

  // update your final color with this factor
  gl_FragColor.rgb = color * fade_factor;
...
}

The second way to do it is to use the projected coordinate's w . I personally prefer to calculate the distance in units of space. I did not test this code, it might have some trivial syntax errors, but if you understand the idea, you can applied it in any other way.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM