Problem with lighting artifacts from hidden faces in my scene.

Started by
7 comments, last by mrgnpz 8 years, 8 months ago

Hi all. I've been going through some OpenGL tutorials and have implemented a fairly basic ambient / diffuse / specular system based on this tutorial.

I have a test scene (using Java/LWJGL) which you can see here. I'm generating a wall out of "wall tiles", with each wall model placed exactly next to each other in a line. As you can see in that picture with the helpful arrows, there are artifacts from the lights present along the wall.

I assume this is because the hidden side faces of each wall tile are being lit up (seen here ) by the lights.

Making a single model would of course solve this, but I'd like to be able to generate maps using various pre-made tiles and so it would be really great if I could get around this problem.

Is there some kind of usual way this kind of thing is solved? Or a way to prevent these faces from being lit up like this?

Thanks!

Advertisement

If it's because of the backfaces being lit (you've ruled out anti-aliasing?), this would only happen when you place the lights exactly on the same plane as your wall tiles (or behind the wall), due to floating-point precision errors. So you should instead place your lights at a small distance in front of the wall.

Hey there.

I don't think it's backfaces but rather front-facing hidden side edges, or at least I think so. Here's an image where I've spaced the wall tiles out so you can see better: http://i.imgur.com/KVqsFCC.png To me it looks like the lighting there is somehow just bleeding through and creating the artifacts.

The wall tiles are all placed as x=0, y=0, with varying z. I tested with a single light placed at x=20, y=20, z=-20 and the artifacts still appear, most noticeably when viewing at an angle that would cause the sides to be the brightest.

Good suggestion about AA but I checked and that wasn't enabled for this testing, and nothing's being forced with the graphics card either.

Is there some way to not light these faces at all? Or would I then just be getting black pixel artifacts instead?

The first image displays cracks between the tiles. This is caused when the two objects are not completely aligned with each other, which can happen for various reasons including floating point in accuracies. This can be solved by ensuring that the vertices from one block align exactly with the neighboring vertices in the next block.

Typically this means ensuring that both objects have vertex positions that are exactly completely aligned and that the blocks themselves are also spaced such that the vertices neighbor each other will also be 100% on the same positions. However even if this is true with the blocks before the vertex shader, passing a different matrix inside of the vertex shader can result in tiny discrepancies which can result in these holes between the two blocks.

A possible easy way to fix this is to round the values off inside of the vertex shader to the nearest small unit such as 0.0125 or so after transforming them by the world matrix (and ensuring that the exact same view projection matrix is used).

The second picture's problem is not clear but could indicate Z-fighting.
There are many topics and strategies for combating this issue, and by searching for that term you should be able to find one suitable for your current situation.


L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid

What L. Spiro said. I've seen this a lot before, especially when people try to align cubes. Instead of rounding the positions like L Spiro said, you could also compute the floating point value for the position of the two touching sides of two adjacent tiles only once, like when computing the positions to draw a line-grid. This means that you will have to stretch your wall-tile model to a grid square/cube, instead of just placing it at a fixed position.

But if your problem is caused by z-fighting, you'll have to find another solution, like not drawing the hidden sides of the walls at all, or changing the order in which you draw them, coupled with z-bias.

Thanks both for your replies.

Sorry for the confusing second picture in the OP, that was just to demonstrate that the hidden side faces of each wall were being lit, and that was what I suspected was leaking through the pixels.

I had a go at trying to pre-calculate the positions but that didn't seem to lessen the effect unfortunately. I also tried rounding to the nearest 0.0125 in the shader which also didn't fix the artifacts.

One test I did was to purposely overlap the tiles by half their width, so they were positioned at x=0, y=0, z=0, .5, 1, 1.5 etc. instead of the previous 0, 1, 2, 3. With this setup, there would be no gaps between the models at all, and I still see the artifacts. This leads me to believe that it might well be some sort of z-fighting issue with the side faces and the front faces at the corner edge.

Anything I've read about z-fighting previously usually refers to 2 faces along the same plane having the issue, so I'm not entirely sure what sort of terminology to search for this case. One "workaround" I came up with was to simply delete the side faces of the model entirely, and the issue goes away. This isn't ideal though and I'd much rather have a programmatic solution.

One "workaround" I came up with was to simply delete the side faces of the model entirely, and the issue goes away. This isn't ideal though and I'd much rather have a programmatic solution.

On the contrary, that IS ideal, because you are sending less data to the graphics card. You should only draw the sides of a tile when there is no adjacent tile on that side.

Anything I've read about z-fighting previously usually refers to 2 faces along the same plane having the issue

In your case, the z-fighting happens between the white side-face of a tile in the back, and the red front-face of the adjacent tile drawn in front of it. The reason is that the z-values (or "depth values") that are computed at the position of those white pixels for the two faces are equal. This is because the Z-values are stored as floating-point values in the Z-buffer, and the floating point rounding precision causes the two depth values to be the same.

The issue might also go away if you draw all of the front faces first, then all the sides, but you probably also have to enable enable Z-bias (also called "depth bias") for it to work. But as I said before, there is no point in drawing hidden faces, unless you also want to enable some kind of transparency later on, in which case you will have other issues to deal with.

You could also try increasing the Z-buffer precision if you can (from 16-bit to 32-bit floating points), but that will only decrease the amount of z-fighting - it will not fix it permanently.

And you should also make sure that the near and far planes of your viewing frustum are not too far apart. The Z-coordinates that span the range from the near plane to the far one are all compressed into the [0.0, 1.0] interval when placed in the Z-buffer. So the farther away you put the far plane from the near one, the more "depth" has to be represented in the same amount of floating-point precision in the Z-buffer, leading to more Z-fighting errors.

By the way, did you try using a depth test of lass-than-or-equal?

My concern is that removing the side faces only hides the issue, but if you add the side faces back and try with GL_LEQUAL and it goes away, then the issue is that the side faces are being drawn first and then the front faces are failing the depth test just on the border.

In that case, it means the side faces are safe to remove as an optimization (as mentioned above).

If, however, the issue remains, it means removing the side faces are simply hiding the issue. Draw them against a white background and it may re-appear.

L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid

- precalculate and round the positions on the cpu (use proper rounding / using some round() function or adding 0.5 before casting to int)

- use the same shader and the same world matrix for all cubes

This topic is closed to new replies.

Advertisement