В рендерманке есть один шейдер, который из
делает:
вершинный
struct VS_OUTPUT {
float4 Pos: POSITION;
float2 texCoord: TEXCOORD;
};
VS_OUTPUT main(float4 Pos: POSITION){
VS_OUTPUT Out;
// Clean up inaccuracies
Pos.xy = sign(Pos.xy);
Out.Pos = float4(Pos.xy, 0, 1);
// Image-space
Out.texCoord.x = 0.5 * (1 + Pos.x);
Out.texCoord.y = 0.5 * (1 - Pos.y);
return Out;
}
пиксельный
float TextureSize: register(c1);
float4 lightness: register(c0);
sampler BaseMap: register(s0);
// The Sobel filter extracts the first order derivates of the image,
// that is, the slope. The slope in X and Y directon allows us to
// given a heightmap evaluate the normal for each pixel. This is
// the same this as ATI's NormalMapGenerator application does,
// except this is in hardware.
//
// These are the filter kernels:
//
// SobelX SobelY
// 1 0 -1 1 2 1
// 2 0 -2 0 0 0
// 1 0 -1 -1 -2 -1
float4 main(float2 texCoord: TEXCOORD) : COLOR {
float off = 1.0 / TextureSize;
// Take all neighbor samples
float4 s00 = tex2D(BaseMap, texCoord + float2(-off, -off));
float4 s01 = tex2D(BaseMap, texCoord + float2( 0, -off));
float4 s02 = tex2D(BaseMap, texCoord + float2( off, -off));
float4 s10 = tex2D(BaseMap, texCoord + float2(-off, 0));
float4 s12 = tex2D(BaseMap, texCoord + float2( off, 0));
float4 s20 = tex2D(BaseMap, texCoord + float2(-off, off));
float4 s21 = tex2D(BaseMap, texCoord + float2( 0, off));
float4 s22 = tex2D(BaseMap, texCoord + float2( off, off));
// Slope in X direction
float4 sobelX = s00 + 2 * s10 + s20 - s02 - 2 * s12 - s22;
// Slope in Y direction
float4 sobelY = s00 + 2 * s01 + s02 - s20 - 2 * s21 - s22;
// Weight the slope in all channels, we use grayscale as height
float sx = dot(sobelX, lightness);
float sy = dot(sobelY, lightness);
// Compose the normal
float3 normal = normalize(float3(sx, sy, 1));
// Pack [-1, 1] into [0, 1]
return float4(normal * 0.5 + 0.5, 1);
}
NormalMapFilter называется