2

Using OpenGL tutorials found here, I constructed an .obj file loader as well as a texture file loader using SOIL. I have an issue with drawing a textured object. (Shown here) Assuming I created the loaders correctly, my issue is within my rendering method (actually drawing the triangles on the screen). As you can see my texture seems to bend and warp in a very strange way. I've asked a couple of different sources and they can't seem to explain why it happens. My (admittedly messy) code is like this:

Shaders:

#version 120
//VShader
attribute vec3 verPos;
attribute vec2 UV;

varying vec2 f_UV;

uniform mat4 MVP;

void main()
{     
    vec4 v = vec4(verPos, 1.0);
    v = MVP * v;
    gl_Position = v;
    f_UV = UV;
}

#version 120
//FShader
uniform sampler2D TextSamp2D;
varying vec2 f_UV;

void main()
{
    gl_FragColor = texture2D(TextSamp2D, f_UV);
}

And this is all of the renderering code compressed into one main file (no I don't frame limit, but my computer is super junky, hence the GLSL 120) :

#include <fstream>
#include <string>
#include <vector>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <cmath>
#define GLM_FORCE_RADIANS
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtx/transform.hpp>
#include <glm/vec3.hpp>
#include <glm/vec2.hpp>
#include <glm/gtx/euler_angles.hpp>
#include <glm/gtc/quaternion.hpp>
#include <glm/gtx/quaternion.hpp>

#include <SDL2/SDL.h>
#include <GL/glew.h>
#include <GL/glu.h>
#include <SDL2/SDL_opengl.h>
#include "SDL2/SDL_image.h"
#include "SOIL/SOIL.h"

bool initGL();
void close();
void Move();
GLuint loadImage(const char*);
GLuint LoadShaders(const char*, const char*);

//The window we'll be rendering to
SDL_Window* window = NULL;

SDL_GLContext glContext;

//Screen dimension constants
bool gameRunning = true;                       
bool arrows[] = {0, 0, 0, 0};

GLuint programID;

glm::vec3 direction;
glm::vec3 myPos;
glm::vec3 myAngle;
glm::vec3 forwardVec;

glm::mat4 projectionMatrix = glm::perspective(glm::radians(45.0f), 4.0f / 3.0f, 0.1f, 1000.0f);
glm::mat4 viewMatrix = glm::mat4(1.0f);

int main(int argc, char* args[]){
    if(SDL_Init(SDL_INIT_VIDEO) < 0){
        std::cout << "Init Error: " << SDL_GetError() << std::endl;
        gameRunning = false;
    }

    window = SDL_CreateWindow("Eximius", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 640, 480, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN);

    if(window == NULL){
        printf("Window could not be created! SDL_Error: %s\n", SDL_GetError());
        gameRunning = false;
    }
    if(!initGL()){
        std::cout << "OpenGL Init Failed" << std::endl;
        gameRunning = false;
    }

    SDL_Event e;

    programID = LoadShaders("vshade.txt", "fshade.txt");

    myPos.y = 0.0f;

    static const GLfloat g_vertex_buffer_data[] = { 
        -1.0f,-1.0f,-1.0f,
        -1.0f,-1.0f, 1.0f,
        -1.0f, 1.0f, 1.0f,
         1.0f, 1.0f,-1.0f,
        -1.0f,-1.0f,-1.0f,
        -1.0f, 1.0f,-1.0f,
         1.0f,-1.0f, 1.0f,
        -1.0f,-1.0f,-1.0f,
         1.0f,-1.0f,-1.0f,
         1.0f, 1.0f,-1.0f,
         1.0f,-1.0f,-1.0f,
        -1.0f,-1.0f,-1.0f,
        -1.0f,-1.0f,-1.0f,
        -1.0f, 1.0f, 1.0f,
        -1.0f, 1.0f,-1.0f,
         1.0f,-1.0f, 1.0f,
        -1.0f,-1.0f, 1.0f,
        -1.0f,-1.0f,-1.0f,
        -1.0f, 1.0f, 1.0f,
        -1.0f,-1.0f, 1.0f,
         1.0f,-1.0f, 1.0f,
         1.0f, 1.0f, 1.0f,
         1.0f,-1.0f,-1.0f,
         1.0f, 1.0f,-1.0f,
         1.0f,-1.0f,-1.0f,
         1.0f, 1.0f, 1.0f,
         1.0f,-1.0f, 1.0f,
         1.0f, 1.0f, 1.0f,
         1.0f, 1.0f,-1.0f,
        -1.0f, 1.0f,-1.0f,
         1.0f, 1.0f, 1.0f,
        -1.0f, 1.0f,-1.0f,
        -1.0f, 1.0f, 1.0f,
         1.0f, 1.0f, 1.0f,
        -1.0f, 1.0f, 1.0f,
         1.0f,-1.0f, 1.0f
    };

    static const GLfloat g_uv_buffer_data[] = { 
        0.000059f, 1.0f-0.000004f, 
        0.000103f, 1.0f-0.336048f, 
        0.335973f, 1.0f-0.335903f, 
        1.000023f, 1.0f-0.000013f, 
        0.667979f, 1.0f-0.335851f, 
        0.999958f, 1.0f-0.336064f, 
        0.667979f, 1.0f-0.335851f, 
        0.336024f, 1.0f-0.671877f, 
        0.667969f, 1.0f-0.671889f, 
        1.000023f, 1.0f-0.000013f, 
        0.668104f, 1.0f-0.000013f, 
        0.667979f, 1.0f-0.335851f, 
        0.000059f, 1.0f-0.000004f, 
        0.335973f, 1.0f-0.335903f, 
        0.336098f, 1.0f-0.000071f, 
        0.667979f, 1.0f-0.335851f, 
        0.335973f, 1.0f-0.335903f, 
        0.336024f, 1.0f-0.671877f, 
        1.000004f, 1.0f-0.671847f, 
        0.999958f, 1.0f-0.336064f, 
        0.667979f, 1.0f-0.335851f, 
        0.668104f, 1.0f-0.000013f, 
        0.335973f, 1.0f-0.335903f, 
        0.667979f, 1.0f-0.335851f, 
        0.335973f, 1.0f-0.335903f, 
        0.668104f, 1.0f-0.000013f, 
        0.336098f, 1.0f-0.000071f, 
        0.000103f, 1.0f-0.336048f, 
        0.000004f, 1.0f-0.671870f, 
        0.336024f, 1.0f-0.671877f, 
        0.000103f, 1.0f-0.336048f, 
        0.336024f, 1.0f-0.671877f, 
        0.335973f, 1.0f-0.335903f, 
        0.667969f, 1.0f-0.671889f, 
        1.000004f, 1.0f-0.671847f, 
        0.667979f, 1.0f-0.335851f
    };
    GLuint vBuffer;
    glGenBuffers(1, &vBuffer);
    glBindBuffer(GL_ARRAY_BUFFER, vBuffer);
    glBufferData(GL_ARRAY_BUFFER, sizeof(g_vertex_buffer_data), g_vertex_buffer_data, GL_STATIC_DRAW);

    GLuint uvBuffer;
    glGenBuffers(1, &uvBuffer);
    glBindBuffer(GL_ARRAY_BUFFER, uvBuffer);
    glBufferData(GL_ARRAY_BUFFER, sizeof(g_uv_buffer_data), g_uv_buffer_data, GL_STATIC_DRAW);

    GLuint tex;
    tex = loadImage("uvtemplate.tga");

    GLuint verPosAttrib = glGetAttribLocation(programID, "verPos");
    GLuint UVAttrib = glGetAttribLocation(programID, "UV");
    GLuint sampID = glGetAttribLocation(programID, "TextSamp2D");
    GLuint mvpID = glGetUniformLocation(programID, "MVP");
    float i = 0.0f;

    myPos.z = 20.0f;
    while(gameRunning){
        Move();

        forwardVec = glm::vec3(-sin(glm::radians(myAngle.y)), 0.0f, -cos(glm::radians(myAngle.y)));
        glUseProgram(programID);

        viewMatrix = glm::lookAt(myPos, myPos + forwardVec, glm::vec3(0,1,0));

        glDepthMask(GL_TRUE);

        glUniform1i(sampID, 0);

        glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);     

        //Just the scale of the model matrix
        glm::mat4 modelScale = glm::scale(glm::vec3(5.0f, 5.0f, 5.0f));
        i += 0.01f;
        glm::quat myQuat;
        myQuat = glm::quat(myAngle);
        glm::mat4 modelRot = glm::eulerAngleYXZ(i, 0.0f, 0.0f);

        glm::mat4 model = modelScale * modelRot;
        glm::mat4 MVP = projectionMatrix * viewMatrix * model;
        glUniformMatrix4fv(mvpID, 1, GL_FALSE, &MVP[0][0]);

        glEnableVertexAttribArray(verPosAttrib);
        glBindBuffer(GL_ARRAY_BUFFER, vBuffer);
        glVertexAttribPointer(
            verPosAttrib, 
            3, 
            GL_FLOAT,
            GL_FALSE,
            0, 
            (GLvoid*)0
        );

        glEnableVertexAttribArray(UVAttrib);
        glBindBuffer(GL_ARRAY_BUFFER, uvBuffer);
        glVertexAttribPointer(
            UVAttrib,
            2, 
            GL_FLOAT,
            GL_FALSE,
            0, 
            (GLvoid*)0
        );

        glActiveTexture(GL_TEXTURE0 + 0);
        glBindTexture(GL_TEXTURE_2D, tex);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

        glDrawArrays(GL_TRIANGLES, 0, 36);

        SDL_GL_SwapWindow(window);

        glDisableVertexAttribArray(verPosAttrib);
        glDisableVertexAttribArray(UVAttrib);

        while(SDL_PollEvent(&e) != 0){
            if(e.type == SDL_QUIT){
                gameRunning = false;
            }
            else if(e.type == SDL_KEYDOWN){
                switch(e.key.keysym.sym){
                    case SDLK_DOWN:
                        arrows[3] = true;
                    break;
                    case SDLK_LEFT:
                        arrows[2] = true;
                    break;
                    case SDLK_RIGHT:
                        arrows[0] = true;
                    break;
                    case SDLK_UP:
                        arrows[1] = true;
                    break;
                }
            }
            else if(e.type == SDL_KEYUP){
                switch(e.key.keysym.sym){
                    case SDLK_DOWN:
                        arrows[3] = false;
                    break;
                    case SDLK_LEFT:
                        arrows[2] = false;
                    break;
                    case SDLK_RIGHT:
                        arrows[0] = false;
                    break;
                    case SDLK_UP:
                        arrows[1] = false;
                    break;
                }
            }
        }
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    }
    close();
    return 0;
}
bool initGL(){
    glContext = SDL_GL_CreateContext(window);

    if(glContext == NULL){
        std::cout << "OpenGL context Failed: " << SDL_GetError() << std::endl;
        return false;
    }

    glewExperimental = GL_TRUE;
    GLenum err = glewInit();
    if(GLEW_OK != err){
        std::cout << "GLEW FAILED" << glewGetErrorString(err) << std::endl;
        return false;
    }
    if(glewIsSupported("GL_VERSION_4_0")){
        printf("Ready for OpenGL 4.0\n");
    }

    GLuint vaID;
    glGenVertexArrays(1, &vaID);
    glBindVertexArray(vaID);

    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);

    SDL_GL_SetSwapInterval(1);
    SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
    SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
    SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);

    SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
    SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);

    glEnable(GL_DEPTH_TEST);
    glShadeModel(GL_SMOOTH);
    glDepthFunc(GL_LEQUAL);
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);

    return true;
}
void close(){
    //Destroy window
    SDL_DestroyWindow(window);
    SDL_GL_DeleteContext(glContext);
    //IMG_Quit();
    SDL_Quit();
}
GLuint loadImage(const char * image){
    GLuint texID;
    glActiveTexture(GL_TEXTURE_2D);

    texID = SOIL_load_OGL_texture(image, SOIL_LOAD_AUTO, SOIL_CREATE_NEW_ID, SOIL_FLAG_INVERT_Y | SOIL_FLAG_NTSC_SAFE_RGB);
    glBindTexture(GL_TEXTURE_2D, texID);

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

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

    if(texID == 0){
        std::cout << "FAILED TO LOAD TEXTURE" << std::endl;
    }
    else return texID;
}

GLuint LoadShaders(const char * vertexFPath, const char * fragmentFPath){
    GLuint vSID = glCreateShader(GL_VERTEX_SHADER);
    GLuint fSID = glCreateShader(GL_FRAGMENT_SHADER);

    std::string VertexShaderCode;
    std::ifstream VertexShaderStream(vertexFPath, std::ios::in);
    if(VertexShaderStream.is_open()){
        std::string Line = "";
        while(getline(VertexShaderStream, Line))
            VertexShaderCode += "\n" + Line;
        VertexShaderStream.close();
    }
    std::string FragmentShaderCode;
    std::ifstream FragmentShaderStream(fragmentFPath, std::ios::in);
    if(FragmentShaderStream.is_open()){
        std::string Line = "";
        while(getline(FragmentShaderStream, Line))
            FragmentShaderCode += "\n" + Line;
        FragmentShaderStream.close();
    }
    GLint Result = GL_FALSE;
    int InfoLogLength;

    std::cout << "Compiling Shader: " << vertexFPath << std::endl;
    char const * VSource = VertexShaderCode.c_str();
    glShaderSource(vSID, 1, &VSource, NULL);
    glCompileShader(vSID);

    glGetShaderiv(vSID, GL_COMPILE_STATUS, &Result);
    glGetShaderiv(vSID, GL_INFO_LOG_LENGTH, &InfoLogLength);
    std::vector<char> VertexShaderErrorMessage(InfoLogLength);
    glGetShaderInfoLog(vSID, InfoLogLength, NULL, &VertexShaderErrorMessage[0]);
    fprintf(stdout, "%s\n", &VertexShaderErrorMessage[0]);


    std::cout << "Compiling Shader: " << fragmentFPath << std::endl;
    char const * FSource = FragmentShaderCode.c_str();
    glShaderSource(fSID, 1, &FSource, NULL);
    glCompileShader(fSID);

    glGetShaderiv(fSID, GL_COMPILE_STATUS, &Result);
    glGetShaderiv(fSID, GL_INFO_LOG_LENGTH, &InfoLogLength);
    std::vector<char> FragmentShaderErrorMessage(InfoLogLength);
    glGetShaderInfoLog(fSID, InfoLogLength, NULL, &FragmentShaderErrorMessage[0]);
    fprintf(stdout, "%s\n", &FragmentShaderErrorMessage[0]);

    std::cout << "Linking Program" << std::endl;
    GLuint ProgramID = glCreateProgram();
    glAttachShader(ProgramID, vSID);
    glAttachShader(ProgramID, fSID);
    glLinkProgram(ProgramID);

    glGetProgramiv(ProgramID, GL_LINK_STATUS, &Result);
    glGetProgramiv(ProgramID, GL_INFO_LOG_LENGTH, &InfoLogLength);
    std::vector<char> ProgramErrorMessage(std::max(InfoLogLength, int(1)));
    glGetProgramInfoLog(ProgramID, InfoLogLength, NULL, &ProgramErrorMessage[0]);
    fprintf(stdout, "%s\n", &ProgramErrorMessage[0]);

    glDeleteShader(vSID);
    glDeleteShader(fSID);
    return ProgramID;
}

void Move(){
    if(arrows[0]){
        myAngle -= glm::radians(45.f);
    }
    if(arrows[1]){
        myPos = myPos + forwardVec * 0.2f;
    }
    if(arrows[2]){
       myAngle.y += glm::radians(45.f);
    }
    if(arrows[3]){
        myPos = myPos - forwardVec * 0.2f;
    }
}

My question is, why does the texture bend and warp when I rotate the view matrix on the y axis? That is, at what horrifically easy juncture did I go so wrong?

UPDATE:

I tried removing model scale and it didn't stop the texture warping. I've also rechecked the code, and I cannot seem to find where it would cause the warping.

UPDATE 2:

I've taken all of the code that has to do with rendering and compressed it into a few files so that I can show every step I've taken in case something went wrong somewhere unexpected. The issue appears the same way as it did before (tested on my Linux box).

devmane144
  • 41
  • 3
  • Do you happen to have a screenshot that demonstrates the problem? – Panda Pajama Jun 22 '15 at 14:12
  • Better, I have a video that does. I posted it here, https://www.youtube.com/watch?v=OfOHd88mXNI&feature=youtu.be – devmane144 Jun 22 '15 at 14:40
  • I don't know what your geometry looks like, but I think your problem is in your matrix math. I'm not familiar with the library you're using, but you may want to simplify on your matrix operations, and perhaps try to multiply them in inverse order to see if you can find more information. Also, you're setting three vertex attribute arrays, yet you're only using two. Also you're not using some of your variants, so you may want to clean up your code to a bare minimum and work your way up from there – Panda Pajama Jun 22 '15 at 15:59
  • I flicked the display into polygon mode using glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); and saw that all of the geometry was straightforward and expected, but the UVs seem to have the issue here. Do I need to apply the transformations to the UVs or similar? – devmane144 Jun 22 '15 at 23:51
  • Also I cleaned some of my junk-code out. – devmane144 Jun 23 '15 at 00:01
  • This looks like a perspective-divide problem. It has the type of creasing/sliding artifacts along triangle edges that we see when using affine interpolation with a perspective camera (or, conversely, perspective interpolation with an orthographic camera). I'm not fluent in OpenGL, but what you're doing looks reasonable. I'd double-check that the perspective matrix is being generated as you expect, and that passing f_UV as varying instead of in a struct doesn't interfere with interpolation. – DMGregory Jun 23 '15 at 03:46
  • Why are you not asking OpenGL for the handles for the indexes of the vertices and uvs? You just assume that the vertices get index 0 and uvs 1? Don't think it's the cause of your problem but it is not nice. Do you do glBindAttribLocation? Otherwise, you should use glGetAttribLocation to get the index of the attributes – SkeletorDragonBlaster420BlazeI Jun 23 '15 at 14:27
  • I can definitely see what you mean with the affine interpolation problem. Though I'm not sure how I would be able to correct that save buying a new laptop and smashing this one like I should. Can I correct the UVs within the shader to bend with the perspective? If so how where would I even begin? – devmane144 Jun 23 '15 at 16:14
  • Where could I have possibly turned off perspective correction? I keep on reading that unless I'm running on really really old hardware this shouldn't even happen. I have a glHint for setting the perspective correction to GL_NICEST, but that literally did nothing at all. – devmane144 Jun 24 '15 at 01:53
  • I've compressed all of my code down into a few files so that I can make sure I didn't miss anything. – devmane144 Jun 25 '15 at 18:26

0 Answers0