Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I render an infinite 2D grid in GLSL?

Tags:

opengl

glsl

Ideally, what I'd like to do is draw a single quad and have GLSL handle the creation of the actual gridlines.

In my attempt so far the vertex shader:

#version 400

layout (location = 0) in vec4 in_position;
layout (location = 2) in vec3 in_UV;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

smooth out vec3 ex_UV;
smooth out vec3 ex_originalPosition;

void main()
{
    gl_Position = projection * view * model * in_position;

    ex_UV = in_UV;
    ex_originalPosition = vec3(in_position.xyz);
}

The model matrix scales the quad up by some large number, like 10,000.

#version 400

layout (location = 0) out vec4 color;

smooth in vec3 ex_UV;
smooth in vec3 ex_originalPosition;

uniform vec4 lineColor;

void main(void)
{
    if(fract(ex_UV.x / 0.001f) < 0.01f || fract(ex_UV.y / 0.001f) < 0.01f)
        color = lineColor;
    else
        color = vec4(0);
}

I've tried using both the Texture coords and the world space positions for this. Both result in the same affect which looks fine at some angles, but at others it starts looking horrid.

enter image description here

enter image description here

I thought maybe I could scale the alpha depending on the distance, so that my grid fades away into nothing, but the problem is that you can see in the second image, from the center of the screen, you can see that even those lines are being rendered with gaps through them (the point of the grid is to give a visual frame of reference around the origin).

Is there a simpler way to do this?

EDIT

Screenshots as requested:

VBO Lines with x8 Multisampled Framebuffer

enter image description here

VBO Lines with x8 Multisampled Framebuffer and glEnable(GL_LINE_SMOOTH)

enter image description here

I settled on

I settled on the either of the above (doesn't really matter which) and simply lowered the alpha as a function of distance from the origin. This serves my purpose though it isn't the question I asked. enter image description here

like image 654
NeomerArcana Avatar asked Jun 15 '15 10:06

NeomerArcana


People also ask

How do I create a grid in OpenGL?

this the code for render the grid: glEnable(GL_DEPTH_TEST); glBindVertexArray(vao); glDrawElements(GL_LINES, lenght, GL_UNSIGNED_INT, NULL); glBindVertexArray(0); glDisable(GL_DEPTH_TEST); c++ opengl.

What is Fwidth?

fwidth — return the sum of the absolute value of derivatives in x and y.

How do GLSL shaders work?

GLSL is executed directly by the graphics pipeline. There are several kinds of shaders, but two are commonly used to create graphics on the web: Vertex Shaders and Fragment (Pixel) Shaders. Vertex Shaders transform shape positions into 3D drawing coordinates.


2 Answers

A simple case of aliasing. Just like with polygon rendering, your fragment shader is run once per pixel. Colour is computed for a single central coordinate only and is not representative of the true colour.

enter image description here

  1. You could create a multisample FBO and enable super-sampling. But that's expensive.
  2. You could mathematically compute exactly how much line area and empty grid area is under each pixel, then colour them accordingly in the fragment shader. Given it's a uniform grid this might be in the realm of possible, but the maths might still get pretty complicated.
  3. Mipmapping already does this for textures. Create a grid texture with just a few lines and map it so it repeats for your really large quad (make sure to set GL_REPEAT). Set up the correct mipmap filtering parameters for the texture and call glGenerateMipmap. When you call texture2D()/texture() in the fragment shader OpenGL automatically calculates which level of the mipmap to use based off the texture coordinate delta between the adjacent pixels. Finally, set up anisotropic filtering for an even more amazing looking grid.

    • https://www.opengl.org/registry/specs/EXT/texture_filter_anisotropic.txt
    • https://gamedev.stackexchange.com/q/69374/34435

    enter image description here

If you want a grid to be truly "infinite", I've seen some ocean renderers connect the edges of the grid to the horizon with vertical geometry. If you have enough grid before them, you could possibly get away with setting them to one flat colour - the colour at the top level of your mipmap.


Examples (relating to comments):

1024x2 GL_LINES drawn from a VBO
enter image description here
45fps (drawn 100 times for benchmark at HD res)

See the comments regarding multisampling to address GL_LINES aliasing.

A 32^2 texture mapped to a quad with mipmapping
enter image description here
954fps (drawn 100 times for benchmark at HD res)

Image img;
int w = 128;
int h = 128;
img.resize(w, h, 1);
for (int j = 0; j < h; ++j)
    for (int i = 0; i < w; ++i)
        img.data[j*w + i] = (i < w / 16 || j < h / 16 ? 255 : 0);

tex = img.upload();

glBindTexture(GL_TEXTURE_2D, tex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 16);
glGenerateMipmap(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, 0);

...

//for the quick and dirty, immediate mode
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, tex);
glBegin(GL_QUADS);
glTexCoord2f(0,       0); glVertex3f(0, 0, 0);
glTexCoord2f(1024,    0); glVertex3f(1, 0, 0);
glTexCoord2f(1024, 1024); glVertex3f(1, 0, 1);
glTexCoord2f(0,    1024); glVertex3f(0, 0, 1);
glEnd();
glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
like image 138
jozxyqk Avatar answered Sep 20 '22 21:09

jozxyqk


you can see that even those lines are being rendered with gaps through them

Oh, of course we can. And this is because you've offended CG 101. (No offense :))

We use GL_LINES for a reason; drawing gapless lines on a computer screen isn't trivial (albeit solved). I'm talking about something like this. What you did does computations on floating-point numbers and will amount to sub-pixel lines being drawn (i.e., gaps).

My suggestion would be to scrap this GLSL code and generate the lines as regular vertices. It's going to be probably faster, and you'll get proper results.

You'll also be able to get the "fade in the distance" effect you want using regular fog computations.

like image 33
Bartek Banachewicz Avatar answered Sep 21 '22 21:09

Bartek Banachewicz