Search - User list
Full Version: Rebuild of Entagma's Thinfilm Shader for using with 3Delight
Root » Houdini Learning Materials » Rebuild of Entagma's Thinfilm Shader for using with 3Delight
FaitelTech

I've rebuilt mantra shader from Entagma Mantra Thin Film Shader [entagma.com] tutorial to use it with 3Delight [www.3delight.com] render engine.
Attached archive contains Thinfilm custom osl utility node (thinfilm.oso) and hip file from Entagma with additional shader.
How to use:
1. Download and extract files from the archive (Thinfilm_Shader_3Delight.zip)
2. Copy compiled shader file - thinfilm.oso to your _3DELIGHT_USER_OSO_PATH system variable folder. See the video [vimeo.com] to learn how to setup the system environment variable if don't have one.
3. Open Houdini to check that custom node is added to mat context - tab - 3Delight - Addons - Thinfilm
4. Open Soapfilm_bubble_FLIP_Render_3delight example file
5. Complete instructions at the file.


Render time comparison:
Mantra: 1 hour 17 min.
3Delight: 1 min 29 sec.


Additional links:
3Delight's osl support [documentation.3delightcloud.com]
3Delight's osl metadata [documentation.3delightcloud.com]
3Delight's standalone tools [documentation.3delightcloud.com]
OSL Specification [github.com]
FaitelTech
OSL shader code for reference
//Thinfilm shader cobbled together from:
//https://docs.chaosgroup.com/display/OSLShaders/Thin+Film+Shader
//and:
//https://www.shadertoy.com/view/ls2Bz1
//copied together by your's truly, Entagma - Dec. 2020
//https://entagma.com/holiday-giveaway-mantra-thinfilm-shader/
//sequentially copied and modified to use with 3delight by Faitel - Dec. 2022

#include "stdosl.h"

surface thinfilm
(
  color col = color(0)
  [[  string help = "Constant color",
      string label = "Color",
      string widget = "number"]],
  float fintensity = 0
  [[  string help = "Fresnel reduction multiplier",
      string label = "Intensity",
      string page = "Fresnel reduction",
      string widget = "number",
      float min = 0
  ]],
    float fior = 1.4
  [[  string help = "Index of reflection for fresnel reduction",
      string label = "Ior",
      string page = "Fresnel reduction",
      string widget = "number",
      float min = 0
  ]],
  int sbiter = 8
  [[  string help = "Number of spectral bands. 8 seems fine, 32 is excessive.",
      string label = "Iterations",
      string page = "Spectral Bands",
      string widget = "number",
  ]],
  int spfunc = 0
  [[
      string label = "Function",
      string page = "Spectral Bands",
      string widget = "mapper",
      string options =
        "Gems:0|"
        "Zucconi:1|"
        "Bruton:2|"
  ]],
  float thickness = 1
  [[  string help = "Thin Film Thickness",
      string label = "Thickness",
      string page = "Thin Film",
      string widget = "number",
      float min = 0,
      float max = 1
  ]],
  float thicknessMin = 100
  [[  string help = "Minimal Thin Film Thickness",
      string label = "Thickness Min",
      string page = "Thin Film",
      string widget = "number",
      float min = 0,
      float max = 1000
  ]],
  float thicknessMax = 900
  [[  string help = "Maximal Thin Film Thickness",
      string label = "Thickness Max",
      string page = "Thin Film",
      string widget = "number",
      float min = 0,
      float max = 1000
  ]],
  float nmedium = 1
  [[  string help = "approximate refractive index of air",
      string label = "nmedium",
      string page = "Thin Film",
      string widget = "number",
      float min = 0,
      float max = 10
  ]],
  float nfilm = 1.4
  [[  string help = "approximate refractive index of water",
      string label = "nfilm",
      string page = "Thin Film",
      string widget = "number",
      float min = 0,
      float max = 10
  ]],
  float ninternal = 1
  [[  string help = "approximate refractive index of the lower material", 
      string label = "ninternal",
      string page = "Thin Film",
      string widget = "number",
      float min = 0,
      float max = 10
  ]],

  output color outColor = 0
)
{

float saturate(float x)
{
    return min(1.0, max(0.0,x));
}
vector saturate (vector x)
{
    return min(color(1.0,1.0,1.0), max(color(0.0,0.0,0.0),x));
}
// --- Spectral Zucconi --------------------------------------------
// By Alan Zucconi
// Based on GPU Gems: https://developer.nvidia.com/sites/all/modules/custom/gpugems/books/GPUGems/gpugems_ch08.html
// But with values optimised to match as close as possible the visible spectrum
// Fits this: https://commons.wikimedia.org/wiki/File:Linear_visible_spectrum.svg
// With weighter MSE (RGB weights: 0.3, 0.59, 0.11)
vector bump3y(vector x, vector yoffset)
{
	vector y = color(1.0,1.0,1.0) - x * x;
	y = saturate(y-yoffset);
	return y;
}
vector spectral_zucconi(float w)
{
    // w: [400, 700]
	// x: [0,   1]
	float x = saturate((w - 400.0)/ 300.0);

	vector cs = color(3.54541723, 2.86670055, 2.29421995);
	vector xs = color(0.69548916, 0.49416934, 0.28269708);
	vector ys = color(0.02320775, 0.15936245, 0.53520021);

	return bump3y (	cs * (x - xs), ys);
}

// --- GPU Gems -------------------------------------------------------
// https://developer.nvidia.com/sites/all/modules/custom/gpugems/books/GPUGems/gpugems_ch08.html
vector bump3(vector x) {
        vector y = color(1.0,1.0,1.0) - x * x;
        y = max(y, {0.,0.,0.});
        return y;
}

vector spectral_gems (float w) {
    // w: [400, 700]
    // x: [0,   1]
    float x = clamp((w - 400.0)/ 300.0, 0., 1.);
    float r = 4. * (x - 0.75);
    float g = 4. * (x - 0.5);
    float b = 4. * (x - 0.25);
    vector rgb = {r,g,b};
    return bump3(rgb);
}

// --- Approximate RGB values for Visible Wavelengths ------------------
// by Dan Bruton
// http://www.physics.sfasu.edu/astro/color/spectra.html
// https://stackoverflow.com/questions/3407942/rgb-values-of-visible-spectrum
vector spectral_bruton(float w)
{
	vector c;

	if (w >= 380. && w < 440.)
		c = color
		(
			-(w - 440.) / (440. - 380.),
			0.0,
			1.0
		);
	else if (w >= 440. && w < 490.)
		c = color
		(
			0.0,
			(w - 440.) / (490. - 440.),
			1.0
		);
	else if (w >= 490. && w < 510.)
		c = color
		(	0.0,
			1.0,
			-(w - 510.) / (510. - 490.)
		);
	else if (w >= 510. && w < 580.)
		c = color
		(
			(w - 510.) / (580. - 510.),
			1.0,
			0.0
		);
	else if (w >= 580. && w < 645.)
		c = color
		(
			1.0,
			-(w - 645.) / (645. - 580.),
			0.0
		);
	else if (w >= 645. && w <= 780.)
		c = color
		(	1.0,
			0.0,
			0.0
		);
	else
		c = color
		(	0.0,
			0.0,
			0.0
		);

	return saturate(c);
}

//--- OSL Thinfilm Shader ---
/* Amplitude reflection coefficient (s-polarized) */
float rs(float n1, float n2, float cosI, float cosT) {
    return (n1 * cosI - n2 * cosT) / (n1 * cosI + n2 * cosT);
}

/* Amplitude reflection coefficient (p-polarized) */
float rp(float n1, float n2, float cosI, float cosT) {
    return (n2 * cosI - n1 * cosT) / (n1 * cosT + n2 * cosI);
}

/* Amplitude transmission coefficient (s-polarized) */
float ts(float n1, float n2, float cosI, float cosT) {
    return 2 * n1 * cosI / (n1 * cosI + n2 * cosT);
}

/* Amplitude transmission coefficient (p-polarized) */
float tp(float n1, float n2, float cosI, float cosT) {
    return 2 * n1 * cosI / (n1 * cosT + n2 * cosI);
}

// cosI is the cosine of the incident angle, that is, cos0 = dot(view angle, normal)
// lambda is the wavelength of the incident light (e.g. lambda = 510 for green)
// From http://www.gamedev.net/page/resources/_/technical/graphics-programming-and-theory/thin-film-interference-for-computer-graphics-r2962
float thinFilmReflectance(float cos0, float lambda, float thickness, float n0, float n1, float n2) {

    float PI=M_PI; //Pi osl constant

    // compute the phase change term (constant)
    float d10 = (n1 > n0) ? 0 : PI;
    float d12 = (n1 > n2) ? 0 : PI;
    float delta = d10 + d12;

    // now, compute cos1, the cosine of the reflected angle
    float sin1 = pow(n0 / n1, 2) * (1 - pow(cos0, 2));
    if (sin1 > 1) return 1.0; // total internal reflection
    float cos1 = sqrt(1 - sin1);

    // compute cos2, the cosine of the final transmitted angle, i.e. cos(theta_2)
    // we need this angle for the Fresnel terms at the bottom interface
    float sin2 = pow(n0 / n2, 2) * (1 - pow(cos0, 2));
    if (sin2 > 1) return 1.0; // total internal reflection
    float cos2 = sqrt(1 - sin2);

    // get the reflection transmission amplitude Fresnel coefficients
    float alpha_s = rs(n1, n0, cos1, cos0) * rs(n1, n2, cos1, cos2); // rho_10 * rho_12 (s-polarized)
    float alpha_p = rp(n1, n0, cos1, cos0) * rp(n1, n2, cos1, cos2); // rho_10 * rho_12 (p-polarized)

    float beta_s = ts(n0, n1, cos0, cos1) * ts(n1, n2, cos1, cos2); // tau_01 * tau_12 (s-polarized)
    float beta_p = tp(n0, n1, cos0, cos1) * tp(n1, n2, cos1, cos2); // tau_01 * tau_12 (p-polarized)

    // compute the phase term (phi)
    float phi = (2 * PI / lambda) * (2 * n1 * thickness * cos1) + delta;

    // finally, evaluate the transmitted intensity for the two possible polarizations
    float ts = pow(beta_s, 2) / (pow(alpha_s, 2) - 2 * alpha_s * cos(phi) + 1);
    float tp = pow(beta_p, 2) / (pow(alpha_p, 2) - 2 * alpha_p * cos(phi) + 1);

    // we need to take into account conservation of energy for transmission
    float beamRatio = (n2 * cos2) / (n0 * cos0);

    // calculate the average transmitted intensity (if you know the polarization distribution of your
    // light source, you should specify it here. if you don't, a 50%/50% average is generally used)
    float myt = beamRatio * (ts + tp) / 2;

    // and finally, derive the reflected intensity
    return 1 - myt;
}

float cos0 = abs(dot(I , N));

float myt = clamp(thickness, 0., 1.);
float thick=thicknessMin*(1.0-myt)+thicknessMax*myt;

color outcol = col; //get color;
float fint = fintensity; //get fresnel multiplier
float fnfilm = fior; //get fresnel ior

int maxiter = sbiter; //number of spectral bands. 8 seems fine, 32 is excessive.
for(int i = 0; i < maxiter; i++)
{
    float lambda = 400. + (300. * (float(i)/float(maxiter))); //Spectral bands ranging from 400nm to 700nm
    float ref = thinFilmReflectance(cos0, lambda, thick, nmedium, nfilm, ninternal);

    //we don't need no fresnel, so we need to divide it out again
    //I know - being cheap, but atm just not in the mood for algebra
    float kr, kt;
    fresnel(-I, N, fnfilm, kr, kt);
    kr = kr;
    if (fint == 0 ) ref = ref;
    if (fint > 0 ) ref = ref / (kr * fint);

    vector rgb;
    if (spfunc == 0) rgb = spectral_gems(lambda);
    if (spfunc == 1) rgb = spectral_zucconi(lambda);
    if (spfunc == 2) rgb = spectral_bruton(lambda);

    rgb = rgb * ref;
    outcol = outcol + rgb;
}

outColor = outcol / float(maxiter);
}
lewis_T
Fantastic work mate. Really nice, and the time comparison is not surprising at all. I see anywhere
from 10x to 200x speed differences depending on what I'm working on.

Thank you for posting code and all.


Lewis
Midphase
"Render time comparison:
Mantra: 1 hour 17 min.
3Delight: 1 min 29 sec."


LOL!
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.