2014-09-25 73 views
2

我想从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兼容。

回答

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]的正常范围。

+0

嗯,即时通讯仍然没有得到任何颜色。事实上即时获得相同的结果。我之前也必须犯错。 – Infodayne 2014-09-25 20:42:51

+1

再看看它,我认为你的纹理坐标有问题。 uv应该在[0,1]的范围内。当你添加它时,它会将它放在范围之外,结果取决于任何纹理寻址模式处于活动状态。在每个tex2D样本中,都需要将第一个组件的纹理宽度除以第二个组件的纹理高度。另外,交叉积的结果的值在[-1,1]范围内。您需要将这些转换为[0,1]以获得颜色。所以交叉(vb,va)* 0.5 + 0.5 – megadan 2014-09-25 22:45:49

+0

太棒了,那就是诀窍。我确实必须将交叉产品保留为交叉(va,vb)。谢谢。 – Infodayne 2014-09-26 02:11:11