CNA-GAP-032: PbrEffect limits: one punctual light per draw, and no back-face normal flip for double-sided materials outside EasyGL
Evidence basis: source-verified at the pinned commit; tests exist (not executed for this page). Claims on this page were checked by reading the CNA source at commit c1c316b9; unless a sentence says otherwise, nothing here was built or executed. Nothing on this page was executed unless the Evidence section says so.
PbrEffect takes one punctual light, and only EasyGL's PBR programs reverse the normal of a double-sided material's back face (GSC-0010); every other PBR renderer lights the back face with the front normal.
- Identifier
CNA-GAP-032- Category
- Functional gap
- Subsystem
- Graphics & renderers
- Status
- Narrowed (partially fixed; describes only what survives)
- Verified against
- CNA
c1c316b9(c1c316b9c7a846ce8002809c151fcd1af14942c9) - Evidence basis
- Source-verified: read at TARGET, not executed
- Tests touching this area
- Yes: see Current tests
- Affected contract
- PbrEffect::setPunctualLightEXT; PbrEffect/SkinnedPbrEffect doubleSided on renderers other than EasyGL
Expected behaviour
glTF 2.0 reverses the normal of a back face of a double-sided material before lighting.
Actual behaviour at TARGET
PbrEffect.hpp holds a single PunctualLightEXT (“Supplies one punctual light and its shadow”). The doubleSided mapping in gltf-limitations.md selects CullNone; PbrEffect and SkinnedPbrEffect pass the material's flag as GpuDrawParams::pbrDoubleSided (IGraphicsRenderer.hpp), which only EasyGL reads: its PBR programs flip N, T and B on a back face. VULKAN, SDL_GPU, WEBGPU, DIRECTX9/11 and METAL ignore it, so their back faces are lit with the front normal.
Fixed since first recorded: the back-face flip on EasyGL (GSC-0010, a1414a1b0). The entry's two limits of CNA-generated environment cubes and shadow casters do not apply at this snapshot: CNA ships no environment-cube or shadow-map generator, and an application supplies those textures to the PbrEffect receivers itself.
Source locations
modules/graphics/include/Microsoft/Xna/Framework/Graphics/PbrEffect.hpp— setPunctualLightEXT: one lightdocs/gltf-limitations.md— doubleSided row: CullNonemodules/graphics/include/CNA/Internal/Renderers/Common/IGraphicsRenderer.hpp— GpuDrawParams::pbrDoubleSidedmodules/renderers/easygl/examples/easygl_pbr_double_sided_test.cpp— EasyGL only: a double-sided back face lit from the side that shows it
Evidence
Checked by reading at c1c316b9 (the front-facing search was a grep of all of modules/); not executed. The original limits were reported by the living-room-simulator sample @ 2bf3f56b against an earlier CNA revision and re-established individually.
Re-verified at b0e97bb1: PbrEffect.hpp still holds one PunctualLightEXT. pbrDoubleSided is read only in EasyGLRenderer.cpp (GSC-0010, a1414a1b0); the other PBR renderers bind the PBR maps but never read the flag. Not executed.
Focused reproduction
No focused reproduction is known. Nothing has been invented here; the evidence above is what exists.
Current tests
EasyGL_Pbr_DoubleSided (easygl_pbr_double_sided_test.cpp) lights a double-sided back face on EasyGL; no other renderer has such a test, and no test draws two punctual lights.
Regression test
A back-face lighting pixel test per PBR renderer; a two-punctual-light draw once that limit is lifted.
Blast radius
glTF scenes with double-sided foliage or cloth on renderers other than EasyGL, or several punctual lights per object.
Workaround
Duplicate double-sided geometry with flipped normals on renderers other than EasyGL; split draws per punctual light.
Related pages
The same subject is explained at several altitudes. These are the neighbouring pages at each one.
- Deep dives
- glTF conformance
- Known issues
- Functional gap index