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 all 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
229 changes: 229 additions & 0 deletions libraries/stdlib/osl/stdosl.h
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,235 @@ closure color volume_matte() BUILTIN;
closure color subsurface(float eta, float g, color mfp, color albedo) BUILTIN;
#endif

#ifdef MATERIALX_CLOSURES

// -------------------------------------------------------------//
// BSDF closures //
// -------------------------------------------------------------//
// Constructs a diffuse reflection BSDF based on the Oren-Nayar reflectance model.
//
// \param N Normal vector of the surface point beeing shaded.
// \param albedo Surface albedo.
// \param roughness Surface roughness [0,1]. A value of 0.0 gives Lambertian reflectance.
// \param label Optional string parameter to name this component. For use in AOVs / LPEs.
//
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.
//
// \param N Normal vector of the surface point beeing shaded.
// \param albedo Surface albedo.
// \param roughness Surface roughness [0,1].
// \param label Optional string parameter to name this component. For use in AOVs / LPEs.
//
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.
//
// \param N Normal vector of the surface point beeing shaded.
// \param U Tangent vector of the surface point beeing shaded.
// \param reflection_tint Weight per color channel for the reflection lobe. Should be (1,1,1) for a physically-correct dielectric surface,
// but can be tweaked for artistic control. Set to (0,0,0) to disable reflection.
// \param transmission_tint Weight per color channel for the transmission lobe. Should be (1,1,1) for a physically-correct dielectric surface,
// but can be tweaked for artistic control. Set to (0,0,0) to disable transmission.
// \param roughness_x Surface roughness in the U direction with a perceptually linear response over its range.
// \param roughness_y Surface roughness in the V direction with a perceptually linear response over its range.
// \param ior Refraction index.
// \param distribution Microfacet distribution. An implementation is expected to support the following distributions: { "ggx" }
// \param thinfilm_thickness Optional float parameter for thickness of an iridescent thin film layer on top of this BSDF. Given in nanometers.
// \param thinfilm_ior Optional float parameter for refraction index of the thin film layer.
// \param label Optional string parameter to name this component. For use in AOVs / LPEs.
//
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.
//
// \param N Normal vector of the surface point beeing shaded.
// \param U Tangent vector of the surface point beeing shaded.
// \param roughness_x Surface roughness in the U direction with a perceptually linear response over its range.
// \param roughness_y Surface roughness in the V direction with a perceptually linear response over its range.
// \param ior Refraction index.
// \param extinction Extinction coefficient.
// \param distribution Microfacet distribution. An implementation is expected to support the following distributions: { "ggx" }
// \param thinfilm_thickness Optional float parameter for thickness of an iridescent thin film layer on top of this BSDF. Given in nanometers.
// \param thinfilm_ior Optional float parameter for refraction index of the thin film layer.
// \param label Optional string parameter to name this component. For use in AOVs / LPEs.
//
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.
//
// \param N Normal vector of the surface point beeing shaded.
// \param U Tangent vector of the surface point beeing shaded.
// \param reflection_tint Weight per color channel for the reflection lobe. Set to (0,0,0) to disable reflection.
// \param transmission_tint Weight per color channel for the transmission lobe. Set to (0,0,0) to disable transmission.
// \param roughness_x Surface roughness in the U direction with a perceptually linear response over its range.
// \param roughness_y Surface roughness in the V direction with a perceptually linear response over its range.
// \param f0 Reflectivity per color channel at facing angles.
// \param f90 Reflectivity per color channel at grazing angles.
// \param distribution Microfacet distribution. An implementation is expected to support the following distributions: { "ggx" }
// \param thinfilm_thickness Optional float parameter for thickness of an iridescent thin film layer on top of this BSDF. Given in nanometers.
// \param thinfilm_ior Optional float parameter for refraction index of the thin film layer.
// \param label Optional string parameter to name this component. For use in AOVs / LPEs.
//
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.
//
// \param N Normal vector of the surface point beeing shaded.
// \param albedo Surface albedo.
// \param label Optional string parameter to name this component. For use in AOVs / LPEs.
//
closure color translucent_bsdf(normal N, color albedo) BUILTIN;

// Constructs a closure that represents straight transmission through a surface.
//
// \param label Optional string parameter to name this component. For use in AOVs / LPEs.
//
// 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.
//
// \param N Normal vector of the surface point beeing shaded.
// \param albedo Single-scattering albedo of the medium.
// \param transmission_depth Distance travelled inside the medium by white light before its color becomes transmission_color by Beer's law.
// Given in scene length units, range [0,infinity). Together with transmission_color this determines the extinction
// coefficient of the medium.
// \param transmission_color Desired color resulting from white light transmitted a distance of 'transmission_depth' through the medium.
// Together with transmission_depth this determines the extinction coefficient of the medium.
// \param anisotropy Scattering anisotropy [-1,1]. Negative values give backwards scattering, positive values give forward scattering,
// and 0.0 gives uniform scattering.
// \param label Optional string parameter to name this component. For use in AOVs / LPEs.
//
closure color subsurface_bssrdf(normal N, color albedo, float transmission_depth, color transmission_color, 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.
//
// \param N Normal vector of the surface point beeing shaded.
// \param albedo Surface albedo.
// \param roughness Surface roughness [0,1].
// \param label Optional string parameter to name this component. For use in AOVs / LPEs.
//
closure color sheen_bsdf(normal N, color albedo, float roughness) BUILTIN;
// -------------------------------------------------------------//
// EDF closures //
// -------------------------------------------------------------//
// Constructs an EDF emitting light uniformly in all directions.
//
// \param emittance Radiant emittance of light leaving the surface.
// \param label Optional string parameter to name this component. For use in AOVs / LPEs.
//
closure color uniform_edf(color emittance) BUILTIN;
// -------------------------------------------------------------//
// VDF closures //
// -------------------------------------------------------------//
// Constructs a VDF scattering light for a general 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
// \param albedo Volume single-scattering albedo.
// \param extinction Volume extinction coefficient.
// \param anisotropy Scattering anisotropy [-1,1]. Negative values give backwards scattering, positive values give forward scattering,
// and 0.0 gives uniform scattering.
// \param label Optional string parameter to name this component. For use in AOVs / LPEs.
//
closure color anisotropic_vdf(color albedo, color extinction, float anisotropy) BUILTIN;

// Constructs a VDF for light passing through a dielectric homogenous medium, such as glass or liquids.
// The parameters transmission_depth and transmission_color control the extinction coefficient of the medium
// in and artist-friendly way. A priority can be set to determine the ordering of overlapping media.
//
// \param albedo Single-scattering albedo of the medium.
// \param transmission_depth Distance travelled inside the medium by white light before its color becomes transmission_color by Beer's law.
// Given in scene length units, range [0,infinity). Together with transmission_color this determines the extinction
// coefficient of the medium.
// \param transmission_color Desired color resulting from white light transmitted a distance of 'transmission_depth' through the medium.
// Together with transmission_depth this determines the extinction coefficient of the medium.
// \param anisotropy Scattering anisotropy [-1,1]. Negative values give backwards scattering, positive values give forward scattering,
// and 0.0 gives uniform scattering.
// \param ior Refraction index of the medium.
// \param priority Priority of this medium (for nested dielectrics).
// \param label Optional string parameter to name this component. For use in AOVs / LPEs.
//
closure color medium_vdf(color albedo, float transmission_depth, color transmission_color, float anisotropy, float ior, int priority) BUILTIN;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@portsmouth @aconty With the use of optional params for ior and priority, as well as the alternative of using transmission_depth/transmission_color to control the extinction coefficient, I felt it was justified to instead break this out to a separate VDF for homogenous media. Let me know what you think?

Copy link

Choose a reason for hiding this comment

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

Yeah, that makes sense.

Copy link

@portsmouth portsmouth Apr 30, 2021

Choose a reason for hiding this comment

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

Hm, it seems that anisotropic_vdf is basically specifying a (homogeneous) volumetric medium, and medium_vdf is doing that plus specifying IOR and priority (of the "embedding" dielectric medium), except with a different way of parameterizing the extinction coefficient.. Wouldn't it be easier to just have one vdf for the volume properties (maybe using the transmission parameterization, which is nicer than raw extinction), which you pass into the full medium_vdf (I think I would have just called it medium..) to define the general case where the volume is embedded in dielectric?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

To my mind anisotropic_vdf is a closure for general participating media (both homogenous and heterogeneous), whereas media_vdf is a specialization for homogenous dielectric media. So a specialization for a common case, where it more efficiently can handle strictly homogenous media, as well as extra logic for priority etc. with nested dielectrics. It's similar to how we have subsurface_bssrdf as a separate closure specialized for that use case.

I suppose we could change the parameterization of anisotropic_vdf to use the transmission parameterization instead of raw extinction here as well, if that makes sense for this more general case?

However I'm not sure about removing these parameters from media_vdf to replace them with a closure input (for plugging in an anisotropic_vdf, if I understood your proposal correctly). To my mind this would make media_vdf harder to implement and more confusing to use for OSL programmers as it allows plugging any closure into this input.


// -------------------------------------------------------------//
// 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.
//
// \param top Closure defining the top layer.
// \param base Closure defining the base layer.
//
// 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;

// NOTE: For "horisontal layering" closure mix() already exists in OSL.
// -------------------------------------------------------------//
// Utility functions //
// -------------------------------------------------------------//
// Converts the artistic parameterization reflectivity and edge_color to complex IOR values.
// To be used with the conductor_bsdf() closure.
// [OG14] "Artist Friendly Metallic Fresnel", http://jcgt.org/published/0003/04/03/paper.pdf
//
// \param reflectivity Reflectivity per color channel at facing angles ('r' parameter in [OG14]).
// \param edge_tint Color bias for grazing angles ('g' parameter in [OG14]).
// NOTE: This is not equal to 'f90' in a Schlick Fresnel parameterization.
// \param ior Output refraction index.
// \param extinction Output extinction coefficient.
//
void artistic_ior(color reflectivity, color edge_tint, output color ior, output color extinction);
#endif // MATERIALX_CLOSURES

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