【问题标题】:Implementing a 32-bit heightmap vertex shader in threejs在threejs中实现一个32位的高度图顶点着色器
【发布时间】:2015-02-23 23:17:21
【问题描述】:

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

高度图正在 .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,结果如下所示:

顶点着色器正在获取此图像数据并将 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 );
}

结果肯定是一团糟:

我可以通过改变这条线让它更平坦:

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

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

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

同样,可在此处找到自包含的工作中代码:https://github.com/bgourlie/three_heightmap

【问题讨论】:

  • 高度图的png 中的噪点确实看起来有问题。即使在深度使用所有通道时,您至少应该在一种颜色内具有平滑的渐变,并且如果您有噪声,它不应该是那个极端。至此,我建议将搜索范围缩小到创建高度图的部分。我猜你在某个地方访问未初始化的内存。
  • 我认为噪音是使用 Alpha 通道的结果(这样做时会出现一些噪音)。这是不使用 alpha 通道的结果高度图;:i.imgur.com/l3zcpcG.jpg。诚然,考虑到它们应该代表的高度,渐变看起来仍然不正确。
  • 对不起,我想我有一个错误的假设:白点实际上是透明像素? png 最初是否具有平滑的透明度?您是否将其保存为 png-8 以在此处显示它并且 alpha 通道已丢失?这可以解释白点。如果您将导出的图像用于着色器,那么您需要检查 PNG 创建,如果您可以在 8 和 24 之间进行选择。(使用带有 alpha 通道的 png 可能是一个问题,因为压缩可能会在图像如果完全透明)。
  • 它实际上是一个 32 位的 PNG。这是在红色背景上显示 Alpha 通道的 PNG 图片:i.imgur.com/iJS3Dqo.png。 AFAIK,PNG 使用无损压缩,因此顶点着色器应该在无损、解压缩的数据上运行。
  • 255^316581375.0 而不是 16 0 581375.0

标签: opengl-es webgl vertex-shader heightmap


【解决方案1】:

你的颜色:

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

...包含renc 的最高有效字节,g 中的第二高字节,等等。

这个:

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

构建vAmountr 在最低有效字节中,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 来实现。

【讨论】:

  • 非常感谢您的解释。这让我意识到我如何以及为什么以一种非常幼稚、错误的方式对高度图进行编码。至少现在一旦我做对了,我就会知道如何在着色器中正确获取 ARGB 值。再次感谢!
【解决方案2】:

如果将宽整数编码到 RGBA 向量的分量中,则必须关闭过滤,以免值之间发生插值。此外,OpenGL 可能会在内部转换为不同的格式,但这只会减少您的样本深度。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-05
    • 2020-06-19
    • 2013-11-07
    • 2016-09-27
    • 1970-01-01
    • 2020-03-10
    相关资源
    最近更新 更多