3
\$\begingroup\$

I'm generating a heightmap in a compute shader in OpenGL v4.6 and storing it to a texture.

Lets say I actually store the full vertex data in that texture instead of just the height, which is a trivial change, and that I could easily also create an index buffer in a separate texture/SSBO at the same time.

Is there a way to use this pre-existing texture/SSBO data to create a vertex and index buffer directly if I made sure the memory layouts were correct?

It seems wasteful to pull the data back from GPU just to copy it to a new vertex array on CPU and then push back to GPU, when I could just get the CPU code to tell the GPU that this data is the vertex array instead and never have the data leave the GPU... But I have no idea how I'd tell OpenGL to map one to the other.

Development:

I've found info about copying buffer data from the one arbitrary buffer type to another, so I've given that a go. It's not as efficient as simply calling the texture buffer a vertex buffer, but this only needs to happen once, so it's a good enough solution. However, I'm getting a black screen...

This is my VAO setup code:


    const size_t num_vertices = _map_terrain_texture_shape.x * _map_terrain_texture_shape.y;
    const size_t total_vertex_position_bytes = num_vertices * sizeof(glm::vec4);
    const size_t total_vertex_colour_bytes = num_vertices * sizeof(glm::vec4);
    const size_t total_vertex_bytes = total_vertex_position_bytes + total_vertex_colour_bytes;

    std::vector<uint32_t> indices = _make_indices(_map_terrain_texture_shape);
    const size_t total_index_bites = indices.size() * sizeof(uint32_t);
    enter code here
    glGenVertexArrays(1, &_vao);
    glGenBuffers(1, &_vbo);
    glGenBuffers(1, &_ebo);

    glBindVertexArray(_vao);

    glBindBuffer(GL_ARRAY_BUFFER, _vbo);
    glBufferData(GL_ARRAY_BUFFER, total_vertex_bytes, nullptr, GL_STATIC_DRAW);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _ebo);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, total_index_bites, indices.data(), GL_STATIC_DRAW);

    glEnableVertexAttribArray(VERTEX_POSITION_ATTRIB_INDEX);
    glEnableVertexAttribArray(VERTEX_COLOUR_ATTRIB_INDEX);

    // vertex draw positions
    glVertexAttribPointer(VERTEX_POSITION_ATTRIB_INDEX, glm::vec4::length(), GL_FLOAT, GL_FALSE, sizeof(glm::vec4), (void*)0);
    // vertex colours
    glVertexAttribPointer(VERTEX_COLOUR_ATTRIB_INDEX, glm::vec4::length(), GL_FLOAT, GL_FALSE, sizeof(glm::vec4), (void*)total_vertex_position_bytes);

    glDisableVertexAttribArray(VERTEX_POSITION_ATTRIB_INDEX);
    glDisableVertexAttribArray(VERTEX_COLOUR_ATTRIB_INDEX);

    glBindVertexArray(0);

And the code running the compute shader that populates the texture buffers (image2Ds) that I copy into vertex buffer looks like this:

    _map_terrain_mesh_shader->use();

    _main_state.terrain_generator->map_terrain_heightmap_texture->bind_for_active_shader(_map_terrain_mesh_shader->id, 0, "i_heightmap_texture");
    _main_state.terrain_generator->map_terrain_vertex_position_texture->bind_for_active_shader(_map_terrain_mesh_shader->id, 1, "o_vertex_position_texture");
    _main_state.terrain_generator->map_terrain_vertex_colour_texture->bind_for_active_shader(_map_terrain_mesh_shader->id, 2, "o_vertex_colour_texture");

    _map_terrain_mesh_shader->dispatch(glm::uvec3{ _map_terrain_texture_shape, 1});

    const size_t num_vertices = _map_terrain_texture_shape.x * _map_terrain_texture_shape.y;
    const size_t total_vertex_position_bytes = num_vertices * sizeof(glm::vec4);
    const size_t total_vertex_colour_bytes = num_vertices * sizeof(glm::vec4);

    const auto position_texture_id = _main_state.terrain_generator->map_terrain_vertex_position_texture->id;
    const auto colour_texture_id = _main_state.terrain_generator->map_terrain_vertex_colour_texture->id;

    glBindBuffer(GL_COPY_WRITE_BUFFER, _vbo);

    glBindBuffer(GL_COPY_READ_BUFFER, position_texture_id);
    glCopyBufferSubData(position_texture_id, _vbo,
                        0, 0,
                        total_vertex_position_bytes);

    glBindBuffer(GL_COPY_READ_BUFFER, colour_texture_id);
    glCopyBufferSubData(colour_texture_id, _vbo,
                        0, total_vertex_position_bytes,
                        total_vertex_colour_bytes);

    glBindBuffer(GL_COPY_READ_BUFFER, 0);
    glBindBuffer(GL_COPY_WRITE_BUFFER, 0);

I have checked that this compute shader produces the correct results by using these buffers in a raytracing renderer I already had setup. That is now using this data instead of the original heightmap data.

I've gone for vec4 for each just to be sure I don't run into packing issues or whatever while I get it working, and I'm purposely not interlacing the position/colour data. I'm keeping it as a block of each.

Now assuming my compute shader is doing it's job correctly, can anyone tell me if I'm doing this right?

\$\endgroup\$
0

1 Answer 1

0
\$\begingroup\$

Have you tried using a GPU debugger like RenderDoc? That should be able to show you what's going on.

However, the approach that you probably want here is to use Vertex Texture Fetch - this is simply reading from textures inside the vertex shader. Those textures can contain whatever you need for your heightmap rendering. Possibly only a single floating point height channel that you can combine with a 2D grid mesh, but nothing is stopping you putting more data in those textures if you want to.

It's also possible to run the vertex shader without a vertex buffer and calculate everything from gl_VertexID, which isn't too complicated if all you need is a uniform grid of triangles. You don't need an index buffer either, but you will probably find that performance is better if you use an index buffer, because that allows the GPU to cache vertex shader results.

When rendering without any buffers the vertex shader will simply be invoked the number of specified times without input data:

glDrawArrays(GL_TRIANGLES, 0, 3);

\$\endgroup\$
13
  • \$\begingroup\$ With vertex texture fetch I need to still setup the grid on CPU side and store that as a vertex buffer though right? VTF is just the ability to read a texture in the vertex buffer right? It's not the end of the world to do that, but would be really smooth to use the vertex data I've generated on the GPU already as the vertex buffer more directly. I already have a compute shader generating that data, I just need to figure out how to tell openGL I want to use that as the vertex data, be it directly or by copying it into a vertex buffer \$\endgroup\$ Commented May 19 at 15:11
  • \$\begingroup\$ Vertex shader without a buffer doesn't look like it helps me here, I'm already rendering 2D quads easily enough, and rendering my terrain in a raytrace shader in one such quad right now. I'm trying to move away from that and rending this heightmap as a mesh in the classic rasterized way \$\endgroup\$ Commented May 19 at 15:17
  • \$\begingroup\$ You don't need to set up the grid on the CPU side. As I said in the last paragraph of my answer, you don't need a vertex buffer at all if you don't want one - you can read the vertex data from a texture / buffer based on gl_VertexID. You can use that as a replacement for the automatic vertex data fetching that's done by the standard rendering pipeline. \$\endgroup\$
    – Adam
    Commented May 19 at 18:49
  • \$\begingroup\$ Do you know how to do this or have any decent reference material? I've read through the link you gave a few times , I don't see any clear info on how this is supposed to be done. It just says \$\endgroup\$ Commented May 19 at 20:08
  • \$\begingroup\$ It appears from both your resource and others I'm reading that this is just a trick for rendering a fullscreen quad \$\endgroup\$ Commented May 19 at 20:27

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .