I've looked at other peoples implementation of it, and I'm still not sure what I'm doing wrong. My graphics drivers are up to date, and I'm getting no error messages.
I'm trying to use a GLSL compute shader to write to a texture which is then rendered onto a screen-size quad.
Here is the code for my main.cpp:
unsigned int hRes = 512;
unsigned int vRes = 512;
int main(void) {
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
GLFWwindow* window = glfwCreateWindow(hRes, vRes, "Learn OpenGL", NULL, NULL);
if (window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
glViewport(0, 0, hRes, vRes);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
//
float vertices[] = {
// positions // Tex coords
1.f, 1.f, 0.0f, 1.f, 1.f, // top right
1.f, -1.f, 0.0f, 1.f, 0.f, // bottom right
-1.f, -1.f, 0.0f, 0.f, 0.f, // bottom left
-1.f, 1.f, 0.0f, 0.f, 1.f, // top left
};
unsigned int indices[] = {
0, 1, 3, // first triangle
1, 2, 3 // second triangle
};
unsigned int VAO;
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
unsigned int VBO;
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(0));
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3));
glEnableVertexAttribArray(1);
unsigned int EBO;
glGenBuffers(1, &EBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
unsigned int texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
float borderColor[] = { 1.0f, 1.0f, 0.0f, 1.0f };
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, hRes, vRes, 0, GL_RGBA, GL_FLOAT, NULL);
glBindImageTexture(0, texture, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA32F);
// Vertex and fragment shaders are pretty much empty, and just pass through vertex/texture coord data
Shader vertex("doNothing.vert", GL_VERTEX_SHADER);
Shader fragment("doNothing.frag", GL_FRAGMENT_SHADER);
ShaderProgram renderProg;
renderProg.attach(vertex);
renderProg.attach(fragment);
renderProg.link();
Shader computeShader("julia.comp", GL_COMPUTE_SHADER);
ShaderProgram computeProg;
computeProg.attach(computeShader);
computeProg.link();
while (!glfwWindowShouldClose(window))
{
// Input (currently does nothing)
processInput(window);
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
computeProg.use();
glDispatchCompute(hRes / 16, vRes / 16, 1); // For local work group size 16. Ensures entire texture is written to
glMemoryBarrier(GL_TEXTURE_FETCH_BARRIER_BIT);
renderProg.use();
glBindTexture(GL_TEXTURE_2D, texture);
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
// End drawing current frame
glfwSwapBuffers(window);
glfwPollEvents();
}
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
glDeleteBuffers(1, &EBO);
glfwTerminate();
return 0;
}
ShaderProgram and Shader are just helper classes that wrap the OpenGL object ID.
Shader code:
doNothing.frag:
#version 460 core
out vec4 FragColor;
in vec2 TexCoord;
uniform sampler2D ourTexture;
void main()
{
FragColor = texture(ourTexture, TexCoord);
}
doNothing.vert:
#version 460 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aTexCoord;
out vec2 TexCoord;
void main()
{
gl_Position = vec4(aPos, 1.0);
TexCoord = aTexCoord;
}
julia.comp:
#version 460
layout (binding = 0, rgba32f) uniform writeonly image2D destTex;
layout (local_size_x = 16, local_size_y = 16) in;
void main() {
ivec2 storePos = ivec2(gl_GlobalInvocationID.xy);
if(storePos.x > 100){
imageStore(destTex, storePos, vec4(0., 0., 1.0, 1.0));
}else{
imageStore(destTex, storePos, vec4(1., 0., 0.0, 1.0));
}
}
I expected this code to output an image where one portion was red, and the other portion was blue.
What instead happens is that it outputs a single-colour image where the entire screen is a blend of the red and blue colours.
I have played around with the conditions inside of the julia.comp shader, and it seems that if I set the conditions such that the corners are each coloured differently, I get a blend of the corner colours (E.G. if I set the condition to storePos.x > 100 && storePos.x < 511 I get only the colour from the else block, but if I set it to storePos.x > 100 && storePos.x < 513 I get a blend of both colours.
Any help would be appreciated.
EDIT:
image of what I get:
And this is what happens if I replace vec4(0., 0., 1., 1.) with vec4(0., 1., 0., 1.) in the "if" code block:
Which is why I believe it is blending the colours somehow. (Note that the yellow colour is also fairly dim, which suggests the colours have been averaged, not added together)
EDIT: I tried setting the magnification filter to GL_NEAREST, and this results in a bright red texture across the entire screen. (No blending between the two colours)
EDIT: the post "OpenGL compute shader - strange results" does not solve my problem. That askers problem was with a particular library he was using for their matrix calculations, and I am not even using a single matrix in my answer. I have also tried other suggestions people on that post had, and none of them worked.
The offset to glVertexAttribPointer is in bytes. You pass a (void*)(3) for what should be (void*)(3*sizeof(float)):
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3*sizeof(float))); // <--- here
Related
I'm trying to use a compute shader to do three-dimensional physical simulations but having trouble storing anything into my 3D texture. My shaders compile successfully but when reading back any value from the 3D texture I get a zero vector. I haven't used compute shaders before either so I'm not sure if I'm even distributing the work load properly in order to achieve what I want.
I've isolated the problem in a small example here. Basically the compute.glsl shader has a uniform image3D and uses imageStore to write a vec4(1,1,0,1) into gl_WorkGroupID. In C++ I create a 100x100x100 3D texture and bind it to the shader's uniform, then I call glDispatchCompute(100,100,100) - to my knowledge, this will create 1,000,000 jobs/shader invocations, one for each coordinate in the texture. In my view.glsl fragment shader I read the value of a random coordinate (in this case (3,5,7)) and output that. I use this shade a cube object.
Everything I've tried results in a black cube being output:
Here's my code (I've been following along with learnopengl.com so it's mostly the same boiler plate stuff except I extended the shader class to handle compute shaders):
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <stb_image.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <learnopengl/shader_m.h>
#include <iostream>
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow* window);
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;
struct vaoinfo
{
unsigned int VBO, VAO, EBO;
vaoinfo() : VAO(0), VBO(0), EBO(0)
{}
};
void create_vao(vaoinfo& info)
{
glGenVertexArrays(1, &info.VAO);
glGenBuffers(1, &info.VBO);
glGenBuffers(1, &info.EBO);
}
void init_vao(vaoinfo& info, float* vertices, int num_vertices, int* indices, int num_indices)
{
glGenVertexArrays(1, &info.VAO);
glGenBuffers(1, &info.VBO);
glGenBuffers(1, &info.EBO);
glBindVertexArray(info.VAO);
glBindBuffer(GL_ARRAY_BUFFER, info.VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * num_vertices, vertices, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, info.EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(int) * num_indices, indices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glBindVertexArray(0);
}
int main()
{
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
if (window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
vaoinfo cube;
create_vao(cube);
glm::vec3 c(0.5, 0.5, 0.5);
float verts[24] =
{
c.x, -c.y, c.z,
c.x, c.y, c.z,
-c.x, c.y, c.z,
-c.x, -c.y, c.z,
c.x, -c.y, -c.z,
c.x, c.y, -c.z,
-c.x, c.y, -c.z,
-c.x, -c.y, -c.z,
};
int indices[36] =
{
7, 4, 5,
5, 6, 7,
3, 0, 1,
1, 2, 3,
2, 6, 7,
7, 3, 2,
1, 5, 4,
4, 0, 1,
7, 4, 0,
0, 3, 7,
6, 5, 1,
1, 2, 6
};
init_vao(cube, verts, 24, indices, 36);
// Create a 3D texture
unsigned int texId;
glGenTextures(1, &texId);
glBindTexture(GL_TEXTURE_3D, texId);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA8_SNORM, 100, 100, 100, 0, GL_RGBA, GL_FLOAT, nullptr);
// Create shaders
Shader computeShader("compute.glsl");
Shader viewShader("coords.glsl", "view.glsl");
while (!glfwWindowShouldClose(window))
{
processInput(window);
computeShader.use();
computeShader.setInt("img", 0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_3D, texId);
glDispatchCompute(100, 100, 100);
glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
viewShader.use();
viewShader.setInt("img", 0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_3D, texId);
glm::mat4 model = glm::mat4(1.0f); // make sure to initialize matrix to identity matrix first
glm::mat4 view = glm::mat4(1.0f);
glm::mat4 projection = glm::mat4(1.0f);
model = glm::rotate(model, (float)glfwGetTime(), glm::vec3(0.5f, 1.0f, 0.0f));
view = glm::translate(view, glm::vec3(0.0f, 0.0f, -3.0f));
projection = glm::perspective(glm::radians(45.0f), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);
unsigned int modelLoc = glGetUniformLocation(viewShader.ID, "model");
unsigned int viewLoc = glGetUniformLocation(viewShader.ID, "view");
unsigned int projLoc = glGetUniformLocation(viewShader.ID, "projection");
glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection));
// render cube
glBindVertexArray(cube.VAO);
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, nullptr);
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwTerminate();
return 0;
}
void processInput(GLFWwindow* window)
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
}
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
glViewport(0, 0, width, height);
}
compute.glsl:
#version 430
layout(local_size_x=1, local_size_y=1, local_size_z=1) in;
layout(rgba8_snorm, binding=0) uniform image3D img;
uniform vec3 position;
void main()
{
ivec3 voxel_coord = ivec3(gl_WorkGroupID);
imageStore(img, voxel_coord, vec4(1, 1, 0, 1));
}
coords.glsl (vertex shader):
#version 430 core
layout (location = 0) in vec3 aPos;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main()
{
gl_Position = projection * view * model * vec4(aPos, 1.0f);
}
view.glsl (fragment shader):
#version 430 core
out vec4 FragColor;
layout(rgba8_snorm, binding=0) uniform image3D img;
void main()
{
FragColor = imageLoad(img, ivec3(3,5,7));
}
It turned out that I was missing a call to glBindImageTexture - I thought that in order to bind my texture to the shader's image variable I needed to set the uniform and call glActiveTexture+glBindTexture but it seems only glBindImageTexture is needed.
I replaced:
computeShader.setInt("img", 0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_3D, texId)
With:
glBindImageTexture(0, texId, 0, true, 0, GL_WRITE_ONLY, GL_RGBA16);
I need to crop the image. But in opencv, it will create error because of integer domain. So I want to crop every single unit in float domain to reduce the offset error. Maybe openGL can help me, but I'm not good at it.
The image just like this ( but not this one ):
I want every black and white unit, then save them in cv::Mat.
I guess maybe shader or loop or something else can repeat to crop all block, then save them.
Now I can get one single unit. Like this:
glfwInit();//initalization
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
GLFWwindow* window = glfwCreateWindow(100, 100, "LearnOpenGL", nullptr, nullptr);
if (window == nullptr)
{
cout << "Failed to create GLFW window" << endl;
glfwTerminate();
return false;
}
glfwMakeContextCurrent(window);
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return false;
}
Shader ourShader("vertex.vs.glsl", "fragment.fs.glsl");
float unitWidth = moduleLength / (float)tex.cols;
float startP_x = 100.0f / (float)tex.cols + unitWidth * 2;
float startP_y = 104.0f / (float)tex.rows + unitWidth * 2;
float vertices[] = {
// positions // texture coords (note that we changed them to 'zoom in' on our texture image)
0.5f, 0.5f, 0.0f, startP_x + unitWidth, startP_y, // top right
0.5f, -0.5f, 0.0f, startP_x + unitWidth, startP_y + unitWidth, // bottom right
-0.5f, -0.5f, 0.0f, startP_x, startP_y + unitWidth, // bottom left
-0.5f, 0.5f, 0.0f, startP_x, startP_y // top left
};
unsigned int indices[] = {
0, 1, 3, // first triangle
1, 2, 3 // second triangle
};
unsigned int VBO, VAO, EBO;
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);
// position attribute
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
// texture coord attribute
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
// texture
// ---------
GLuint texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
//// set the texture wrapping parameters
// set the texture wrapping parameters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
// set texture filtering parameters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
GLubyte* pixels;
int width = tex.cols;
int height = tex.rows;
int pixellength = width * height*tex.channels();
pixels = new GLubyte[pixellength];
memcpy(pixels, tex.data, pixellength * sizeof(unsigned char));
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_BGR, GL_UNSIGNED_BYTE, pixels);
free(pixels);
while (!glfwWindowShouldClose(window))
{
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// bind Texture
glBindTexture(GL_TEXTURE_2D, texture);
// draw our first triangle
ourShader.use();
// create transformations
glm::mat4 model = glm::mat4(1.0f); // make sure to initialize matrix to identity matrix first
glm::mat4 view = glm::mat4(1.0f);
glm::mat4 projection = glm::mat4(1.0f);
model = glm::rotate(model, glm::radians(0.0f), glm::vec3(1.0f, 0.0f, 0.0f));
view = glm::translate(view, glm::vec3(0.0f, 0.0f, -1.1f));
projection = glm::perspective(glm::radians(45.0f), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);
// retrieve the matrix uniform locations
unsigned int modelLoc = glGetUniformLocation(ourShader.ID, "model");
unsigned int viewLoc = glGetUniformLocation(ourShader.ID, "view");
unsigned int transformLoc = glGetUniformLocation(ourShader.ID, "projection");
glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
glUniformMatrix4fv(viewLoc, 1, GL_FALSE, &view[0][0]);
glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(projection));
glBindVertexArray(VAO); // seeing as we only have a single VAO there's no need to bind it every time, but we'll do so to keep things a bit more organized
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); // set the count to 6 since we're drawing 6 vertices now (2 triangles); not 3!
glfwPollEvents();
glfwSwapBuffers(window);
}
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
glDeleteBuffers(1, &EBO);
glfwTerminate();
return true;
vetex shader:
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aTexCoord;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
out vec2 TexCoord;
void main()
{
gl_Position = projection * view * model *vec4(aPos, 1.0);
TexCoord = vec2(aTexCoord.x, aTexCoord.y);
}
fragment shader:
#version 330 core
out vec4 FragColor;
in vec2 TexCoord;
uniform sampler2D texture1;
void main()
{
FragColor = texture(texture1, TexCoord);
}
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);
}
When I run this program, it seems to only be mapping my textures to 3 sides of a right angle triangle (the first 3 points in my vertices array), and completely missing out the fourth, as when I change that bit, nothing changes in the image. https://imgur.com/a/MvhsYYv here is the current output as the top image, and the expected output as the 2nd image. When I texture it with just a colour in my shader, it maps fine, with a texture, it still seems that it is drawing a square, but then mapping to the top left when it should be mapping to the top right.
Vertex vertices[] = {
//Position //Colour
vec3(-0.5f, 0.5f, 0.f), vec3(1.f, 0.f, 0.f), vec2(0.f, 1.f), //Top left
vec3(-0.5f, -0.5f, 0.f), vec3(0.f, 1.f, 0.f), vec2(0.f, 0.f), //Bottom Left
vec3(0.5f, -0.5f, 0.f), vec3(0.f, 0.f, 1.f), vec2(1.f, 0.f), //Bottom Right
vec3(0.5f, 0.5f, 0.f), vec3(1.f, 1.f, 1.f), vec2(1.f, 1.f) //Top Right
};
unsigned int numOfVertices = sizeof(vertices) / sizeof(Vertex);
GLuint indices[] = {
0,1,2,
3,0,2
};
unsigned numOfIndices = sizeof(indices) / sizeof(GLint);
void updateInput(GLFWwindow* window) {
if(glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, GL_TRUE);
}
int main() {
//INIT GLFW
glfwInit();
Wrapper mainWrapper = Wrapper();
Shader *mainShader = new Shader(vs_source.c_str(), fs_source.c_str());
GLuint VAO;
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
GLuint VBO;
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
GLuint EBO;
glGenBuffers(1, &EBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
glVertexAttribPointer(0,4, GL_FLOAT, GL_FALSE, sizeof(Vertex),(GLvoid*)offsetof(Vertex, position));
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)offsetof(Vertex, color));
glEnableVertexAttribArray(1);
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)offsetof(Vertex, texcoord));
glEnableVertexAttribArray(2);
glBindVertexArray(0);
//TEXTURE INIT
int image_width = 0;
int image_height = 0;
unsigned char* image = SOIL_load_image("images/super-meat-boy.png", &image_width, &image_height, NULL, SOIL_LOAD_RGBA);
GLuint texture0;
glGenTextures(1, &texture0);
glBindTexture(GL_TEXTURE_2D, texture0);
glGenerateMipmap(GL_TEXTURE_2D);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
if (image) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image_width, image_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image);
glGenerateMipmap(GL_TEXTURE_2D);
std::cout << "IMAGE LOADED";
}
else {
std::cout << "Error loading image";
}
//Clean up, needed when stuff done with texture
glActiveTexture(0);
glBindTexture(GL_TEXTURE_2D, 0);
SOIL_free_image_data(image);
//MAIN LOOP
while (!glfwWindowShouldClose(mainWrapper.getWindow())) {
//UPDATE INPUT
updateInput(mainWrapper.getWindow());
glfwPollEvents();
//UPDATE
//DRAW------
//clear
glClearColor(0.f,0.f,0.f,1.f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
//draw
glUseProgram(mainShader->myProgram);
//activate texture
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture0);
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, numOfIndices, GL_UNSIGNED_INT, 0);
//END DRAW
glfwSwapBuffers(mainWrapper.getWindow());
glFlush();
}
//END OF PROGRAM
glfwDestroyWindow(mainWrapper.getWindow());
delete mainShader;
glfwTerminate();
return 0;
}
#version 440
layout (location = 0) in vec3 vertex_position;
layout (location = 1) in vec3 vertex_color;
layout (location = 2) in vec3 vertex_texcoord;
out vec3 vs_position;
out vec3 vs_color;
out vec2 vs_texcoord;
void main() {
vs_position = vertex_position;
vs_color = vertex_color;
vs_texcoord = vec2(vertex_texcoord.x, vertex_texcoord.y * -1.f);
gl_Position = vec4(vertex_position, 1.f);
}
#version 440
in vec3 vs_position;
in vec3 vs_color;
in vec2 vs_texcoord;
out vec4 fs_color;
uniform sampler2D texture0;
void main() {
fs_color = vec4(vs_color, 1.f);
fs_color = texture(texture0, vs_texcoord);
}
The 2nd parameter of glVertexAttribPointer has to be the tuple size (number) of components) of the attribute in the vertex buffer.
Your vertex coordinates have 3 components x, y, z. The color attributes have 3 components too (red, green, blue). The texture coordinate have 2 components u and v:
glVertexAttribPointer(0,
3, // 3 instead of 4
GL_FLOAT, GL_FALSE, sizeof(Vertex),(GLvoid*)offsetof(Vertex, position));
glEnableVertexAttribArray(0);
glVertexAttribPointer(1,
3, // 3 instead of 4
GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)offsetof(Vertex, color));
glEnableVertexAttribArray(1);
glVertexAttribPointer(2,
3, // 2 instead of 3
GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)offsetof(Vertex, texcoord));
glEnableVertexAttribArray(2);
The parameter of 3 for the tuple size of the texture coordinates causes that you access the vertex buffer out of bounds for the vertex coordinate with index 3.
It was incorrect sizing in my glVertexAttribPointer. I have since changed my vertices to vec2, vec3, vec2, and changed the sizing accordingly.
I am attempting to draw a png file using OpenGL and seem to have a problem with how I am setting up the textures.
Main.cpp:
float positions[] = {
-1.0f, -1.0f,
0.0f, 1.0f,
1.0f, 1.0f,
};
float texCoords[] = {
-1.0f, -1.0f,
-1.0f, 1.0f,
1.0f, 1.0f,
};
unsigned int buffer;
glGenBuffers(1, &buffer);
glBindBuffer(GL_ARRAY_BUFFER, buffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(positions), positions, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void*)0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
unsigned int texBuffer;
glGenBuffers(1, &texBuffer);
glBindBuffer(GL_ARRAY_BUFFER, texBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(texCoords), texCoords, GL_STATIC_DRAW);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void*)0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
int w;
int h;
int comp;
unsigned char* image = stbi_load("res/images/background_level.png", &w, &h, &comp, STBI_rgb_alpha);
if (image == nullptr)
throw(std::string("Failed to load texture"));
//std::cout << image << std::endl;
unsigned int m_texture;
glGenTextures(1, &m_texture);
glBindTexture(GL_TEXTURE_2D, m_texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
if (comp == 3)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
else if (comp == 4)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, image);
glBindTexture(GL_TEXTURE_2D, 0);
stbi_image_free(image);
/* Loop until the user closes the window */
while (!glfwWindowShouldClose(window))
{
/* Render here */
glClearColor(1.0, 1.0, 1.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
// draw our first triangle
glUseProgram(programID);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, 0);
int uniformLoc = glGetUniformLocation(programID, "tex");
glUniform1i(uniformLoc, 0);
glDrawArrays(GL_TRIANGLES, 0, 3);
/* Swap front and back buffers */
glfwSwapBuffers(window);
/* Poll for and process events */
glfwPollEvents();
}
vertex.shader:
#shader VERTEX
#version 330 core
layout (location=0) in vec2 vert;
layout (location=1) in vec2 vertTexCoord;
out vec2 fragTexCoord;
void main() {
// Pass the tex coord straight through to the fragment shader
fragTexCoord = vertTexCoord;
gl_Position = vec4(vert, 0, 1);
}
fragment.shader:
#shader FRAGMENT
#version 330 core
uniform sampler2D tex; //this is the texture
in vec2 fragTexCoord; //this is the texture coord
out vec4 finalColor; //this is the output color of the pixel
void main() {
//finalColor = vec4(1.0, 1.0, 1.0, 1.0);
finalColor = texture(tex, fragTexCoord);
}
If I swap the line commented out in the fragment shader, I get an all white triangle rendered. However with the line attempting to use the texture, the triangle comes out all black. I am just beginning with OpenGL so I don't necessarily have all of the beginning concepts down pat. Textures may be ahead of my skill level but I figured it couldn't hurt to try and ask here as someone may be able to show me where I have things confused.
You are calling glBindTexture(GL_TEXTURE_2D, 0) instead of glBindTexture(GL_TEXTURE_2D, m_texture) inside the renderloop. With this command, a texture is bound to the currently active texture unit.