PixarAnimationStudios / OpenUSD

Universal Scene Description
http://www.openusd.org
Other
6.08k stars 1.21k forks source link

usdchecker: normal validation doesn't allow normals that are scaled on purpose #2606

Open hybridherbst opened 1 year ago

hybridherbst commented 1 year ago

Description of Issue

usdchecker currently always warns when normal scale and bias are not scale=(2,2,2,1) and bias=(-1,-1,-1,0). However, that prevents assets from passing that use scaled normals – e.g. scale=(0.5,0.5,0.5,1) and bias=(-0.25,-0.25,-0.25,0).

I think what usdchecker should check instead is that the data is symmetric, that is, bias == -scale / 2.

(The math should be a bit different to make sure that results have unit length, so scale=(0,0,0,1) needs bias=(0,1,0,0), but you get the idea)

Steps to Reproduce

  1. Download Needle-MadeWithNeedle-20230818-164137.zip

  2. Better file: Needle-MadeWithNeedle-20230821-083855.usdz.zip

  3. Run through usdchecker

  4. Expected: passes, all normals are symmetric

  5. Actual: complains that e.g. "has non-standard inputs:scale and inputs:bias values"

jesschimein commented 1 year ago

Filed as internal issue #USD-8602

spiffmon commented 1 year ago

Thanks, @hybridherbst , that sounds like a good generalization to us!

hybridherbst commented 1 year ago

Cool!

I think an interesting detail question would be what the expected math for scaled normals is in USD land (and if there's any convention for it).

E.g. this is what Unity's ShaderGraph uses:

void Unity_NormalStrength_float(float3 In, float Strength, out float3 Out)
{
    Out = float3(In.rg * Strength, lerp(1, In.b, saturate(Strength)));
}

But this is in MaterialX:

vec3 B = normalize(cross(N, T));
value.xy *= normal_scale;
value = T * value.x + B * value.y + N * value.z;

So if I'm not mistaken, the result of applying the same normal scale would differ in all scales but 0 and 1... And also looks like normal_scale in MaterialX is ignored in object space in all implementations 👀, interesting.

erich666 commented 1 year ago

I like bias == -scale / 2 as a starting spot.

To be a little less restrictive, you might want to include cases where bias == 0 and scale == 1, which can happen with some (quite old style) normal maps for the Z component. That is, they map 0 to 1 input to 0 to 1 output. See the second test case in https://github.com/usd-wg/assets/tree/main/test_assets/NormalsTextureBiasAndScale - happily rare today (no one deeply cares about adding a bit of precision to the Z component, which this allows).

Basically, you could use the rule for normal maps of "if the range 0-1 as input maps to -1 to +1 as output, or some subset of that range such as 0 to 1, then don't give a warning." Assume the user knows what they're doing - they wouldn't set these values otherwise.

Or, to be more restrictive, at least allow only the variant bias=1 and scale=-2 as a reasonable alternate setting for the Y component. This is needed for DirectX-style normal maps - see this section. It's a special, but common enough, case. Any other settings are somewhat less likely, though could be used as a poor-man's way to scale the heights (sort of) of the bumps.

hybridherbst commented 1 year ago

As for my testing, just bias = -scale / 2 won't do – the result will be normalized by applications, and then normalize(map * 2 - 1) == normalize(map * 0.02 - 0.01)

so in usdview all objects look all the same in the first test (but the one with normals scaled to 0): image

while in the second one, which keeps Z at 1, it looks as expected in usdview (at least normals are scaled, as said not sure about the correct math): image

Ground truth (Unity): image

tcauchois commented 11 months ago

Hey all, just to jump in and clarify: does the first screenshot above indicate that our rendering is doing the wrong thing with scaled normals? IIRC we apply bias and scale, normalize, transform from tangent space to eye space, and then normalize again.

And is there a synopsis of what scale/bias combos we want to warn about vs accept?

hybridherbst commented 11 months ago

@tcauchois I believe the rendering in usdview is correct – the first screenshot is effectively from a "bad file" where Y is scaled as well, so after normalization all of them look the same. The first black cube is effectively "garbage in, garbage out", – it's basically normals being scaled to 0 and then normalized.

My understanding is that scale/bias have to be chosen and verified in a way that the resulting vector

So I think something along the lines of: