# Normal texture shader

**URL:** <https://forum.aousd.org/t/normal-texture-shader/2873>\
**Category:** USD\
**Created:** [March 16, 2026, 2:52pm UTC](https://forum.aousd.org/t/normal-texture-shader/2873 "2026-03-16T14:52:59Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![BigRayLee](https://sea2.discourse-cdn.com/flex016/user_avatar/forum.aousd.org/bigraylee/32/1112_2.png) [@BigRayLee](https://forum.aousd.org/u/BigRayLee)\
**Post date:** [March 16, 2026, 2:52pm UTC](https://forum.aousd.org/t/normal-texture-shader/2873/1 "2026-03-16T14:52:59Z")

</div>

We are currently trying to export our scene to the USD format.  
For normal textures, if we already have a tangent-space normal map with values in the 0,1 range, why do we need to remap it to the -1,1 range? What is the purpose of this transformation? I came across the following documentation mentioning this step, but it doesn’t really explain the reasoning behind it.

“Expects normal in tangent space [(-1,-1,-1), (1,1,1)]. This means your texture  
reader implementation should provide data to this node that is properly scaled  
and ready to be consumed as a tangent space normal.  
If the texture has 8 bits per component, then scale and bias must be adjusted  
to be (2.0, 2.0, 2.0, 1.0) and (-1, -1, -1, 0) respectively in order to  
satisfy tangent space requirements. Normal map data is commonly expected to be  
linearly encoded. However, many image-writing tools automatically set the  
profile of three-channel, 8-bit images to SRGB. To prevent an unwanted  
transformation, the sourceColorSpace must also be set to “raw”.”

---

<div class="post-metadata">

**Author:** ![spiff](https://sea2.discourse-cdn.com/flex016/user_avatar/forum.aousd.org/spiff/32/22_2.png) [@spiff](https://forum.aousd.org/u/spiff)\
**Post date:** [March 17, 2026, 5:43pm UTC](https://forum.aousd.org/t/normal-texture-shader/2873/2 "2026-03-17T17:43:21Z")

</div>

The constraint/expectation is specifically attached to the specification for UsdPreviewSurface, from which you are quoting. It’s not a declaration about “the _cosmically right_ way to do normal mapping” 🙂 , it was the decision our graphics folks made to keep the PreviewSurface code simpler, pushing the transformation it would otherwise have to do itself out into the UsdUVTexture node/implementation, since we needed to provide a basic value scale/offset ability for that node anyways.

---

<div class="post-metadata">

**Author:** ![BigRayLee](https://sea2.discourse-cdn.com/flex016/user_avatar/forum.aousd.org/bigraylee/32/1112_2.png) [@BigRayLee](https://forum.aousd.org/u/BigRayLee)\
**Post date:** [March 18, 2026, 3:27pm UTC](https://forum.aousd.org/t/normal-texture-shader/2873/3 "2026-03-18T15:27:25Z")

</div>

Thanks for the reply.

So, to have the correct rendering result, should I set these two factors in UsdShadeShader in general? I did some round-trip tests for importing the USD file into our system first, and then I exported the file by setting this transformation. It seems that I can get the correct result.

---

<div class="post-metadata">

**Author:** ![spiff](https://sea2.discourse-cdn.com/flex016/user_avatar/forum.aousd.org/spiff/32/22_2.png) [@spiff](https://forum.aousd.org/u/spiff)\
**Post date:** [March 19, 2026, 12:36am UTC](https://forum.aousd.org/t/normal-texture-shader/2873/4 "2026-03-19T00:36:16Z")

</div>

Yes, in fact I _think_ there is a validator that usdchecker runs that flags a UsdUVTexture Shader prim/node that:

1. Is connected to a UsdPreviewSurface’s `inputs:normal` input
2. reads from an 8-bit texture (and therefore is almost certainly in the range 0..1)

And tells you you probably want the scale/bias on the node set to the appropriate values to compensate.

---

<div class="post-metadata">

**Author:** ![BigRayLee](https://sea2.discourse-cdn.com/flex016/user_avatar/forum.aousd.org/bigraylee/32/1112_2.png) [@BigRayLee](https://forum.aousd.org/u/BigRayLee)\
**Post date:** [March 23, 2026, 11:06am UTC](https://forum.aousd.org/t/normal-texture-shader/2873/5 "2026-03-23T11:06:54Z")

</div>

I see! Thanks for the reply.
