在threejs中实现一个32位高度图顶点着色器
Implementing a 32-bit heightmap vertex shader in threejs
我正在尝试重新利用找到的高度图着色器示例 here into one that will work with 32-bits of precision instead of 8. The work-in-progress code is on 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 值转换回原始高度值(使用我的问题 中回答的技术):
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
如果将宽整数编码到 RGBA 向量的分量中,则必须关闭过滤,这样值之间才不会发生插值。此外,OpenGL 可能会在内部转换为不同的格式,但这只会减少您的采样深度。
你的肤色:
return new Color((enc >> 24 & 255)/255f, (enc >> 16 & 255)/255f, (enc >> 8 & 255)/255f,
(enc & 255)/255f);
... 包含 r
中 enc
的最高有效字节,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.0
到 255.0 / 255.0
。您想要缩放下一个通道,使其最小值是该比例下的下一个值。所以它的最小值应该是256 / 255.0
。所以你需要把1.0 / 255.0
变成256.0 / 255.0
。您可以通过乘以 256
而不是乘以 255
.
来实现
我正在尝试重新利用找到的高度图着色器示例 here into one that will work with 32-bits of precision instead of 8. The work-in-progress code is on 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 值转换回原始高度值(使用我的问题
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
如果将宽整数编码到 RGBA 向量的分量中,则必须关闭过滤,这样值之间才不会发生插值。此外,OpenGL 可能会在内部转换为不同的格式,但这只会减少您的采样深度。
你的肤色:
return new Color((enc >> 24 & 255)/255f, (enc >> 16 & 255)/255f, (enc >> 8 & 255)/255f,
(enc & 255)/255f);
... 包含 r
中 enc
的最高有效字节,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.0
到 255.0 / 255.0
。您想要缩放下一个通道,使其最小值是该比例下的下一个值。所以它的最小值应该是256 / 255.0
。所以你需要把1.0 / 255.0
变成256.0 / 255.0
。您可以通过乘以 256
而不是乘以 255
.