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.
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.
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;
}


