代码之家  ›  专栏  ›  技术社区  ›  Mario

OpenGL地形系统,GPU和CPU之间的高度差很小

  •  0
  • Mario  · 技术社区  · 4 年前

    快速总结:

    我有一个简单的基于四叉树的地形渲染系统,它可以构建地形补丁,然后在顶点着色器中对高度贴图进行采样,以确定每个顶点的高度。

    对于对象放置和co,在CPU上进行完全相同的计算。

    超级简单,但现在添加了一些系统来按程序放置对象后,我发现它们似乎只是放错了一小部分地方。为了调试这一点,我在地形上渲染了几个十字架作为单个模型。这个 十字架 (红、绿、蓝线)表示从CPU读取的高度。地形网格使用着色器来平移顶点。

    (我还在每个高度值上添加了一个简单的奇数/偶数间隙,以排除简单的偏移问题。 所以那些丑陋的悬崖是意料之中的事,淹没的十字架是问题所在 )

    我明确使用 GL_NEAREST 能够显示“原始”高度值:

    正如你所看到的,十字架有时被淹没在地形下,而不是代表它的确切高度。

    高度图只是CPU和GPU上的一个简单的浮点数组。

    数据的存储方式

    一个简单的 vector<float> 它被上传到 GL_RGB32F GL_FLOAT 缓冲器浮动没有标准化,我的地形通常包含-100到500之间的值。

    如何在着色器中访问数据

    我尝试了一些方法来排除错误,最初:

    vec2 terrain_heightmap_uv(vec2 position, Heightmap heightmap)
    {
        return (position + heightmap.world_offset) / heightmap.size;
    }
    
    float terrain_read_height(vec2 position, Heightmap heightmap)
    {
        return textureLod(heightmap.heightmap, terrain_heightmap_uv(position, heightmap), 0).r;
    }
    

    顶点着色器的基础知识(完整的着色器代码很长,所以我提取了实际读取高度的部分):

    void main()
    {
        vec4 world_position = a_model * vec4(a_position, 1.0);
        vec4 final_position = world_position;
    
        // snap vertex to grid
        final_position.x = floor(world_position.x / a_quad_grid) * a_quad_grid;
        final_position.z = floor(world_position.z / a_quad_grid) * a_quad_grid;
    
        final_position.y = terrain_read_height(final_position.xz, heightmap);
    
        gl_Position = projection * view * final_position;
    }
    

    为了确保位置的确定方式略有不同,我使用与C++读取高度方式相同的硬编码值对其进行了测试:

    return texelFetch(heightmap.heightmap, ivec2((position / 8) + vec2(1024, 1024)), 0).r;
    

    这给出了完全相同的结果。。。

    如何在应用程序中访问数据

    在C++中,高度如下所示:

    inline float get_local_height_safe(uint32_t x, uint32_t y)
    {
        // this macro simply clips x and y to the heightmap bounds
        // it does not interfer with the result
        BB_TERRAIN_HEIGHTMAP_BOUND_XY_TO_SAFE;
    
        uint32_t i = (y * _size1d) + x;
    
        return buffer->data[i];
    }
    
    inline float get_height_raw(glm::vec2 position)
    {
        position = position + world_offset;
    
        uint32_t x = static_cast<int>(position.x);
        uint32_t y = static_cast<int>(position.y);
    
        return get_local_height_safe(x, y);
    }
    
    float BB::Terrain::get_height(const glm::vec3 position)
    {
        return heightmap->get_height_raw({position.x / heightmap_unit_scale, position.z / heightmap_unit_scale});
    }
    

    我尝试过什么:

    • 比较缓冲区
      我已经从向量中丢弃了最初的几百个值。并将其与使用Nvidia Nsight上传到GPU的浮点缓冲区进行比较,它们是相等的,四舍五入/精度误差。
    • 取样方法
      我试过了 texture , textureLod texelFetch 为了排除一些问题,他们都给了我相同的结果。
    • 舍入
      当我四舍五入所有的高度值时,这是一件非常奇怪的事情。它们完全对齐,这只是浮点精度问题。
    • 位置捕捉
      我已经尝试过将位置舍入、铺地板和天花板,以确保该位置始终映射到相同的纹理元素。我还尝试添加ε偏移量来排除位置精度误差(可能很愚蠢,因为地形稳定…)
    • 高度图尺寸
      我试过各种不同尺寸的高度图。
    • 高度图图案
      我创建了一个包含图案的高度图,以确保位置不只是偏移。
    0 回复  |  直到 4 年前
    推荐文章