Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added initial draft for OSL closures #614

Merged
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
163 changes: 163 additions & 0 deletions libraries/stdlib/osl/stdosl.h
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,169 @@ closure color volume_matte() BUILTIN;
closure color subsurface(float eta, float g, color mfp, color albedo) BUILTIN;
#endif

// ******************* MATERIALX PBS LIBRARY CLOSURES - DRAFT BEGIN ******************* //

// -------------------------------------------------------------//
// BSDF closures //
// -------------------------------------------------------------//
// Constructs a diffuse reflection BSDF based on the Oren-Nayar reflectance model.
// A roughness of 0.0 gives Lambertian reflectance.
//
closure color oren_nayar_diffuse_bsdf(normal N, color albedo, float roughness) BUILTIN;
// Constructs a diffuse reflection BSDF based on the corresponding component of
// the Disney Principled shading model.
//
closure color burley_diffuse_bsdf(normal N, color albedo, float roughness) BUILTIN;
// Constructs a reflection and/or transmission BSDF based on a microfacet reflectance
// model and a Fresnel curve for dielectrics. The two tint parameters control the
// contribution of each reflection/transmission lobe. The tints should remain 100% white
// for a physically correct dielectric, but can be tweaked for artistic control or set
// to 0.0 for disabling a lobe.
// The closure may be vertically layered over a base BSDF for the surface beneath the
// dielectric layer. This is done using the layer() closure. By chaining multiple
// dielectric_bsdf closures you can describe a surface with multiple specular lobes.
// If transmission is enabled (transmission_tint > 0.0) the closure may be layered over
// a VDF closure describing the surface interior to handle absorption and scattering
// inside the medium.
//
closure color dielectric_bsdf(normal N, vector U, color reflection_tint, color transmission_tint, float roughness_x, float roughness_y, float ior, string distribution) BUILTIN;
// Constructs a reflection BSDF based on a microfacet reflectance model.
// Uses a Fresnel curve with complex refraction index for conductors/metals.
// If an artistic parametrization is preferred the artistic_ior() utility function
// can be used to convert from artistic to physical parameters.
//
closure color conductor_bsdf(normal N, vector U, float roughness_x, float roughness_y, color ior, color extinction, string distribution) BUILTIN;
niklasharrysson marked this conversation as resolved.
Show resolved Hide resolved
// Constructs a reflection and/or transmission BSDF based on a microfacet reflectance model
// and a generalized Schlick Fresnel curve. The two tint parameters control the contribution
// of each reflection/transmission lobe.
// The closure may be vertically layered over a base BSDF for the surface beneath the
// dielectric layer. This is done using the layer() closure. By chaining multiple
// dielectric_bsdf closures you can describe a surface with multiple specular lobes.
// If transmission is enabled (transmission_tint > 0.0) the closure may be layered over
// a VDF closure describing the surface interior to handle absorption and scattering
// inside the medium.
//
// TODO:
// - Transmission handling for this node has not been fully defined yet in MaterialX.
// In particular how is IOR for refractions derived from the f0, f90 parameterization?
// Do we just derive it from f0? For an artist it seems hard to control refractions that way.
//
closure color generalized_schlick_bsdf(normal N, vector U, color reflection_tint, color transmission_tint, float roughness_x, float roughness_y, color f0, color f90, float exponent, string distribution) BUILTIN;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How is external IOR / IOR layering tracked? If it's done behind the scenes by passing the IOR ratio to the closure, that may not be enough information for the generalized Schlick BSDF closure.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nhoffman-lucasfilm That's a great question. I believe this will be tracked through the use of the medium_vdf closure that describes the medium which the light travels through before hitting a new interface. The medium_vdf has an ior parameter that is an absolute IOR value for the medium.

For the primary intersection I guess air/vacuum is assumed outside. I'm not sure how it's handled for situations where the camera starts inside, like a camera under water, but I suppose a media closure could be attached to the camera.

For cases where BSDFs are layered without the use of media in-between I think all the needed information should be available still, since IOR could be derived from the parameters on each BSDF.

But maybe people from Imageworks, Adsk/Arnold or other teams that have implemented OSL closures in practice can fill in more details here?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We attach medium data to every ray (if present). If a camera is inside some medium we find the object and evaluate the shader to grab what you're calling medium_vdf here. We also compute relative IOR outside the shader. Basically we intercept the BSDF creation from closure data and override the IOR parameters.

May be worth mentioning that we have an optional parameter to all bsdfs called "force_eta" (float from 0 to 1). This allows the shader, and the artist, to override the dynamic IOR. For instance, if the look changes too much for them when underwater, they can tone down the change.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We track the IOR of the bulk medium as the ray transmits through dielectric boundaries, via the usual priority system approach. The interior bulk medium IOR is taken from the specular lobe in the standard surface model (i.e. this lobe effectively defines the IOR of the bulk). We don't take into account IOR jumps between the various layers in the model though (e.g the coat BSDF "above" the specular layer, is not aware of the specular IOR), which is probably a reasonable approach considering the closure mixture model is a somewhat ad-hoc representation of the layered structure anyway.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't currently handle the "camera under water" situation, unfortunately. Though it seems that determining this is a matter for the renderer to handle, e.g. by casting a ray to infinity and tracking the media.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"We also compute relative IOR outside the shader. Basically we intercept the BSDF creation from closure data and override the IOR parameters" - in other words the ratio (external_ior / bulk_ior) is passed into the closure's "ior" parameter? In that case, for the generalized_schlick_bsdf closure a different parameter adjustment would be needed, adjusting f0 using something like the equations from slides 107-108 or slide 111 in https://blog.selfshadow.com/publications/s2020-shading-course/hoffman/s2020_pbs_hoffman_slides.pdf (a similar adjustment might need to be made for the f90 parameter - I've spent a little bit of time on that but don't have a formula for that one ready yet). Would this kind of closure-specific parameter adjustment be feasible, or is does that logic need to be agnostic to which closure is used?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Related question - is there a way to determine the reflection's "sidedness" inside the closure? When using IOR as a parameter, internal vs. external reflection can be easily determined from the relative IOR - if it is greater or small than 1. But 1/eta and eta produce the same value for f0, so an extra bit (literally) of information is needed to disambiguate those two cases for the generalized_schlick_bsdf closure.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not overriding the closure parameter itself, but the BSDF internal object that gets created on the renderer side. We do some translation for non-ior parametrizations, but we haven't polished that a lot, it just works. Also, this doesn't affect internal BSDF layering, we don't track stuff there.

About sidedness, our convention: The shader always puts the IOR of the front surface regardless of what side is being rendered. The implementation is responsible for doing 1/eta if on the backside. We did this to remove the burden from the shaders, but other people might have other opinions.

// Constructs a translucent (diffuse transmission) BSDF based on the Lambert reflectance model.
//
closure color translucent_bsdf(normal N, color albedo) BUILTIN;

// Constructs a closure that represents straight transmission through a surface.
//
// NOTE:
// - This is not a node in the MaterialX library, but the surface shader constructor
// node has an 'opacity' parameter to control textured cutout opacity.
//
closure color transparent_bsdf() BUILTIN;
// Constructs a BSSRDF for subsurface scattering within a homogeneous medium.
//
// TODO:
// - Do we want/need this dedicated BSSRDF closure? We have had discussions before about
niklasharrysson marked this conversation as resolved.
Show resolved Hide resolved
// describing this more rigorously as a BSDF layered over a VDF instead.
// In the MaterialX library we want this as a node for the user friendly interface,
// but for back-end implementation maybe a dedicated closure is not needed?
//
closure color subsurface_bssrdf(normal N, color albedo, color radius, float anisotropy) BUILTIN;
// Constructs a microfacet BSDF for the back-scattering properties of cloth-like materials.
// This closure may be vertically layered over a base BSDF, where energy that is not reflected
// will be transmitted to the base closure.
//
closure color sheen_bsdf(normal N, color albedo, float roughness) BUILTIN;
// Adds an iridescent thin film layer over a microfacet base BSDF. This must be layered over
niklasharrysson marked this conversation as resolved.
Show resolved Hide resolved
// another base BSDF using the layer() closure, as this is a modifier and cannot be used as a
// standalone closure.
//
// TODO:
// - This might be better to represent as optional extra arguments on the closures that do support
// thin-film iridescence (dielectric_bsdf, conductor_bsdf and generalized_schlick_bsdf)?
//
closure color thin_film_bsdf(float thickness, float ior) BUILTIN;
// -------------------------------------------------------------//
// EDF closures //
// -------------------------------------------------------------//
// Constructs an EDF emitting light uniformly in all directions.
//
closure color uniform_edf(color emittance) BUILTIN;

// Constructs an EDF emitting light inside a cone around the normal direction.
//
closure color conical_edf(color emittance, normal N, float inner_angle, float outer_angle) BUILTIN;

// Constructs an EDF emitting light according to a measured IES light profile.
niklasharrysson marked this conversation as resolved.
Show resolved Hide resolved
//
closure color measured_edf(color emittance, normal N, string file) BUILTIN;
// -------------------------------------------------------------//
// VDF closures //
// -------------------------------------------------------------//
// Constructs a VDF for pure volumetric absorption.
//
closure color absorption_vdf(color absorption) BUILTIN;
// Constructs a VDF scattering light for a participating medium, based on the Henyey-Greenstein
// phase function. Forward, backward and uniform scattering is supported and controlled by the
// anisotropy input.
//
niklasharrysson marked this conversation as resolved.
Show resolved Hide resolved
closure color anisotropic_vdf(color absorption, color scattering, float anisotropy) BUILTIN;
niklasharrysson marked this conversation as resolved.
Show resolved Hide resolved
// -------------------------------------------------------------//
// Layering closures //
// -------------------------------------------------------------//
// Vertically layer a layerable BSDF such as dielectric_bsdf, generalized_schlick_bsdf or
// sheen_bsdf over a BSDF or VDF. The implementation is target specific, but a standard way
// of handling this is by albedo scaling, using "base*(1-reflectance(top)) + top", where
// reflectance() calculates the directional albedo of a given top BSDF.
//
// TODO:
// - This could also be achived by closure nesting where each layerable closure takes
// a closure color "base" input instead.
// - One advantage having a dedicated layer() closure is that in the future we may want to
// introduce parameters to describe the sandwitched medium between the layer interfaces.
// Such parameterization could then be added on this layer() closure as extra arguments.
// - Do we want/need parameters for the medium here now, or do we look at that later?
//
closure color layer(closure color top, closure color base) BUILTIN;
// Mix two closures according to a weight. Performs horizontal layering by linear interpolation
// between the two inputs: "bg∗(1−weight) + fg∗weight".
//
closure color mix(closure color bg, closure color fg, float weight);
niklasharrysson marked this conversation as resolved.
Show resolved Hide resolved
// -------------------------------------------------------------//
// Utility functions //
// -------------------------------------------------------------//
// Converts the artistic parameterization reflectivity and edge_color to complex IOR values.
// To be used with the conductor_bsdf() closure.
//
void artistic_ior(color reflectivity, color edge_color, output color ior, output color extinction);
niklasharrysson marked this conversation as resolved.
Show resolved Hide resolved

// ******************* MATERIALX PBS LIBRARY CLOSURES - DRAFT END ******************* //


// Renderer state
int backfacing () BUILTIN;
int raytype (string typename) BUILTIN;
Expand Down