OSL – MixNormalMaps

A shader for mixing or blending together normal maps as outlined on this page:
http://blog.selfshadow.com/publications/blending-in-detail/
(Colin Barré-Brisebois, Stephen Hill)
This gives much better results than simply blending or overlaying two normal maps. It uses Reoriented Normal Mapping as described in the article as well as the idea from the second comment, by “jarkko” in that article, which I have named Displacement Vector Normal Blending here just to give it a name. The modes are fairly similar in results.

mixnormalmaps_rndr

Download links
http://www.rensheeren.com/osl/mixNormalMaps_tex_v003.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
– Blends two normal maps.
– Two algorithms available.
– With mix amount to define the second map blend strength.

How to use
VRay:
– Create a VRay OSL Texture map.
– Load in the downloaded OSL shader.
– Plug in two normal maps.
– You can then set the mode and mix amount.

mixnormalmaps_ui

mixnormalmaps_node

UI Properties
input_base_1
– The first normal map.
input_detail_2
– The second map.
mix_mode
– Choose the way the maps get blended.
mix_amount
– The strength of the second map.

Code

#include "rhfunctions.h"

/*

mixNormalMaps_tex_v003


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

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


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


A shader for mixing together normal maps as outlined on this page
http://blog.selfshadow.com/publications/blending-in-detail/
(Colin Barré-Brisebois, Stephen Hill)

It uses Reoriented Normal Mapping as described in the article as well as
the idea from the second comment, by "jarkko" in that article, which I have
named Displacement Vector Normal Blending just to give it a name...

Usage:
- Plug in two normal maps, and set the mode to either RNM or DVNB. Mix amount
  mixes back to to base input, meaning 1 is the blended result and 0 is the 
  original base as output.


Use at your own risk.



2016-01-10 - v001 - Created (rh)
2016-01-20 - v002 - Added mix texture input. (rh)
2016-05-29 - v003 - Added multi-renderer support. (rh)

*/


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


shader
mixNormalMaps_tex_v003
(
#ifdef __VRAY_OSL__

	string input_base_map_1 = ""
		[[string description = "Normal map 1. The 'base' map." ]],
	color input_base_color_1 = color(0.5, 0.5, 1)
		[[string description = "Normal map 1. The 'base' map." ]],
	int use_texture_1 = 1
		[[ string widget = "checkBox",
		string description = "Use the texture instead of the colour."]],
		
	string input_detail_map_2 = ""
		[[string description = "Normal map 2. The map with detail to add." ]],
	color input_detail_color_2 = color(0.5, 0.5, 1)
		[[string description = "Normal map 2. The map with detail to add." ]],
	int use_texture_2 = 1
		[[ string widget = "checkBox",
		string description = "Use the texture instead of the colour."]],
		
	int mix_mode = 1
		[[ string widget = "mapper",
		string description = "The blending method to use.",
		string options = "Reoriented_Normal_Mapping:1|Displacement_Vector:2" ]],
	
	float mix_amt = 1
		[[string description = "Blend/Mix/Strength amount, 0 is the base input as output and 1 is the result of both maps." ]],
	string mix_amt_tex = ""
		[[string description = "Blend/Mix/Strength amount, 0 is the base input as output and 1 is the result of both maps." ]],
	int use_mix_texture = 0
		[[ string widget = "checkBox",
		string description = "Use the mix texture instead of the amount."]],
		
#else
		
	color input_base_color_1 = color(0.5, 0.5, 1)
		[[string description = "Normal map 1. The 'base' map." ]],
		
	float input_base_alpha_1 = 1
		[[string description = "Normal map 1 alpha." ]],
	
	color input_detail_color_2 = color(0.5, 0.5, 1)
		[[string description = "Normal map 2. The map with detail to add." ]],
		
	float input_base_alpha_2 = 1
		[[string description = "Normal map 2 alpha." ]],
	
	int mix_mode = 1
		[[ string widget = "mapper",
		string description = "The blending method to use.",
		string options = "Reoriented_Normal_Mapping:1|Displacement_Vector:2" ]],
		
	float mix_amt = 1
		[[string description = "Blend/Mix/Strength amount, 0 is the base input as output and 1 is the result of both maps." ]],
		
#endif

	string info_page = "www.rensheeren.com/blog/osl-mixnormalmaps/"
	  [[ string widget = "string" ]],
	
	output color out_mixed = color(0),
	output color out_inputBase = color(0),
	output color out_inputDetail = color(0),
	
	//output float out_alpha_mixed = 0,
	output float out_alpha_inputBase = 0,
	output float out_alpha_inputDetail = 0,
	output float out_alpha_mixAmt = 0
)

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


{
	// Declare Inputs
	
	float f_in1 = 1;
	float f_in2 = 1;
	color c_in1 = color(0);
	color c_in2 = color(0);
	
	float f_mixAmt = 0;
	
	int i_mixMode = mix_mode;
	
#ifdef __VRAY_OSL__

	if (use_texture_1)
	{
		c_in1 = texture(input_base_map_1, u, v, "alpha", f_in1);
	}
	else
	{
		c_in1 = input_base_color_1;
	}
	
	if (use_texture_2)
	{
		c_in2 = texture(input_detail_map_2, u, v, "alpha", f_in2);
	}
	else
	{
		c_in2 = input_detail_color_2;
	}
	
	if (use_mix_texture)
	{
		color c_mixtex = texture(mix_amt_tex, u, v);
		f_mixAmt = clamp(c_mixtex[0], 0, 1);
	}
	else
	{
		f_mixAmt = clamp(mix_amt, 0, 1);
	}

#else

		c_in1 = input_base_color_1;
		c_in2 = input_detail_color_2;
		
		f_in1 = input_base_alpha_1;
		f_in2 = input_base_alpha_2;

		f_mixAmt = clamp(mix_amt, 0, 1);
	
#endif
	
	
	color c_out = color(0);
	
	
	// Shader
	
	if (f_mixAmt > 0)
	{
		c_in2 = mix(color(0.5, 0.5, 1), c_in2, f_mixAmt);
		
		vector v_in1 = vector(c_in1[0], c_in1[1], c_in1[2]);
		vector v_in2 = vector(c_in2[0], c_in2[1], c_in2[2]); 
		vector v_out = vector(0, 0, 0);
		
		if (i_mixMode == 1)
		{
			v_out = fn_nrmMixRnm(v_in1, v_in2);
		}
		if (i_mixMode == 2)
		{
			v_out = fn_nrmMixDisp(v_in1, v_in2);
		}
		c_out = color(v_out[0], v_out[1], v_out[2]);
	}
	else 
	{
		c_out = c_in1;

	}
	
	
	// Outputs
	
	out_mixed = c_out;
	out_inputBase = c_in1;
	out_inputDetail = c_in2;
	
	//out_alpha_mixed = 
	out_alpha_inputBase = f_in1; 
	out_alpha_inputDetail = f_in2;
	out_alpha_mixAmt = f_mixAmt;
}