Jump to content
  • Advertisement
Sign in to follow this  
ChugginWindex

OpenGL Efficient drawing of 2d gridlines with shaders

This topic is 2357 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I'm working on an android app and I'm targeting OpenGL ES 2 only. I want to draw grid lines around quads and I'm currently compiling them as a VBO and drawing them after I draw the quad itself. It currently looks like this:
Untitled.png

Now as you can see the grid lines aren't entirely symmetrical with respect to the quad itself. Neither is the fade-out I'm applying in the shader. I know I could fix this with a bit of math when I'm calculating the grid line VBO and applying a translation to the center-point used to calculate the fade, but that won't work when I start varying the size of the quads itself. Is there a simpler way to draw the grid lines than what I've come up with? I just want them to show up surrounding the quad and with a bit of a fade off that I can adjust no matter whether the quad is 1 unit or 3 units on a side or whatever. I've considered using a texture that looks like the grid lines but I don't think that'll work because of aliasing as I zoom out.

Share this post


Link to post
Share on other sites
Advertisement
You can just draw a fullscreen quad with a shader that tests the screen position. Use the modulus operator (%) to test if a point lies inside the grid (and then you can calculate an opacity value)

Share this post


Link to post
Share on other sites

You can just draw a fullscreen quad with a shader that tests the screen position. Use the modulus operator (%) to test if a point lies inside the grid (and then you can calculate an opacity value)


I actually tried that recently. The problem is that when you zoom out you get horrendous aliasing issues:

nope.png

Thanks for the reply tho! I'm starting to wonder if this is even possible to do without just calculating a huge vbo to put all the lines in every time the view changes...

Share this post


Link to post
Share on other sites
Hi!
The anti-aliasing is indeed a nasty problem. Would it be of any help to you if lines would fade out, if you zoom out? Let’s say, if you are close you see all lines, if you get farer away, you see each second. If you get even more farer away, you see each fourth and so on. There is an easy way to achieve this for procedural patterns without any need for mipmapped textures (so it is fast). If that would help you, I could search a little in my old code and show that here to you.

Share this post


Link to post
Share on other sites

Hi!
The anti-aliasing is indeed a nasty problem. Would it be of any help to you if lines would fade out, if you zoom out? Let’s say, if you are close you see all lines, if you get farer away, you see each second. If you get even more farer away, you see each fourth and so on. There is an easy way to achieve this for procedural patterns without any need for mipmapped textures (so it is fast). If that would help you, I could search a little in my old code and show that here to you.


Yeah that would be great. I'd considered fading it out with the "altitude" of the camera so to speak, but I hadn't considered skipping grid lines entirely like you suggested. I might give this a shot myself if I get time today but if you've got something similar I'd love to take a look.

Share this post


Link to post
Share on other sites
Alright. You can render a quad in world space with texture coordinates (tx) in [0,1]. We are going to use the screen space derivatives of the texture coordinate to compute the best fitting mipmap level. This is going to be some rational number so we will compute the pattern for the two adjacent integer levels and then interpolate between them. Since, we use texture derivatives to estimate the mipmap level, and not only the depth, this approach also works for different viewing angles. (Besides, texture2D uses the same approach to compute the mipmap level.) You find some notes on this approach in the IRIS paper of Hummel et al. in section 4.1.

// get the length of the texture derivatives
float lambda_s = length(dFdx(tx));
float lambda_t = length(dFdy(tx));

// compute the sample level, based on the maximum derivative, and the interpolation factor between ceiled and floored level
float level_s;
float interp_s = modf(-log2(max(lambda_s, lambda_t)), level_s);

// get the new texture coordinate by scaling the texture coordinate by the floored and ceiled level
vec2 tx_down = tx * pow(2, level_s);
vec2 tx_up = tx * pow(2, level_s + 1);

// compute the color procedural
vec2 highlight_down = clamp(cos(tx_down * 0.2) - 0.95, 0,1) * 10;
vec2 highlight_up = clamp(cos(tx_up * 0.2) - 0.95, 0,1) * 10;
// this might be faster, but it doesn't fade nicely...
//float highlight_down = step(0.9, fract(tx_down * 0.04)) * 0.8;
//float highlight_up = step(0.9, fract(tx_up * 0.04)) * 0.8;

// lerp between the intensities
fragColor = vec4(1 - clamp(mix(
max(highlight_down.x, highlight_down.y),
max(highlight_up.x, highlight_up.y),
interp_s), 0,1));


The scale factor in the computation of the procedural pattern depends on the size of your quad.
As Hummel I used this too, to put some path lines on a streak surface. Fairly simple to use and works great.

I hope this helps you a little. smile.png

Share this post


Link to post
Share on other sites
Hmm, I'm struggling to get this working. Still relatively new to shaders.

I had to switch one thing to get it running at all, you had this:


// compute the sample level, based on the maximum derivative, and the interpolation factor between ceiled and floored level
int level_s;
float interp_s = modf(-log2(max(lambda_s, lambda_t)), level_s);


and from what I've read about modf(), it actually needed to be this:


// compute the sample level, based on the maximum derivative, and the interpolation factor between ceiled and floored level
float interp_s;
int level_s = modf(-log2(max(lambda_s, lambda_t)), interp_s);


No big deal I think. But this is what I get when I run the program:

nope2.png

Share this post


Link to post
Share on other sites
Hi!
The order of parameters in modf was right. GLSL just wants the out parameter to be float, too. Sorry for that inconvenience.
I attached you a small sample and updated the code above. smile.png

Share this post


Link to post
Share on other sites
Okay it worked after changing that one value. I'm sorry tho but I'm really having trouble understand how this works exactly. What's involved in modifying this to do vertical lines as well? I took a look at the paper you linked to but I'm failing to find anything familiar in how this works, so I can't seem to see what I would add for verticals.

Share this post


Link to post
Share on other sites
The attached code from my post before and the piece of code above do both vertical and horizontal lines.
Let’s have a closer look then. I’ll just go over the shader code and tell you a little more about it.

At first we look how much the texture coordinate changes vertically and horizontally. If it changes very much we have to go down in the mipmap pyramid to some very coarse level. We will use the maximum of both as a conservative choice. (There would be other options which give qualitative better results if we look at the quad under some small angle, but for your use case this is totally fine, since you are looking right on top of it.)
// get the length of the texture derivatives
float lambda_s = length(dFdx(tx));
float lambda_t = length(dFdy(tx));


With each mipmap level we would like to halve the number of lines. We can do this by scaling our pattern. In fact, we would like to scale it by 2^miplevel. By this we ensure that with each level every second line vanishes.
Alright, here is an example:
[font=courier new,courier,monospace]|||||||||||||[/font] mip level: 0 (scaled by 1=2^0)
[font=courier new,courier,monospace]|_|_|_|_|_|_|[/font] mip level: 1 (scaled by 2=2^1)
[font=courier new,courier,monospace]|___|___|___|[/font] mip level: 2 (scaled by 4=2^2)
[font=courier new,courier,monospace]|_______|___ [/font]mip level: 3 (scaled by 8=2^3)
[font=courier new,courier,monospace]|___________ [/font] ...
As you can see we have exponential growth. This isn’t intended, since we would like our pattern to double linearly with distance to the quad. For this reason we take the log2 of the derivatives. Afterwards we have a nice linear miplevel, let’s call it m.
Since we can only compute the pattern at the integer miplevels and not in between them we have to find out which two levels are adjacent to our miplevel. Let’s say our miplevel is m=2.6. Then we would have to compute the pattern for the levels 2 and 3 and have to interpolate between them at 0.6. The lower level (in my example the 2) is stored in level_s and the 0.6 is store in interp_s.
// compute the sample level, based on the maximum derivative, and the interpolation factor between ceiled and floored level
float level_s;
float interp_s = modf(-log2(max(lambda_s, lambda_t)), level_s);


Okay, now comes the actual scaling of our pattern. As I have illustrated before, we scale the texture coordinate by a power of two. The lower adjacent mipmap level is level_s and the upper adjacent mipmap level is level_s+1.
// get the new texture coordinate by scaling the texture coordinate by the floored and ceiled level
vec2 tx_down = tx * pow(2, level_s);
vec2 tx_up = tx * pow(2, level_s + 1);


Next we compute the pattern. Therefore I just used a cosine, because it is periodic. As you now it is in [-1,1]. I moved that range down to [-1.95, 0.05] which gives us only closely around the maxima some positive values. Clamping this and scaling it by some big number gives us some peaks at the maximum with a small falloff to 0.
// compute the color procedural
vec2 highlight_down = clamp(cos(tx_down * 0.2) - 0.95, 0,1) * 10;
vec2 highlight_up = clamp(cos(tx_up * 0.2) - 0.95, 0,1) * 10;


The following thing does also give you a procedural pattern, but it won’t have that small falloff.
// this might be faster, but it doesn't fade nicely...
//float highlight_down = step(0.9, fract(tx_down * 0.04)) * 0.8;
//float highlight_up = step(0.9, fract(tx_up * 0.04)) * 0.8;


With that we have computed the pattern in both, the x and y component.
highlight_down.x is the horizontal pattern in the lower adjacent mipmap level.
highlight_down.y is the vertical pattern in the lower adjacent mipmap level.
And for the upper adjacent mipmap level we have the same with highlight_up.
We will show a line if either the vertical or horizontal component indicate the presence of a line, so we can simply take the maximum of both.
And last but not least we will interpolate between both adjacent miplevels. And we are done. smile.png
The 1-clamp(...) in the end is just done to invert the lines (lines are black now, the rest is white).
// lerp between the intensities
fragColor = vec4(1 - clamp(mix(
max(highlight_down.x, highlight_down.y),
max(highlight_up.x, highlight_up.y),
interp_s), 0,1));


I hope that clears this up a little. smile.png

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!