Jump to content
  • Advertisement
Sign in to follow this  
  • entries
  • comments
  • views

Seams Shady

Sign in to follow this  


My lightmap code has been working well for a long time. I haven't touched the chart generation or chart packing in forever, and haven't had any issues with them - until last Saturday.

I made a level with a couple of ramps that you could also walk under. When I went to light it, I got some very strange artifacts with the lighting. When I looked at it closely, it looked as if the top of the ramp were somehow fighting with the floor underneath the ramp.

Because the ramp was connected to the floor, shallow, and facing mainly up, it was included in the same chart ( lightmap triangle group ) as the floor. The ramp top and floor were overlapping in UV space.

I verified this with Ultimate Unwrap3D after dumping out the level geometry, then set about finding a solution.

My algorithm was a local greedy recursive algorithm that, for each neighbor tri not already claimed in a chart, recursively called it and its neighbors to add them to the chart group.

What I needed was some sort of more global overview that could detect & prevent overlaps in uv space. Obviously a triangle is not going to have an overlap problem with its neighbor, if the uv mapping is reasonable, b/c there's no way to overlap a tri with a connected tri without flipping it over, so there needed to be some sort of persistence on a per-chart basis to find overlaps.

My first attempt was to have a per-chart AABB tree for each existing triangle, do a quick test for overlaps, then do triangle->triangle testing geometrically. This extra memory during the recursive call was causing a stack overflow, and I wasn't totally confident that the overlap testing math would handle the common case where two tris make a quad, and touch but don't overlap.

So, I decided to software rasterize instead. For each chart, I clear a 512x512 area of memory, and then write an 'x' into each spot containing a triangle. Before adding each additional neighbor tri, I check it by walking through the triangle pixels and testing for the fill color first. If there is no conflict, then I put 'x' in to the triangle's pixels.

Once I got this working as expected, I ran into an unexpected problem. Basically, I was getting non-overlapping areas, but since the tris came in to the grouping algorithm in random order, I got strange chart shapes like jigsaw puzzle pieces. This was causing havoc with the texel lightmap code which implicitly assumes that a nearby texel in lightmap space corresponds to a nearby texel in world space. This caused no lighting errors on the actual lightmap texels, but on the chart border pixels, which would bleed in due to bilinear filtering.

I then realized that one option to reduce the problem was to take advantage of the fact that the triangle order was random. In other words, I was free to order the triangles in any way I wanted, and to traverse the neighbors in any order, so I sorted the 3 neighbors of each triangle to chart those neighbors with the most similar normals first.

This solved the strange shape issue, but now there were nice straight seams.

The last fix was to add a feature to the level editor. The user can add a trigger, really only needed at the start of a ramp that could cross over the floor later on, that marks which triangles should be in their own chart. These marked charts are handled first, and then the normal algorithm proceeds afterwards.

I'm happy to report that this fixes all known issues.....
Sign in to follow this  


Recommended Comments


Could you not have tried gutters to avoid the linear filtering artefacts? Sure it wastes a few pixels but it isn't hard to implement...


Share this comment

Link to comment
Sure, you can use gutters, and then have other artifacts at seams. If you have a shadow that falls across a lightmap border, you if you either duplicate or copy texels from local neighbors, you will get artifacts - I tried this myself in an earlier version of the engine.

The only way to avoid artifacts in all scenarios would be to copy the world-space neighbor texels directly from another lightmap or chart in the lightmap. The next best thing, which is what I do, is to perform the lighting on the edge texels, which is still correct in the case where the two chart neighbors are relatively flat wrt each other.

Share this comment

Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Advertisement

Important Information

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

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!