Skip to content
This repository has been archived by the owner on Mar 28, 2024. It is now read-only.

[BUG-228952] Mesh Debug Normals display incorrectly. Normal Maps on scaled objects appear to use incorrect mesh normals for shading calcs. #6925

Closed
1 task
sl-service-account opened this issue Jun 19, 2020 · 7 comments

Comments

@sl-service-account
Copy link

sl-service-account commented Jun 19, 2020

Updated Report( As best I can understand how things function anyhow):

Display of Normal maps uses incorrect Vertex Normal Data.

If I take an asset, and squash it flat, or near flat, as best I can tell using tools that are not the Render Debug > Vertex Normal settings~ that object has it's vertex normals handled correctly with an inverse-scale relationship, and will render correctly in the presence of light, as long as it does not have a normal map applied to it's surface.  This is confirmed by reading the various bits of code Beq dug up for me in the viewer code, and testing with a single point light in the scene and an ambient dark windlight.  Angles of illumination seem to confirm that this largely behaves correctly.

However!! If I turn on Render Debug-Normals, it's clearly visible that the little drawn debug normal vector lines are being squashed with object scale, and not with inverse_scale, which lead to the rather confused testing / bug hunting loop that is in the preserved text below.  Furthermore ~ the issue still remains that when a normal map is assigned to any scaled object ( Almost ALL objects are scaled in SL, no one really makes unit cuboid meshes), the normal map renders as if it was using the vertex normal vectors that the (apparently) incorrectly drawn vertex normal debug settings illustrate.  It reflects light as such, and distorts the normal map in a manner that is consistent with the manner that SL renders the little debug normals lines.  That is to say, if I take a basic cylinder prim and slap a normal map on the round barrel part of it ( not the circular end caps ) and flatten the cylinder until the end caps are nearly thin lines and the cylinder itself is a near planar entity: it still renders the normal map on the side of the cylinder as if the cylinder was still in its original rounded form.  This further lead me to believe that the underlying mesh normals were being incorrectly stored, though I presently believe now, that this is not the case, though with debug normals being drawn incorrectly, it's difficult to prove.

 

At this point ~ my stab-in-the-dark guess is ~ ~ that somewhere, deep in the shader calcs, the original unit-scaled mesh form ~ ( before any transforms get applied ) is getting piped directly into the normal map shader calculation, or the object scale * vertex normal results that we can see with the debug normals are being piped in....  ( One of these two ?  ~or something?? ) and this data is being used to draw the lighting info ( on the correctly flattened surface, with proper correct inverse-scale calculated normals ) ~ making the flat surface look completely bent.

That being said, with anything that has a normal map on it, because of this error it looks like SL is using scale ~ rather than inverse scale in it's vertex normal calcs.  Good news, the mesh assets (probably) aren't all corrupted.  Bad news is they still all look corrupted if they have a normal map on them.

 

As an addendum : Rather unrelated to this bug, 3ds Max apparently uses scale * vertex normals in its' own internal functionality.  So that has further contributed to the elaborate amount of confusion about this bug.  Maya and Blender on the other hand both seem to use Inverse_Scale to calc their vertex normals.   However, 3ds Max corrects this bizarreness upon export or "Apply XForm" within the software.  Why it does this ~ I can only guess.  But it's interesting to know!  Or at least I thought so...

Original Bug Report:

I have been chasing the trail of this bug for the better part of the last 5 years.

When a mesh is uploaded to Second Life, the original DAE asset is compacted into a unit-cube. This squishification process processes all the vertex locations correctly. Unfortunately, during this process, the vertex normal data is pushed through an inverse-scale matrix calculation, instead of a scale matrix calculation ( like the rest of the mesh asset is ), which causes all the vertex normal data to be slightly bent by several orders of magnitude, dependent upon the original scale of the object. This bad data is then stored in the SLM file on the server ~ where it is then retrieved and rendered incorrectly for all eternity. This also completely borks any baked out normal maps that are subsequently uploaded and applied overtop of this bad mesh data, causing a visual discrepancy in lighting environments, that has been driving me bonkers for half a decade~ This is the case for any meshes that aren't uploaded with perfect 1:1:1 ratio bounding boxes. ( which is pretty much all of them ).

Just to use an example ~ if I upload a squished mesh sphere that's 0.25m x 1.0m x 1.0m large, the vertex normal calculation measures that the source mesh is 1/4 of a unit bounding box ( and that the mesh needs to be scaled up 4x on that axis ), but it recalculates each normal as if the mesh is a 4.0m x 1.0m x 1.0m object. ( a 16x difference )  In fact ~ in order to get my vertex normals back to the way they were in my 3D application, the mesh asset in Second Life needs to be scaled up to that size in order to get the vertex normals to "point" properly in the proper world-space coordinates, and get handled by the lighting calculations correctly. As you can imagine, the thinner the object ~ the more pronounced this defect is.

It was a forum post by ZedConroy : that tipped me off on where to look: https://community.secondlife.com/forums/topic/456171-vertex-normals-issue/

What were you doing when it happened?

I've included a test mesh object where I intentionally modified the vertex normals to be very specific angles. This is a squashed hemisphere has 3 specific zones to it.

1 : The flat portion of the hemisphere ~ that has all of its vertex normals canted at a 45 degree angle off of the plane ( instead of the usual 90 degree angle ) These vertex normals get calculated with the incorrect inverse scale matrix and come out nearly flat with the plane at default scale.
2: The top half of the sphere portion has vertex normals pointed straight up, as a kind of "control" these should not be ( and are not ) affected by the calculation.
3: The bottom portion of the sphere has vertex normals of a sphere that has not been squished. That is to say, that the verticies are angled in a manner of a sphere 3.5 times "fatter" than it is.

When uploaded with a broken viewer the area that's easiest to tell what's going wrong with is the flat back face.
In order to get vertex normals that have a 45 degree angle relative to world-space this test object needs to be scaled up to 7.0m x 1.0m x 1.0m. Which was my proof for the inverse scale matrix, though Beq Janus found exactly where an inverse scale is in the code, this test object was integral in proving we'd fixed the issue when she patched it.

What were you expecting to happen instead?

I'm expecting the vertex normals to function like they were in the original asset.

Other information

Beq Janus and ZedConroy helped out with this a lot. Beq as she always does, Zed, with his forum post and bounding box discovery.  Beq Janus made a modified viewer, where we tested uploading meshes with the proper scale calculation, and you can see the difference in how the lighting calculations deal with the data on your average clothing object here : https://gyazo.com/c66e087da14b3e99459e6f28b17c28f0

 

Here is a side by side comparison of a mesh uploaded with the current SL viewer, and Beq's modified viewer.  The red dress has incorrect surface normals, due to the scaling issue, the second dress copy in the video with perfect perpendicular surface normals was uploaded using a viewer with the fix Beq implemented : https://gyazo.com/983df72993027132af3a3458d4c6ad68

Note: This dress is only approximately 2:1 scaled off of a cube, it's very far from being the worst case scenario, but I included it as an example of how this affects your average mesh.

 

Appended Jira picture is of two instances of the test-hemisphere, one encased in a cube (named appropriately 'inaBox' ) to make sure there is no scaling operations performed upon it during the upload process, regardless of the viewer that's used,  and the other is not.  In the appended picture, you can see in order to get the "Flat faced" 45 degree vertex normals aligned between the two objects, the test object that is not "encased" needs to be scaled up to 7x the size of the 'boxed' mesh ( 49x it's original scale in that dimension ~ see the attached screenshot ), giving it an awkward topedo shape, which makes sense, since approximately 7 of these little wonky half-spheres can be packed into a unit cube at default scale.

 

One last thing: I made the test-meshes in Maya, and they exported in cm scale, so they're tiny.  When you upload them, hop over into the scale tab and give them a uniform 100x scale or they appear in SL super tiny, but this doesn't affect the test in any way.

Attachments

Links

Related

Original Jira Fields
Field Value
Issue BUG-228952
Summary Mesh Debug Normals display incorrectly. Normal Maps on scaled objects appear to use incorrect mesh normals for shading calcs.
Type Bug
Priority Unset
Status Closed
Resolution Triaged
Created at 2020-06-19T21:13:58Z
Updated at 2021-06-07T17:42:54Z
{
  'Build Id': 'unset',
  'Business Unit': ['Platform'],
  'Date of First Response': '2020-06-19T16:45:55.703-0500',
  "Is there anything you'd like to add?": 'Beq Janus and ZedConroy helped out with this a lot.  Beq as she always does, Zed, with his forum post and bounding box discovery.',
  'ReOpened Count': 0.0,
  'Severity': 'Unset',
  'System': 'SL Viewer',
  'Target Viewer Version': 'viewer-development',
  'What just happened?': 'I have been chasing the trail of this bug for the better part of the last 5 years.\r\n\r\nWhen a mesh is uploaded to Second Life, the original DAE asset is compacted into a unit-cube.  This squishification process processes all the vertex locations, correctly.  But during this process, the vertex normal data is pushed through an inverse-scale matrix calculation, instead of a scale matrix calculation ( like the rest of the mesh asset is ), which causes all the vertex normal data to be slightly bent by several orders of magnitude, dependent upon the original scale of the object.   This bad data is then stored in the SLM file on the server  ~ where it is then retrieved and rendered incorrectly for all eternity.   This also completely borks any baked out normal maps that are subsequently uploaded and applied overtop of this bad mesh data.  This is the case for any meshes that aren\'t uploaded with perfect 1:1:1 ratio bounding boxes. ( which is pretty much all of them ).\r\n\r\nJust to use an example ~ if I upload a squished mesh sphere that\'s 0.25m x 1.0m x 1.0m large, the vertex normal calculation measures that the source mesh is 1/4 of a unit bounding box ( and that the mesh needs to be scaled up 4x on that axis ), but it recalculates each normal as if the mesh is a 16.0m x 1.0m x 1.0m object.  In fact ~ in order to get my vertex normals back to the way they were in my 3D application, the mesh asset in Second Life needs to be scaled up to be a 4.0m x 1.0m x 1.0m asset to get the vertex normals to "point" in the proper world-space coordinates, and get handled by the lighting calculations correctly.  As you can imagine, the thinner the object ~ the more pronounced this defect is.\r\n\r\nIt was a forum post by ZedConroy : that tipped me off on where to look: https://community.secondlife.com/forums/topic/456171-vertex-normals-issue/',
  'What were you doing when it happened?': 'I\'ve included a test mesh object where I intentionally modified the vertex normals to be very specific angles.   This is a squashed hemisphere has 3 specific zones to it.\r\n\r\n1 : The flat portion of the hemisphere ~ that has all of its vertex normals canted at a 45 degree angle off of the plane ( instead of the usual 90 degree angle )  These vertex normals get calculated with the incorrect inverse scale matrix and come out nearly flat with the plane at default scale.\r\n2: The top half of the sphere portion has vertex normals pointed straight up, as a kind of "control" these should not be ( and are not ) affected by the calculation.\r\n3: The bottom portion of the sphere has vertex normals of a sphere that has not been squished.  That is to say, that the verticies are angled in a manner of a sphere 3.5 times "fatter" than it is.\r\n\r\nWhen uploaded with a broken viewer the area that\'s easiest to tell what\'s going wrong with is the flat back face. \r\nIn order to get vertex normals that have a 45 degree angle relative to world-space this test object needs to be scaled up to 7.0m x 1.0m x 1.0m.  Which was my proof for the inverse scale matrix, though Beq Janus found exactly where an inverse scale is in the code, this test object was integral in proving we\'d fixed the issue when she patched it.',
  'What were you expecting to happen instead?': "I'm expecting the vertex normals to function like they were in the original asset.",
  'Where': 'Mesh Uploader',
}
@sl-service-account
Copy link
Author

Beq Janus commented at 2020-06-19T21:45:56Z, updated at 2020-06-19T21:47:33Z

The fix for this is simple, at the minimum it is a one line change in fact, applied to llmodel.cpp. The patch also removes the now redundant inv_scale.

I am slightly hesitant only because the original change from 2011 that introduced this did so in a very deliberate way, declaring and calculating the inv_scale vector before applying it. So it would be advisable to check the background to internal issue SH-914 to confirm that in fixing this issue we are not unfixing something else (perhaps a latent bug further downstream), my honest guess is that this is just a mistake.

In llmodel.cpp the asset to be uploaded is normalised ready for quantisation and as part of that process, the entire model is transformed into the 0:1 domain. In doing this the inverse was mistakenly applied for normals.

The problem occurs within 
void LLModel::normalizeVolumeFaces()
Within the loop for scaling the individual mesh volumes of the model.
            for (U32 j = 0; j < face.mNumVertices; ++j)
            {
                pos[j].add(trans);
                pos[j].mul(scale);
                if (norm && !norm[j].equals3(LLVector4a::getZero()))
                {
//<FS:Beq> Fixup the incorrect scaling of normals
                    // norm[j].mul(inv_scale);
                    norm[j].mul(scale);
// </FS:Beq>
                    norm[j].normalize3();
                }
            }
 

A patch against viewer repo master branch is attached

 

 

@sl-service-account
Copy link
Author

Dan Linden commented at 2020-06-22T17:15:08Z

Thank you for the report and the investigation on this.

@sl-service-account
Copy link
Author

polysail commented at 2020-06-23T07:41:17Z, updated at 2020-06-23T11:50:34Z

Doing a lot more digging.    Every time I think I figure something out and pin this down.  Things get even more confusing.   If I turn off all shaders things start to visually behave in a manner where their normals are actually handled correctly... Sort of.

@sl-service-account
Copy link
Author

polysail commented at 2020-06-23T12:22:16Z

If I turn off all atmospheric shaders and completely ignore the render-debug normals~ and presume they're incorrectly drawn, lighting from a single light source seems to behave more or less correctly.   Which implies my initial assertion might be incorrect, and that mesh assets are being stored correctly ( and unpacked correctly to be rendered ), but that the shaders that are being used on top of those mesh assets, might be pulling data incorrectly and then rendering everything in a manner that is problematic?

@sl-service-account
Copy link
Author

polysail commented at 2020-06-23T23:08:07Z

Beq went on a deep render code digging expedition and located where the "unpack the mesh unit cube" calculations are for me, and those use inverse_scale normals as well, which means that the mesh system appears to be entirely self-consistent (unless I'm misunderstanding things still), and this bug isn't anywhere nearly as horrifying as it initially seemed to be.  (I downgraded it's priority)  It simply means the debug-normals are totally screwed up and normal maps on scaled objects are still wrong, in a manner that seems to be consistent with the behavior of the debug normals ( which is really strange ), but it's hardly the apocalyptic-rendering bug that the debug-normals lead me to believe it was.  That being said, meshes with a normal map applied to them ARE still rendering completely wrong.  This anomaly in the appended forum thread is just a lot different than I originally thought ( Thankfully )!  I'm going to keep poking this time permitting, but the problem is likely deep within the shader code.  Not something I know much about.

@sl-service-account
Copy link
Author

polysail commented at 2020-07-05T06:44:46Z, updated at 2020-07-05T06:46:10Z

Beq dug through almost the entire render codebase for days and has come up with what so far ~ seems like a functioning solution to this issue.  She changes the way that surface (vertex) tangents are calculated for non-uniform scaled meshes.  As well as the manner that those tangents are then fed into the tangent-based normal map material shaders.  I assume the changes will be directly contributed at some point.  For now the problem seems more or less resolved on Firestorm.

Dealing with this bug has been confusing at best, and downright frustrating at worst.  Here's to hoping the fix is actually a fix.

I'll leave it to Beq to elaborate further if she feels the need to.

@sl-service-account
Copy link
Author

Beq Janus commented at 2020-07-07T01:20:26Z

As Liz noted, we spent a lot of time thrashing through from top to bottom on this. The net result was a fix to llface.cpp and the materials and bump vertex shaders.

The problem appears to have been that the tangents were being transformed using the same matrices as the normals. I continue to say "Appears to be" as this space is not my area of expertise, however, the problems all appear to have been resolved as fully as they can be (rigged mesh normals remain broken see - https://jira.secondlife.com/browse/BUG-228952) as this is not so easily addressed.

I give my permission for the appropriate code to be pulled in see - https://vcs.firestormviewer.org/phoenix-firestorm/changeset/615e982eab3496322e64f054c9fa6177f43f7602

Those changes include a rewrite of the debug renderNormals function.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

1 participant