Add interference effects to glossy and glass shaders #49125

Open
opened 2016-08-20 19:35:59 +02:00 by Robert M · 17 comments

As a response to patch D2003 and my own work in OSL (interference script), I suggest to add the option of interference effects to the glossy and glass shaders natively in cycles. Please note that I'm not familiar with the source code of Cycles, so this task description is based on cursory glancing through the code.

The OSL script suffers from the limitation that the effect of roughness on the surface normal does not influence the reflectance and transmittance factors calculated with the generalized Fresnel equations. Only the macroscale normal is used for the reflectance and transmittance. Patch D2003 handles this in a more appropriate way for metals by applying the correct reflectance to each sample from a distribution of angles in case of rough surfaces.

I propose to build on top of patch D2003, which I expect to have pretty much laid out all the ground work, and to extend it to include one or two additional layers of an arbitrary material that can be described with a complex refractive index.

Things to discuss:

  • Implementation: multiple glossy shaders, or a single shader that can handle substrate-only, substrate+1 layer, substrate+2 layers?
  • The phase of the reflectance is required for physically-correct behavior (no approximation). I implemented a complex math library in OSL just for this. Is it acceptable to do the same for cycles?
  • If for substrate-only the approximate Fresnel reflectances are used, then for 1 or two layers with zero thickness a small color change can occur as in that case the exact solution is used. Is that acceptable? (disregarding a check for zero thickness of course)
  • What to show the user? My OSL script has 3 n,k pairs (RGB) for each material (substrate, layer 1, layer 2). Then two thicknesses, one for each layer. I think this is the minimum. I optionally have the wavelengths of the RGB triplet and the refractive index of the outside medium as input via conditional compilation. The latter is perhaps the least useful, but the wavelengths determine the spacing between the interference fringes and therefore have an artistic value.

There are probably more things that I didn't think of, but this is a start. I am familiar with the physics and the equations that calculate such a feature and have experience with coding in C++. I would volunteer to implement interference effects in cycles and am thinking along the lines of co-development with a more senior Cycles developer.

As a response to patch [D2003](https://archive.blender.org/developer/D2003) and my own work in OSL ([interference script](https://blenderartists.org/forum/showthread.php?403299-UPDATE-v1-6-Cycles-PBR-thin-film-interference-and-metals)), I suggest to add the option of interference effects to the glossy and glass shaders natively in cycles. Please note that I'm not familiar with the source code of Cycles, so this task description is based on cursory glancing through the code. The OSL script suffers from the limitation that the effect of roughness on the surface normal does not influence the reflectance and transmittance factors calculated with the generalized Fresnel equations. Only the macroscale normal is used for the reflectance and transmittance. Patch [D2003](https://archive.blender.org/developer/D2003) handles this in a more appropriate way for metals by applying the correct reflectance to each sample from a distribution of angles in case of rough surfaces. I propose to build on top of patch [D2003](https://archive.blender.org/developer/D2003), which I expect to have pretty much laid out all the ground work, and to extend it to include one or two additional layers of an arbitrary material that can be described with a complex refractive index. Things to discuss: - Implementation: multiple glossy shaders, or a single shader that can handle substrate-only, substrate+1 layer, substrate+2 layers? - The phase of the reflectance is required for physically-correct behavior (no approximation). I implemented a complex math library in OSL just for this. Is it acceptable to do the same for cycles? - If for substrate-only the approximate Fresnel reflectances are used, then for 1 or two layers with zero thickness a small color change can occur as in that case the exact solution is used. Is that acceptable? (disregarding a check for zero thickness of course) - What to show the user? My OSL script has 3 n,k pairs (RGB) for each material (substrate, layer 1, layer 2). Then two thicknesses, one for each layer. I think this is the minimum. I optionally have the wavelengths of the RGB triplet and the refractive index of the outside medium as input via conditional compilation. The latter is perhaps the least useful, but the wavelengths determine the spacing between the interference fringes and therefore have an artistic value. There are probably more things that I didn't think of, but this is a start. I am familiar with the physics and the equations that calculate such a feature and have experience with coding in C++. I would volunteer to implement interference effects in cycles and am thinking along the lines of co-development with a more senior Cycles developer.
Brecht Van Lommel was assigned by Robert M 2016-08-20 19:35:59 +02:00
Author

Changed status to: 'Open'

Changed status to: 'Open'
Author

Added subscriber: @prutser

Added subscriber: @prutser

As I understand it, the OSL shader you implemented is not just about thin film interference, but a general substrate + 2 glossy layers shader? At that point, aren't you almost writing an uber shader, like the Disney principled BSDF that's being implemented?

We should try to end up with nodes that complement each other and have consistent and "intuitive" parameters.

I don't have the full picture for this. Perhaps more rare or specific effects like thin film interference or car paint materials do not need to be part of any uber shader. Instead we could have a node that takes some BSDF as input, and outputs a modified BSDF with an extra layer on top? Or maybe for most materials you could have one parameter that controls the amount of interference, and you don't really need so much control?

For the complex math functions, if they are required we could have them of course. If we can use a simpler and faster approximation that could be fine too, I'm not too familiar with the specifics here.

As I understand it, the OSL shader you implemented is not just about thin film interference, but a general substrate + 2 glossy layers shader? At that point, aren't you almost writing an uber shader, like the Disney principled BSDF that's being implemented? We should try to end up with nodes that complement each other and have consistent and "intuitive" parameters. I don't have the full picture for this. Perhaps more rare or specific effects like thin film interference or car paint materials do not need to be part of any uber shader. Instead we could have a node that takes some BSDF as input, and outputs a modified BSDF with an extra layer on top? Or maybe for most materials you could have one parameter that controls the amount of interference, and you don't really need so much control? For the complex math functions, if they are required we could have them of course. If we can use a simpler and faster approximation that could be fine too, I'm not too familiar with the specifics here.
Author

No, the OSL script is definitely not an uber shader. It really is only about thin-film interference. Let me for simplicity stick to the glossy version: the OSL implementation is the direct analog of the fresnel_conductor() function. Only, it's not limited to just metallic responses, but takes into account the coherent bouncing of light in between the films, which gives the colorful surfaces you see in soap bubbles.

There is no actual BSDF output from the OSL script. Instead, the calculated color/reflectance is inserted into the color input of a single glossy shader. This is not an effect that you can simulate by stacking multiple glossy shaders with different materials, not even when it's a glossy shader based on the complex refractive index (aka n, k pair). The crucial part to get the interference pattern is that you calculate with the (complex) amplitudes of the reflectances, and not the reflected intensities. I therefore also suspect that creating a node which modifies an input node will not work, as the output of any BSDF is related to intensity.

The analogy with the fresnel_conductor function is the precise reason why I think patch D2003 is very close to get a working implementation quickly. Instead of calling fresnel_conductor(), you'd call generalized_fresnel (making something up here) and that's it. Of course, there's ample room to discuss how to make the parameters intuitive.

With respect to the complex math, I figure that using the C++ template for the float3 type should be sufficient. I guess only the actually used functions from the class would be linked into the binary(?)

No, the OSL script is definitely not an uber shader. It really is only about thin-film interference. Let me for simplicity stick to the glossy version: the OSL implementation is the direct analog of the fresnel_conductor() function. Only, it's not limited to just metallic responses, but takes into account the *coherent* bouncing of light in between the films, which gives the colorful surfaces you see in soap bubbles. There is no actual BSDF output from the OSL script. Instead, the calculated color/reflectance is inserted into the color input of a *single* glossy shader. This is not an effect that you can simulate by stacking multiple glossy shaders with different materials, not even when it's a glossy shader based on the complex refractive index (aka n, k pair). The crucial part to get the interference pattern is that you calculate with the (complex) amplitudes of the reflectances, and not the reflected intensities. I therefore also suspect that creating a node which modifies an input node will not work, as the output of any BSDF is related to intensity. The analogy with the fresnel_conductor function is the precise reason why I think patch [D2003](https://archive.blender.org/developer/D2003) is very close to get a working implementation quickly. Instead of calling fresnel_conductor(), you'd call generalized_fresnel (making something up here) and that's it. Of course, there's ample room to discuss how to make the parameters intuitive. With respect to the complex math, I figure that using the <complex> C++ template for the float3 type should be sufficient. I guess only the actually used functions from the class would be linked into the binary(?)

Ok, I'm not expert at this so bear with me. The layers you have in this model, aren't they like typical BSDF layers? But you more accurately simulate the interaction of the complex IOR, while making simplifying assumptions about e.g. the roughness of the layers, that let you fold everything into a fresnel function?

If that's the case, it might be fine to treat them in a special way if doing the complex IOR interaction in a general framework is not practical. But I wonder if this strikes the right balance. Adding 6-8 parameters seems way too much for things like soap bubbles, while for car paint just a different fresnel function might not be enough and you want to be able to give different layers different roughness, normals for flakes, etc.

It could be done as a separate node that modifies a BSDF if we want to, where the node would basically change the fresnel function of the BSDF closure. Such a node could use a model like this, or a color ramp, etc. I'm not sure if that's the right design, but it is possible.

We can't use in the kernel, there's no C++ support in OpenCL at the moment, we must use C code still.

Ok, I'm not expert at this so bear with me. The layers you have in this model, aren't they like typical BSDF layers? But you more accurately simulate the interaction of the complex IOR, while making simplifying assumptions about e.g. the roughness of the layers, that let you fold everything into a fresnel function? If that's the case, it might be fine to treat them in a special way if doing the complex IOR interaction in a general framework is not practical. But I wonder if this strikes the right balance. Adding 6-8 parameters seems way too much for things like soap bubbles, while for car paint just a different fresnel function might not be enough and you want to be able to give different layers different roughness, normals for flakes, etc. It could be done as a separate node that modifies a BSDF if we want to, where the node would basically change the fresnel function of the BSDF closure. Such a node could use a model like this, or a color ramp, etc. I'm not sure if that's the right design, but it is possible. We can't use <complex> in the kernel, there's no C++ support in OpenCL at the moment, we must use C code still.
Brecht Van Lommel removed their assignment 2016-08-21 15:57:38 +02:00

Added subscriber: @brecht

Added subscriber: @brecht
Author

Well of course, and since I'm not an expert on the computer science part, I ask of you to do the same with me :)

With that in mind, I would say 'maybe' as an answer to your question. Indeed, you disregard the possibility of different roughnesses between the layers. This isn't just an approximation, it's a requirement for interference to be visible. On any substrate, the additional layers need to be conformal, and the roughness needs to be of 'macroscopic' size. In other words, on the scale of the wavelength, the surface needs to be smooth for a sufficiently large area for the light to bounce around in a coherent way at least a couple of times. See the picture here: bsdf.png
I've tried to illustrate what I mean: in (a) you'd have the typical stack of BSDF functions, what is not what I have in mind. Instead, the situation in (b) is what you'd typically see with interference effects. There, a conformal layer is on top of the substrate which gives rise to interference. Because of this conformality, the stack can indeed be described with a generalized fresnel function, which takes constructive and destructive interference of light into account.

While I do agree that 6-8 additional parameters seem excessive for a soap bubble, I'd like to stress that interference effects are much more present in daily live than you might think and aren't limited to just rainbow effects in soap. For example, fingerprints on bare metal give rise to interference, heating of iron changes its color because of the formation of a layer of iron oxide, aging copper turns first darker red and then to somewhat grey because of a layer of cupric oxide (among other materials), and silver tarnishes with silver sulfide over the years. These can all be easily simulated with interference terms and yield photo-realistic and physically-correct results (disregarding the microfacet half-vector issue of course).

With respect to your remark about car paint shaders, I come back to the point that there can't be interference if the layers aren't conformal. I therefore don't believe that the amount of degrees of freedom is insufficient for a car paint shader. Moreover, there is no reason why you couldn't mix multiple interference shaders with different roughness settings if that's the look you'd want. The input for normals for flakes is, if I get what you mean, already in place as it is with the current glossy shaders and doesn't need any additional input.

Regarding your thinking-out-loud suggestion of a node that modifies a BSDF: it sounds like the equivalent of a callback function to me? That would be precisely what I missed in the OSL language. I'm also not sure it is the right design, but it's good to consider the options.

It's a pity cannot be used, but other options then might be the C99 complex functions, or worst-case I could re-implement my complex math library from OSL in C.

Some images of the non-trivial examples:
oil_stained_aluminum.png
iron heated.png
{F340834}pete2.png
silver tarnished.png
Edit: and one more obvious example for completeness' sake:
soap.png

Well of course, and since I'm not an expert on the computer science part, I ask of you to do the same with me :) With that in mind, I would say 'maybe' as an answer to your question. Indeed, you disregard the possibility of different roughnesses between the layers. This isn't just an approximation, it's a requirement for interference to be visible. On any substrate, the additional layers need to be conformal, and the roughness needs to be of 'macroscopic' size. In other words, on the scale of the wavelength, the surface needs to be smooth for a sufficiently large area for the light to bounce around in a coherent way at least a couple of times. See the picture here: ![bsdf.png](https://archive.blender.org/developer/F340803/bsdf.png) I've tried to illustrate what I mean: in (a) you'd have the typical stack of BSDF functions, what is not what I have in mind. Instead, the situation in (b) is what you'd typically see with interference effects. There, a conformal layer is on top of the substrate which gives rise to interference. Because of this conformality, the stack can indeed be described with a generalized fresnel function, which takes constructive and destructive interference of light into account. While I do agree that 6-8 additional parameters seem excessive for a soap bubble, I'd like to stress that interference effects are much more present in daily live than you might think and aren't limited to just rainbow effects in soap. For example, fingerprints on bare metal give rise to interference, heating of iron changes its color because of the formation of a layer of iron oxide, aging copper turns first darker red and then to somewhat grey because of a layer of cupric oxide (among other materials), and silver tarnishes with silver sulfide over the years. These can all be easily simulated with interference terms and yield photo-realistic and physically-correct results (disregarding the microfacet half-vector issue of course). With respect to your remark about car paint shaders, I come back to the point that there can't be interference if the layers aren't conformal. I therefore don't believe that the amount of degrees of freedom is insufficient for a car paint shader. Moreover, there is no reason why you couldn't mix multiple interference shaders with different roughness settings if that's the look you'd want. The input for normals for flakes is, if I get what you mean, already in place as it is with the current glossy shaders and doesn't need any additional input. Regarding your thinking-out-loud suggestion of a node that modifies a BSDF: it sounds like the equivalent of a callback function to me? That would be precisely what I missed in the OSL language. I'm also not sure it is the right design, but it's good to consider the options. It's a pity <complex> cannot be used, but other options then might be the C99 complex functions, or worst-case I could re-implement my complex math library from OSL in C. Some images of the non-trivial examples: ![oil_stained_aluminum.png](https://archive.blender.org/developer/F340823/oil_stained_aluminum.png) ![iron heated.png](https://archive.blender.org/developer/F340828/iron_heated.png) {[F340834](https://archive.blender.org/developer/F340834/copper_aged.png)}![pete2.png](https://archive.blender.org/developer/F340826/pete2.png) ![silver tarnished.png](https://archive.blender.org/developer/F340830/silver_tarnished.png) Edit: and one more obvious example for completeness' sake: ![soap.png](https://archive.blender.org/developer/F340838/soap.png)

Thanks for the explanation. I think it could fit in the Metallic BSDF node as another fresnel mode, only showing extra parameters when a "Thin Film" mode is enabled. The Glass BSDF could get a similar mode.

Still, a lot of parameters like this with indirect effect on the look is not really in line with how I think Cycles shader nodes should be designed. Personally I think we should try to find parametrizations like the Artistic fresnel or the Disney BSDF, where there is a relatively intuitive and direct relationship to the look of the material. With 8-10 total parameters for just fresnel, it seems more an interface for an engineer than an artist.

If we can somehow expose this as a color ramp for example, driven by the angles with the incoming and outgoing light, that might be more intuitive. But I'm not sure if it is possible.

Thanks for the explanation. I think it could fit in the Metallic BSDF node as another fresnel mode, only showing extra parameters when a "Thin Film" mode is enabled. The Glass BSDF could get a similar mode. Still, a lot of parameters like this with indirect effect on the look is not really in line with how I think Cycles shader nodes should be designed. Personally I think we should try to find parametrizations like the Artistic fresnel or the Disney BSDF, where there is a relatively intuitive and direct relationship to the look of the material. With 8-10 total parameters for just fresnel, it seems more an interface for an engineer than an artist. If we can somehow expose this as a color ramp for example, driven by the angles with the incoming and outgoing light, that might be more intuitive. But I'm not sure if it is possible.
Author

I understand your hesitation to 'dump' many parameters onto users but I think that the way Cycles seems to be progressing is the correct way to proceed: artistic settings for those that want to go with that workflow, and physically-correct settings for those in favor of that type of workflow.

The artistic method has always been an option, however with ever-increasing computational power I think it's logical to pursue better photorealism based on physical principles. Cycles itself is of course a prime example, but also the upcoming n,k metallic shader is a good example. I also think that refractive index and layer thickness are easy enough to understand, which leaves the extinction coefficient (k) that can be more elusive. Even if you don't really know, finding measured values of n and k is not hard to do and as a user you're immediately rewarded with, for example, metals that look and behave just like they would in the real world. There simply is no way to get that result without the physical approach. The issue can be alleviated further by providing demo files that already contain the correct settings for many metals/materials, as a sort of material library, and documentation that goes beyond describing just the parameters but also the pricinples. I would also volunteer for that.

It's interesting that you bring up the option of color ramps vs. angle, as that approach actually would be best suited for the simple n,k materials. Unless you parameterize the values of n and k, you can fit the physically-correct behaviour of metals with such an approach. However, making these curves by hand and getting them correct is much harder than filling in the listed values for the metal you'd like. But, as soon as you would like to parameterize anything (in this particular case layer thicknesses, for example), this method doesn't work at all. In order to expose the additional functionality, would be something to make 'modifier frames' of the simple shaders, that add additional functionality to the simple shader? For example, you draw an 'interference frame', drop in a glossy shader and it that makes it expose the additional parameters? Indeed in the backend I would implement them as separate shaders, but the presentation might be more intuitive as you wouldn't have an enormous list of different types of shaders. This also means that the user is only exposed to them when they purposely take the action to get the functionality.

I understand your hesitation to 'dump' many parameters onto users but I think that the way Cycles seems to be progressing is the correct way to proceed: artistic settings for those that want to go with that workflow, and physically-correct settings for those in favor of that type of workflow. The artistic method has always been an option, however with ever-increasing computational power I think it's logical to pursue better photorealism based on physical principles. Cycles itself is of course a prime example, but also the upcoming n,k metallic shader is a good example. I also think that refractive index and layer thickness are easy enough to understand, which leaves the extinction coefficient (k) that can be more elusive. Even if you don't really know, finding measured values of n and k is not hard to do and as a user you're immediately rewarded with, for example, metals that look and behave just like they would in the real world. There simply is no way to get that result without the physical approach. The issue can be alleviated further by providing demo files that already contain the correct settings for many metals/materials, as a sort of material library, and documentation that goes beyond describing just the parameters but also the pricinples. I would also volunteer for that. It's interesting that you bring up the option of color ramps vs. angle, as that approach actually would be best suited for the simple n,k materials. Unless you parameterize the values of n and k, you can fit the physically-correct behaviour of metals with such an approach. However, making these curves by hand and getting them correct is much harder than filling in the listed values for the metal you'd like. But, as soon as you would like to parameterize anything (in this particular case layer thicknesses, for example), this method doesn't work at all. In order to expose the additional functionality, would be something to make 'modifier frames' of the simple shaders, that add additional functionality to the simple shader? For example, you draw an 'interference frame', drop in a glossy shader and it that makes it expose the additional parameters? Indeed in the backend I would implement them as separate shaders, but the presentation might be more intuitive as you wouldn't have an enormous list of different types of shaders. This also means that the user is only exposed to them when they purposely take the action to get the functionality.

Added subscriber: @semaph0r

Added subscriber: @semaph0r

Added subscriber: @smilebags

Added subscriber: @smilebags

After reading through this I thought I would add that there should be a more artistic way to get reasonably accurate results with inputs of color, thickness, and roughness. I've played with this idea, but not having the technical understanding @prutser has, I didn't create a shader with the physical attributes. I guess the implementation will depend on striking the right balance of ease of use to accuracy and flexibility.

I definitely think having all possible needed values for a 3 layer material is flexible, and if you know the numbers it can give you perfect results. That being said, for things like soap bubbles a transparent/glossy mix with its colour determined by a thin film interference node with just two inputs is sufficient.

I'm looking forward to seeing this added.

After reading through this I thought I would add that there should be a more artistic way to get reasonably accurate results with inputs of color, thickness, and roughness. I've played with this idea, but not having the technical understanding @prutser has, I didn't create a shader with the physical attributes. I guess the implementation will depend on striking the right balance of ease of use to accuracy and flexibility. I definitely think having all possible needed values for a 3 layer material is flexible, and if you know the numbers it can give you perfect results. That being said, for things like soap bubbles a transparent/glossy mix with its colour determined by a thin film interference node with just two inputs is sufficient. I'm looking forward to seeing this added.
Author

Well maybe a gentle course of action would be to introduce a simple shader first, with an input for refractive index of the layer and the substrate, and an input for the layer thickness. That would be sufficient for iridescent films and can be used to mimic oil spills and soap.

The interaction of oil or oxide films on metals and thin metal films on plastics and glasses could come at a later stage, also depending on how the simple shader is received by the users.

Well maybe a gentle course of action would be to introduce a simple shader first, with an input for refractive index of the layer and the substrate, and an input for the layer thickness. That would be sufficient for iridescent films and can be used to mimic oil spills and soap. The interaction of oil or oxide films on metals and thin metal films on plastics and glasses could come at a later stage, also depending on how the simple shader is received by the users.

Added subscriber: @Harvester

Added subscriber: @Harvester

Added subscriber: @padthai

Added subscriber: @padthai

Added subscriber: @lemenicier_julien

Added subscriber: @lemenicier_julien

Added subscriber: @Defka

Added subscriber: @Defka
Brecht Van Lommel added this to the Render & Cycles project 2023-02-07 19:08:35 +01:00
Philipp Oeser removed the
Interest
Render & Cycles
label 2023-02-09 13:59:21 +01:00
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 Assignees
8 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#49125
No description provided.