OSL – ArchGlass

An OSL shader for creating realistic glass window panes. Window reflections are amazingly complex; you get for one pane of glass reflections from the front, back, and scattering from inbetween, light gets absorbed and passed through to the other side only to be reflected back by the next pane of glass. This shader allows you to simulate full window glass geometry with only one plane. Useful for everything but close-ups.

archglass_rndr

Download links
http://www.rensheeren.com/osl/archGlass_mat_v002.osl
http://www.rensheeren.com/osl/rhfunctions.h
Right-click and save both in the same folder, both are needed for the shader to work. Overwrite rhfunctions.h if asked.

Features
– Specify the amount of window panes (1-3).
– Absorbtion/tint colour.
– Reflective coating layer, for example a one-way mirror or high reflectance building glass.
– Dirt layer, like a layer of dust or sand on the outside.
– IOR of glass and surrounding medium (air usually).
– Normal map inputs for creating warped reflections for each pane separately.
– Supports ray traced refraction, transparency, and black opaque for the refraction/transmission.

How to use
VRay:
– Create a VRay OSL Material.
– Load in the downloaded OSL shader.
– Set how many panes are in your window. Most modern windows in temperate and cooler climates have two panes for insulation.
– Optionally set a colour for the reflective coating if your window needs one.
– Same for the dirt. The colour is usually fine, you define how much dirt there is on top of the windows with the dirt amount input.
– Set the refraction mode, 1 – ray traced refraction, 2 – transparency, 3 – no refraction, just black, for mixing with a self-illuminated interior or to just have only the glass reflection.
– Normal map inputs need a normal map with RGB 0.5 0.5 1.0 as the ‘base’, meaning that there won’t be any normal difference with that colour.
– Apply to a one-sided plane object.

archglass_node

archglass_ui

UI Properties

number_of_glass_panes
– How many individual window panes make up the window.

pane_absorbtion_tint
– The absorbtion or tint of the window. To get an effect like sunglasses make this a darker grey colour.

front_reflective_coating
– Simulates a reflective coating that sometimes gets applied to windows.

front_dirt_color
– The colour of the dirt layer that can be added on top of the glass. Can be left at default for most cases.

dirt_amount
– The amount of dirt. Plug in a noise map here.

ior_out
– The Index Of Refraction for the outside medium, usually air, which is IOR ~1.0.

ior_glass
– The IOR of the glass itself. Usually around 1.5 – 1.7.

refraction_mode
How to handle the transmission or in other words how light passes though the glass.
1 – Ray traced refraction.
2 – Transparency.
3 – Opaque. Just black, nothing passes.

normalmap_pane1
– The normal map for the first pane. Very useful for adding subtle wavy reflections as in real windows. This is available for the other panes as well to add to realism.
The base colour it expects is RGB 0.5 0.5 1.0. You can mix that with a noise with two values, for example, RGB 0 1 1 and RGB 1 0 1. See the node setup above.

use_first_normalmap_for_all
– Use the normal map of pane1 for all of the panes.

Code

#include "rhfunctions.h"

/*

archGlass_mat_v002


By Rens Heeren (mail [at] rensheeren [dot] com)

Info Page: http://www.rensheeren.com/blog/osl-archglass/


Use as an OSL material, not as an OSL texture.


An OSL shader for creating realistic glass window panes. Window reflections are amazingly complex;
you get for one pane of glass reflections from the front, back, and scattering from inbetween,
light gets absorbed and passed through to the other side only to be reflected back by the next
pane of glass. This shader allows you to simulate full window glass geometry with only one plane.
Useful for everything but close-ups.


Features:
- Specify the amount of window panes (1-3).
- Absorbtion/tint colour.
- Reflective coating layer, for example a one-way mirror or high reflectance building glass.
- Dirt layer, like a layer of dust or sand on the outside.
- IOR of glass and surrounding medium (air usually).
- Normal map inputs for creating warped reflections for each pane separately.
- Supports ray traced refraction, transparency, and black opaque for the refraction/transmission.

Usage:
- Set how many panes are in your window. Most modern windows in temperate and cooler climates have
	two panes for insulation.
- Optionally set a colour for the reflective coating if your window needs one.
- Same for the dirt. The colour is usually fine, you define how much dirt there is on top of the
	windows with the dirt amount input.
- Set the refraction mode, 1 - ray traced refraction, 2 - transparency, 3 - no refraction, just 
	black, for mixing with a self-illuminated interior or to just have only the glass reflection.
- Normal map inputs need a normal map with RGB 0.5 0.5 1.0 as the 'base', meaning that there won't
	be any normal difference with that colour.



Use at your own risk.



2016-02-21 - v001 - Created (rh)
2016-06-15 - v002 - UI Tweaks, logic tweaks, added dirt, normal map inputs. (rh)

*/



/////////////////////////////////////////////////////////


surface
archGlass_mat_v002
(
	int number_of_glass_panes = 1,
	
	color pane_absorbtion_tint = color(0.957, 0.975, 0.965),
	
	color front_reflective_coating = color(0),
	
	color front_dirt_color = color(0.035, 0.027, 0.024),
	float dirt_amount = 0.0,
	
	float ior_out = 1,
	float ior_glass = 1.55,
	
	int refraction_mode = 2
	[[ string widget = "mapper",
		string description = "1 - Refraction, 2 - Transparency, 3 - Opaque",
		string options = "Refraction:1|Transparency:2|Opaque:3" ]],
	
	string normalmap_pane1 = "",
	int use_normalmap1 = 0
		[[ string widget = "checkBox" ]],
	string normalmap_pane2 = "",
	int use_normalmap2 = 0
		[[ string widget = "checkBox" ]],
	string normalmap_pane3 = "",
	int use_normalmap3 = 0
		[[ string widget = "checkBox" ]],
	
	int use_first_normalmap_for_all = 1
		[[ string widget = "checkBox" ]],
	
	string pane_absorbtion_tint_tex = "",
	int use_absorbtion_tex = 0
		[[ string widget = "checkBox" ]],
	string front_reflective_coating_tex = "",
	int use_coating_tex = 0
		[[ string widget = "checkBox" ]],
	string front_dirt_color_tex = "",
	int use_dirt_col_tex = 0
		[[ string widget = "checkBox" ]],
	string dirt_amount_tex = "",
	int use_dirt_amount_tex = 0
		[[ string widget = "checkBox" ]],
		
	string info_page = "www.rensheeren.com/blog/osl-archglass/"
		[[ string widget = "string" ]]
)


///////////////////////////////////////////////////


{	
	int i_refrMode = refraction_mode;
	int i_numPanes = number_of_glass_panes;
	int i_useN1 = use_normalmap1;
	int i_useN2 = use_normalmap2;
	int i_useN3 = use_normalmap3;
	int i_useN1ForAll = use_first_normalmap_for_all;
	int i_useAbsTex = use_absorbtion_tex;
	int i_useCoatTex = use_coating_tex;
	int i_useDirtTex = use_dirt_col_tex;
	int i_useDirtAmtTex = use_dirt_amount_tex;

	

	color c_absorbtion = color(0);
	if (i_useAbsTex)
	{
		c_absorbtion = texture(pane_absorbtion_tint_tex, u, v);
	}
	else
	{
		c_absorbtion = pane_absorbtion_tint;
	}
	
	color c_coating = color(0);
	if (i_useCoatTex) 
	{
		c_coating = texture(front_reflective_coating_tex, u , v);
	}
	else
	{
		c_coating = front_reflective_coating;
	}
	
	color c_dirtCol = color(0);
	if (i_useDirtTex) 
	{
		c_dirtCol = texture(front_dirt_color_tex, u , v);
	}
	else
	{
		c_dirtCol = front_dirt_color;
	}
	
	float f_dirtAmt = 0;
	if (i_useDirtAmtTex) 
	{
		color c_dirtAmt = texture(dirt_amount_tex, u , v);
		f_dirtAmt = c_dirtAmt[0];
	}
	else
	{
		f_dirtAmt = dirt_amount;
	}
	
	color c_n1 = texture(normalmap_pane1, u, v);
	color c_n2 = texture(normalmap_pane2, u, v);
	color c_n3 = texture(normalmap_pane3, u, v);
	
	
	
	normal n_n1 = N;
	normal n_n2 = N;
	normal n_n3 = N;
	
	if (i_useN1)
	{
		n_n1 = normal(c_n1[0], c_n1[1], c_n1[2]);
		n_n1 = (n_n1 * 2) - 1;
		n_n1 = transform ("shader", "world", n_n1);
	}
	
	if (i_useN1ForAll)
	{
		n_n2 = n_n1;
		n_n3 = n_n1;
	}
	else
	{
		if (i_useN2)
		{
			n_n2 = normal(c_n2[0], c_n2[1], c_n2[2]);
			n_n2 = (n_n2 * 2) - 1;
			n_n2 = transform ("shader", "world", n_n2);
		}
		
		if (i_useN3)
		{
			n_n3 = normal(c_n3[0], c_n3[1], c_n3[2]);
			n_n3 = (n_n3 * 2) - 1;
			n_n3 = transform ("shader", "world", n_n3);
		}
		
	}
	

	f_dirtAmt = clamp(f_dirtAmt, 0, 1);
	
	
	color c_refl1 = color(0);
	color c_transmitted1 = color(0);
	
	color c_refl2 = color(0);
	color c_transmitted2 = color(0);
	
	color c_refl3 = color(0);
	color c_transmitted3 = color(0);
	
	color c_transmittedOut = color(0);
	
	
	float f_iorOut = ior_out;
	float f_iorIn = ior_glass;
	
	int i_noTransp = 0;
	if (refraction_mode == 3)
	{
		i_noTransp = 1;
	}
	
	
	////////////////
	
	
	float f_angle = abs(dot(-I,N));
	
	float f_reflFrontSide = fn_FresnelSchlickDiDi(f_angle, f_iorOut, f_iorIn);

	color c_reflAllPanes = fn_windowReflectance(f_angle, f_reflFrontSide);
	
	// mix curve for coating blend
	float f_reflZeroAngle = fn_FresnelSchlickDiDi(0.0, f_iorOut, f_iorIn);
	float f_mixCurve = ((f_reflFrontSide - 1) * (1 + f_reflZeroAngle)) + 1;
	
	// half of absorbtion for back face reflection absorbtion
	color c_absHalf = 1 - ((1 - c_absorbtion) / 2.0);

	// coating blended down to 0 towards 90 degrees (grazing) to add onto Fresnel curve.
	color c_coatingFresnel = mix((c_coating / 2.0), 0, f_mixCurve);
	
	c_refl1 = clamp((c_reflAllPanes[0] + c_coatingFresnel), 0, 1);
	c_transmitted1 = (1 - c_refl1) * c_absorbtion;
	c_refl1 = clamp(((c_reflAllPanes[0] * c_absHalf) + c_coatingFresnel), 0, 1);
	
	c_transmittedOut = c_transmitted1;
	
	closure color cl_refl = emission();
	
	if (i_numPanes == 1)
	{
		//closure color cl_refl = reflection(normal(0,0,0)) * c_refl1;
		cl_refl = microfacet_ggx(n_n1, 0, 150) * c_refl1;
	}
	
	if (i_numPanes == 2)
	{	
		if (i_useN1ForAll)
		{
			c_refl2 = clamp((((c_reflAllPanes[1] - c_reflAllPanes[0]) * c_transmitted1) + c_refl1), 0 ,1);
			c_transmitted2 = (1 - c_refl2) * c_absorbtion;
			c_refl2 = clamp(((((c_reflAllPanes[1] - c_reflAllPanes[0]) * c_transmitted1) * c_absHalf) + c_refl1), 0 ,1);
			
			cl_refl = microfacet_ggx(n_n1, 0, 150) * c_refl2;
		}
		else
		{
			c_refl2 = clamp((((c_reflAllPanes[1] - c_reflAllPanes[0]) * c_transmitted1) + c_refl1), 0 ,1);
			c_transmitted2 = (1 - c_refl2) * c_absorbtion;
			c_refl2 = clamp(((((c_reflAllPanes[1] - c_reflAllPanes[0]) * c_transmitted1) * c_absHalf)), 0 ,1);
			
			cl_refl = microfacet_ggx(n_n2, 0, 150) * c_refl2 + microfacet_ggx(n_n1, 0, 150) * c_refl1;
			
		}
		
		c_transmittedOut = c_transmitted2;
	}
	
	if (i_numPanes == 3)
	{
		if (i_useN1ForAll)
		{
			c_refl2 = clamp((((c_reflAllPanes[1] - c_reflAllPanes[0]) * c_transmitted1) + c_refl1), 0 ,1);
			c_transmitted2 = (1 - c_refl2) * c_absorbtion;
			c_refl2 = clamp(((((c_reflAllPanes[1] - c_reflAllPanes[0]) * c_transmitted1) * c_absHalf) + c_refl1), 0 ,1);
			
			c_refl3 = clamp((((c_reflAllPanes[2] - c_reflAllPanes[0]) * c_transmitted2) + c_refl2), 0 ,1);
			c_transmitted3 = (1 - c_refl3) * c_absorbtion;
			c_refl3 = clamp(((((c_reflAllPanes[2] - c_reflAllPanes[0]) * c_transmitted2) * c_absHalf) + c_refl2), 0 ,1);
			
			cl_refl = microfacet_ggx(n_n1, 0, 150) * c_refl2;
		}
		else
		{
			c_refl2 = clamp((((c_reflAllPanes[1] - c_reflAllPanes[0]) * c_transmitted1) + c_refl1), 0 ,1);
			c_transmitted2 = (1 - c_refl2) * c_absorbtion;
			c_refl2 = clamp(((((c_reflAllPanes[1] - c_reflAllPanes[0]) * c_transmitted1) * c_absHalf) + c_refl1), 0 ,1);
			
			c_refl3 = clamp((((c_reflAllPanes[2] - c_reflAllPanes[0]) * c_transmitted2) + c_refl2), 0 ,1);
			c_transmitted3 = (1 - c_refl3) * c_absorbtion;
			
			c_refl2 = clamp(((((c_reflAllPanes[1] - c_reflAllPanes[0]) * c_transmitted1) * c_absHalf)), 0 ,1);
			c_refl3 = clamp(((((c_reflAllPanes[2] - c_reflAllPanes[0]) * c_transmitted2) * c_absHalf)), 0 ,1);
			
			cl_refl = microfacet_ggx(n_n3, 0, 150) * c_refl3 + microfacet_ggx(n_n2, 0, 150) * c_refl2 + microfacet_ggx(n_n1, 0, 150) * c_refl1;
		}
		
		c_transmittedOut = c_transmitted3;
	}
	
	
	closure color cl_transp1 = emission() * color(0);
	
	if (i_refrMode == 1)
	{
		cl_transp1 = refraction(N, 1.001, "auto", 1) * c_transmittedOut;
	}
	if (i_refrMode == 2)
	{
		cl_transp1 = transparent() * c_transmittedOut;
	}
	
	Ci = cl_refl + cl_transp1;
	
	if (f_dirtAmt > 0)
	{
		Ci = (f_dirtAmt * (diffuse(n_n1, "roughness", 0.3) * c_dirtCol)) + ((1 - f_dirtAmt) * Ci);
	}
	
}