高度图的法线贴图

时间:2014-09-25 18:25:00

标签: opengl textures glsl shader hlsl

我正在尝试从HLSL中的高度图创建法线贴图。我跟着这个用于GLSL的https://stackoverflow.com/a/5284527/451136。以下是我将GLSL翻译为HLSL的方法:

GLSL:

uniform sampler2D unit_wave
noperspective in vec2 tex_coord;
const vec2 size = vec2(2.0,0.0);
const ivec3 off = ivec3(-1,0,1);

    vec4 wave = texture(unit_wave, tex_coord);
    float s11 = wave.x;
    float s01 = textureOffset(unit_wave, tex_coord, off.xy).x;
    float s21 = textureOffset(unit_wave, tex_coord, off.zy).x;
    float s10 = textureOffset(unit_wave, tex_coord, off.yx).x;
    float s12 = textureOffset(unit_wave, tex_coord, off.yz).x;
    vec3 va = normalize(vec3(size.xy,s21-s01));
    vec3 vb = normalize(vec3(size.yx,s12-s10));
    vec4 bump = vec4( cross(va,vb), s11 );

HLSL:

sampler2D image : register(s0);

noperspective float2 TEXCOORD;
static const float2 size = (2.0,0.0);
static const int3 off = (-1,0,1);

float4 main(float2 uv : TEXCOORD) : COLOR 
{ 

    float4 color = tex2D(image, uv);
    float s11 = color.x;
    float s01 = tex2D(image, uv + off.xy).x;
    float s21 = tex2D(image, uv + off.zy).x;
    float s10 = tex2D(image, uv + off.yx).x;
    float s12 = tex2D(image, uv + off.yz).x;
    float3 va = normalize((size.xy,s21-s01));
    float3 vb = normalize((size.yx,s12-s10));
    float4 bump = (cross(va,vb), s11);

    return bump; 
}

输出是黑白图像,较暗的像素更透明(因为alpha是高度)。
如何从高度图生成像http://www.filterforge.com/filters/3051-normal.jpg这样的法线贴图?

修改
我遵循了megadan的建议,我也遇到了一些我修复过的synthax错误。这是有效的HLSL代码:

float Width : register(C0);

float Height : register(C1);

sampler2D image : register(s0);

noperspective float2 TEXCOORD;
static const float2 size = {2.0,0.0};
static const float3 off = {-1.0,0.0,1.0};
static const float2 nTex = {Width, Height};
float4 main(float2 uv : TEXCOORD) : COLOR 
{ 

    float4 color = tex2D(image, uv.xy);

    float2 offxy = {off.x/nTex.x , off.y/nTex.y};
    float2 offzy = {off.z/nTex.x , off.y/nTex.y};
    float2 offyx = {off.y/nTex.x , off.x/nTex.y};
    float2 offyz = {off.y/nTex.x , off.z/nTex.y};

    float s11 = color.x;
    float s01 = tex2D(image, uv.xy + offxy).x;
    float s21 = tex2D(image, uv.xy + offzy).x;
    float s10 = tex2D(image, uv.xy + offyx).x;
    float s12 = tex2D(image, uv.xy + offyz).x;
    float3 va = {size.x, size.y, s21-s01};
    float3 vb = {size.y, size.x, s12-s10};
    va = normalize(va);
    vb = normalize(vb);
    float4 bump = {(cross(va,vb)) / 2 + 0.5, 1.0};

    return bump; 
}

我读到使用GetDimensions自动获取纹理的宽度和高度在Direct3D 10.1或更高版本上受支持,因此纹理的宽度和高度必须传递到着色器,因为我需要它与Direct3D 9兼容同样。

1 个答案:

答案 0 :(得分:2)

一种可能性是你的法线指向错误的方向。这可能是因为OpenGL使用纹理坐标,图像左下角的原点是Y轴指向的,而DirectX使用纹理坐标,原点位于Y轴指向下方的左上角。

如果您确定代码为DirectX采取高度样本的位置,您将看到s01在左侧,s21在右侧,s10在顶部,s12在底部。在OpenGL中,s10和s12将被颠倒。

当您通过从s12减去s10来创建向量vb时,它将指向DirectX的一种方式,而对于OpenGL则指向相反的方式。现在,当您在两个向量之间取一个叉积时,它会形成一个垂直向量,可以使用右手规则根据交叉积的顺序指向两个方向中的任意一个:http://en.wikipedia.org/wiki/Cross_product。这将导致DirectX和OpenGL的交叉产品指向相反的方向。

因此,如果这是一个问题,修复可能是交叉产品中的向量顺序或交换s10和s12样本的位置。

纹理坐标也存在问题。 uv应该在[0,1]的范围内。当你添加它时,它会使它超出范围,结果取决于任何纹理寻址模式是否有效。在每个tex2D样本中,您需要将纹理宽度的第一个分量除以纹理高度除以第二个分量。

最后,交叉积的结果具有[-1,1]范围内的值。如果要将其存储为颜色,则需要将它们转换为[0,1]。所以像cross(vb,va)* 0.5 + 0.5这样的东西会起作用。在着色器中对法线贴图进行采样时,颜色会转换回[-1,1]的正常范围。