I'm trying to load a texture in OpenGL, but all I get is black. I've verified that the shaders are returning GL_TRUE when they compile. So, I don't think that is the issue...
Some things to note:
I have taken and modified code from https://open.gl/content/code/c3_multitexture.txt. This was a tutorial from https://open.gl/textures
The texture I am trying to load is from a 640 x 400 PGM file. You can assume the loader pgmImage correctly loads the file (I have verified it in GDB). PGM is 8-bit B&W data. Here I am trying to just use it as the red channel, although I'd like to be able to eventually display it as B&W, too.
Where am I going wrong?
Thanks!
Code compiled with: -lglut -lGLU -lGL -lm -lGLEW flags in g++ (some aren't necessary, I know).
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <math.h>
// Include GLEW
#include <GL/glew.h>
//Glut
#include <GL/glut.h>
#include "shader.hpp"
#include "PGM.hpp"
GLuint programID;
GLuint output_image;
// Shader sources
const GLchar* vertexSource =
"#version 450 core\n"
"in vec2 position;"
"in vec2 texcoord;"
"out vec2 Texcoord;"
"void main()"
"{"
" Texcoord = texcoord;"
" gl_Position = vec4(position, 0.0, 1.0);"
"}";
const GLchar* fragmentSource =
"#version 450 core\n"
"in vec2 Texcoord;"
"out vec4 outColor;"
"uniform sampler2D texData;"
"void main()"
"{"
" outColor = texture(texData, Texcoord);"
"}";
void display()
{
glClearColor(0.0f, 0.0f, 1.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glFlush();
glutSwapBuffers();
}
void reshape(int width,int height)
{
double w2h = (height>0) ? (double)width/height : 1;
// Set viewport as entire window
glViewport(0,0, width,height);
}
int main(int argc, char** argv)
{
// Image setup
PGM pgmImage;
pgmImage.ReadFile("test.pgm");
// Window Setup
glutInitWindowSize(640, 400);
glutInitWindowPosition (140, 140);
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);
glutInit(&argc, argv);
glutCreateWindow( "OpenGL Application" );
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glewExperimental = true; // Needed for core profile
if (glewInit() != GLEW_OK) {
fprintf(stderr, "Failed to initialize GLEW\n");
return -1;
}
// Vertices & texture init
GLuint vao;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
GLuint vbo;
glGenBuffers(1, &vbo);
GLfloat vertices[] = {
// X Y S T
-0.5f, 0.5f, 0.0f, 0.0f, // Top-left
0.5f, 0.5f, 1.0f, 0.0f, // Top-right
0.5f, -0.5f, 1.0f, 1.0f, // Bottom-right
-0.5f, -0.5f, 0.0f, 1.0f // Bottom-left
};
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
GLuint ebo;
glGenBuffers(1, &ebo);
GLuint elements[] = {
0, 1, 2,
2, 3, 0
};
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(elements), elements, GL_STATIC_DRAW);
// Create shaders
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexSource, NULL);
glCompileShader(vertexShader);
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentSource, NULL);
glCompileShader(fragmentShader);
GLuint shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glBindFragDataLocation(shaderProgram, 0, "outColor");
glLinkProgram(shaderProgram);
glUseProgram(shaderProgram);
// Vertex data specification
GLint posAttrib = glGetAttribLocation(shaderProgram, "position");
glEnableVertexAttribArray(posAttrib);
glVertexAttribPointer(posAttrib, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), 0);
GLint texAttrib = glGetAttribLocation(shaderProgram, "texcoord");
glEnableVertexAttribArray(texAttrib);
glVertexAttribPointer(texAttrib, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (void *)(2 * sizeof(GLfloat)));
// Load Textures
//TODO: don't really need 2 textures, but just following along with example source code for now...
GLuint textures[2];
unsigned char* image;
int width, height;
width = pgmImage.GetWidth();
height = pgmImage.GetHeight();
image = (unsigned char *)pgmImage.GetData();
glGenTextures(2, textures);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, textures[0]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RED_INTEGER, GL_UNSIGNED_BYTE, image);
glUniform1i(glGetUniformLocation(shaderProgram, "texData"), 0);
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_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// Start program
glutMainLoop();
// Teardown
glDeleteTextures(2, textures);
glDeleteProgram(shaderProgram);
glDeleteShader(fragmentShader);
glDeleteShader(vertexShader);
glDeleteBuffers(1, &ebo);
glDeleteBuffers(1, &vbo);
glDeleteVertexArrays(1, &vao);
return 0;
}
As you had already found, the internalFormat and format of your glTexImage2D() call are incompatible:
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RED_INTEGER, GL_UNSIGNED_BYTE, image);
GL_RED_INTEGER is for integer texture types. For 1-component texture data with normalized textures, the correct format is GL_RED:
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RED, GL_UNSIGNED_BYTE, image);
For new code, I would always recommend to use sized internal types, even though unsized internal types like GL_RGB are still valid for backwards compatibility. So for a normalized texture with 3 8-bit components:
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, width, height, 0, GL_RED, GL_UNSIGNED_BYTE, image);
Your attempt at using GL_R8UI as the internal format failed because using integer textures requires additional changes, particularly in the shader code, where you would need to use usampler2D as the sampler type, and deal with getting integer values when you sample the textures. But in your case, that's not really what you wanted in the first place.
Related
I am trying to render a texture with OpenGL. The texture I am using as a test is a bunch of black rectangles on a white background, as follows:
However, when rendering, the texture seem to be duplicated and overlayed multiple times on top of itself:
I set the scene up using:
std::string vertexSource = ShaderLoader::load("vertexSource.vert");
const char* vsource = vertexSource.c_str();
std::string fragmentSource = ShaderLoader::load("fragmentSource.frag");
const char* fsource = fragmentSource.c_str();
int vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vsource, NULL);
glCompileShader(vertexShader);
int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fsource, NULL);
glCompileShader(fragmentShader);
shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
float vertices[] = {
0.5f, 0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
-0.5f, -0.5f, 0.0f,
-0.5f, 0.5f, 0.0f,
1.0f, 1.0f,
1.0f, 0.0f,
0.0f, 0.0f,
0.0f, 1.0f,
};
unsigned int indices[] = {
0, 1, 3,
1, 2, 3
};
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glGenBuffers(1, &EBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1,2,GL_FLOAT, GL_FALSE, 2* sizeof(float), (void*)(sizeof(float)*12));
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
unsigned char* data = stbi_load("image.png", &width, &height,&nrOfChannels, 0);
glGenTextures(1, &textureId);
glBindTexture(GL_TEXTURE_2D, textureId);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
stbi_image_free((void *) data);
My render code is:
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(shaderProgram);
glBindVertexArray(VAO);
glBindTexture(GL_TEXTURE_2D, textureId);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
My vertex shader is:
#version 330 core
layout (location = 0) in vec3 inVertex;
layout (location = 1) in vec2 inTexture;
out vec2 TextureCoordinate;
void main()
{
gl_Position = vec4( inVertex, 1 );
TextureCoordinate = inTexture;
}
and my fragment shader is:
#version 330 core
out vec4 FragColor;
in vec2 TextureCoordinate;
uniform sampler2D Texture;
void main() {
FragColor = texture(Texture,TextureCoordinate);
}
When an image with 3 channels (GL_RGB) is loaded to a texture object, GL_UNPACK_ALIGNMENT needs to be set to 1. By default GL_UNPACK_ALIGNMENT is 4, so each line of an image is assumed to be aligned to 4 bytes. The pixels in the buffer have a size of 3 bytes and are tightly packed, this would cause a misalignment.
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glPixelStorei(GL_UNPACK_ALIGNMENT, 4); // default
Since the image format is PNG, it is very likely that the image has 4 channels (GL_RGBA). Evaluate nrOfChannels before specifying the texture image:
unsigned char* data = stbi_load("image.png", &width, &height, &nrOfChannels, 0);
// [...]
unsigned int format = nrOfChannels == 4 ? GL_RGBA : GL_RGB;
glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
It is also possible to force stbi_load to generate an image with 4 channels, by explicitly pass 4 to the last parameter:
int reqChannels= 4;
unsigned char* data = stbi_load("image.png", &width, &height, &nrOfChannels, reqChannels);
// [...]
glTexImage2D(GL_TEXTURE_2D, GL_RGBA, format, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
I am trying to render a texture with OpenGL. The texture I am using as a test is a bunch of black rectangles on a white background, as follows:
However, when rendering, the texture seem to be duplicated and overlayed multiple times on top of itself:
I set the scene up using:
std::string vertexSource = ShaderLoader::load("vertexSource.vert");
const char* vsource = vertexSource.c_str();
std::string fragmentSource = ShaderLoader::load("fragmentSource.frag");
const char* fsource = fragmentSource.c_str();
int vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vsource, NULL);
glCompileShader(vertexShader);
int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fsource, NULL);
glCompileShader(fragmentShader);
shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
float vertices[] = {
0.5f, 0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
-0.5f, -0.5f, 0.0f,
-0.5f, 0.5f, 0.0f,
1.0f, 1.0f,
1.0f, 0.0f,
0.0f, 0.0f,
0.0f, 1.0f,
};
unsigned int indices[] = {
0, 1, 3,
1, 2, 3
};
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glGenBuffers(1, &EBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1,2,GL_FLOAT, GL_FALSE, 2* sizeof(float), (void*)(sizeof(float)*12));
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
unsigned char* data = stbi_load("image.png", &width, &height,&nrOfChannels, 0);
glGenTextures(1, &textureId);
glBindTexture(GL_TEXTURE_2D, textureId);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
stbi_image_free((void *) data);
My render code is:
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(shaderProgram);
glBindVertexArray(VAO);
glBindTexture(GL_TEXTURE_2D, textureId);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
My vertex shader is:
#version 330 core
layout (location = 0) in vec3 inVertex;
layout (location = 1) in vec2 inTexture;
out vec2 TextureCoordinate;
void main()
{
gl_Position = vec4( inVertex, 1 );
TextureCoordinate = inTexture;
}
and my fragment shader is:
#version 330 core
out vec4 FragColor;
in vec2 TextureCoordinate;
uniform sampler2D Texture;
void main() {
FragColor = texture(Texture,TextureCoordinate);
}
When an image with 3 channels (GL_RGB) is loaded to a texture object, GL_UNPACK_ALIGNMENT needs to be set to 1. By default GL_UNPACK_ALIGNMENT is 4, so each line of an image is assumed to be aligned to 4 bytes. The pixels in the buffer have a size of 3 bytes and are tightly packed, this would cause a misalignment.
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glPixelStorei(GL_UNPACK_ALIGNMENT, 4); // default
Since the image format is PNG, it is very likely that the image has 4 channels (GL_RGBA). Evaluate nrOfChannels before specifying the texture image:
unsigned char* data = stbi_load("image.png", &width, &height, &nrOfChannels, 0);
// [...]
unsigned int format = nrOfChannels == 4 ? GL_RGBA : GL_RGB;
glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
It is also possible to force stbi_load to generate an image with 4 channels, by explicitly pass 4 to the last parameter:
int reqChannels= 4;
unsigned char* data = stbi_load("image.png", &width, &height, &nrOfChannels, reqChannels);
// [...]
glTexImage2D(GL_TEXTURE_2D, GL_RGBA, format, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
I wrote a simple OpenGL program to sample a 256x256 PNG texture and render it on a rectangle in a window. I relied heavily on a tutorial for this. When I followed the tutorial almost directly (making minimal changes so as to use SDL2 for [x]), the texture did not render correctly and seemed to be somehow scaled down by a factor of 2. Here is the first version of the source code, as well as a screenshot of the results:
// Link statically with GLEW
#define GLEW_STATIC
// Headers
#include <GL/glew.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <SDL2/SDL_opengl.h>
// Shader sources
const GLchar* vertexSource = R"glsl(
#version 150 core
in vec2 position;
in vec2 color;
out vec2 Color;
void main()
{
Color = color;
gl_Position = vec4(position, 0.0, 1.0);
}
)glsl";
const GLchar* fragmentSource = R"glsl(
#version 150 core
in vec2 Color;
out vec4 outColor;
uniform sampler2D tex;
void main()
{
outColor = texture(tex,Color);
}
)glsl";
int main()
{
SDL_Init(SDL_INIT_VIDEO);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
SDL_Window* window = SDL_CreateWindow("OpenGL", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 600, SDL_WINDOW_OPENGL | SDL_WINDOW_ALLOW_HIGHDPI);
SDL_GLContext context = SDL_GL_CreateContext(window);
glViewport(0, 0, 800, 600);
// Initialize GLEW
glewExperimental = GL_TRUE;
glewInit();
// Create Vertex Array Object
GLuint vao;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
// Create a Vertex Buffer Object and copy the vertex data to it
GLuint vbo;
glGenBuffers(1, &vbo);
GLfloat vertices[] = {
// x y u v
-0.5f, 0.5f, 0.0f, 0.0f, // Top-left
0.5f, 0.5f, 1.0f, 0.0f, // Top-right
0.5f, -0.5f, 1.0f, 1.0f, // Bottom-right
-0.5f, -0.5f, 0.0f, 1.0f // Bottom-left
};
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// Create an element array
GLuint ebo;
glGenBuffers(1, &ebo);
GLuint elements[] = {
0, 1, 2,
2, 3, 0
};
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(elements), elements, GL_STATIC_DRAW);
// Create and compile the vertex shader
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexSource, NULL);
glCompileShader(vertexShader);
// Create and compile the fragment shader
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentSource, NULL);
glCompileShader(fragmentShader);
// Link the vertex and fragment shader into a shader program
GLuint shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glBindFragDataLocation(shaderProgram, 0, "outColor");
glLinkProgram(shaderProgram);
glUseProgram(shaderProgram);
// Specify the layout of the vertex data
GLint posAttrib = glGetAttribLocation(shaderProgram, "position");
glEnableVertexAttribArray(posAttrib);
glVertexAttribPointer(posAttrib, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), 0);
GLint colAttrib = glGetAttribLocation(shaderProgram, "color");
glEnableVertexAttribArray(colAttrib);
glVertexAttribPointer(colAttrib, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (void*)(2 * sizeof(GLfloat)));
GLuint tex;
glGenTextures(1, &tex);
glBindTexture(GL_TEXTURE_2D, tex);
SDL_Surface* surface = IMG_Load("../textures/16x16-sb-ascii.png");
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, surface->w, surface->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, surface->pixels);
SDL_FreeSurface(surface);
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_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
bool running = true;
while (running)
{
SDL_Event windowEvent;
while (SDL_PollEvent(&windowEvent))
{
switch (windowEvent.type)
{
case SDL_QUIT:
running = false;
break;
}
}
// Clear the screen to black
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// Draw a rectangle from the 2 triangles using 6 indices
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
// Swap buffers
SDL_GL_SwapWindow(window);
}
glDeleteProgram(shaderProgram);
glDeleteShader(fragmentShader);
glDeleteShader(vertexShader);
glDeleteBuffers(1, &ebo);
glDeleteBuffers(1, &vbo);
glDeleteVertexArrays(1, &vao);
SDL_Quit();
return 0;
}
Initial results
For reference, here is the actual texture file:
Original texture file
As you can see, the texture appears to have been quadrupled in the different quadrants of the rectangle, with the lower two copies inexplicably being thoroughly corrupted.
As one might expect, normalizing all the texture coordinates to 0.5 produces a single copy of the texture at the intended scale, although many strange yellow/blue artifacts are still visible, as are some very subtle noisy artifacts around the edges:
GLfloat vertices[] = {
// x y u v
-0.5f, 0.5f, 0.0f, 0.0f, // Top-left
0.5f, 0.5f, 0.5f, 0.0f, // Top-right
0.5f, -0.5f, 0.5f, 0.5f, // Bottom-right
-0.5f, -0.5f, 0.0f, 0.5f // Bottom-left
};
Results after scaling down the coordinates.
I'm doing this on a Dell laptop which came with Ubuntu 18.04.4 LTS pre-installed. My graphics card is an Intel UHD Graphics 630 (Coffeelake 3x8 GT2).
Running glxinfo | grep "OpenGL version" gives the following result: OpenGL version string: 3.0 Mesa 19.2.8.
Any help with either the scaling or the blue/yellow artifacts would be greatly appreciated. Thank you!
The generated texture image is a luminance/alpha image and has just one color channel and one alpha channel. Thus you have to use the format GL_RG when you specify the 2 dimensional texture image by glTexImage2D:
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RG, surface->w, surface->h, 0,
GL_RG, GL_UNSIGNED_BYTE, surface->pixels);
Either set a Swizzle mask to read the green and blue color from the red color channel and the alpha value from the green color channel:
GLint swizzleMask[] = {GL_RED, GL_RED, GL_RED, GL_GREEN};
glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask);
or use Swizzling to get the red color channel and the alpha channel when you do the texture lookup in the fragment shader:
#version 150 core
in vec2 Color;
out vec4 outColor;
uniform sampler2D tex;
void main()
{
outColor = texture(tex,Color).rrrg;
}
I'm trying to render my first texture with Qt & OpenGL.
This is the code.
initializeGL function
void OpenGLWidget::initializeGL()
{
initializeOpenGLFunctions();
glEnable(GL_DEBUG_OUTPUT);
glDebugMessageCallback(debugCallback, nullptr);
program = genProgram("../06_HelloTexture/vert.glsl", "../06_HelloTexture/frag.glsl");
glUseProgram(program);
glClearColor(0.3f, 0.3f, 0.3f, 1.0f);
glEnable(GL_DEPTH_TEST);
// vertices is std::vector<QVector3D>
vertices = {
{-0.5f, -0.5f, 0.0f}, {0.0f, 0.0f, 0.0f},
{-0.5f, +0.5f, 0.0f}, {0.0f, 1.0f, 0.0f},
{+0.5f, -0.5f, 0.0f}, {1.0f, 0.0f, 0.0f},
{+0.5f, +0.5f, 0.0f}, {1.0f, 1.0f, 0.0f},
};
// indices is std::vector<GLuint>
indices = {
0, 1, 2,
1, 2, 3,
};
GLuint VAO, VBO, EBO;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glGenBuffers(1, &EBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, vertices.size()*sizeof(QVector3D), &vertices[0], GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size()*sizeof(GLuint), &indices[0], GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 2*sizeof(QVector3D), static_cast<void*>(nullptr));
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 2*sizeof(QVector3D), (void*)sizeof(QVector3D));
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, 0);
QImage image("../lightbulb-solid.svg");
// QImage image("../test.png");
qDebug() << image;
// this outputs
// QImage(QSize(352, 512),format=6,depth=32,devicePixelRatio=1,bytesPerLine=1408,sizeInBytes=720896)
GLuint texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width(), image.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, image.constBits());
// glGenerateMipmap(GL_TEXTURE_2D);
lightbulbSolidLocation = glGetUniformLocation(program, "lightbulbSolid");
glUniform1i(lightbulbSolidLocation, 0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
}
paintGL function
void OpenGLWidget::paintGL()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(indices.size()), GL_UNSIGNED_INT, nullptr);
}
vertex shader
#version 450 core
layout (location = 0) in vec3 vertPosition;
layout (location = 1) in vec3 vertTexCoord;
out vec3 fragTexCoord;
void main()
{
gl_Position = vec4(vertPosition, 1.0);
fragTexCoord = vertTexCoord;
}
fragment shader
#version 450 core
in vec3 fragTexCoord;
out vec4 pixelColor;
uniform sampler2D lightbulbSolid;
void main()
{
// pixelColor = vec4(fragColor, 1.0f);
// pixelColor = texture(lightbulbSolid, vec2(fragTexCoord.x, fragTexCoord.y));
vec4 temp = texture(lightbulbSolid, vec2(fragTexCoord.x, fragTexCoord.y));
if (!all(equal(temp.xyz, vec3(0.0f))))
{
pixelColor = temp;
}
else
{
pixelColor = vec4(fragTexCoord, 1.0f);
}
}
And this is the result
As you can see, all the colors generated by the texture function in the fragment shader are black, so the if statement choose the texture coordinate as color rather than the real texture color.
I tried to catch some error, but the callback function didn't find something wrong in the code. I also tried with another image (of PNG format rather than SVG), but the result is the same.
If you want to load a PNG file, then you can load it to a QImage directly,
QImage image("../test.png");
but if you want to render SVG file, then you have to use the QSvgRenderer and QPainter to paint the content to an QImage. You have to choose the format, the resolution and the background color of the target bitmap:
e.g.:
#include <Qtsvg/QSvgRenderer>
QSvgRenderer renderer(QString("../lightbulb-solid.svg"));
QImage image(512, 512, QImage::Format_RGBA8888); // 512x512 RGBA
image.fill(0x00ffffff); // white background
QPainter painter(&image);
renderer.render(&painter);
const uchar *image_bits = image.constBits();
int width = image.width();
int height = image.height();
Note, you have to link qt5svgd.lib (debug) respectively qt5svg.lib (release).
The default minifying function parameter (GL_TEXTURE_MIN_FILTER) is GL_NEAREST_MIPMAP_LINEAR. See glTexParameteri
This means either you have to change the GL_TEXTURE_MIN_FILTER parameter to GL_LINEAR
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0,
GL_RGBA, GL_UNSIGNED_BYTE, image_bits);
or you have to create mipmaps
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0,
GL_RGBA, GL_UNSIGNED_BYTE, image_bits);
glGenerateMipmap(GL_TEXTURE_2D);
In the initializeGL method the texture is bound to texture unit 0, but it is not guaranteed, that this is still the current state in the paintGL method. You have to bind the texture in the paintGL method:
void OpenGLWidget::paintGL()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(indices.size()), GL_UNSIGNED_INT, nullptr);
}
This question already has an answer here:
OpenGL texture tilted
(1 answer)
Closed 1 year ago.
I'm trying to upload a texture in OpenGL but the texture is crooked.
#include <iostream>
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <SOIL/SOIL.h>
#define GLSL(version, shader) "#version " #version "\n" #shader
const char * vertShader = GLSL(430,
in vec2 position;
in vec2 texcoord;
out vec2 coordinates;
void main() {
coordinates = texcoord;
gl_Position = vec4(position, 0.0, 1.0);
}
);
const char * fragShader = GLSL(430,
in vec2 coordinates;
out vec4 color;
uniform sampler2D texture;
void main() {
color = texture(texture, coordinates);
}
);
int main(int argc, char ** argv)
{
if (not glfwInit())
{
std::cerr << "Failed to initialize GLFW" << std::endl;
return -1;
}
GLFWwindow *window;
window = glfwCreateWindow(720, 480, "Textures", 0, 0);
if (not window)
{
std::cerr << "Failed to open GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
glewExperimental = true;
if (glewInit() != GLEW_OK)
{
std::cerr << "Failed to initialize GLEW" << std::endl;
return -1;
}
glfwSetInputMode(window, GLFW_STICKY_KEYS, GL_TRUE);
GLuint vao;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
GLuint vbo;
glGenBuffers(1, &vbo);
GLfloat vertices[] = {
-1.0f, +1.0f, 0.0f, 0.0f,
+1.0f, +1.0f, 1.0f, 0.0f,
+1.0f, -1.0f, 1.0f, 1.0f,
-1.0f, -1.0f, 0.0f, 1.0f,
};
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
GLuint ebo;
glGenBuffers(1, &ebo);
GLuint elements[] = {
0, 1, 2,
2, 3, 0
};
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(elements), elements, GL_STATIC_DRAW);
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertShader, 0);
glCompileShader(vertexShader);
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragShader, NULL);
glCompileShader(fragmentShader);
GLuint shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
glUseProgram(shaderProgram);
GLint posAttrib = glGetAttribLocation(shaderProgram, "position");
GLint texAttrib = glGetAttribLocation(shaderProgram, "texcoord");
glVertexAttribPointer(posAttrib, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), 0);
glEnableVertexAttribArray(posAttrib);
glVertexAttribPointer(texAttrib, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void *)(2 * sizeof(float)));
glEnableVertexAttribArray(texAttrib);
GLuint tex;
glGenTextures(1, &tex);
glBindTexture(GL_TEXTURE_2D, tex);
int width, height;
unsigned char * image = SOIL_load_image("happyface.png", &width, &height, 0, SOIL_LOAD_RGB);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
SOIL_free_image_data(image);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
do {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glfwPollEvents();
glfwSwapBuffers(window);
} while (glfwGetKey(window, GLFW_KEY_ESCAPE) != GLFW_PRESS
&& glfwWindowShouldClose(window) == 0);
glDeleteTextures(1, &tex);
glDeleteProgram(shaderProgram);
glDeleteShader(fragmentShader);
glDeleteShader(vertexShader);
glDeleteBuffers(1, &ebo);
glDeleteBuffers(1, &vbo);
glDeleteVertexArrays(1, &vao);
glfwTerminate();
return 0;
}
What am I doing wrong? As you can see the texture is crooked and repeating, but the texture is just one smiley face.
That is the result:
And that is the texture:
I'm no OpenGL guru, but some graphics systems require textures that are multiples of 2 or 4 pixels wide.
Your texture is 479 wide - add another column of pixels and I think you'll be fine.