opengl - 来自高度图的法线图

标签 opengl textures glsl shader hlsl

我正在尝试从 HLSL 中的高度图创建法线图。我关注了这个 https://stackoverflow.com/a/5284527/451136这是针对 GLSL 的。以下是我将 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 的建议,我还修复了一些合成器错误。这是工作的 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; 
}

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

最佳答案

一种可能性是您的法线指向错误的方向。这可能是因为 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 样本中,您需要将 off 的第一个分量除以纹理宽度,将第二个分量除以纹理高度。

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

关于opengl - 来自高度图的法线图,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26045527/

相关文章:

c - 从 GIMP 导出的 C 源文件加载图像

memory - 合并访问模式对 CUDA 中的纹理和表面内存很重要吗?

opengl - 法线贴图并保留切线

android - OpenGL 与 OpenGL ES(GLchar 等)

c++ - Ubuntu 13.10 C++ OpenGL GLUT - 链接问题 - 未定义对 `glClearColor' 的引用

opengl - 在 OpenGL 中渲染到纹理数组的一层

opengl - 将两个纹理发送到 GLSL 着色器

opengl - GLSL 编辑器程序

c - 使用 OpenGL 时显示功能不工作

opengl - 顶点数组对象和顶点缓冲区对象的使用