用于读取您的高度或深度图的采样器。
/// same data as HeightMap, but in a format that the pixel shader can read
/// the pixel shader dynamically generates the surface normals from this.
extern Texture2D HeightMap;
sampler2D HeightSampler = sampler_state
{
Texture=(HeightMap);
AddressU=CLAMP;
AddressV=CLAMP;
Filter=LINEAR;
};
请注意,我的输入贴图是 512x512 单分量灰度纹理。从中计算法线非常简单:
#define HALF2 ((float2)0.5)
#define GET_HEIGHT(heightSampler,texCoord) (tex2D(heightSampler,texCoord+HALF2))
///calculate a normal for the given location from the height map
/// basically, this calculates the X- and Z- surface derivatives and returns their
/// cross product. Note that this assumes the heightmap is a 512 pixel square for no particular
/// reason other than that my test map is 512x512.
float3 GetNormal(sampler2D heightSampler, float2 texCoord)
{
/// normalized size of one texel. this would be 1/1024.0 if using 1024x1024 bitmap.
float texelSize=1/512.0;
float n = GET_HEIGHT(heightSampler,texCoord+float2(0,-texelSize));
float s = GET_HEIGHT(heightSampler,texCoord+float2(0,texelSize));
float e = GET_HEIGHT(heightSampler,texCoord+float2(-texelSize,0));
float w = GET_HEIGHT(heightSampler,texCoord+float2(texelSize,0));
float3 ew = normalize(float3(2*texelSize,e-w,0));
float3 ns = normalize(float3(0,s-n,2*texelSize));
float3 result = cross(ew,ns);
return result;
}
还有一个像素着色器来调用它:
#define LIGHT_POSITION (float3(0,2,0))
float4 SolidPS(float3 worldPosition : NORMAL0, float2 texCoord : TEXCOORD0) : COLOR0
{
/// calculate a normal from the height map
float3 normal = GetNormal(HeightSampler,texCoord);
/// return it as a color. (Since the normal components can range from -1 to +1, this
/// will probably return a lot of "black" pixels if rendered as-is to screen.
return float3(normal,1);
}
LIGHT_POSITION 可以(并且可能应该)从您的主机代码中输入,尽管我在这里作弊并使用了一个常量。
请注意,此方法每个法线需要 4 次纹理查找,而不是计算一次来获取颜色。这对你来说可能不是问题(取决于你在做什么)。如果这对性能造成太大影响,您可以在纹理发生变化时调用它,渲染到目标,然后将结果捕获为法线贴图。
另一种方法是将带有高度图的屏幕对齐四边形纹理绘制到渲染目标,并使用ddx/ddy HLSL 内在函数来生成法线,而无需重新采样源纹理。显然,您会在预传递步骤中执行此操作,然后读取生成的法线贴图,然后将其用作后续阶段的输入。
无论如何,这对我来说已经足够快了。