Related
I have a problem about activating a single texture to multiple buffer objects. I am trying to draw two different graphical objects that using the same texture. However could not make it bind.
After searchs so far, I could find an answer that fits to my question in this (Re-using one texture for multiple OpenGL Quads) link. I tried this method in my code by using the glBindTexture() function before the main opengl render loop, and also inside of the render loop but it did not bind. Also it is said that, bind the texture before the draw call, in here (OpenGl all models are with the same texture). But this way also did not work for me.
How can I use the exact same texture which is named texture1 for second buffer object?
I use two buffer objects and two vertex array objects for different graphical objects. What I really want to accomplish to describe only one texture after the first buffer array and then use the exact same texture for the second object. But when I try to bind the texture after vertex array object it does not work.
By the way I do not load any images for the texture. The main purpose that I use texture in order to utilize the texture coordinates. I do necessary works in the fragment shader.
#shader vertex
#version 330 core
layout (location=0) in vec3 position;
layout (location=1) in vec2 TextureCoords;
out vec2 TexCoord;
void main()
{
TexCoord = vec2(TextureCoords.x, TextureCoords.y);
gl_Position = vec4(position, 1.0);
};
#shader fragment
#version 330 core
out vec4 fragColor;
in vec2 TexCoord;
void main()
{
vec2 xy = TexCoord.xy;
vec4 color = vec4(vec3(xy.x), 1.0);
fragColor = color;
};
float vertexArray1[] = {
// Vertex Position | Texture Coords
// x y z x y
0.9f, 0.9f, 0.0f, 1.0f, 1.0f,
0.9f, 0.2f, 0.0f, 1.0f, 0.0f,
0.2f, 0.2f, 0.0f, 0.0f, 0.0f,
0.2f, 0.9f, 0.0f, 0.0f, 1.0f,
0.9f, 0.9f, 0.0f, 1.0f, 1.0f,
0.2f, 0.2f, 0.0f, 0.0f, 0.0f,
};
float vertexArray2[] = {
-0.2f, 0.9f, 0.0f, 1.0f, 1.0f,
-0.2f, 0.2f, 0.0f, 1.0f, 0.0f,
-0.9f, 0.2f, 0.0f, 0.0f, 0.0f,
-0.9f, 0.9f, 0.0f, 0.0f, 1.0f,
-0.2f, 0.9f, 0.0f, 1.0f, 1.0f,
-0.9f, 0.2f, 0.0f, 0.0f, 0.0f,
};
unsigned int buffer;
unsigned int VAO;
glGenBuffers(1, &buffer);
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, buffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertexArray1), vertexArray1, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 5, 0);
glEnableVertexAttribArray(0);
unsigned int texture1;
glGenTextures(1, &texture1);
glBindTexture(GL_TEXTURE_2D, texture1);
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_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
unsigned int buffer2;
unsigned int VAO2;
glGenBuffers(1, &buffer2);
glGenVertexArrays(1, &VAO2);
glBindVertexArray(VAO2);
glBindBuffer(GL_ARRAY_BUFFER, buffer2);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertexArray2), vertexArray2, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 5, 0);
glEnableVertexAttribArray(0);
// I want to use the previously described texture1 in here, but it does not bind.
glBindTexture(GL_TEXTURE_2D, texture1);
while(!glfwWindowShouldClose(window)){
processInput(window);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(shader);
glBindVertexArray(VAO);
glDrawArrays(GL_LINES, 0, 6*3*1);
glBindVertexArray(VAO2);
glDrawArrays(GL_LINES, 0, 6*3*1);
glfwSwapBuffers(window);
glfwPollEvents();
}
Note: When I create another different texture for the buffer2 object it works like a charm. I mean texture1 for buffer1, texture2 for buffer2... But I want to keep the number of the texture as possible as minimum. Maybe in future developments, I will have to create more than 50 buffer object, thus I do not want to create different textures for every single buffer object.
EDIT: I have changed my misunderstanding of "binding a texture to a vertex array" to "enabling attributes of a texture for other objects". I've also included my shader scripts for debugging purposes.
Textures are not bound to buffer objects. When you draw something, it uses the currently bound texture, not one from a buffer.
Textures are not bound to VAOs either so there's no need to worry about that.
To use a texture, you do need to have a sampler in your shader, bind the texture unit (not the texture) to the sampler with glUniform1i, and call the texture function in the shader and do something with the return value.
Since you say your shader isn't doing anything with your texture anyway, I wonder how you can tell whether it's bound or not.
In fact, there's no reason to have a texture if you don't have a shader that does something with it! There is no magical texture-shader interaction. A texture is something you have to specifically query (by calling texture) or else it doesn't do anything by itself.
You don't need to have a texture to have texture coordinates.
Since you haven't shown your shaders, it's impossible to debug them for you.
After I review my code again and search once more, I came across a solution for my question.
And I guess where I was wrong was trying to bind a texture which was already bound to the global context, as #ColonelThirtyTwo pointed out in the comment.
As I understood, instead of trying to bind the same texture I should have called the glVertexAttribPointer() and enable with glEnableVertexAttribArray() for bounded texture.
Since in the answer of this question (glVertexAttribPointer clarification), it is said that:
"A Vertex Array Object or VAO is used to store the state of all the glVertexAttribPointer calls and the VBOs that were targeted when each
of the glVertexAttribPointer calls were made."
So I came up to this solution and it worked:
unsigned int buffer;
unsigned int VAO;
glGenBuffers(1, &buffer);
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, buffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertexArray1), vertexArray1, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 5, 0);
glEnableVertexAttribArray(0);
unsigned int texture1;
glGenTextures(1, &texture1);
glBindTexture(GL_TEXTURE_2D, texture1);
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_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
unsigned int buffer2;
unsigned int VAO2;
glGenBuffers(1, &buffer2);
glGenVertexArrays(1, &VAO2);
glBindVertexArray(VAO2);
glBindBuffer(GL_ARRAY_BUFFER, buffer2);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertexArray2), vertexArray2, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 5, 0);
glEnableVertexAttribArray(0);
// Inseted of the bind call,
// I called the glVertexAttribPointer() and glEnableVertexAttribArray() here.
// And if I am not wrong these calls use the currently bounded texture which is texture1.
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
while(!glfwWindowShouldClose(window)){
processInput(window);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(shader);
glBindVertexArray(VAO);
glDrawArrays(GL_LINES, 0, 6*3*1);
glBindVertexArray(VAO2);
glDrawArrays(GL_LINES, 0, 6*3*1);
glfwSwapBuffers(window);
glfwPollEvents();
}
I'm trying to implement a background-blur effect with OpenGL.
Here is my thought:
drawing all background element to colorFBO
draw colorFBO into pingpongFBO[0] and pingpongFBO1
use pingpongFBO[0] as texture, draw hori-blur rect to pingpongFBO1
use pingpongFBO1 as texture, draw vert-blur rect to pingpongFBO[0]
Here is the result with blur radius of 200:
Result
As you can see, the hori-blur effect disappeared almost.
The edge of blue rectangle is still sharp.
If I only draw hori-blur part, it looks correct, the edge is now blurry.
Hori Only
Here is my blur frag shader code
#version 330 core
#define pow2(x)(x*x)
#define PI 3.14159265
uniform sampler2D screenTexture;
uniform bool horizontal;
uniform float radius;
out vec4 FragColor;
float gaussian(float x){
float sigma2=2.*pow2(radius/3.);
return(1./(sqrt(PI*sigma2)))*exp(-pow2(x)/sigma2);
}
void main(){
vec2 resolution=vec2(600,600);
vec2 uv=vec2(gl_FragCoord.xy/resolution);
vec4 color=vec4(0.);
float weight=gaussian(0);
color+=texture2D(screenTexture,uv)*weight;
float accum=weight;
if(horizontal){
for(int i=1;i<radius+1;i++){
vec2 off=vec2(i,0)/resolution;
weight=gaussian(i);
color+=texture2D(screenTexture,uv+off)*weight;
color+=texture2D(screenTexture,uv-off)*weight;
accum+=weight*2;
}
}else{
for(int i=1;i<radius+1;i++){
vec2 off=vec2(0,i)/resolution;
weight=gaussian(i);
color+=texture2D(screenTexture,uv+off)*weight;
color+=texture2D(screenTexture,uv-off)*weight;
accum+=weight*2;
}
}
FragColor=vec4((color/accum).xyz,1.);
}
Here is main CPP :
#include "Common.hh"
const unsigned int SCR_WIDTH = 600;
const unsigned int SCR_HEIGHT = 600;
float left = 150;
float top = 200;
float radius = 200;
void processInput(GLFWwindow *window);
int main()
{
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
GLFWwindow *window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
glfwMakeContextCurrent(window);
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
float *quad1Vertices = rectWithSize(SCR_WIDTH, 200);
float *quad2Vertices = rectWithSize(200, SCR_WIDTH);
float *blurQuadVertices = rectWithSize(200.0, 200.0);
float backgroundVertices[] = {
-1.0f, 1.0f, 0.0f, 1.0f,
-1.0f, -1.0f, 0.0f, 0.0f,
1.0f, -1.0f, 1.0f, 0.0f,
-1.0f, 1.0f, 0.0f, 1.0f,
1.0f, -1.0f, 1.0f, 0.0f,
1.0f, 1.0f, 1.0f, 1.0f};
unsigned int quad1VAO, quad1VBO;
glGenVertexArrays(1, &quad1VAO);
glGenBuffers(1, &quad1VBO);
glBindVertexArray(quad1VAO);
glBindBuffer(GL_ARRAY_BUFFER, quad1VBO);
glBufferData(GL_ARRAY_BUFFER, RECT_SIZE, quad1Vertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void *)0);
unsigned int quad2VAO, quad2VBO;
glGenVertexArrays(1, &quad2VAO);
glGenBuffers(1, &quad2VBO);
glBindVertexArray(quad2VAO);
glBindBuffer(GL_ARRAY_BUFFER, quad2VBO);
glBufferData(GL_ARRAY_BUFFER, RECT_SIZE, quad2Vertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void *)0);
unsigned int quad3VAO, quad3VBO;
glGenVertexArrays(1, &quad3VAO);
glGenBuffers(1, &quad3VBO);
glBindVertexArray(quad3VAO);
glBindBuffer(GL_ARRAY_BUFFER, quad3VBO);
glBufferData(GL_ARRAY_BUFFER, RECT_SIZE, blurQuadVertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void *)0);
unsigned int backgroundVAO, backgroundVBO;
glGenVertexArrays(1, &backgroundVAO);
glGenBuffers(1, &backgroundVBO);
glBindVertexArray(backgroundVAO);
glBindBuffer(GL_ARRAY_BUFFER, backgroundVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(backgroundVertices), &backgroundVertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void *)0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void *)(2 * sizeof(float)));
Shader shader("simple_rect.vs", "simple_rect.fs");
Shader screenShader("screen.vs", "screen.fs");
Shader blurShader("blur_rect.vs", "blur_rect.fs");
glm::mat4 projection = glm::ortho(0.0f, (float)SCR_WIDTH, 0.0f, (float)SCR_HEIGHT, -1.0f, 1.0f);
glm::mat4 model = glm::mat4(1.0);
shader.use();
shader.setMat4("projection", projection);
blurShader.use();
blurShader.setMat4("projection", projection);
blurShader.setInt("screenTexture", 0);
screenShader.use();
screenShader.setMat4("projection", glm::mat4(1.0));
screenShader.setMat4("model", glm::mat4(1.0));
screenShader.setInt("screenTexture", 0);
GLuint colorFBO;
GLuint colorBuffer;
glGenFramebuffers(1, &colorFBO);
glGenTextures(1, &colorBuffer);
glBindFramebuffer(GL_FRAMEBUFFER, colorFBO);
glBindTexture(GL_TEXTURE_2D, colorBuffer);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, SCR_WIDTH, SCR_HEIGHT, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
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);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorBuffer, 0);
GLuint pingPongFBO[2];
GLuint pingPongColorBuffer[2];
glGenFramebuffers(2, pingPongFBO);
glGenTextures(2, pingPongColorBuffer);
for (GLuint i = 0; i < 2; i++)
{
glBindFramebuffer(GL_FRAMEBUFFER, pingPongFBO[i]);
glBindTexture(GL_TEXTURE_2D, pingPongColorBuffer[i]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, SCR_WIDTH, SCR_HEIGHT, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
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);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, pingPongColorBuffer[i], 0);
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
while (!glfwWindowShouldClose(window))
{
processInput(window);
glBindFramebuffer(GL_FRAMEBUFFER, colorFBO);
glClearColor(229.0 / 255.0, 229.0 / 255.0, 229.0 / 255.0, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
shader.use();
shader.setMat4("model", glm::translate(model, glm::vec3(0.0f, 100.0f, 0.0f)));
shader.setVec4("uColor", glm::vec4(0.3451, 0.7333, 0.2, 1.0));
glBindVertexArray(quad1VAO);
glDrawArrays(GL_TRIANGLES, 0, 6);
shader.setVec4("uColor", glm::vec4(0, 178.0 / 255.0, 1, 1.0));
shader.setMat4("model", glm::translate(model, glm::vec3(50.0f, 0.0f, 0.0f)));
glBindVertexArray(quad2VAO);
glDrawArrays(GL_TRIANGLES, 0, 6);
glBindFramebuffer(GL_FRAMEBUFFER, pingPongFBO[0]);
glBindTexture(GL_TEXTURE_2D, colorBuffer);
glClearColor(0.0f, 1.0f, 0.1f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
screenShader.use();
glBindVertexArray(backgroundVAO);
glDrawArrays(GL_TRIANGLES, 0, 6);
glBindFramebuffer(GL_FRAMEBUFFER, pingPongFBO[1]);
glBindTexture(GL_TEXTURE_2D, colorBuffer);
glClearColor(0.0f, 1.0f, 0.1f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
screenShader.use();
glBindVertexArray(backgroundVAO);
glDrawArrays(GL_TRIANGLES, 0, 6);
glBindFramebuffer(GL_FRAMEBUFFER, pingPongFBO[1]);
glBindTexture(GL_TEXTURE_2D, pingPongColorBuffer[0]);
blurShader.use();
blurShader.setMat4("model", glm::translate(model, glm::vec3(left, top, 0)));
blurShader.setInt("screenTexture", 0);
blurShader.setBool("horizontal", true);
blurShader.setFloat("radius", radius);
glBindVertexArray(quad3VAO);
glDrawArrays(GL_TRIANGLES, 0, 6);
glBindFramebuffer(GL_FRAMEBUFFER, pingPongFBO[0]);
glBindTexture(GL_TEXTURE_2D, pingPongColorBuffer[1]);
blurShader.setBool("horizontal", false);
glBindVertexArray(quad3VAO);
glDrawArrays(GL_TRIANGLES, 0, 6);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glClearColor(0.0f, 1.0f, 0.1f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
screenShader.use();
glBindVertexArray(backgroundVAO);
glBindTexture(GL_TEXTURE_2D, pingPongColorBuffer[0]);
glDrawArrays(GL_TRIANGLES, 0, 6);
glfwSwapBuffers(window);
glfwPollEvents();
}
glDeleteVertexArrays(1, &quad1VAO);
glDeleteBuffers(1, &quad1VBO);
glDeleteVertexArrays(1, &quad2VAO);
glDeleteBuffers(1, &quad2VBO);
glDeleteVertexArrays(1, &quad3VAO);
glDeleteBuffers(1, &quad3VBO);
glDeleteVertexArrays(1, &backgroundVAO);
glDeleteBuffers(1, &backgroundVBO);
glfwTerminate();
return 0;
}
The full source code is here
The vertical edge is blurry. But the effect is reduced and covered by the vertical blur in the 2nd pass. Note, the vertical blur reinforced the vertical edge, because it blurs along this edge.
After the 1st pass (horizontal blur), the main color on the left side is still blue and the main color on the right side is still green and white. The vertical blur, mix the colors along the columns of the image. That causes that the transition along the columns between the left (blue) and right (green/white) becomes a noticeable edge.
If you change the order of the passed (1st vertical blur, 2nd horizontal blur), then the horizontal edge becomes visible again:
In general the algorithm works. Compare the result, when a diagonal blur is used:
void main(){
// [...]
if(horizontal){
for(int i=1;i<radius+1;i++){
vec2 off=vec2(i,i)/resolution;
// [...]
}
}else{
for(int i=1;i<radius+1;i++){
vec2 off=vec2(-i,i)/resolution;
// [...]
}
}
// [...]
}
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);
}
I've begun switching my rendering code to support shaders, and that all works fine when rendering to the back buffer. So now I'm working towards rendering to FBOs, but all I get are white textures for both the color and normals.
Here is my FBO creation code:
void RenderTarget_GL::CreateFBO (void)
{
// if the machine supports the GL FBO extension
if (s_supportfbo)
{
// Create FBO
glGenFramebuffersEXT(1, &m_fbo);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_fbo);
// Create default texture buffer
char *buffer = new char [static_cast<int>(g_window->GetWidth() * m_screenWidth) * static_cast<int>(g_window->GetHeight() * m_screenHeight) * 4];
std::memset(buffer, 0, static_cast<int>(g_window->GetWidth() * m_screenWidth) * static_cast<int>(g_window->GetHeight() * m_screenHeight) * 4);
// Create Render Texture
glGenTextures(1, &m_rendertexture);
glBindTexture(GL_TEXTURE_2D, m_rendertexture);
glTexImage2D(GL_TEXTURE_2D, 0, 4, static_cast<int>(g_window->GetWidth() * m_screenWidth), static_cast<int>(g_window->GetHeight() * m_screenHeight), 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
// Bind Render Texture to FBO
glBindTexture(GL_TEXTURE_2D, 0);
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, m_rendertexture, 0);
// Create Normal Texture if this FBO will be rendering normals
if (m_hasnormal)
{
glGenTextures(1, &m_normaltexture);
glTexImage2D(GL_TEXTURE_2D, 0, 4, static_cast<int>(g_window->GetWidth() * m_screenWidth), static_cast<int>(g_window->GetHeight() * m_screenHeight), 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
// Bind Normal Texture to FBO
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT1_EXT, GL_TEXTURE_2D, m_normaltexture, 0);
}
// UnBind FBO and cleanup default buffer
delete [] buffer;
Clear();
}
}
And the code I use to set the current render target:
void RenderTarget_GL::Set (void)
{
if (s_supportfbo && g_glgraphics->GetShaderEnabled())
{
static const GLenum buffer1[] = {GL_COLOR_ATTACHMENT0_EXT};
static const GLenum buffer2[] = {GL_COLOR_ATTACHMENT0_EXT, GL_COLOR_ATTACHMENT1_EXT};
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_fbo);
if (m_hasnormal)
glDrawBuffers(2, buffer2);
else
glDrawBuffers(1, buffer1);
}
}
And finally, my actual drawing code:
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
// Setup the camera transformation
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
if (m_camera)
m_camera->GLMatrix();
else
m_defaultCam.GLMatrix();
// Setup Render Target
if (m_shaderenabled)
{
glPushAttrib(GL_VIEWPORT_BIT);
glViewport(0,0,g_window->GetWidth(),g_window->GetHeight());
m_initialpass->Set();
}
// Draw All Objects with their per-object shaders
// Clear render target and shader bindings
if (m_shaderenabled)
{
glPopAttrib();
RenderTarget_GL::Clear();
Shader_GL::ClearShaderBinding();
}
glPopMatrix();
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
// Draw Scene
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, m_initialpass->GetColorTexture());
glBegin(GL_QUADS);
glTexCoord2f(0.0f, 0.0f);
glVertex3f(-1.0f, -1.0f, 0.0f);
glTexCoord2f(1.0f, 0.0f);
glVertex3f(1.0f, -1.0f, 0.0f);
glTexCoord2f(1.0f, 1.0f);
glVertex3f(1.0f, 1.0f, 0.0f);
glTexCoord2f(0.0f, 1.0f);
glVertex3f(-1.0f, 1.0f, 0.0f);
glEnd();
Texture_GL::ClearTextureBinding();
glPopMatrix();
// Swap Buffers
GL_TEXTURE_MIN_FILTER is GL_NEAREST_MIPMAP_LINEAR by default. Supply mipmaps or switch to GL_LINEAR or GL_NEAREST.
The OpenGL Wiki has more.
I'll begin by apologizing for the length of the question. I believe I've committed some small, dumb error, but since I'm entirely unable to find it, I decided to post all relevant code just in case.
I finally got texture loading working using QImage, and am able to render textures in immediate mode.
However, vertex arrays don't work, and I'm at a loss as to why.
The most obvious things like "Have you enabled vertex arrays and texture coordinate arrays?" are probably not the answer. I'll post the initialization code.
Here's the init function:
/* general OpenGL initialization function */
int initGL()
{
glShadeModel(GL_SMOOTH); // Enable Smooth Shading
glClearColor(0, 0, 0, 1); // Black Background
glEnable ( GL_COLOR_MATERIAL );
glColorMaterial ( GL_FRONT, GL_AMBIENT_AND_DIFFUSE );
glDisable(GL_DEPTH_TEST);
//ENABLED VERTEX ARRAYS AND TEXTURE COORDINATE ARRAYS
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnableClientState(GL_VERTEX_ARRAY);
//ENABLED 2D TEXTURING
glEnable ( GL_TEXTURE_2D );
glPixelStorei ( GL_UNPACK_ALIGNMENT, 1 );
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
//seed random
srand(time(NULL));
return( TRUE );
}
I have initialization, resize and draw functions that are called by a QGLWidget (which is itself just a skeleton that calls the real work functions)
The texture loading function:
GLuint LoadGLTextures( const char * name )
{
//unformatted QImage
QImage img;
//load the image from a .qrc resource
if(!img.load(":/star.bmp"))
{
qWarning("ERROR LOADING IMAGE");
}
//an OpenGL formatted QImage
QImage GL_formatted_image;
GL_formatted_image = QGLWidget::convertToGLFormat(img);
//error check
if(GL_formatted_image.isNull())
qWarning("IMAGE IS NULL");
else
qWarning("IMAGE NOT NULL");
//texture ID
GLuint _textures[1];
//enable texturing
glEnable(GL_TEXTURE_2D);
//generate textures
glGenTextures(1,&_textures[0]);
//bind the texture
glBindTexture(GL_TEXTURE_2D,_textures[0]);
//texture parameters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glBindTexture(GL_TEXTURE_2D,_textures[0]);
//generate texture
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, GL_formatted_image.width(),
GL_formatted_image.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE,
GL_formatted_image.bits());
glBindTexture(GL_TEXTURE_2D,_textures[0]);
//return the texture ID
return _textures[0];
}
Here's the draw code:
//this does draw
//get the texture ID
GLuint tex_id = LoadGLTextures(":/star.png");
glBindTexture(GL_TEXTURE_2D, tex_id); // Actually have an array of images
glColor3f(1.0f, 0.0f, 0.5f);
glBegin(GL_QUADS);
glTexCoord2f(1.0f, 0.0f);glVertex2f(1.0f, 0.0f);
glTexCoord2f(1.0f, 1.0f);glVertex2f(1.0f, 1.0f);
glTexCoord2f(0.0f, 1.0f);glVertex2f(0.0f, 1.0f);
glTexCoord2f(0.0f, 0.0f);glVertex2f(0.0f, 0.0f);
glEnd();
//this does not draw
//translations code
glLoadIdentity();
glTranslatef(-1.0f, 0.0f, 0.0f);
//bind the texture
glBindTexture(GL_TEXTURE_2D, tex_id);
//set color state
glColor4f(0.0f, 1.0f, 0.0f, 0.5);
//vertices to be rendered
static GLfloat vertices[] =
{
1.0f, 0.0f,
1.0f, 1.0f,
0.0f, 1.0f,
0.0f, 0.0f
};
static GLshort coord_Data[] =
{
1, 0,
1, 1,
0, 1,
0, 0
};
//bind the texture
glBindTexture(GL_TEXTURE_2D, tex_id);
//pointer to the vertex array
glVertexPointer(2, GL_FLOAT, 0, vertices);
//texture coordinate pointer
glTexCoordPointer(2, GL_SHORT, 0, coord_Data);
//draw the arrays
glDrawArrays(GL_QUADS, 0, 4);
Thanks for all help,
Dragonwrenn
One possibility is that the problem stems from calling glVertexCoordPointer before calling glTexCoordPointer. Weird things happen when you specify the texture coordinate after the vertex coordinate. I know this is true for drawing a single vertex with a texture coordinate. I'm not sure if it's true with arrays.
A few other things...
Have you tried using QPixMap instead of QImage? I doubt this is the answer to your problem since it sounds like the texture is applied to the first quad properly.
There are two calls to bindTexture.
Have you tried just drawing the vertices (without the texture) in the second part of the code?
Finally, do you get any compiler warnings?
The way you place your OpenGL state manipulations, it is difficult to keep track of things. It's a good idea to set OpenGL state on demand. So
Move this
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_CORRD_ARRAY);
right before
//bind the texture
glBindTexture(GL_TEXTURE_2D, tex_id);
glVertexPointer(2, GL_FLOAT, 0, vertices);
glTexCoordPointer(2, GL_SHORT, 0, coord_Data);
//draw the arrays
glDrawArrays(GL_QUADS, 0, 4);
also you should move the other code from initGL.
Belonging into the texture loader, before supplying the data to glTexImage:
glPixelStorei ( GL_UNPACK_ALIGNMENT, 1 );
Belonging to the beginning of the drawing function:
glShadeModel(GL_SMOOTH);
glClearColor(0, 0, 0, 1);
glEnable( GL_COLOR_MATERIAL );
glColorMaterial ( GL_FRONT, GL_AMBIENT_AND_DIFFUSE );
glDisable(GL_DEPTH_TEST);
Following the scheme you should set viewport and projection matrices in the drawing function, too. I'm just telling this, because most of the tutorials do it differently, which tricks people into thinking this was the right way. Technically projection and viewport and on-demand-states as well.
You should not re-load the texture with every draw call. Note that initializing the texture on demand through the drawing handler is a good idea, you should just add some flag to the texture encapsulating class telling, if the referenced texture is already available to OpenGL.
Just for debugging purposes try changing the type of the texture coordinates to floats.