这个OSL可以把凹凸或者置换贴图转成法线贴图,节点连接方式如下:


Source:默认是Green通道,因为该通道受到压缩的影响最小。如果你想匹配置换贴图的效果,可以切换到Red通道。

Quality:

Radius:采样区域的半径

Radius_x_Source Power_x_Source

#include <octane-oslintrin.h>

// IMPORTANT: Make sure the input
// is using OSL delayed UV projection.
//
// This shader will convert a height map to a tangent normal map
// based on the average value, luminance or intensity of one of the RGB channels.
//
// If you need many of these nodes with Quality set to High,
// please consider using the baking texture node.
//
// Version: 1.0
//
// Milan - milanm @ Otoy forums


// 0 - Average, 1 - Luminance, 2 - Red, 3 - Green, 4 - Blue.
float toFloat( int method, color input )
{
	if      (method<=0||method>=5)
		{return  (input[0]+input[1]+input[2])/3;}
	else if (method==1)
		{return  luminance(input);}
	return  input[method-2];
}

float liftz(float z, float lift) { return 1-lift*(1-z); }

shader mm_HeightToNormal(
		color Input = 1,
		point Projection = point(u,v,0),
		int Source = 3
			[[ string widget = "mapper",
			   string options = "Average:0|Luminance:1|Red:2|Green:3|Blue:4" ]],
		int Quality = 1		
			[[ string widget = "mapper",
			   string options = "Low (fast):0|Medium:1|High (slow):2" ]],
		float Radius = 1 [[ float sensitivity = 0.01, float min=0, float slidermax=2 ]],
		float Power = 1 [[ float sensitivity = 0.1, float min=0, float slidermax=5 ]],
		color PowerMap = 1,
		float Z_Power = 0.5 [[float min=0,float slidermax=5]],
		float Scale = 1 [[ float sensitivity = 0.1, float min=0, float slidermax=1 ]],
		float Radius_x_Source = 0.5 [[float min=0,float max=1]],
		float Power_x_Source = 0.5 [[float min=0,float max=1]],
    float Falloff = 0 [[ float min=0, float max=1 ]],
    output color c = 0)
{
	color norm = 1;
	float x, y, z, bScale, sc;
	float uu = Projection[0];
	float vv = Projection[1];
	float Power2 = Power*PowerMap[1];

	if (Radius_x_Source||Power_x_Source)
	{
		bScale = toFloat(Source,_evaluateDelayed(Input,uu,vv));
		sc = mix(Power2,Power2*bScale,Power_x_Source)*Scale;
	}
	else { bScale = 1; sc = Power2*Scale; }

	float o = (( mix(Radius,Radius*bScale,Radius_x_Source) )/1000)*Scale;
	float VD = mix(1,dot(-I,N),Falloff);
	
	if (Quality==0)
	
	{
			sc*=2;
			float ho = o*0.5;
			// 3 samples
			float m  = toFloat(Source,_evaluateDelayed(Input,uu-ho,vv-ho));
			float up = toFloat(Source,_evaluateDelayed(Input,uu-ho,vv+ho));
			float rg = toFloat(Source,_evaluateDelayed(Input,uu+ho,vv-ho));			
			x = (m-rg);
			y = (m-up);
			z = liftz( sqrt(1-((x*x)+(y*y))), Z_Power );
			
			norm = mix(color(0.5,0.5,1),color(x,y,z)/2+0.5,sc*VD);
			norm[2] = clamp(norm[2],0.5,1.0);
			c = norm;
			c = normalize(c*2-1)*0.5+0.5;
			return;
	}
	
	else if (Quality==1)
	{
			// 4 samples
			float dn = toFloat(Source,_evaluateDelayed(Input,uu,vv-o));
			float lf = toFloat(Source,_evaluateDelayed(Input,uu-o,vv));
			float up = toFloat(Source,_evaluateDelayed(Input,uu,vv+o));
			float rg = toFloat(Source,_evaluateDelayed(Input,uu+o,vv));
			x = (lf-rg);
			y = (dn-up);
			z = liftz( sqrt(1-((x*x)+(y*y))), Z_Power );
			
			norm = mix(color(0.5,0.5,1),color(x,y,z)*0.5+0.5,sc*VD);
			norm[2] = clamp(norm[2],0.500001,1.0);
			c = normalize(norm*2-1)*0.5+0.5;
			return;
	}
	
	else if (Quality>=2) // Some plugins don't have a mapper ui
	{
			sc*=0.25;
			// 8 samples
			float up = toFloat(Source,_evaluateDelayed(Input,uu,vv+o));
			float dn = toFloat(Source,_evaluateDelayed(Input,uu,vv-o));
			float lf = toFloat(Source,_evaluateDelayed(Input,uu-o,vv));
			float rg = toFloat(Source,_evaluateDelayed(Input,uu+o,vv));
			float ur = toFloat(Source,_evaluateDelayed(Input,uu+o,vv+o));
			float dr = toFloat(Source,_evaluateDelayed(Input,uu+o,vv-o));
			float ul = toFloat(Source,_evaluateDelayed(Input,uu-o,vv+o));
			float dl = toFloat(Source,_evaluateDelayed(Input,uu-o,vv-o));			

			x = (sc*VD) * -(dr-dl+2*(rg-lf)+ur-ul);
			y = (sc*VD) * -(ul-dl+2*(up-dn)+ur-dr);
			z = 1;
			
			norm = normalize(vector(x,y,z));
			norm[2] = liftz( sqrt(1-((norm[0]*norm[0])+(norm[1]*norm[1]))), Z_Power*0.25 );
			c = norm*0.5+0.5;
			c[2] = clamp(c[2],0.5,1);
			return;
	}
}