Page MenuHome

Cycles: Add a new Metallic BSDF, combining condictive fresnel and multi-scattering GGX
Needs RevisionPublic

Authored by Lukas Stockner (lukasstockner97) on May 18 2016, 1:53 AM.
"Like" token, awarded by MetinSeven."Mountain of Wealth" token, awarded by cardboard."Mountain of Wealth" token, awarded by marcog.


Brecht Van Lommel (brecht)
Group Reviewers

This patch implements a new BSDF called "Metallic BSDF", which essentially is the
Glossy BSDF with Multi-Scattering GGX combined with the conductive fresnel
absorption term applied at each bounce.
To specify the properties of the conductor, either the usual 6 NK-values (real and
imaginary components of the IOR, each for the three color channels) or the
more intuitive reparametrization to "Reflectivity" and "Edge Tint" from the paper
"Artist Friendly Metallic Fresnel" can be used.

Depends on D2002.

Diff Detail

rB Blender
Build Status
Buildable 6
Build 6: arc lint + arc unit

Event Timeline

Lukas Stockner (lukasstockner97) retitled this revision from to Cycles: Add a new Metallic BSDF, combining condictive fresnel and multi-scattering GGX.
Brecht Van Lommel (brecht) requested changes to this revision.May 19 2016, 12:24 AM

Would it makes sense to merge this with the Anisotropic BSDF node, and rename that to Metallic BSDF and have an extra "No Fresnel" model? I think that's what you use anisotropy for anyway, and I think we can do this without breaking backwards compatibility.

I guess one optimization here would be that if you're using artistic and you have fixed values, then it could be converted to a physical model with the values precomputed. Doesn't have to be done now but could be a comment in the code.


Haha, pretty clever, I guess it will work correctly for now.

At some point we should bite the bullet and make dynamically sized ShaderClosures, so that you can have something like:

struct MetallicClosure {
    ShaderClosure base;
    float3 N, T;
    float3 n, k;

Not necessarily very complicated but will require some deeper changes that I can try doing after this patch is in master.


Same comment as the other patch, make this a single function call to avoid divergence on the GPU.

This revision now requires changes to proceed.May 19 2016, 12:24 AM
Lukas Stockner (lukasstockner97) edited edge metadata.

Okay, here's the new version: Instead of adding a new BSDF, the patch now renames the Anisotropic BSDF and adds the option
to choose the type of coloring applied: "Color" is like the old node and just multplies with the color. "Physical" uses
the typical NK-parametrization and the conductive fresnel function for coloring (even with the correct microfacet handling).
"Artistic" uses the same fresnel term, but allows the user to define the NK-values with the mode intuitive parametrization.

Lukas Stockner (lukasstockner97) edited edge metadata.

So, it seems that I have included a bunch of changes that actually aren't part of this change at all.
Here's a clean version.

Brecht Van Lommel (brecht) requested changes to this revision.Jul 17 2016, 3:07 PM
Brecht Van Lommel (brecht) edited edge metadata.

I should still test what kind of performance impact this has on GPU render and if compiles correctly with OpenCL, but some more comments for now.


The number of functions is getting a bit out of hand here. I think we could use keyword parameters to simplify this and the code in osl_closures.cpp.

Maybe we can leave it as for now, and as a follow up I'll add a unified closure to replace a bunch of these functions:

microfacet(N, T, ax, ay, "distribution", "multi_ggx", "n", n, "k", k);

Do we need a new stack_assign_offset function?

For the texture mapping node we have a similar situation, and there we output to the same stack location as the input. I think the same could be done here? Like:

int offset_metal_n, offset_metal_k;

    if(refl_in->link == NULL && edge_in->link == NULL) {
       offset_metal_n = compiler.stack_assign(n_in);
       offset_metal_k = compiler.stack_assign(k_in);
    else {
       offset_metal_n = compiler.stack_assign(refl_in);
       offset_metal_k = compiler.stack_assign(edge_in);
           compiler.encode_uchar4(offset_metal_n, offset_metal_k));
else {
    offset_metal_n = compiler.stack_assign(n_in);
    offset_metal_k = compiler.stack_assign(k_in);


        compiler.add_node(SVM_STACK_INVALID, offset_metal_n, offset_metal_k);

The new argument needs to be added here as well, for building without OSL.


#include <cuda_fp16.h> must be added for these CUDA functions to be available. Maybe best to add it in kernel_compat_cuda.h next to #include <cuda.h>.


I would get rid of "Reflectivity" and just use "Color", that's the term we use for reflectance or reflectivity in all other closure nodes.


Anisotropy should default to 0.0 now.


I'd make SHD_METALLIC_ARTISTIC the default.

This revision now requires changes to proceed.Jul 17 2016, 3:07 PM
Thomas Dinges (dingto) added inline comments.

When we change that, we should check if we can match OSL spec, see

We can have one microfacet() closure, matching OSL spec then, and one microfacet_nk() for the additional closure.

I would like to make a case for a more generic approach, if possible. The background: I have written an OSL script which calculates interference terms in layer stacks, where the substrate and (one or two) additional layers can be composed of materials with arbitrary n and k. See the post at here for examples.

This type of shader is obviously suitable for simulating soap bubbles, oil stains and the like, but also multichromic car paints, heating effects in metals, aging, tarnishing etc. The OSL script is performing adequately, but I ran into the fact that OSL doesn't allow access to the half vectors, so there is no physically-correct way to take roughness into account. In its current state, I don't think there are too many steps to take to implement interference with physically-correct handling of roughness starting from this patch.

I have no experience with the Cycles source code, but I do have experience with C++. I also am quite familiar with the physics behind the OSL script. Therefore, if desired, I would certainly volunteer to implement such a feature in Cycles. I would need some guidance from a more experienced developer (Lukas?) to get started, though

How does that sound?

@Robert M (prutser_), thin film interference would be a useful feature I think. If you want to discuss how to best implement that, you can do that on the mailing list, on irc, or you can create a design task for the Cycles project.