EEVEE-Next: Thickness ouput behavior #120384

Open
opened 2024-04-07 21:19:56 +02:00 by Clément Foucault · 7 comments

The EEVEE rewrite introduced the Thickness material output.

This parameter is used for approximating the inner geometry structure of the object without having to rely on raytracing for that. This is currently used for SSS, Translucent BSDF, Refraction BSDF and the nodes containing them.

It is used for computing the exit location and normal of transmission rays within an object.
In other render engine, this is computed either by:

  • raytracing inside the object and find the correct intersection point (too slow for our purpose, hard to implement currently).
  • using depth peeling to find the next nearest surface in the view direction (lead to view dependency artifacts from effects that should be view independent like translucency).
  • use precomputed thickness taking either the average thickness or the thickness in a single view independant direction.

We chose this last option for the following reasons:

  • it completely avoids the runtime cost of thickness calculation techniques inside the object for each effect.
  • it allows to model some effect on materials that cannot use other techniques (ShaderToRGB, Blended Materials).
  • it is view independent and stable.

Model Approximation

For all effects needing it, we consider the local geometry either as a sphere tangent to the shading point and of diameter Thickness, or as an semi-infinite slab of same thickness.

Spherical Assumption Planar Assumption

Other Standards

Adobe Standard Material

The Adobe Standard Material has a reference to a similar parameter called Volume Thickness.

Pre-computed measure of average thickness of the volume beneath the surface as a fraction of the bounding box width. This can be used by rasterizers to emulate the behavior of material properties that would normally require ray tracing through the volume, like scattering or absorption.

The usage is different (as it is for volumetric approximation) and there is no mention about how the average is computed (uniform cone? cosine lobe?).

However, the parametrization is quite good (object local) as it can be baked into texture maps in LDR and reused across assets.

In addition, there is a Volume Thickness Scale parameter to scale the Volume Thickness after baking.

Output Node Socket Behavior

The output thickness value is treated as local space thickness. This mimics the displacement output and allow for a similar workflow (an intermediate node is used to scale back to world space if needed).

A value of 0 assumes the object is single sided and will only model one transmission event.

Unconnected Socket

An unconnected socket will infer the size of the whole object based solely on its bounding box largest axis length and assume uniform thickness. While this is a very crude approximation, this will match perfectly for sphere objects and give relatively good result for simple objects.

Note that considering the smallest axis might be better as it would allow for a single sided plane to have correct refraction.

This behavior is still opened to discussion. The other possibility is to assume 0 thickness and use one transmission event by default.

Versioning

Compatibility with older file should be as simple as adding a value node to the output and set it to the value that Refraction Depth.

Input Data Baking

Generating good input data is not so trivial. We are reducing the model complexity quite drastically and there is many degrees of freedom and heuristics we can use. In any case, we need to shoot rays in a uniform hemisphere (or cosine, or cone) distribution inside the object.

The thickness can be computed as:

  • the minimum distance
  • the maximum distance
  • the average distance
  • the cosine weighted average distance.

The distance can be:

  • the raw ray length (easy to understand, export compatibility?)
  • the distance to the normal plane (better for box like models)
  • the tangent sphere diameter (better for organic models, matches EEVEE interpretation)

These options can be implemented as part of an operator or geometry node group.

Thickness Scaling Node

To follow the displacement workflow, we will add a new node that will have World Space and Object Space options, along with two input sockets Thickness and Scaling. It will output the thickness as a Float (as expected by the material output node).

The World Space option will scale the Thickness input by the bounding box largest axis length so that it stays constant with object scaling. The Scaling option will just scale the result of the space conversion.

This allow full flexibility and clarity to amend the baked data and provide compatibility with the ASM standard.

Open Questions

Thickness from shadow

This was the default mode in legacy EEVEE. It was only used by BSSRDFs (subsurface scattering). Now that we support more transmission BSDFs, this interact with all of them. This create some funny looking refraction which are distorted by the shadows (which are not even aligned with the shading).

For now we take the min between the user thickness and the shadow one which creates some artifact because of shadow map aliasing. But if we have a cheap way to pre-compute something more correct than this, then it become rather pointless to have this option. Which would speed up rendering and simplify the usage.

The EEVEE rewrite introduced the `Thickness` material output. This parameter is used for approximating the inner geometry structure of the object without having to rely on raytracing for that. This is currently used for SSS, Translucent BSDF, Refraction BSDF and the nodes containing them. It is used for computing the exit location and normal of transmission rays within an object. In other render engine, this is computed either by: - raytracing inside the object and find the correct intersection point (too slow for our purpose, hard to implement currently). - using depth peeling to find the next nearest surface in the view direction (lead to view dependency artifacts from effects that should be view independent like translucency). - use precomputed thickness taking either the average thickness or the thickness in a single view independant direction. We chose this last option for the following reasons: - it completely avoids the runtime cost of thickness calculation techniques inside the object for each effect. - it allows to model some effect on materials that cannot use other techniques (ShaderToRGB, Blended Materials). - it is view independent and stable. ### Model Approximation For all effects needing it, we consider the local geometry either as a sphere tangent to the shading point and of diameter `Thickness`, or as an semi-infinite slab of same thickness. | Spherical Assumption | Planar Assumption | | -------- | -------- | | ![](https://projects.blender.org/attachments/93ae749f-6d2a-413f-91cb-e4401aa36d23) | ![](https://projects.blender.org/attachments/9cc8c3fb-f5aa-4f55-8fe8-d2fcb7112c70) | ### Other Standards #### Adobe Standard Material The [Adobe Standard Material](https://helpx.adobe.com/content/dam/help/en/substance-3d/documentation/s3d/files/225969597/225969613/1/1647027222890/adobe-standard-material-specification.pdf) has a reference to a similar parameter called `Volume Thickness`. > Pre-computed measure of average thickness of the volume beneath the surface as a fraction of the bounding box width. This can be used by rasterizers to emulate the behavior of material properties that would normally require ray tracing through the volume, like scattering or absorption. The usage is different (as it is for volumetric approximation) and there is no mention about how the average is computed (uniform cone? cosine lobe?). However, the parametrization is quite good (object local) as it can be baked into texture maps in LDR and reused across assets. In addition, there is a `Volume Thickness Scale` parameter to scale the `Volume Thickness` after baking. ### Output Node Socket Behavior The output thickness value is treated as local space thickness. This mimics the displacement output and allow for a similar workflow (an intermediate node is used to scale back to world space if needed). A value of 0 assumes the object is single sided and will only model one transmission event. #### Unconnected Socket An unconnected socket will infer the size of the whole object based solely on its bounding box largest axis length and assume uniform thickness. While this is a very crude approximation, this will match perfectly for sphere objects and give relatively good result for simple objects. Note that considering the smallest axis might be better as it would allow for a single sided plane to have correct refraction. This behavior is still opened to discussion. The other possibility is to assume 0 thickness and use one transmission event by default. #### Versioning Compatibility with older file should be as simple as adding a value node to the output and set it to the value that `Refraction Depth`. ### Input Data Baking Generating good input data is not so trivial. We are reducing the model complexity quite drastically and there is many degrees of freedom and heuristics we can use. In any case, we need to shoot rays in a uniform hemisphere (or cosine, or cone) distribution inside the object. The thickness can be computed as: - the minimum distance - the maximum distance - the average distance - the cosine weighted average distance. The distance can be: - the raw ray length (easy to understand, export compatibility?) - the distance to the normal plane (better for box like models) - the tangent sphere diameter (better for organic models, matches EEVEE interpretation) These options can be implemented as part of an operator or geometry node group. ### Thickness Scaling Node To follow the displacement workflow, we will add a new node that will have `World Space` and `Object Space` options, along with two input sockets `Thickness` and `Scaling`. It will output the thickness as a `Float` (as expected by the material output node). The `World Space` option will scale the `Thickness` input by the bounding box largest axis length so that it stays constant with object scaling. The `Scaling` option will just scale the result of the space conversion. This allow full flexibility and clarity to amend the baked data and provide compatibility with the ASM standard. ### Open Questions #### Thickness from shadow This was the default mode in legacy EEVEE. It was only used by BSSRDFs (subsurface scattering). Now that we support more transmission BSDFs, this interact with all of them. This create some funny looking refraction which are distorted by the shadows (which are not even aligned with the shading). For now we take the min between the user thickness and the shadow one which creates some artifact because of shadow map aliasing. But if we have a cheap way to pre-compute something more correct than this, then it become rather pointless to have this option. Which would speed up rendering and simplify the usage.
Clément Foucault added this to the 4.2 LTS milestone 2024-04-07 21:19:56 +02:00
Clément Foucault added the
Interest
EEVEE
Module
EEVEE & Viewport
Type
Design
labels 2024-04-07 21:19:56 +02:00
Author
Member

Note that I think we can still release EEVEE-Next with only the default socket behavior and the world space output. The thickness scaling node can be added in 4.3 if that is too short to implement it. However, I'll provide a geometry node setup to output the thickness as an attribute.

Note that I think we can still release EEVEE-Next with only the default socket behavior and the world space output. The thickness scaling node can be added in 4.3 if that is too short to implement it. However, I'll provide a geometry node setup to output the thickness as an attribute.
Author
Member

After testing, the spherical object assumption doesn't work at all for planar surfaces. So we must have an option to select which mode to use.

Maybe the planar assumption might be made default for now (for compatibility with older file) and we'll decide if we have time to add the spherical one.

EDIT: it is not as simple. I committed the spherical one first. We will see how feedback goes.

After testing, the spherical object assumption doesn't work at all for planar surfaces. So we must have an option to select which mode to use. Maybe the planar assumption might be made default for now (for compatibility with older file) and we'll decide if we have time to add the spherical one. EDIT: it is not as simple. I committed the spherical one first. We will see how feedback goes.
Author
Member

Adding an option to have the planar assumption back is possible. If we go that way, I would also add a Thin mode so that the surface is considered having 0 thickness regardless of the material output. That would make foliage material easier to setup.

Adding an option to have the planar assumption back is possible. If we go that way, I would also add a `Thin` mode so that the surface is considered having 0 thickness regardless of the material output. That would make foliage material easier to setup.
Author
Member

Here is the comparison between the different BSDFs and the different engines.
Note that:

  • EEVEE (legacy) uses the planar assumption for all BSDFs.
  • EEVEE (legacy) doesn't support translucent BSDF with thickness.
  • EEVEE (legacy) uses constant thickness over all objects here (because they use the same material).
  • Cycles is setup to only allow 2 transmission events here (for fair comparison).
  • The self shadowing issue in EEVEE-Next could be worked around by adding some bias at some level (light or thickness baking).
  • Some of the issues in EEVEE-Next are due to the Thickness from shadow issue discussed in the task.
BSDF Cycles EEVEE-Next EEVEE (legacy)
Translucent Capture d’écran du 2024-04-15 14-04-11.png Capture d’écran du 2024-04-15 14-04-02.png Capture d’écran du 2024-04-15 14-03-50.png
Refraction Capture d’écran du 2024-04-15 14-01-36.png Capture d’écran du 2024-04-15 14-01-24.png Capture d’écran du 2024-04-15 14-01-06.png
Sub-Surface Capture d’écran du 2024-04-15 14-02-14.png Capture d’écran du 2024-04-15 14-02-57.png Capture d’écran du 2024-04-15 14-02-45.png

Blend file attached to this message.

Here is the comparison between the different BSDFs and the different engines. Note that: - EEVEE (legacy) uses the planar assumption for all BSDFs. - EEVEE (legacy) doesn't support translucent BSDF with thickness. - EEVEE (legacy) uses constant thickness over all objects here (because they use the same material). - Cycles is setup to only allow 2 transmission events here (for fair comparison). - The self shadowing issue in EEVEE-Next could be worked around by adding some bias at some level (light or thickness baking). - Some of the issues in EEVEE-Next are due to the `Thickness from shadow` issue discussed in the task. | BSDF | Cycles | EEVEE-Next | EEVEE (legacy) | | --- | -------- | -------- | -------- | | Translucent | ![Capture d’écran du 2024-04-15 14-04-11.png](/attachments/1f576828-5429-4ca1-8a22-1fbee1e3dc65) | ![Capture d’écran du 2024-04-15 14-04-02.png](/attachments/062d2b80-88de-4c5e-9bbf-34d4e89b3d95) | ![Capture d’écran du 2024-04-15 14-03-50.png](/attachments/370a653a-6254-420c-9820-76d0ba419048) | | Refraction | ![Capture d’écran du 2024-04-15 14-01-36.png](/attachments/b4696e2f-b30e-4b40-aaaa-43f39f95ed15) | ![Capture d’écran du 2024-04-15 14-01-24.png](/attachments/60edadc0-6a7c-477a-8470-b9defd625306) | ![Capture d’écran du 2024-04-15 14-01-06.png](/attachments/09b2b585-fe59-47d1-8d45-0ecfd296c850) | | Sub-Surface | ![Capture d’écran du 2024-04-15 14-02-14.png](/attachments/b695f37c-3bb4-46eb-95f6-1bc5c1f40fea) | ![Capture d’écran du 2024-04-15 14-02-57.png](/attachments/36894506-fa64-4a5d-9320-4f0adc784eed) | ![Capture d’écran du 2024-04-15 14-02-45.png](/attachments/2c0208ba-7a25-437a-9083-e8953f7449de) | Blend file attached to this message.

Here is the comparison between the different BSDFs and the different engines.
Note that:

  • EEVEE (legacy) uses the planar assumption for all BSDFs.
  • EEVEE (legacy) doesn't support translucent BSDF with thickness.
  • EEVEE (legacy) uses constant thickness over all objects here (because they use the same material).
  • Cycles is setup to only allow 2 transmission events here (for fair comparison).
  • The self shadowing issue in EEVEE-Next could be worked around by adding some bias at some level (light or thickness baking).
  • Some of the issues in EEVEE-Next are due to the Thickness from shadow issue discussed in the task.
BSDF Cycles EEVEE-Next EEVEE (legacy)
Translucent Capture d’écran du 2024-04-15 14-04-11.png Capture d’écran du 2024-04-15 14-04-02.png Capture d’écran du 2024-04-15 14-03-50.png
Refraction Capture d’écran du 2024-04-15 14-01-36.png Capture d’écran du 2024-04-15 14-01-24.png Capture d’écran du 2024-04-15 14-01-06.png
Sub-Surface Capture d’écran du 2024-04-15 14-02-14.png Capture d’écran du 2024-04-15 14-02-57.png Capture d’écran du 2024-04-15 14-02-45.png

Blend file attached to this message.

Not bad !! But a lot of this still rest noisy in shadow and refracted elements :/

> Here is the comparison between the different BSDFs and the different engines. > Note that: > - EEVEE (legacy) uses the planar assumption for all BSDFs. > - EEVEE (legacy) doesn't support translucent BSDF with thickness. > - EEVEE (legacy) uses constant thickness over all objects here (because they use the same material). > - Cycles is setup to only allow 2 transmission events here (for fair comparison). > - The self shadowing issue in EEVEE-Next could be worked around by adding some bias at some level (light or thickness baking). > - Some of the issues in EEVEE-Next are due to the `Thickness from shadow` issue discussed in the task. > > | BSDF | Cycles | EEVEE-Next | EEVEE (legacy) | > | --- | -------- | -------- | -------- | > | Translucent | ![Capture d’écran du 2024-04-15 14-04-11.png](/attachments/1f576828-5429-4ca1-8a22-1fbee1e3dc65) | ![Capture d’écran du 2024-04-15 14-04-02.png](/attachments/062d2b80-88de-4c5e-9bbf-34d4e89b3d95) | ![Capture d’écran du 2024-04-15 14-03-50.png](/attachments/370a653a-6254-420c-9820-76d0ba419048) | > | Refraction | ![Capture d’écran du 2024-04-15 14-01-36.png](/attachments/b4696e2f-b30e-4b40-aaaa-43f39f95ed15) | ![Capture d’écran du 2024-04-15 14-01-24.png](/attachments/60edadc0-6a7c-477a-8470-b9defd625306) | ![Capture d’écran du 2024-04-15 14-01-06.png](/attachments/09b2b585-fe59-47d1-8d45-0ecfd296c850) | > | Sub-Surface | ![Capture d’écran du 2024-04-15 14-02-14.png](/attachments/b695f37c-3bb4-46eb-95f6-1bc5c1f40fea) | ![Capture d’écran du 2024-04-15 14-02-57.png](/attachments/36894506-fa64-4a5d-9320-4f0adc784eed) | ![Capture d’écran du 2024-04-15 14-02-45.png](/attachments/2c0208ba-7a25-437a-9083-e8953f7449de) | > > Blend file attached to this message. Not bad !! But a lot of this still rest noisy in shadow and refracted elements :/

I feel like having specular response in the Refraction on EEVEE-Next is already a big improvement over legacy considering how many times glass materials would look wrong because of no specular highlights, the SSS seems to be good too all though i am curious if it exhibits the same Ambient occlusion issue when there is a non subsurface object near it leading to cyan SSS shifts.

I feel like having specular response in the Refraction on EEVEE-Next is already a big improvement over legacy considering how many times glass materials would look wrong because of no specular highlights, the SSS seems to be good too all though i am curious if it exhibits the same Ambient occlusion issue when there is a non subsurface object near it leading to cyan SSS shifts.
Author
Member

Spoke with @SimonThommes about this.

Thickness from shadow map

we came to the conclusion that we do need to support both old behavior with thickness from shadow map, and new behavior with only the socket value.

Since we do not yet support for baking thickness automatically for transmission materials, this is the only way to fill the gap and not break older files. Also there might be valid workflow reasons to use the shadow map approximation (procedural geometry, weird geometry with poor parametrization, performance ...).

This will be presented as a checkbox in the material panel. We can afford to run this only on materials that needs it using a stencil buffer bit.

The issue would then be to decide on the way that the thickness from shadow map interacts with the thickness from the socket.

Currently we use the maximum of both since the automatic thickness is a quite good upper bound. But it is not always enough to avoid self shadow.

Additionally, the shadow map is quite imprecise and can lead to underestimation of the thickness leading to quite ugly fringes around the geometry. We can improve the sampling but there will always be failure case where the thickness from shadows will be zero.

So I propose to expose a parameter to scale the upper bound, and a parameter to set a lower bound as a fraction of the thickness socket. This way they would depends on object scale and almost never need adjustment. Good defaults should make these settings almost forgettable.

The setting cannot be per material nor per object for performance and architectural reasons, so it would be in the scene panel.

Thickness mode

For compatibility reasons (and mainly to support thin glass panels) we do need to support the slab approximation.

So this would be exposed per material using an enum:

  • Sphere Mode: 2 transmission events if thickness output is greater than 0.
  • Slab Mode: 2 transmission event if thickness output is greater than 0.

It would also be convenient to have a setting to disable the thickness approximation all together. But adding an option for it makes the workflow different than displacement (where there is no disabled mode). Also, adding a No thickness mode would mean there would be case where the plugged values in the nodetree do nothing. So for now, the only way to remove the thickness approximation is to connect a value node to the thickness socket and set it to 0.

Spoke with @SimonThommes about this. #### Thickness from shadow map we came to the conclusion that we do need to support both old behavior with thickness from shadow map, and new behavior with only the socket value. Since we do not yet support for baking thickness automatically for transmission materials, this is the only way to fill the gap and not break older files. Also there might be valid workflow reasons to use the shadow map approximation (procedural geometry, weird geometry with poor parametrization, performance ...). This will be presented as a checkbox in the material panel. We can afford to run this only on materials that needs it using a stencil buffer bit. The issue would then be to decide on the way that the thickness from shadow map interacts with the thickness from the socket. Currently we use the maximum of both since the automatic thickness is a quite good upper bound. But it is not always enough to avoid self shadow. Additionally, the shadow map is quite imprecise and can lead to underestimation of the thickness leading to quite ugly fringes around the geometry. We can improve the sampling but there will always be failure case where the thickness from shadows will be zero. So I propose to expose a parameter to scale the upper bound, and a parameter to set a lower bound as a fraction of the thickness socket. This way they would depends on object scale and almost never need adjustment. Good defaults should make these settings almost forgettable. The setting cannot be per material nor per object for performance and architectural reasons, so it would be in the scene panel. #### Thickness mode For compatibility reasons (and mainly to support thin glass panels) we do need to support the slab approximation. So this would be exposed per material using an enum: - Sphere Mode: 2 transmission events if thickness output is greater than 0. - Slab Mode: 2 transmission event if thickness output is greater than 0. It would also be convenient to have a setting to disable the thickness approximation all together. But adding an option for it makes the workflow different than displacement (where there is no disabled mode). Also, adding a `No thickness` mode would mean there would be case where the plugged values in the nodetree do nothing. So for now, the only way to remove the thickness approximation is to connect a value node to the thickness socket and set it to 0.
Sign in to join this conversation.
No Label
Interest
Alembic
Interest
Animation & Rigging
Interest
Asset Browser
Interest
Asset Browser Project Overview
Interest
Audio
Interest
Automated Testing
Interest
Blender Asset Bundle
Interest
BlendFile
Interest
Collada
Interest
Compatibility
Interest
Compositing
Interest
Core
Interest
Cycles
Interest
Dependency Graph
Interest
Development Management
Interest
EEVEE
Interest
EEVEE & Viewport
Interest
Freestyle
Interest
Geometry Nodes
Interest
Grease Pencil
Interest
ID Management
Interest
Images & Movies
Interest
Import Export
Interest
Line Art
Interest
Masking
Interest
Metal
Interest
Modeling
Interest
Modifiers
Interest
Motion Tracking
Interest
Nodes & Physics
Interest
OpenGL
Interest
Overlay
Interest
Overrides
Interest
Performance
Interest
Physics
Interest
Pipeline, Assets & IO
Interest
Platforms, Builds & Tests
Interest
Python API
Interest
Render & Cycles
Interest
Render Pipeline
Interest
Sculpt, Paint & Texture
Interest
Text Editor
Interest
Translations
Interest
Triaging
Interest
Undo
Interest
USD
Interest
User Interface
Interest
UV Editing
Interest
VFX & Video
Interest
Video Sequencer
Interest
Virtual Reality
Interest
Vulkan
Interest
Wayland
Interest
Workbench
Interest: X11
Legacy
Blender 2.8 Project
Legacy
Milestone 1: Basic, Local Asset Browser
Legacy
OpenGL Error
Meta
Good First Issue
Meta
Papercut
Meta
Retrospective
Meta
Security
Module
Animation & Rigging
Module
Core
Module
Development Management
Module
EEVEE & Viewport
Module
Grease Pencil
Module
Modeling
Module
Nodes & Physics
Module
Pipeline, Assets & IO
Module
Platforms, Builds & Tests
Module
Python API
Module
Render & Cycles
Module
Sculpt, Paint & Texture
Module
Triaging
Module
User Interface
Module
VFX & Video
Platform
FreeBSD
Platform
Linux
Platform
macOS
Platform
Windows
Priority
High
Priority
Low
Priority
Normal
Priority
Unbreak Now!
Status
Archived
Status
Confirmed
Status
Duplicate
Status
Needs Info from Developers
Status
Needs Information from User
Status
Needs Triage
Status
Resolved
Type
Bug
Type
Design
Type
Known Issue
Type
Patch
Type
Report
Type
To Do
No Milestone
No project
No Assignees
3 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: blender/blender#120384
No description provided.