Home Technical Talk

Normal Maps: Can Somone Explain This "Black Edge" issue

polycounter lvl 11
Offline / Send Message
jocose polycounter lvl 11
Hi All,

At the start of this weekend I had a list of normal issues that I was hoping to resolve and so far polycount has helped me with all but one. You guys are awesome, I don't know what I would do with out this community :)

My last issue I'm trying to wrangle with is why black borders along certain UV border edges.

In this write up Ariel Chai shows the following issue:

i_05.png

He explains that:
The problems occurs because each face normalmap points to a different direction as illustrated below.
Even if the uv edge is exactly between the pixels, the way those pixels would be pulled by the realtime renderer will mix them up resulting in a sharp normal transition which stands out from the rest.

From the sounds of it, for some reason, the shader is simply sampling the wrong pixels. However exactly what's going on eludes me.

I am able to reproduce the issue in Xnormal:

blackid.jpg

blackborderissue.jpg

I'm basically trying to figure out the anatomy of the issue. What about those particular edges is making the shader "sample" the wrong pixels, and why would it be doing this in the first place.

My intuitive understanding of what should be happening is that the pixels would be sampled like any others and receive the same lighting information.

I accept that breaking up the UVs fixes this, I would just really like to understand why.

Replies

  • r_fletch_r
    Offline / Send Message
    r_fletch_r polycounter lvl 9
    The way i have it in my head is something like this 'Its sampling normals from the other face. Since the normals were baked 'hard', normals from other faces are incompatible.' Thus you need a bleed of the edge normals to prevent the shader from sampling from the wrong face.
  • MoP
    Offline / Send Message
    MoP polycounter lvl 18
    It's because your mesh has hard edges and the UVs are not broken along the hard edges. You need some "padding" whenever you have a hard edge, or you will get a seam like this in your normals. This is because, as you say, the pixels along that edge are bleeding across to the face on the other side of the edge, which makes no sense in tangent space.

    The only way to avoid this is to either move your UVs apart (create UV seams along the hard edges), or soften the edges.

    It'll become more obvious with mip-mapping too since then the "bleed" will be exacerbated.
  • jocose
    Offline / Send Message
    jocose polycounter lvl 11
    r_fletch_r, and MoP, thanks a bunch for the responces.

    This morning, on my way into work, I had this finally click for me. The issue, in part, is the direction the rays are cast. When you are not using a cage the rays are controlled by the smoothing (angle of the vertex normals).

    This means if you have a hard edge at a 90 degree angle your rays will be shot out of the polygons perpendicular to the surface. This means every pixel on that surface will be expecting to be facing perpendicular to the face on which it rests.

    However texture bleeding means that those pixels will get shifted, 90 degrees in this case, when they bleed between the faces.

    If you use a cage this will reduce the issue because the rays will be cast in such a way that the resulting pixels wont be expecting to be perpendicular to the surface which gives you a bit more leeway with texture bleeding. It will still produce errors, they just wont be as noticeable.

    Ultimately texture bleeding is always going to be an issue with normal maps, but its a lot worse when the rays are cast perpendicular to the surface rather than at an angle.
  • SHEPEIRO
    Offline / Send Message
    SHEPEIRO polycounter lvl 17
    not quite correct...they are two seperate issues... the cage thing is so that the normal and the offset created by the normal map are sampling the same part of the bake... the hard edge with no uv split is creating a seam will happen no matter the cage, as textures filter IE a pixel is a point that is blended to the next pixel in U or V if one edge sample one offset then the other will have a completely different one, the resulting filtering (half half mix) is wrong for both sides of the hard edge giving you the seam... again a cage will help your bake, but you discribe two issues ontop of each other, two problems two solutions just have very similar symptoms
  • jocose
    Offline / Send Message
    jocose polycounter lvl 11
    Ah, very good point, man, I love figuring this all out. The entire process gets more and more intuitive the more questions I ask. Polycount is awesome :)
  • jocose
    Offline / Send Message
    jocose polycounter lvl 11
    Hey SHEPEIRO, actually, I was re-reading what you wrote today and the way you phrased your explanation confuses me a bit. From my understanding the cage is simply to control the direction the rays are cast. Rays are cast out to a corresponding point on the cage and then shot back down. This allows you to specifically control what parts of the model are getting sampled rather than relying on a uniform solution.

    What I was trying to say in my explanation was that, when you use a cage, it can be like baking with averaged vertex normals. This means the difference between the normals of the adjacent pixels (that are getting mixed and causing the problem) is less. Because the rays are being cast around the corner rather than perpendicular to either face (man this stuff is really hard to describe with just words).

    I wasn't trying to say that a cage would fix the issue at all, just that it might make it slightly less noticeable. Is that a correct statement, or have a missed something, I think I might have :(

    Anyways, I didn't want to drag this out, but I wanted to make sure I properly understood this before I moved on.
  • EarthQuake
    What shep is saying is correct, and again these are two separate issues. Let me see if i can clear this up.

    A. When you use an averaged cage on a mesh, you will not get any "gaps" or missed detail from the unwelded vertices that result when you use a hard edge on your low. This can appear to be a similar problem to the other thing, but is *entirely* different.

    B. When you do not split your uvs, but split edges on your lowpoly, what you're getting is essentially two drastically different normal directions(both sides of the same edge) trying to draw on the same pixel. You thus get an averaged result of the two, pointing in a totally broken direction.

    Using an averaged cage will fix the first problem, but will do absolutely nothing to help the second, in any situation. However it is possible to do both of these and get a double-broken result. So perhaps you were doing that and saw some improvements when you fixed the first issue.

    Heres an image.
    edgetest.jpg

    1. Correct method, averaged cage, UVs slit at every hard edge.
    2. Broken, point A above, we've solved the cage problem, but the uv problem still exists.
    3. Broken, point B above, we've solved the UV problem, but the cage problem still exists.
    4. Double broken, both cage and UVs are set up poorly.
  • r_fletch_r
    Offline / Send Message
    r_fletch_r polycounter lvl 9
    EarthQuake wrote: »
    What shep is saying is correct, and again these are two separate issues. Let me see if i can clear this up.

    A. When you use an averaged cage on a mesh, you will not get any "gaps" or missed detail from the unwelded vertices that result when you use a hard edge on your low. This can appear to be a similar problem to the other thing, but is *entirely* different.

    B. When you do not split your uvs, but split edges on your lowpoly, what you're getting is essentially two drastically different normal directions(both sides of the same edge) trying to draw on the same pixel. You thus get an averaged result of the two, pointing in a totally broken direction.

    Using an averaged cage will fix the first problem, but will do absolutely nothing to help the second, in any situation. However it is possible to do both of these and get a double-broken result. So perhaps you were doing that and saw some improvements when you fixed the first issue.

    Heres an image.
    edgetest.jpg

    1. Correct method, averaged cage, UVs slit at every hard edge.
    2. Broken, point A above, we've solved the cage problem, but the uv problem still exists.
    3. Broken, point B above, we've solved the UV problem, but the cage problem still exists.
    4. Double broken, both cage and UVs are set up poorly.

    That was very informative, thanks for posting it. One questions though. Is this such a problem with your new quality normals modifier/shader? i dont seem to be suffering from this issue and i havent been splitting my uvs (using ray distance to bake) Does this only count when using smoothing groups?
  • jocose
    Offline / Send Message
    jocose polycounter lvl 11
    @EQ: Wow, thank you so much for taking the time to illustrate this. That helped so much, and you were exactly right, I was only fixing one issue but not the other so I was fooling myself.

    So my understanding now is that hard edges create "gaps" or missed rays, and that is one issue.

    What I don't understand is this:

    uvimage.jpg

    I have my UVs directly in between the pixels. So why then does the baker mix them? It would make sense if the UV edge was half way on and half way off, but its directly in between them. I guess I'm having trouble wraping my head around why the baker is still mixing the values in this case.

    Hey r_fletch_r, as far as I know the "quality mode" fix doesn't do anything new. These issues exist in all game engines. Its just that the tangent basis between the 3dsmax baker and its viewport renderer are off. So the math within max wasn't syncing up.

    This caused any problem in your normal maps to become worse, it doesn't solve any fundamental issues it just stops max from poring salt on an open wound.
  • jocose
    Offline / Send Message
    jocose polycounter lvl 11
    as textures filter IE a pixel is a point that is blended to the next pixel in U or V

    Just a note of clarification about where I am coming from. This issue appears to be happening with mip mapping turned off. I think this is really just me being ignorant of how exactly these two pixels are being "mixed". If anyone can explain how/why the mixing is occurring I think it will clear this all up for me.

    :)
  • EarthQuake
    r_fletch_r:
    This issue is still the same with Quality mode, its down to the limitations and content of the image/resolution so its not really something we can fix.

    jocose:
    If you can force unfiltered textures you may see a correct result with uvs snapped perfectly like that, its not only a mipping problem but a problem in that texture filtering "blurs" all of your textures, so those pixels are still getting blended together. If it is still an issue after that, you've got me stumped, as how i've always understood the problem is that the two very distinct normal directions blend together and create a normal that is incorrect for both sides of the edge.
  • G3L
    Offline / Send Message
    G3L polycounter lvl 9
    when you guys mention averaged cage, how do you go about doing that in max/maya? i dont think ive noticed a button that says average cage unless im an idiot and haven't seen that.

    i hope this isn't getting off topic but i ran my own tests when i was taking a break from work on thursday and ran these similar tests. then i tried doing tests with a box that had chamfered edges. how would you go about getting good normals out of that? where exactly would you split the uvs and is there any way to get parts of the box seamless as in the edges are sown together kind of like how jocose had in his first example?

    hope this made sense O_O but i was having a problem with a box that had chamfered edges and trying to bake a normal out of the high res, yet the edges came out funky. when i get on my pc later on tonight i'll see if i can post examples of my problem.
  • EarthQuake
    In max your cage is averaged by default, you have to do "use offset" or whatever the check is to get a non-averaged cage(it simply uses a numeric input for ray distance

    In maya, under advanced settings you can select either "surface" or "geometry" normals, i dont remember which is which, but one is averaged and the other is not, experiment there and you'll figure it out.
  • roosterMAP
    Offline / Send Message
    roosterMAP polycounter lvl 14
    if faces are of the same smoothing group, try to connect them. if not, they must be separated.

    Poly's at angles that are 80 adn bellow shouldnt be of the same smoothing group. this rule is very general, so it doent apply as well in other area. chamfering key edges helps if you want to make the the same smoothing group. racer made a great tut. watch it.

    http://cg.tutsplus.com/tutorials/3d-art/how-to-bake-a-flawless-normal-map-in-3ds-max/
  • G3L
    Offline / Send Message
    G3L polycounter lvl 9
    EQ - thanks for the explanation!

    roosterMAP - oooo I've seen his gun tutorial but I take it this one goes more in depth with baking out normals? Thanks for the link.
  • Racer445
    Offline / Send Message
    Racer445 polycounter lvl 12
    don't use that tutorial. it's outdated and contains incorrect information.

    this one isn't as basic though it contains a lot better information: http://cg.tutsplus.com/tutorials/autodesk-3ds-max/project-workflow-creating-a-next-gen-sci-fi-prop-day-2/
  • jocose
    Offline / Send Message
    jocose polycounter lvl 11
    Hey everyone,

    I just wanted to post back here. I made another post over at GameDev.net and posted this issue. After I uploaded a test file one of the members there was kind enough to test it out for me and confirm that the issue is the result of bilinear interpolation.

    He also was kind enough to post an explanation of how it works.

    Here are some pics:

    Bilinear Interpolation:

    67e46a8.jpg

    Linear Interpolation:
    92b333e.jpg

    I didn't actually understand what Bilinear interpolation was. Which was a big part of why I was so confused. Venezon on gamedev.net explained it like this:
    "As you can see from the bilinear filtering image, the center of the enlarged pixel is the same color as the source pixel, but the rest of it is blended with the adjacent pixel (in cases where the adjacent pixel is the same value, you don't notice that it's doing this, but it is)."
    Normally texture filtering wont ever cause a problem provided your tangent space is continuous (meaning you all your normals are averaged). This is because all pixels will always have neighboring pixels whose color values reside on the same space and are there for continuous (have similar color values).

    The issue comes about when we break continuity (introduce a hard edge). This is because the baker correctly assumes that space ends at that hard edge. Meaning that one polygon isn't smoothly interpolated to the next. This creates a major difference in the color values that get baked (a difference that would have never occurred had our tangent space been continuous).

    By having two values that are so different next to each other we open ourselves up for trouble. Once bilinear interpolation is applied the values get blended and we get a normal pointing in a crazy direction.

    This is why we add in the edge padding to basically manually say "don't interpolate this". I suppose you could just think of it as a hack to get around bilinear interpolation which is usually our friend, but is in this case is our enemy.

    Don't know if that helps anyone, but I know feel like I have an intuitive understanding of the issue and also learned a thing or two about texture filtering.

    Here is the GameDev post in case anyone is interested: http://www.gamedev.net/community/forums/topic.asp?topic_id=574157
  • MoP
    Offline / Send Message
    MoP polycounter lvl 18
    Yes, that's a pretty good explanation of it.
  • SimonT
    Offline / Send Message
    SimonT interpolator
    i also had this problem and for me uv edge splitting was the solution:

    tutorial_edge01.jpg
  • PhilipK
    Offline / Send Message
    PhilipK polycounter lvl 10
    SimonT wrote: »
    i also had this problem and for me uv edge splitting was the solution:
    IMAGE

    I agree with you there. It's always a case to case judgement. And splitting/hard edges generates more vertices, still the texture will be a lot more "reusable" if there aren't as many huge gradients on the shells in the texture. If you have a few pretty basic shapes in your texture you'll be able to reuse those a lot easier if they don't have a concave shaped normal over the whole shell.

    When I work with big modular sets I can reuse parts of the texture ridiculously many times but for different objects, not always just copied shapes, and it is here I can really make use of the texture parts more if they are not uniquely made for a certain shading.

    But yeah, very case sensetive :)
  • Firebert
    Offline / Send Message
    Firebert polycounter lvl 15
    I just wanna jump in here and make a request for a Normal Map sticky. While the Wiki holds a wealth of information about it, many new comers and even some that have been around for a while are not fully aware of its existence... or just simply forget about it.

    I am fairly certain that EQ has an entire hard drive of images and text docs dedicated to this subject alone.... ready for any and all new normal map threads that arise since his expertise can be found in almost every one of them. The guy needs a break people. :P
  • SimonT
    Offline / Send Message
    SimonT interpolator
    PhilipK wrote: »
    And splitting/hard edges generates more vertices,

    I think you understood it wrong: i didn't split the vertices - i just split the UVs. The verts are welded together BUT:

    I programmer told me some time ago, that the model is split by code where different smoothing groups hit together. Means:
    • 1 smoothing group for whole object: no vert splitting
    • 1 smoothing group for every face: massiv vert splitting by code
  • Neox
    Offline / Send Message
    Neox godlike master sticky
    afaik splitting uvs creates rendervertices as well as does splitting normals (via smoothing groups, hard edges etc), and how could it not? its an extra set of data per vertex thats been split, in case of your box it will create 3times more information than with welded uvs, so you're not only creating more data with the smoothinggroups (3 normals per vertex in your box example) but also with the uvs, information is information and has to be handled somehow i guess.
  • arrangemonk
    Offline / Send Message
    arrangemonk polycounter lvl 17
    isnt this issue the reason why padding was invented? and between uv splits there usually is no interpolation(except for anti alizing)
  • Mypeople
    @jocose
    thanks for this thread jocose, so my question is...if i split the uvs and average normals to get clean results on this hard poly stuff...then i have gaps in my texture. i see the work that has to be done to get this to bake correctly, but in the end, now we're going to have a lot of texture gaps. oy...am i missing something?
  • EarthQuake
    Neox wrote: »
    afaik splitting uvs creates rendervertices as well as does splitting normals (via smoothing groups, hard edges etc), and how could it not? its an extra set of data per vertex thats been split, in case of your box it will create 3times more information than with welded uvs, so you're not only creating more data with the smoothinggroups (3 normals per vertex in your box example) but also with the uvs, information is information and has to be handled somehow i guess.

    Yes, Neox is quite correct here. But the thing to keep in mind is, if you were going to use hard edges anyway, splitting your UVs will add no extra verts. A uv split alone = 2 verts, a hard edge along = 2 verts, and both together also = 2 verts.

    Some people will try to make the argument that you should just bevel the edges instead of using hard edges as the vertex hit can be around the same, but ignore the fact that triangles are not free, and that long-thin triangles can be a bit of a bottleneck when rendering.
  • jocose
    Offline / Send Message
    jocose polycounter lvl 11
    @MyPeople

    If by gaps you mean breaks in the continuity of the rays projected out of the low poly mesh during baking, then there is a quick fix for that: Use a cage.

    If by gaps, you mean general seams in your textures then yes you will, but keep in mind this issue only occurs along very sharp angles, and usually you want to planar project your UVs along co-planar faces. So I often find that my nearly ideal UVs (low distortion) just so happen to coinside with my ideal UVs for baking normals (split edges along sharp angles).

    If it really becomes an issue for you then just use bevels instead of hard edges. It will make your model look better up close anyways and will have the same result. Its just less efficient.

    In the end you have all the options you really need, you just have to weight it all out and makes some informed decisions.
  • Vilnis
    Hello!
    I wanted to make a new thread, but I guess I cant atm, so I hope its okey that I ask this here. Ive been trying to learn how to create decent looking normal maps from hipoly source for the past few days and get problems that I dont think I can solve myself, so sorry for asking those noob questions.
    What I did was create a simple hipoly and lowpoly test mesh split the uvs where I have hard edges, added projection, reset it and did a small push so all the model is covered, then baked the thing out and this is what I got (using max RTTnormalmap shader).
    http://sleekupload.com/uploads/5/problem1_2.jpg
    It looks pretty good from a distance but close up you can see the problem where the hard edge is, it should not be the error that is from pulled pixels because my uvs are split at that part. Heres how the uvs and lowpoly look.
    http://sleekupload.com/uploads/5/normalmap.jpg
    http://sleekupload.com/uploads/5/lowpoly.jpg
    The other problem I get and Im not sure by it is like that is that the round thingy I modeled in the hipoly gets distorted when baked down and I dont understand why it is like that if the cage is pushed out evenly in all directions, heres what I mean.
    http://sleekupload.com/uploads/5/distortion.jpg
    Again sorry for those noob questions.
  • Ikuru
    Hey guys, after reading this thread and several other threads about Normalmap Baking issues I pretty much came to the conclusion that I need to split my UVs wherever I have a Hard edge/sharp edge in order to avoid Black Edge issues and such errors on my Map. Just wanne confirm Im getting this right. Any input would be gladly appreciated.
    Here is a test I did to elaborate further. These tests where baked in xnormal 3.17.4 with cages (for some reason 3.17.5 does not read cages properly).
    testcube.jpg
    Thanx Ya :)
  • Ben Apuna
    You get smoothing errors if your normal baking app is not synched with your engine. Smoothing errors that come from using a non-synched engine can be alleviated by splitting UVs on angles that are >= 90 degrees on your model (and then re-baking the normal map), however this is a separate issue from the black edge issue.

    You get black edges if you don't bake with a cage AND if you don't have enough padding between split UVs.
  • cptSwing
    Offline / Send Message
    cptSwing polycounter lvl 11
    yeah, ever since it's become more commonplace to be able to import/export tangents to and from engines, i've had waaay fewer shading errors. awesome.
Sign In or Register to comment.