在三个js中实现32位高度图顶点着色器

时间:2015-02-22 20:42:03

标签: opengl-es webgl vertex-shader heightmap

我正在尝试将找到here的高度图着色器示例重新用于一个可以使用32位精度而不是8的工作。正在进行中的代码在github上:https://github.com/bgourlie/three_heightmap < / p>

高度图是在.NET中生成的。高度在0f ... 200f范围内,并使用以下方法转换为32位颜色值(Unity Color struct):

private static Color DepthToColor(float height)
{
    var depthBytes = BitConverter.GetBytes(height);
    int enc = BitConverter.ToInt32(depthBytes, 0);
    return new Color((enc >> 24 & 255)/255f, (enc >> 16 & 255)/255f, (enc >> 8 & 255)/255f,
            (enc & 255)/255f);
}

颜色数据编码为png,结果如下:

enter image description here

顶点着色器正在获取此图像数据并将RBGA值转换回原始高度值(使用我的问题here中回答的技术):

uniform sampler2D bumpTexture; // defined in heightmap.js
varying float vAmount;
varying vec2 vUV;

void main()
{
  vUV = uv;
  vec4 bumpData = texture2D( bumpTexture, uv );
  vAmount = dot(bumpData, vec4(1.0, 255.0, 65025.0, 16581375.0));
  // Uncomment to see a "flatter" version
  //vAmount = dot(bumpData, vec4(1.0, 1.0/255.0, 1.0/65025.0, 1.0/160581375.0));

  // move the position along the normal
  vec3 newPosition = position + normal * vAmount;
  gl_Position = projectionMatrix * modelViewMatrix * vec4( newPosition, 1.0 );
}

结果肯定搞砸了:

enter image description here

我可以通过改变这一行来使它更平坦:

vAmount = dot(bumpData, vec4(1.0, 1.0/255.0, 1.0/65025.0, 1.0/16581375.0));

这将给我一个更平坦的图像,至少显示生成的地形的一个很好的轮廓,但有一个几乎完全平坦的平面(有轻微的,尽管不明显的变化):

enter image description here

我认为我做错了一些事情,我只是不知道是什么。我不确定我是否正确编码原始浮点数。我不确定我是否在顶点着色器中正确解码它(我得到的值肯定超出0 ... 200的范围)。我在3D图形方面也不是很有经验。因此,任何关于我做错的指示,或者一般如何实现这一点都将非常感激。

同样,可以在此处找到自包含的正在进行的代码:https://github.com/bgourlie/three_heightmap

2 个答案:

答案 0 :(得分:1)

如果将宽整数编码到RGBA矢量的分量中,则必须关闭滤波,以便在值之间不进行插值。 OpenGL也可以在内部转换为不同的格式,但这只会减少您的样本深度。

答案 1 :(得分:1)

你的颜色:

return new Color((enc >> 24 & 255)/255f, (enc >> 16 & 255)/255f, (enc >> 8 & 255)/255f,
        (enc & 255)/255f);

...包含encr的最重要字节,g中第二位最重要的字节等。

此:

vAmount = dot(bumpData, vec4(1.0, 255.0, 65025.0, 160581375.0));

在最低有效字节中构建vAmount r,在下一个最低有效字节中构建g等等(尽管被乘数应为256,65536等*)。所以字节的顺序不正确。更平坦的版本:

vAmount = dot(bumpData, vec4(1.0, 1.0/255.0, 1.0/65025.0, 1.0/160581375.0));

以正确的顺序获取字节,但将输出值缩放到范围[0.0, 1.0],这可能是它看起来基本上平坦的原因。

因此,切换编码顺序或解码字节并选择适当的比例。

(*)以这种方式思考:可以在任何频道上进行的最小数字是1.0 / 255.0。最不重要的频道将在[0, 1.0]范围内 - 从0 / 255.0255.0 / 255.0。您希望缩放下一个通道,使其最小值成为该范围内的下一个值。所以它的最小值应该是256 / 255.0。因此,您需要将1.0 / 255.0转换为256.0 / 255.0。您可以通过乘以256而不是255来实现这一目标。

相关问题