0
\$\begingroup\$

I'm atempting to make a game using OpenGL. So far I have loaded an .obj model, and I have generated a terrain as well as a very simple UI.

My issue is that it 'seams' (tihi : P) that there are some visible seams on the the texturing. As seen on the image, each quad in the terrain has black line around it. The model also appears to have such quads on the seams that were marked when the uv's were made in the 3D software, yet this is not as clearly visible.

I'm not sure how or where the issue occurs.

view rom the game

When i Generate the terrain, I add 6 vertecies per quad, without using index buffers, so they are just added to a buffer on the fly. (I'm aware that this is not the best solution, but I chose it just to get started).

Below is the code that makes a 'region' of the terrain:

I'm sorry that it's rather long : /

TerrainRegion::TerrainRegion(glm::vec2 start, glm::vec2 dims, int samples){

// Prepare the mesh data
std::vector<glm::vec3> mesh_vertecies;
std::vector<glm::vec3> mesh_normals;
std::vector<glm::vec2> mesh_uvs;

// check if a quad, and uniformize samples
int x_samples, y_samples;
if (dims.x != dims.y){
    if (dims.x > dims.y){
        x_samples = samples;
        y_samples = floor((dims.y / dims.x) * float(samples));
    }
    else{
        y_samples = samples;
        x_samples = floor((dims.x / dims.y) * float(samples));
    }
}
else{
    x_samples = y_samples = samples;
}

// Make space for the height values
double **height_map = new double*[x_samples+2];
for (int i = 0; i < x_samples + 2; ++i) {
    height_map[i] = new double[y_samples + 2];
}
std::cout << x_samples << ", " << y_samples << std::endl;

// fillin the height map
for (int y = 0; y < y_samples + 2; y++){
    for (int x = 0; x < x_samples + 2; x++){
        double x_space = (float(x - 1) / float(x_samples-1))*dims.x + start.x;
        double y_space = (float(y - 1) / float(y_samples-1))*dims.y + start.y;
        //std::cout << std::setw(6) << std::setprecision(1) << std::fixed << x_space << ", " << y_space << " ";
        height_map[x][y] = OctavePerlin(x_space / 100.0f, y_space / 100.0f, 1.0, 6, 0.28) * 30.0f;
    }
    //std::cout << std::endl;
}
//std::cout << std::endl;

// print the height map
/*
for (int y = 0; y < y_samples + 2; y++){
    for (int x = 0; x < x_samples + 2; x++){
        std::cout << std::setw(6) << std::setprecision(2) << std::fixed << height_map[x][y] << " ";
    }
    std::cout << std::endl;
}
*/


// make mesh data from height map
for (int y = 1; y < y_samples; y++){
    for (int x = 1; x < x_samples; x++){
        double x_space = (float(x) / float(x_samples - 1))*dims.x;
        double y_space = (float(y) / float(y_samples - 1))*dims.y;
        double x_dspace = (float(x+1) / float(x_samples - 1))*dims.x;
        double y_dspace = (float(y+1) / float(y_samples - 1))*dims.y;
        double x_dmspace = (float(x-1) / float(x_samples - 1))*dims.x;
        double y_dmspace = (float(y-1) / float(y_samples - 1))*dims.y;


        glm::vec3 v0 = glm::vec3(x_space, height_map[x][y], y_space);
        glm::vec3 v1 = glm::vec3(x_space, height_map[x][y + 1], y_dspace);
        glm::vec3 v2 = glm::vec3(x_dspace, height_map[x + 1][y + 1], y_dspace);
        glm::vec3 v3 = glm::vec3(x_dspace, height_map[x + 1][y], y_space);
        glm::vec3 v4 = glm::vec3(x_dspace, height_map[x + 1][y-1], y_dmspace);
        glm::vec3 v5 = glm::vec3(x_space, height_map[x][y - 1], y_dmspace);
        glm::vec3 v6 = glm::vec3(x_dmspace, height_map[x-1][y], y_space);
        glm::vec3 v7 = glm::vec3(x_dmspace, height_map[x - 1][y + 1], y_dspace);
        glm::vec3 v8 = glm::vec3(x_space, height_map[x][y + 2], (float(y +2) / float(y_samples - 1))*dims.y);
        glm::vec3 v9 = glm::vec3(x_dspace, height_map[x+1][y + 2], (float(y + 2) / float(y_samples - 1))*dims.y);
        glm::vec3 v10 = glm::vec3((float(x + 2) / float(x_samples - 1))*dims.x, height_map[x + 2][y + 1], y_dspace);
        glm::vec3 v11 = glm::vec3((float(x + 2) / float(x_samples - 1))*dims.x, height_map[x + 2][y], y_space);

        mesh_vertecies.push_back(v0);
        mesh_vertecies.push_back(v1);
        mesh_vertecies.push_back(v2);
        mesh_vertecies.push_back(v0);
        mesh_vertecies.push_back(v2);
        mesh_vertecies.push_back(v3);

        // 0
        mesh_normals.push_back(glm::normalize((
            glm::normalize(glm::cross(v1 - v0, v3 - v0))+
            glm::normalize(glm::cross(v3 - v0, v5 - v0)) +
            glm::normalize(glm::cross(v5 - v0, v6 - v0)) +
            glm::normalize(glm::cross(v6 - v0, v1 - v0))
            )));

        // 1
        mesh_normals.push_back(glm::normalize((
            glm::normalize(glm::cross(v8 - v1, v2 - v1)) +
            glm::normalize(glm::cross(v2 - v1, v0 - v1)) +
            glm::normalize(glm::cross(v0 - v1, v7 - v1)) +
            glm::normalize(glm::cross(v7 - v1, v8 - v1))
            )));

        // 2
        mesh_normals.push_back(glm::normalize((
            glm::normalize(glm::cross(v9 - v2, v10 - v2)) +
            glm::normalize(glm::cross(v10 - v2, v3 - v2)) +
            glm::normalize(glm::cross(v3 - v2, v1 - v2)) +
            glm::normalize(glm::cross(v1 - v2, v9 - v2))
            )));

        // 0
        mesh_normals.push_back(glm::normalize((
            glm::normalize(glm::cross(v1 - v0, v3 - v0)) +
            glm::normalize(glm::cross(v3 - v0, v5 - v0)) +
            glm::normalize(glm::cross(v5 - v0, v6 - v0)) +
            glm::normalize(glm::cross(v6 - v0, v1 - v0))
            )));

        // 2
        mesh_normals.push_back(glm::normalize((
            glm::normalize(glm::cross(v9 - v2, v10 - v2)) +
            glm::normalize(glm::cross(v10 - v2, v3 - v2)) +
            glm::normalize(glm::cross(v3 - v2, v1 - v2)) +
            glm::normalize(glm::cross(v1 - v2, v9 - v2))
            )));

        // 3
        mesh_normals.push_back(glm::normalize((
            glm::normalize(glm::cross(v2 - v3, v11 - v3)) +
            glm::normalize(glm::cross(v11 - v3, v4 - v3)) +
            glm::normalize(glm::cross(v4 - v3, v0 - v3)) +
            glm::normalize(glm::cross(v0 - v3, v2 - v3))
            )));



        double nul = 0.0;
        double et = 1.0;
        mesh_uvs.push_back(glm::vec2(nul, nul));
        mesh_uvs.push_back(glm::vec2(nul,et));
        mesh_uvs.push_back(glm::vec2(et,et));

        mesh_uvs.push_back(glm::vec2(nul,nul));
        mesh_uvs.push_back(glm::vec2(et, et));
        mesh_uvs.push_back(glm::vec2(et, nul));



    }
    //std::cout << std::endl;
}

this->mesh = new gf::Mesh();
this->mesh->set_vertex_data(mesh_vertecies);
this->mesh->set_normal_data(mesh_normals);
this->mesh->set_UV_data(mesh_uvs);
this->mesh->compute_tangents();
this->mesh->generate_buffers();


for (int i = 0; i < x_samples + 2; ++i) {
    delete height_map[i];
}
delete height_map;

}

And here is the vertex shader:

#version 410

struct LightInfo {
  vec4 Position; // Position of light
  vec3 Intensity; // Intensity of the light
  vec3 Ambient;  // Ambient component of the light
  vec3 Diffuse;  // Diffuse component of the light
  vec3 Specular; // Specular component of the light
}; 


struct MaterialInfo {
  vec3 Ambient;  // Ambinet component of material
  vec3 Diffuse;  // Diffuse component of material
  vec3 Specular; // Specular component of material
  float Shininess; // shininess of material
};

layout(location = 0) in vec3 VertexPosition;
layout(location = 1) in vec3 VertexNormal;
layout(location = 2) in vec2 VertexUV;
layout(location = 3) in vec3 VertexTangent;
layout(location = 4) in vec3 VertexBiTangent;

// Transform matricies
uniform mat4 ModelMatrix;
uniform mat4 ViewMatrix;
uniform mat4 ProjectionMatrix;
uniform mat4 MVP;
uniform mat3 NormalMatrix;

// Light uniforms
uniform LightInfo light[12];
uniform int n_lights;

// Meterial uniforms
uniform MaterialInfo material;

// texture
uniform sampler2D diffuse_texture;

out vec4 Position;
out vec3 Normal;
out vec2 UV;
out mat3 TBN;

void main(){

  Position = ViewMatrix * ModelMatrix * vec4(VertexPosition, 1.0);
  Normal =  normalize(NormalMatrix * VertexNormal);
  UV = VertexUV;


  vec3 Tangent =  normalize(NormalMatrix * VertexTangent);
  vec3 BiTangent = normalize(NormalMatrix * VertexBiTangent);
  TBN = transpose(mat3(
        Tangent,
        BiTangent,
        Normal
    ));

  gl_Position = MVP * vec4(VertexPosition, 1.0);
}

And the fragment shader:

#version 410

struct LightInfo {
  vec4 Position; // Position of light
  vec3 Intensity; // Intensity of the light
  vec3 Ambient;  // Ambient component of the light
  vec3 Diffuse;  // Diffuse component of the light
  vec3 Specular; // Specular component of the light
};

struct MaterialInfo {
  vec3 Ambient;  // Ambinet component of material
  vec3 Diffuse;  // Diffuse component of material
  vec3 Specular; // Specular component of material
  float Shininess; // shininess of material
};

uniform LightInfo light[12];
uniform int n_lights;

// Meterial uniforms
uniform MaterialInfo material;

// texture
uniform sampler2D diffuse_texture;
uniform sampler2D normal_map;
uniform sampler2D specular_occlusion_map;

in vec4 Position;
in vec3 Normal;
in vec2 UV;
in mat3 TBN;

layout(location = 0) out vec4 FragColor;

vec4 tex_col(){
    return texture(diffuse_texture, UV);
}

vec3 ads(LightInfo l){
  vec3 texcol = tex_col().xyz;
    vec3 n = normalize( texture( normal_map, UV ).rgb*2.0 - 1.0);
    vec3 s = normalize( TBN * vec3(l.Position - Position) );
    vec3 v = normalize( TBN * vec3(-Position) );
    vec3 r = reflect( -s, n );
    return l.Intensity * ( (material.Ambient * l.Ambient + 
                          material.Diffuse * l.Diffuse * max( dot(s,n), 0.0 )) * tex_col().xyz +
               texture(specular_occlusion_map, UV).xyz * material.Specular * l.Specular * pow(max(dot(r,v), 0.0), material.Shininess));
}

void main() {

  //vec3 color = material.Ambient;
  vec3 color = vec3(0.0,0.0,0.0);

  for(int i = 0; i < n_lights; i++){
      color = color + ads(light[i]);
  }
  //color = Normal;



  FragColor = vec4(color, 1.0);
  //FragColor = vec4(Normal, 1.0);
  //FragColor = vec4(light[0].Diffuse, 1.0);
  //FragColor = vec4(Position.xyz, 1.0);
}

Is there something clearly wrng with this? Or shoudl I present more info on the errors?

Btw

The grass texture does NOT have a black border : )

Edit:

The code that lods textures:

gf::Texture *gf::Texture::load_texture_from_file(std::string filename){

  gf::Texture *texture = new gf::Texture();

  GLuint tex_ID;

  tex_ID = SOIL_load_OGL_texture(
    filename.c_str(),
    SOIL_LOAD_AUTO,
    SOIL_CREATE_NEW_ID,
    SOIL_FLAG_POWER_OF_TWO
    | SOIL_FLAG_MIPMAPS
    | SOIL_FLAG_MULTIPLY_ALPHA
    | SOIL_FLAG_COMPRESS_TO_DXT
    | SOIL_FLAG_DDS_LOAD_DIRECT
    | SOIL_FLAG_INVERT_Y
    );

  if (tex_ID > 0)
  {
    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, tex_ID);

    texture->texture_id = tex_ID;
  }
  else{
    delete texture;
    return (gf::Texture*) 0;
  }

  return texture;

}

I just use SOIL, and an example that I found online. The Texture class does only hold the texture id.

\$\endgroup\$
6
  • \$\begingroup\$ Hmmm... odd thing. I tried the exact same program on a MacBook Pro. This gave me no seams and everything was looking great. I use glfw, does that have some shortcomings on windows? \$\endgroup\$ Jul 5, 2015 at 20:21
  • \$\begingroup\$ Some graphics cards and drivers behave differently than others. I think that the issue might be in texture loading, you should post that too. Also a tip: decouple mesh generation from rendering/buffering. Also: Does this appear without texturing? \$\endgroup\$
    – akaltar
    Jul 5, 2015 at 20:33
  • \$\begingroup\$ I added how I load textures in the main post. No borders appear when I remove all textureing code from the shader. When you say I should seperate the generation process from rendering, I thought I aready did that? I add vertex, normal and uv data as vectors, and then finally call functions in the mesh class to generate OpenGL buffers. Have I misunderstood something? \$\endgroup\$ Jul 5, 2015 at 21:00
  • \$\begingroup\$ What happens when you remove the MIPMAPS, MULTIPLY_ALPHA and COMPRESS_TO_DXT? \$\endgroup\$
    – Eejin
    Jul 5, 2015 at 22:45
  • \$\begingroup\$ Spoiler: In the end, it's going to turn out that the source image's dimensions aren't a power of two, and so are being padded during the load due to SOIL_FLAG_POWER_OF_TWO. \$\endgroup\$ Jul 6, 2015 at 7:02

1 Answer 1

0
\$\begingroup\$

I found a solution.

I cannot really explain why it changes the outcome since all UV's are in the [0;1] range. Yet, addin the lines

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

Just after loading the texture solved the problem just fine.

\$\endgroup\$

You must log in to answer this question.

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