Hello there,
I’ve got some questions regarding my code:
How to implement/manipulate texture coordinates / uv mapping here:
Obviously something is wrong with the shader or the mesh class, because the non quadratic texture is always stretched.
How to make it appear in a correct way? (non stretched and repeated texture on my square mesh).
Many thanks in advance.
Full minimal code example, thats shows the problem:
#include <GL/glew.h>
#include <SFML/Graphics.hpp>
#include <iostream>
#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <string>
#include <fstream>
#include <sstream>
#include <vector>
#include <cstddef>
class Camera
{
public:
static const glm::vec3 WorldUp;
Camera(glm::vec3 position, float pitch = 0.0f, float yaw = -90.0f, float fov = 60.0f);
void UpdateDirectionVectors();
glm::mat4 GetProjectionMatrix(float width, float height);
glm::mat4 GetViewMatrix();
glm::vec3 position;
float fov, yaw, pitch;
private:
glm::vec3 forward, right, up;
};
const glm::vec3 Camera::WorldUp{ 0.0f, 1.0f, 0.0f };
Camera::Camera(glm::vec3 position, float pitch, float yaw, float fov)
: position(position), pitch(pitch), yaw(yaw), fov(fov), forward(), right(), up()
{
}
glm::mat4 Camera::GetProjectionMatrix(float width, float height)
{
return glm::perspective(glm::radians(fov), width / height, 0.1f, 100000.0f); // draw distance 100.0f
}
void Camera::UpdateDirectionVectors()
{
forward = glm::normalize(glm::vec3(
glm::cos(glm::radians(yaw)) * glm::cos(glm::radians(pitch)),
glm::sin(glm::radians(pitch)),
glm::sin(glm::radians(yaw)) * glm::cos(glm::radians(pitch))
));
right = glm::normalize(glm::cross(forward, WorldUp));
up = glm::normalize(glm::cross(right, forward));
}
glm::mat4 Camera::GetViewMatrix()
{
return glm::lookAt(position, position + forward, up);
}
class Shader
{
public:
Shader(const std::string& vertexCode, const std::string& fragmentCode);
void Use();
void SetValue(const std::string& name, glm::vec3 value);
void SetValue(const std::string& name, glm::mat4 value);
private:
uint32_t programID;
};
Shader::Shader(const std::string& vertexCode, const std::string& fragmentCode)
{
const char* vertexShaderCode = vertexCode.c_str();
const char* fragmentShaderCode = fragmentCode.c_str();
// vertexshader
size_t vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexShaderCode, nullptr);
glCompileShader(vertexShader);
int success{};
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
char infoLog[1024]{};
if (!success)
{
glGetShaderInfoLog(vertexShader, 1024, nullptr, infoLog);
std::cerr << "Failed to compile shader!\nInfoLog:\n" << infoLog;
}
//fragmentshader
size_t fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderCode, nullptr);
glCompileShader(fragmentShader);
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(fragmentShader, 1024, nullptr, infoLog);
std::cerr << "Failed to compile shader!\nInfoLog:\n" << infoLog;
}
//
programID = glCreateProgram();
glAttachShader(programID, vertexShader);
glAttachShader(programID, fragmentShader);
glLinkProgram(programID);
glGetProgramiv(programID, GL_LINK_STATUS, &success);
if (!success)
{
glGetProgramInfoLog(programID, 1024, nullptr, infoLog);
std::cerr << "Failed to link shader!\nInfoLog:\n" << infoLog;
}
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
}
void Shader::Use()
{
glUseProgram(programID);
}
void Shader::SetValue(const std::string& name, glm::vec3 value)
{
glUniform3f(glGetUniformLocation(programID, name.c_str()), value.x, value.y, value.z);
}
void Shader::SetValue(const std::string& name, glm::mat4 value)
{
glUniformMatrix4fv(glGetUniformLocation(programID, name.c_str()), 1, GL_FALSE,
glm::value_ptr(value));
}
sf::Texture texture0;
class Mesh
{
public:
Mesh(std::vector<glm::vec3> vertices, std::vector<uint32_t> indices, std::vector<glm::vec2> TexCoords);
void Draw() const;
glm::mat4 transformation;
private:
std::vector<glm::vec3> vertices;
std::vector<uint32_t> indices;
std::vector<glm::vec2> TexCoords;
uint32_t vao, vbo, ebo;
};
Mesh::Mesh(std::vector<glm::vec3> vertices, std::vector<uint32_t> indices, std::vector<glm::vec2> TexCoords)
: vertices(vertices), indices(indices), TexCoords(TexCoords), vao(), vbo(), ebo()
{
glGenBuffers(1, &vbo);
glGenBuffers(1, &ebo);
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(glm::vec3), vertices.data(), GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(glm::uint32_t), indices.data(), GL_STATIC_DRAW);
//vertex positions
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), nullptr);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, vertices.size() * 3, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindVertexArray(0);
glBindVertexArray(vao);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
texture0.loadFromFile("coins_seamless.jpg");
glActiveTexture(GL_TEXTURE0);
sf::Texture::bind(&texture0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
}
void Mesh::Draw() const
{
glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, nullptr);
}
class Object
{
public:
Object(const Mesh* mesh, glm::vec3 position = {}, glm::vec3 rotation = {}, glm::vec3 scale = glm::vec3(1.0f));
void Draw(Shader& shader, glm::vec3 color = {});
glm::mat4 GetTransformationMatrix();
const Mesh* mesh;
glm::vec3 position, rotation, scale;
private:
};
Object::Object(const Mesh* mesh, glm::vec3 position, glm::vec3 rotation, glm::vec3 scale)
: mesh(mesh), position(position), rotation(rotation), scale(scale)
{
}
void Object::Draw(Shader& shader, glm::vec3 color)
{
shader.SetValue("model", GetTransformationMatrix());
shader.SetValue("color", color);
mesh->Draw();
}
glm::mat4 Object::GetTransformationMatrix()
{
glm::mat4 t = glm::mat4(1.0f);
t = glm::translate(t, position);
t = t * glm::mat4_cast(glm::quat(glm::radians(rotation)));
t = glm::scale(t, scale);
return t;
}
int main()
{
const char* vertexShaderCode = "#version 330 core\n"
"layout (location = 0) in vec3 pos;\n"
"layout (location = 1) in vec2 uv;\n"
"uniform mat4 projection;\n"
"uniform mat4 view;\n"
"uniform mat4 model;\n"
"out vec2 TexCoords;\n"
"void main() { gl_Position = projection * view * model * vec4(pos, 1.0);\n"
"TexCoords = uv;\n"
"}\n";
const char* fragmentShaderCode = "#version 330 core\n"
"in vec2 TexCoords;\n"
"out vec4 FragColor;\n"
"uniform vec3 color;\n"
"uniform sampler2D sampler;\n"
//"void main() { FragColor = texture(sampler,TexCoords) * vec4(color, 1.0); }\n";
"void main() { FragColor = texture(sampler,TexCoords); }\n";
sf::RenderWindow window(sf::VideoMode(800, 800), "3D OpenGL");
if (glewInit() != GLEW_OK)
{
std::cerr << "Failed to initialize GLEW\n";
return -1;
}
Shader shader(vertexShaderCode, fragmentShaderCode);
Mesh mesh({
glm::vec3(0.8f, 0.8f, 0.0f), // top right
glm::vec3(0.8f, -0.8f, 0.0f), // bottom right
glm::vec3(-0.8f,-0.8f, 0.0f), // bottom left
glm::vec3(-0.8f, 0.8f, 0.0f), // top left
}, { 0,1,3,1,2,3, }, {
glm::vec2(1.0f,1.0f), // 1.0f,1.0f
glm::vec2(1.0f,0.0f), // 1.0f,0.0f
glm::vec2(0.0f,0.0f), // 0.0f,0.0f
glm::vec2(0.0f,1.0f) // 0.0f,1.0f
});
Camera camera(glm::vec3(0.0f, 0.0f, 3.0f), 0.0f);
Object object(&mesh, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(1.0f, -1.0f, 1.0f)); // position, rotation, scale
bool isFirstMouse = true;
sf::Vector2i lastMousePos{};
while (window.isOpen())
{
sf::Event event{};
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
window.close();
else if (event.type == sf::Event::Resized)
glViewport(0, 0, window.getSize().x, window.getSize().y);
}
camera.UpdateDirectionVectors();
glClear(GL_COLOR_BUFFER_BIT);
shader.Use();
shader.SetValue("view", camera.GetViewMatrix());
shader.SetValue("projection", camera.GetProjectionMatrix((float)window.getSize().x, (float)window.getSize().y));
object.Draw(shader, glm::vec3(1.0f, 0.5f, 0.5f));
window.display();
}
}