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.

Details

Reviewers
Brecht Van Lommel (brecht)
Group Reviewers
Cycles
Summary

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

Repository
rB Blender
Branch
metallic_bsdf
Build Status
Buildable 6
Build 6: arc lint + arc unit
Lukas Stockner (lukasstockner97) retitled this revision from to Cycles: Add a new Metallic BSDF, combining condictive fresnel and multi-scattering GGX.May 18 2016, 1:53 AM
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.

intern/cycles/kernel/closure/bsdf_microfacet_multi.h
499

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.

568–570

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

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.

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

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.

intern/cycles/kernel/shaders/stdosl.h
529–540

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);
intern/cycles/render/nodes.cpp
1903–1904

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(fresnel == NODE_METALLIC_FRESNEL_ARTISTIC) {
    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.add_node(NODE_ARTISTIC_FRESNEL,
           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);
}

...

if(use_fresnel)
        compiler.add_node(SVM_STACK_INVALID, offset_metal_n, offset_metal_k);
intern/cycles/render/osl.cpp
1165

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

intern/cycles/util/util_half.h
51–52

#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>.

source/blender/nodes/shader/nodes/node_shader_bsdf_metallic.c
34

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.

39

Anisotropy should default to 0.0 now.

54

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.
intern/cycles/kernel/shaders/stdosl.h
529–540

When we change that, we should check if we can match OSL spec, see https://github.com/imageworks/OpenShadingLanguage/commit/3dc344ce3e44e80940424a8e9b2b68cd5189a8a7

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 blenderartists.org 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.