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).