一个用于读取高度图或深度图的采样器。
extern Texture2D HeightMap;
sampler2D HeightSampler = sampler_state
;
请注意,我的输入贴图是一个512x512的单通道灰度纹理。从中计算法线相当简单:
#define HALF2 ((float2)0.5)
#define GET_HEIGHT(heightSampler,texCoord) (tex2D(heightSampler,texCoord+HALF2))
float3 GetNormal(sampler2D heightSampler, float2 texCoord)
{
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
{
float3 normal = GetNormal(HeightSampler,texCoord);
return float3(normal,1);
}
LIGHT_POSITION
可能(并且可能应该)从您的主机代码输入,但我在这里使用了一个常量。
请注意,每个法线需要 4 次纹理查找,不包括获取颜色的一次。这对您可能不是问题(取决于您正在做什么其他事情)。如果这对性能造成了太大的影响,您可以在纹理更改时调用它,渲染到目标并将结果捕获为法线贴图。
另一种方法是绘制一个屏幕对齐的四边形,用高度图进行纹理映射,并使用 ddx
/ddy
HLSL 内置函数生成法线,而无需重新采样源纹理。显然,您会在预处理步骤中执行此操作,读取生成的法线贴图,然后将其用作后续阶段的输入。
无论如何,这对我来说已经足够快了。