# Get bent, or what is normal today anyway?

**URL:** <https://discourse.threejs.org/t/get-bent-or-what-is-normal-today-anyway/88635>\
**Category:** Resources\
**Created:** [December 13, 2025, 4:05pm UTC](https://discourse.threejs.org/t/get-bent-or-what-is-normal-today-anyway/88635 "2025-12-13T16:05:09Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![Usnul](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/usnul/32/2355_2.png) [@Usnul](https://discourse.threejs.org/u/Usnul)\
**Post date:** [December 13, 2025, 4:05pm UTC](https://discourse.threejs.org/t/get-bent-or-what-is-normal-today-anyway/88635/1 "2025-12-13T16:05:09Z")

</div>

Today we’re going to answer the question

> What are bent normals?

It’s a confusing topic, and there’s sparse information available, so I thought I’d offer some insight.

To help me demonstrate the concept, I will be using Sponza scene as a reference

 ![Screenshot 2025-09-06 150724](https://canada1.discourse-cdn.com/flex035/uploads/threejs/original/3X/5/7/57aa1ebf349d5e8b18ad76b2350523df9e2dd98c.jpeg)

First, let’s step back and answer a question of what do we mean by “normal”.

A normal is the “surface normal” vector, it’s the direction that defines “front” of the surface.

 ![image](https://canada1.discourse-cdn.com/flex035/uploads/threejs/original/3X/d/d/dd2ac30f0a4be214b39f101476203df22b103022.png)

We cheat in graphics, and we add “shading” detail to surfaces that isn’t there using normal maps, here’s one for the lion’s head at the back as an illustration

 ![759203620573749278](https://canada1.discourse-cdn.com/flex035/uploads/threejs/original/3X/f/0/f0906aa5d5ae5169dc53951c809c098fd1a52291.jpeg)

[Shade](https://discourse.threejs.org/t/shade-webgpu-graphics/66969) (my graphics engine) is a deferred renderer, so are most AAA graphics engines out there. One of the major reasons is that you need a g-buffer for most of the post-processing stack anyway, so you might as well go full deferred.

As a result, we have normals for each frame, here’s the same Sponza shot, but visualizing just the shading normals

 ![Screenshot 2025-06-27 015809](https://canada1.discourse-cdn.com/flex035/uploads/threejs/original/3X/d/a/da79c9516a1b7932db3208456d00ac059ff06c14.jpeg)

It’s a bit strange perhaps, because my normals are world-space normals. But I promise it makes sense. The normals are encoded as RGB, so red surfaces are facing +X direction, Green surfaces are +Y and blue surface are +Z, hopefully it makes more sense now.

Now, back to getting bent. When we sample our environment map, or any other kind of global illumination source, we use surface normal to answer the question

> “which direction does light arrive from to this point?”

And if you imagine a situation like this

 ![image](https://canada1.discourse-cdn.com/flex035/uploads/threejs/original/3X/b/3/b38e8630f26d362b8b89030be06798730897a943.png)

We have 2 adjecent surface A and B forming a corner, with normals `Na` and `Nb`. The problem is that corner, if we zoom way in and imagine the very adjacent pixels in the corner:

 ![image](https://canada1.discourse-cdn.com/flex035/uploads/threejs/original/3X/2/3/234763bab0f0079ad899b0b96dd2987738bf8898.png)

You can see that the sampling direction on surface A is `(1, 0, 0)` and on B it’s `(0, 1, 0)`

If we consider the angle between these two directions, it’s 90 degrees, that sounds fine, but what it means in practice is that that corner pixel on A will be sampling, say, the horizon, so trees and grass and bushes and such from your environment map, while the surface B will be sampling the sky directly overhead. The brightness of these is very different, so are the colors.

In reality, light, especially on rough surfaces, will never behave like that. Instead of a sharp transition like we saw above

 ![image](https://canada1.discourse-cdn.com/flex035/uploads/threejs/original/3X/e/c/ec0a913a4f42b37dbcf3b1dd73c1cb3287b311d5.png)

We actually will observe something more like this

 ![image](https://canada1.discourse-cdn.com/flex035/uploads/threejs/original/3X/e/5/e56ae786bbb5c1a7c76ded0ee7c00fed8c53fb96.png)  
 ![image](https://canada1.discourse-cdn.com/flex035/uploads/threejs/original/3X/a/2/a2b97ea8319d3248d5e0cdc96385b47ce45761a2.png)

And this very observation is what bent normals are all about. We calculate normals that “bend” towards the least occluded direction.  
And here’s what that looks like in practice:

 ![Screenshot 2025-06-28 232049](https://canada1.discourse-cdn.com/flex035/uploads/threejs/original/3X/d/a/da1dec2ea4240f9421d15e458135806da036ba81.jpeg)

Here’s another scene with more obvious differences, first the shading normals:

 ![Screenshot 2025-06-28 232132](https://canada1.discourse-cdn.com/flex035/uploads/threejs/original/3X/7/b/7b9c37c0dd41b31cc3b3ef91cdcb5acfede4145a.jpeg)

And bent normals

 ![Screenshot 2025-06-28 232355](https://canada1.discourse-cdn.com/flex035/uploads/threejs/original/3X/b/0/b01c40f35bc347ffa2a13520eff06a8724772ca5.jpeg)

I briefly mentioned bent normals before, in [my article on SSGI](https://discourse.threejs.org/t/ssgi-screen-space-global-illumination/85190). The bent normals should generally only be used to sample global illumination and only for diffuse lighting. But it does create an appreciable difference in the resulting image. Here’s just the diffuse GI, first with standard shading normals:

 ![Screenshot 2025-06-29 193136](https://canada1.discourse-cdn.com/flex035/uploads/threejs/original/3X/5/4/544727ff04229c839a869f91a3b7e51ee2ea9326.jpeg)

And now with bent normals

 ![Screenshot 2025-06-29 193123](https://canada1.discourse-cdn.com/flex035/uploads/threejs/original/3X/e/1/e10d8c7d0e2ba6c96e6fe2b61cfa3f0e4545c134.jpeg)

It softens the image up a bit, and gives it a more natural look. Corners that previously looked unnaturally sharp, now look more grounded.

The standard technique for calculating bent normals is to do it in your SSAO pass, as you typically already track the least occluded normal in there, and if you don’t - it’s trivial to add that code.

With all that - I believe it’s a very valuable addition to any renderer.

## TL;RD;

* * *

Bent normals should be used for sampling diffuse global illumination **instead of** the shading normals.

---

<div class="post-metadata">

**Author:** ![PavelBoytchev](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/pavelboytchev/32/38639_2.png) [@PavelBoytchev](https://discourse.threejs.org/u/PavelBoytchev)\
**Post date:** [December 13, 2025, 7:20pm UTC](https://discourse.threejs.org/t/get-bent-or-what-is-normal-today-anyway/88635/2 "2025-12-13T19:20:10Z")

</div>

Many years ago (before WebGL was a thing), I had a problem with normal maps that were too sharp. And when the camera was far, this sharpness turned into noise. I remember that at that time I solved the problem by blurring the normal map. It added smoothness similar to what I see above, but it also blurred other details. Then I made the blur factor dependent on the depth difference.

---

<div class="post-metadata">

**Author:** ![Usnul](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/usnul/32/2355_2.png) [@Usnul](https://discourse.threejs.org/u/Usnul)\
**Post date:** [December 14, 2025, 10:50pm UTC](https://discourse.threejs.org/t/get-bent-or-what-is-normal-today-anyway/88635/3 "2025-12-14T22:50:15Z")

</div>

> I solved the problem by blurring the normal map.

I remember reading, I believe, SIGGRAPH presentation on Unreal 4, and they did something similar. Applying essentially a low-pass filter to normals with some edge detection going on. I thought at the time “that’s such an ugly hack… I love it”

Found a couple more screenshots of bent normals in action on my drive

 ![Screenshot 2025-06-29 193046](https://canada1.discourse-cdn.com/flex035/uploads/threejs/original/3X/b/b/bb638f0093b425ade641a4adcd3c24661380cf55.jpeg)  
 ![Screenshot 2025-06-29 193033](https://canada1.discourse-cdn.com/flex035/uploads/threejs/original/3X/4/a/4ae7e6fdf7356f043e28b4895afc16d8b261731b.jpeg)

Second one is with bent normals
