1
\$\begingroup\$

I am constructing some terrain for a 3D game (in C) as a 2D grid of tiles, the corners of which have height into the 3rd dimension. In the middle of each tile is another vertex, the height of which is the average of the heights of the tile corners around it. This means that, for each tile, there are 4 triangles to be drawn, with every triangle sharing a 1 point in the middle and 2 on the corners of the tile. I figured that a GL_TRIANGLE_FAN would be appropriate for this task.

In the code below, struct vbo_vert is a struct containing a vec3 and an unsigned char, in that order. Each triangle fan consists of 6 vertices (one for the middle, one for each of the corners, and another back on top of the first corner to close the square), and each vertex is sent to the GPU with a number from 0 to 4 in the byte member of the struct so that the vertex shader can determine the texturing. c_vec is an array of vec3 positions of the tile corners, and t_vec is a similar array for the positions of those central vertices.

//Assembling the triangle fans
struct vbo_vert tri_fan[256][1536] = {0};
for (int i = 0; i < 255; i++) {
    for (int j = 0; j < 255; j++) {

        int j_6 = (j * 6);
            tri_fan[i][j_6 + 0]  =
            (struct vbo_vert){t_vec[i][j], 0};
        tri_fan[i][j_6 + 1]  =
            (struct vbo_vert){c_vec[i][j], 1};
        tri_fan[i][j_6 + 2]  =
            (struct vbo_vert){c_vec[i + 1][j], 2};
        tri_fan[i][j_6 + 3]  =
            (struct vbo_vert){c_vec[i + 1][j + 1], 3};
        tri_fan[i][j_6 + 4]  =
            (struct vbo_vert){c_vec[i][j + 1], 4};
        tri_fan[i][j_6 + 5]  =
            (struct vbo_vert){c_vec[i][j], 1};
    }
}

After this I open a texture. I fairly sure that the image-reading and texture-writing code is sound because I use practically the same code to texture models made of GL_TRIANGLES elsewhere and it works great. Then I send tri_fan to the GPU, and I know that the VAO and Array Pointers are good because I can draw the terrain as a wireframe and it looks fine. That just leaves the shader program.

//Vertex shader
#version 330 core
layout (location = 0) in vec3 a_pos;
layout (location = 1) in int a_texref;

out vec3 v_pos;
out vec2 v_tex;

uniform mat4 model;
uniform mat4 view;
uniform mat4 proj;

void main()
{
    gl_Position = proj * view * model * vec4(a_pos, 1.0f);
    v_pos = a_pos;

    if ((a_texref == 0)) {
        v_tex = vec2(0.5, 0.5);
    } else if (a_texref == 1) {
        v_tex = vec2(0.0, 1.0);
    } else if (a_texref == 2) {
        v_tex = vec2(0.0, 0.0);
    } else if (a_texref == 3) {
        v_tex = vec2(1.0f, 0.0f);
    } else if (a_texref == 4) {
        v_tex = vec2(1.0, 1.0);
    }
}
#version 330 core
out vec4 FragColour;

in vec3 v_pos;
in vec2 v_tex;

uniform sampler2D tex_sam1;

void main()
{
    FragColour = texture(tex_sam1, v_tex);
}

I do not see a problem with these shaders. However I do not understand shaders as well as I could. When I render the scene with this test texture:

test texture

It renders like this:

Bad texturing

The blue lines lie on the edge of the tiles, and the white dot is in the center.

EDIT: upon testing further I worked out that the center vertex's uv is being set correctly, but all the other vertices are being set to whatever comes last in the else if ladder in the vertex shader.

I haven't been able to get through this issue. I was having similar texturing problems when I was using triangle strips to make the mesh before I realised that I could be more efficient using fans compared to the way I was doing it. I'm guessing that it is some weird quirk with how the shaders handle fans that I don't understand, or I have miswritten some code somewhere, in which case I will leave the question up as a testament to the importance of re-reading your code :-P. And There is probably a way better way to do the texturing that I am trying to do. Any insight into this issue is appreciated.

\$\endgroup\$
5
  • \$\begingroup\$ In your Code for assembling the trifans, you assign on vertex 0 and 1 the same coordinates. I assume you should calculate vertex 0 out of the other 4 vertexes? \$\endgroup\$ Jul 9 '21 at 9:30
  • 1
    \$\begingroup\$ @Peter Parker For vertex 0, the array that I choose from is for the vertices at the center of the tiles, t_vec, not the array of tile corners, c_vec, like the rest of them. \$\endgroup\$
    – Architect
    Jul 9 '21 at 9:40
  • \$\begingroup\$ I'd say your approach sends a lot of unnecessary vertex data. You could encode your tile IDs into an index map and render the whole thing with only 2 triangles per tile, or even fewer since you don't need triangle splits at every tile boundary, only where the incline changes. \$\endgroup\$
    – DMGregory
    Jul 9 '21 at 13:15
  • 1
    \$\begingroup\$ @DMGregory Let's say, for the sake of solving the problem at hand, that I'd like to stick with the system (Per-tile rendering and texturing) that I have already. Besides, I'm seeing some very strange behavior from the vertex shader that I'd like to understand. \$\endgroup\$
    – Architect
    Jul 9 '21 at 23:28
  • \$\begingroup\$ To get better help, you should try to make a minimal viable example available. If the source is too long to post here, you may want to use pastebin. I think a simple renderloop with 2 (hardcoded) triangle fans would be sufficient. \$\endgroup\$ Jul 14 '21 at 11:07

You must log in to answer this question.

Browse other questions tagged .