Do offscreen render(openGL) with Qt5 - c++

Using openGL to do some image processing, the first experiment is
convert the color image to gray, everything are fine except I don’t want
to show the widget.
If I don’t call “show()” the QGLWidget would not begin to render the texture
Could I render the texture without showing the widget?
Is QGLWidget a right tool to do that?
part of the codes
#include <QDebug>
#include "toGray.hpp"
toGray::toGray(std::string const &vertex_file, std::string const &fragment_file, QWidget *parent)
:basicGLWidget(vertex_file, fragment_file, parent) //read shaders, compile them, allocate VBO
{
}
void toGray::initializeVertexBuffer()
{
std::vector<GLfloat> const vertex{
-1.0f, 1.0f, 0.0f, 1.0f,
1.0f, 1.0f, 0.0f, 1.0f,
-1.0f, -1.0f, 0.0f, 1.0f,
1.0f, 1.0f, 0.0f, 1.0f,
1.0f, -1.0f, 0.0f, 1.0f,
-1.0f, -1.0f, 0.0f, 1.0f,
};
initializeVertexBufferImpl(vertex); //copy the data into QOpenGLBuffer
QImage img(":/simpleGPGPU/images/emili.jpg");
texture_addr_ = bindTexture(img);
resize(img.width(), img.height());
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);
}
void toGray::paintGL()
{
qglClearColor(Qt::white);
glClear(GL_COLOR_BUFFER_BIT);
program_.bind();
bind_buffer();
program_.enableAttributeArray("qt_Vertex");
program_.setAttributeBuffer( "qt_Vertex", GL_FLOAT, 0, 4);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture_addr_);
glDrawArrays(GL_TRIANGLES, 0, get_buffer(0).size());
program_.disableAttributeArray("qt_Vertex");
program_.release();
glActiveTexture(0);
release_buffer();
}
vertex shader
attribute highp vec4 qt_Vertex;
varying highp vec2 qt_TexCoord0;
void main(void)
{
gl_Position = qt_Vertex;
qt_TexCoord0 = (qt_Vertex.xy + vec2(1.0)) * 0.5;
}
fragment shader
uniform sampler2D source;
varying highp vec2 qt_TexCoord0;
vec3 toGray(vec3 rgb)
{
return vec3(rgb.r * 0.299 + rgb.g * 0.587 + rgb.b * 0.114);
}
void main(void)
{
vec3 gray = toGray(texture2D(source, qt_TexCoord0).rgb);
gl_FragColor = vec4(gray, 0.0);
}
Edit: use QWindow FBO to do offscreen render, but the result is a blank image
the codes can compile, but the QImage return by the QOpenGLFrameBufferObject is empty.
.hpp
#ifndef OFFSCREENEXP_HPP
#define OFFSCREENEXP_HPP
#include <QOpenGLFunctions>
#include <QWindow>
class QImage;
class QOpenGLContext;
class offScreenExp : public QWindow, protected QOpenGLFunctions
{
public:
explicit offScreenExp(QWindow *parent = 0);
QImage render();
private:
QOpenGLContext *context_;
};
#endif // OFFSCREENEXP_HPP
.cpp
#include <QImage>
#include <QOpenGLBuffer>
#include <QOpenGLContext>
#include <QOpenGLFramebufferObject>
#include <QOpenGLShaderProgram>
#include <QString>
#include <QWidget>
#include <QDebug>
#include "offScreenExp.hpp"
offScreenExp::offScreenExp(QWindow *parent) :
QWindow(parent),
context_(nullptr)
{
setSurfaceType(QWindow::OpenGLSurface);
setFormat(QSurfaceFormat());
create();
}
QImage offScreenExp::render()
{
//create the context
if (!context_) {
context_ = new QOpenGLContext(this);
QSurfaceFormat format;
context_->setFormat(format);
if (!context_->create())
qFatal("Cannot create the requested OpenGL context!");
}
context_->makeCurrent(this);
initializeOpenGLFunctions();
//load image and create fbo
QString const prefix("/Users/Qt/program/experiment_apps_and_libs/openGLTest/simpleGPGPU/");
QImage img(prefix + "images/emili.jpg");
if(img.isNull()){
qFatal("image is null");
}
QOpenGLFramebufferObject fbo(img.size());
qDebug()<<"bind success? : "<<fbo.bind();
//if(glCheckFramebufferStatus(fbo.handle()) != GL_FRAMEBUFFER_COMPLETE){
// qDebug()<<"frame buffer error";
//}
qDebug()<<"has opengl fbo : "<<QOpenGLFramebufferObject::hasOpenGLFramebufferObjects();
//use two triangles two cover whole screen
std::vector<GLfloat> const vertex{
-1.0f, 1.0f, 0.0f, 1.0f,
1.0f, 1.0f, 0.0f, 1.0f,
-1.0f, -1.0f, 0.0f, 1.0f,
1.0f, 1.0f, 0.0f, 1.0f,
1.0f, -1.0f, 0.0f, 1.0f,
-1.0f, -1.0f, 0.0f, 1.0f,
};
//initialize vbo
QOpenGLBuffer buffer(QOpenGLBuffer::VertexBuffer);
buffer.create();
buffer.setUsagePattern(QOpenGLBuffer::StaticDraw);
buffer.bind();
buffer.allocate(&vertex[0], vertex.size() * sizeof(GLfloat) );
buffer.release();
//create texture
GLuint rendered_texture;
glGenTextures(1, &rendered_texture);
// "Bind" the newly created texture : all future texture functions will modify this texture
glBindTexture(GL_TEXTURE_2D, rendered_texture);
//naive solution, better encapsulate the format in a function
if(img.format() == QImage::Format_Indexed8){
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, img.width(), img.height(), 0, GL_RED, GL_UNSIGNED_BYTE, img.scanLine(0));
}else if(img.format() == QImage::Format_RGB888){
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, img.width(), img.height(), 0, GL_RGB, GL_UNSIGNED_BYTE, img.scanLine(0));
}else{
QImage temp = img.convertToFormat(QImage::Format_RGB888);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, img.width(), img.height(), 0, GL_RGB, GL_UNSIGNED_BYTE, temp.scanLine(0));
}
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);
glBindTexture(GL_TEXTURE_2D, 0);
//compile and link program
QOpenGLShaderProgram program;
if(!program.addShaderFromSourceCode(QOpenGLShader::Vertex,
"attribute highp vec4 qt_Vertex;"
"varying highp vec2 qt_TexCoord0;"
"void main(void)"
"{"
" gl_Position = qt_Vertex;"
" qt_TexCoord0 = (qt_Vertex.xy + vec2(1.0)) * 0.5;"
"}")){
qDebug()<<"QOpenGLShader::Vertex error : " + program.log();
}
// Compile fragment shader
if (!program.addShaderFromSourceCode(QOpenGLShader::Fragment,
"uniform sampler2D source;"
"varying highp vec2 qt_TexCoord0;"
"vec3 toGray(vec3 rgb)"
"{"
" return vec3(rgb.r * 0.299 + rgb.g * 0.587 + rgb.b * 0.114);"
"}"
"void main(void)"
"{"
"vec3 gray = toGray(texture2D(source, qt_TexCoord0).rgb);"
"gl_FragColor = vec4(gray, 0.0);"
"}"
)){
qDebug()<<"QOpenGLShader::Fragment error : " + program.log();
}
// Link shader pipeline
if (!program.link()){
qDebug()<<"link error : " + program.log();
}
//render the texture as usual
//render the texture as usual
glClearColor(0, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT);
glViewport(0, 0, img.width(), img.height());
program.bind();
buffer.bind();
program.enableAttributeArray("qt_Vertex");
program.setAttributeBuffer( "qt_Vertex", GL_FLOAT, 0, 4);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, rendered_texture);
//bind and create fbo
QOpenGLFramebufferObject fbo(img.size());
qDebug()<<"bind success? : "<<fbo.bind();
glDrawArrays(GL_TRIANGLES, 0, buffer.size());
QImage result = fbo.toImage();
program.disableAttributeArray("qt_Vertex");
program.release();
buffer.release();
fbo.release();
glActiveTexture(0);
glBindTexture(GL_TEXTURE_2D, 0);
context_->doneCurrent();
return result;
}
I try my best to simplify the codes, but it is still pretty verbose

For offscreen rendering, try rendering into a QOpenGLFramebufferObject, which can be converted into a QImage, which in turn can easily be saved to disk.
For that however, you still need a surface to render onto (as required by QOpenGLContext::makeCurrent()), so your only choice is indeed using a QWindow or a QGLWidget to get such a surface.
In Qt 5.1, there will be a new class called QOffscreenSurface, which will probably be most suitable for your usecase. You would use QOffscreenSurface to get a surface for your OpenGL context, and then render into a FBO using QOpenGLFramebufferObject, and then call QOpenGLFramebufferObject::toImage() to get access to the pixels.

Related

Why is my cubemap rendered to FBO only rendering one side of my cube?

I want to render my scene into a cubemap FBO but I can't seem to get it working properly. Only 1 side of my cube is showing the rendered cubemap as a test.
This is my FBO setup:
// create cubemap FBO
unsigned int cubemapFBO;
glGenFramebuffers(1, &cubemapFBO);
glBindFramebuffer(GL_FRAMEBUFFER, cubemapFBO);
// create color cubemap texture
unsigned int colorCubemap;
glGenTextures(1, &colorCubemap);
glBindTexture(GL_TEXTURE_CUBE_MAP, colorCubemap);
for (unsigned int i = 0; i < 6; ++i)
{
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGBA8, size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
}
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
// create depth cubemap
unsigned int depthCubemap;
glGenTextures(1, &depthCubemap);
glBindTexture(GL_TEXTURE_CUBE_MAP, depthCubemap);
for (unsigned int i = 0; i < 6; ++i)
{
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_DEPTH_COMPONENT24, size, size, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
}
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
// attach depth texture as FBO's depth buffer
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, colorCubemap, 0);
glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depthCubemap, 0);
//glDrawBuffer(GL_COLOR_ATTACHMENT0);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
std::cout << "ERROR on cubemapFBO: Framebuffer is not complete!" << std::endl;
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// -- End cubemap FBO -- //
This is my projection and view matrices to capture 6 cubemap faces
// set up projection and view matrices for capturing 6 cubemap faces
glm::mat4 captureProjection = glm::perspective(glm::radians(90.0f), 1.0f, 0.1f, 10.0f);
glm::mat4 captureViews[] = {
captureProjection * glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3( 1.0f, 0.0f, 0.0f), glm::vec3(0.0f, -1.0f, 0.0f)),
captureProjection * glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(-1.0f, 0.0f, 0.0f), glm::vec3(0.0f, -1.0f, 0.0f)),
captureProjection * glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3( 0.0f, -1.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f)),
captureProjection * glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3( 0.0f, 1.0f, 0.0f), glm::vec3(0.0f, 0.0f, -1.0f)),
captureProjection * glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3( 0.0f, 0.0f, -1.0f), glm::vec3(0.0f, -1.0f, 0.0f)),
captureProjection * glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3( 0.0f, 0.0f, 1.0f), glm::vec3(0.0f, -1.0f, 0.0f))
};
// -- End projection and view matrices setup -- //
This is my scene rendering to cubemap FBO
// render scene to cubemap FBO
glViewport(0, 0, size, size);
glBindFramebuffer(GL_FRAMEBUFFER, cubemapFBO);
glClear(GL_DEPTH_BUFFER_BIT);
simpleDepthShader.Use();
for (unsigned int i = 0; i < 6; ++i)
{
simpleDepthShader.setMat4("shadowMatrices[" + std::to_string(i) + "]", captureViews[i]);
}
// skybox test
cubemap.Use();
glm::mat4 cubemapModel = glm::translate(glm::mat4(1.0f), positions);
//glm::mat4 cubemapRotate = glm::rotate(glm::mat4(1.0f), glm::radians(-(float)glfwGetTime() * 0.2f), glm::vec3(0.0f, 1.0f, 0.0));
//glm::mat4 cubemapRotate = glm::rotate(glm::mat4(1.0f), glm::radians(-(float)glfwGetTime() * 0.2f), glm::vec3(0.0f, 1.0f, 0.0));
//cubemapModel = cubemapModel * cubemapRotate;
cubemap.setMat4("projection", projection);
cubemap.setMat4("view", skyboxView);
cubemap.setMat4("model", cubemapModel);
glDepthFunc(GL_LEQUAL);
glDepthMask(GL_TRUE);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_CUBE_MAP, secondCubemapTexture);
renderCubemap();
glDepthFunc(GL_LESS);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// -- End render to cubemap FBO -- //
And this is my vertex + geometry + fragment shader
#shader vertex
#version 450 core
layout (location = 0) in vec3 aPos;
uniform mat4 model;
void main()
{
gl_Position = model * vec4(aPos, 1.0);
}
#shader fragment
#version 450 core
out vec4 FragColor;
in vec4 FragPos;
void main()
{
FragColor = FragPos;
}
#shader geometry
#version 450 core
layout (triangles) in;
layout (triangle_strip, max_vertices=18) out;
uniform mat4 shadowMatrices[6];
out vec4 FragPos; // FragPos from GS (output per emitvertex)
void main()
{
for(int face = 0; face < 6; ++face)
{
gl_Layer = face; // built-in variable that specifies to which face we render.
for(int i = 0; i < 3; ++i) // for each triangle's vertices
{
FragPos = gl_in[i].gl_Position;
gl_Position = shadowMatrices[face] * FragPos;
EmitVertex();
}
EndPrimitive();
}
}
And then testing out the cubemap onto a cube with the fbo cubemap texture
// test cube with FBO cubemap
cube.Use();
cube.setMat4("projection", projection);
cube.setMat4("view", view);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, silverTeapotTexture);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_CUBE_MAP, colorCubemap); // <-- here
glm::mat4 cubeModel = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, -0.5f, 0.0f));
cube.setMat4("model", cubeModel);
renderTexturedCube();

Texture isn't loading properly OpenGL

I am using MS visual studio 2019 and just learning opengl in c++. I started learning from https://learnopengl.com and it showed many ways to load textures . I just used stb image for loading textures and here's the function for loading a texture :
static unsigned int loadTexture(char const* path)
{
unsigned int textureID;
glGenTextures(1, &textureID);
int width, height, nrComponents;
unsigned char* data = stbi_load(path, &width, &height, &nrComponents, 0);
if (data)
{
GLenum format;
if (nrComponents == 1)
format = GL_RED;
else if (nrComponents == 3)
format = GL_RGB;
else if (nrComponents == 4)
format = GL_RGBA;
glBindTexture(GL_TEXTURE_2D, textureID);
glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
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_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
stbi_image_free(data);
}
else
{
std::cout << "Texture failed to load at path: " << path << std::endl;
stbi_image_free(data);
}
return textureID;
}
I tried loading it in a rectangle but somehow it doesn't seem to print properly on it. I'm stuck for a couple of hours and still couldn't find what mistake I've made.
here's the code for initializing the VAO and VBO:
inline void InitTextureBuffer(const char* img_filepath)
{
texture_shader.Bind();
tex_img_id = loadTexture(img_filepath);
glGenVertexArrays(1, &tex_VAO);
glGenBuffers(1, &tex_VBO);
glBindVertexArray(tex_VAO);
glBindBuffer(GL_ARRAY_BUFFER, tex_VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 6 * 4, NULL, GL_DYNAMIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), 0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(2 * sizeof(float)));
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
texture_shader.Unbind();
}
and function to draw the rect :
inline void Draw_Rectangle_IMG(_Point _min, _Point _max)
{
glActiveTexture(GL_TEXTURE0);
glBindVertexArray(tex_VAO);
texture_shader.Bind();
float vertices[] =
{
_min.x, _min.y, 0.0f, 0.0f ,
_max.x, _min.y, 0.0f, 1.0f ,
_max.x, _max.y, 1.0f, 1.0f ,
_min.x, _min.y, 0.0f, 0.0f ,
_max.x, _max.y, 1.0f, 1.0f ,
_min.x, _max.y, 1.0f, 0.0f ,
};
glBindTexture(GL_TEXTURE_2D, tex_img_id);
glBindBuffer(GL_ARRAY_BUFFER, tex_VBO);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glDrawArrays(GL_TRIANGLES, 0, 6);
glBindVertexArray(0);
glBindTexture(GL_TEXTURE_2D, 0);
texture_shader.Unbind();
}
the shaders I've used:
const std::string texture_shader_vs =
{
"#version 330 core\n"
"layout(location = 0) in vec2 aPos;\n"
"layout(location = 1) in vec2 aTexCoord;\n"
"\n"
"out vec2 TexCoord;\n"
"\n"
"void main()\n"
"{\n"
" gl_Position = vec4(aPos,0.0f, 1.0f);\n"
" TexCoord = aTexCoord;\n"
"}\n"
};
const std::string texture_shader_fs =
{
"#version 330 core\n"
"out vec4 FragColor;\n"
"\n"
"in vec2 TexCoord;\n"
"\n"
"// texture sampler\n"
"uniform sampler2D texture1;\n"
"\n"
"void main()\n"
"{\n"
" FragColor = texture(texture1, TexCoord);\n"
"}\n"
};
And the function where I did the calls:
_Point p1, p2;
p1 = _Point(-0.5f, -0.25f);
p2 = _Point(0.5f, 0.25f);
ogl.InitTextureBuffer("resources/textures/1.png");
while (!glfwWindowShouldClose(ogl.GetWindow()))
{
glClear(GL_COLOR_BUFFER_BIT);
ogl.Draw_Rectangle_IMG(p1, p2);
glfwSwapBuffers(ogl.GetWindow());
glfwPollEvents();
}
The texture i tried to print is :
but the result I get is
I can't seem to find the error .Any help would be appreciated. Thanks!
The association of the texture coordinates to the vertices is wrong. Change to:
float vertices[] =
{
_min.x, _min.y, 0.0f, 1.0f,
_max.x, _min.y, 1.0f, 1.0f,
_max.x, _max.y, 1.0f, 0.0f,
_min.x, _min.y, 0.0f, 1.0f,
_max.x, _max.y, 1.0f, 0.0f,
_min.x, _max.y, 0.0f, 0.0f,
};
By default OpenGL assumes that the start of each row of an image is aligned to 4 bytes.
This is because the GL_UNPACK_ALIGNMENT parameter by default is 4. When a RGB image with 3 color channels is loaded to a texture object and 3*width is not divisible by 4 this may cause a misalignment.
Change the alignment by setting the GL_UNPACK_ALIGNMENT to 1, before specifying the texture image with glTexImage2D:
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
When you remove glGenerateMipmap(GL_TEXTURE_2D);, then you have to change the minifying function (GL_TEXTURE_MIN_FILTER) as well. Since the filter is GL_LINEAR_MIPMAP_LINEAR, the texture would be "Mipmap Incomplete" if you do not change the minimize function to GL_NEAREST or GL_LINEAR.

Why is this texture not showing up?

I am following the tutorial from learnopengl.com to make a game engine.
I am using stb_image.h to load the textures, and my own header file to compile and link the shaders.
I got to the part in Getting Started/Textures where the container.jpg texture is supposed to fill up the rectangle on the window. But whenever I compile and run the code the texture does not appear anywhere in the window. This is my texture loading code:
// Load and create a texture
unsigned int texture1;
glGenTextures(1, &texture1);
glBindTexture(GL_TEXTURE_2D, texture1);
// Set the texture wrapping parameters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
// Set texture filtering parameters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// Load image, create texture, and generate minmaps
int width, height, nrChannels;
unsigned char* data = stbi_load("C:\\Users\\Lucas\\OneDrive\\Desktop\\C++ Projects\\Working Game Engine\\container.jpg", &width, &height, &nrChannels, 0);
if (data) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
}
else {
std::cout << "Failed to load texture(s)\n";
}
These are the quad's coords:
float vertices[] = {
// Positions // Colors // Texture Coords
0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f,
0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,
-0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
-0.5f, 0.5f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f
};
unsigned int indices[] = {
0, 1, 3,
1, 2, 3
};
Render code:
// Process inputs
processInput(window);
// Render
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// Bind Texture
glBindTexture(GL_TEXTURE_2D, texture1);
// Render Container
ourShader.use();
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
// Swap buffers and poll IO events (Key pressed/released, mouse moved etc.)
glfwSwapBuffers(window);
glfwPollEvents();
Vertex shader code:
#version 460 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;
layout (location = 2) in vec2 aTexCoord;
out vec3 ourColor;
out vec2 TexCoord;
void main()
{
gl_Position = vec4(aPos, 1.0);
ourColor = aColor;
TexCoord = vec2(aTexCoord.x, aTexCoord.y);
}
Fragment shader code:
#version 460 core
out vec4 FragColor;
in vec3 ourColor;
in vec2 TexCoord;
uniform sampler2D texture1;
void main()
{
FragColor = texture(texture1, TexCoord);
}
Note that no error codes are returned from the image loader or shader compilers.
The rectangle appears like this.
You need to update the texture1 uniform like this:
glUniform1i(glGetUniformLocation(program_id, "texture1"), 0);
So it turns out I didn't glEnableVertexAttribArray(); the texture coords. Quite embarrassing.

Switching more than one shader program (in OpenGL-ES 2.0)

I am really having nightmare to achieve what I required in OpenGles 2.0
Before posting the code reference, let me tell what I need.
I have 2D texture fragment shader. On top of the texture I want to draw red color line. I am able to draw the line but coloring to red is not working.
Shader declaration:
static const char s_v_shader[] =
"attribute vec4 vPosition; \n"
"attribute vec2 my_Texcoor; \n"
"uniform mat4 u_TransMatrix; \n"
"varying vec2 vTexcoor; \n"
"void main() \n"
"{ \n"
" vTexcoor = my_Texcoor; \n"
" gl_Position = u_TransMatrix*vPosition; \n"
"} \n";
static const char s_f_shader[] =
"precision mediump float;\n"
"uniform sampler2D my_Sampler; \n"
"varying vec2 vTexcoor; \n"
"void main() \n"
"{ \n"
" vec4 tex = texture2D(my_Sampler, vTexcoor); \n"
" gl_FragColor = tex; \n"
"} \n";
On top of texture I am rendering video frames from camera in infinite loop.
Before rendering video, I am setting up co-ordinates of 2D texture with below code.
Now I will explain my code from main function
main()
{
const GLfloat vertices[][2] = {
{ -1.0f, -1.0f},
{ 1.0f, -1.0f},
{ -1.0f, 1.0f},
{ 1.0f, 1.0f}
};
const GLfloat texcoords[][2] = {
{ 0.0f, 1.0f},
{ 1.0f, 1.0f},
{ 0.0f, 0.0f},
{ 1.0f, 0.0f}
};
GLfloat transformMatrix[16] =
{
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
};
// setup OpenGl environment......
Setup_coordinates()
}
Setup_coordinates()
{
LoadShaders(s_v_shader, s_f_shader);
-- Complete function defined below
// By now I should be using shader program.
// Grab location of shader attributes.
GLint locVertices = glGetAttribLocation(programHandle, "vPosition");
GLint locTexcoord = glGetAttribLocation(programHandle, "my_Texcoor");
// Transform Matrix is uniform for all vertices here.
GLint locTransformMat = glGetUniformLocation(programHandle, "u_TransMatrix");
GLint locSampler = glGetUniformLocation(programHandle, "my_Sampler");
/* Create the texture. */
glGenTextures(1, &gTexObj);
glBindTexture(GL_TEXTURE_2D, gTexObj);
if (gTexObj == 0)
{
printf("Could not load the texture \n");
return -1;
}
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);
glUniformMatrix4fv(locTransformMat, 1, GL_FALSE, transformMatrix);
glUniform1i(locSampler, 0);
glClearColor(0.0f, 0.5f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
while(1) -- Infinite loop to render video frames on 2D texture and draw red color line.
{
// enable vertex arrays to push the data.
glEnableVertexAttribArray(locVertices);
glEnableVertexAttribArray(locTexcoord);
// set data in the arrays.
glVertexAttribPointer(locVertices, 2, GL_FLOAT, GL_FALSE, 0, &vertices[0][0]);
glVertexAttribPointer(locTexcoord, 2, GL_FLOAT, GL_FALSE, 0, &texcoords[0][0]);
Render video frames logic goes here...................................
Each frame of video is abosultely rendering fine.
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
Now comes the tricky part to draw the line and color it with red.
float red_left_1[] =
{
-0.85f, -0.9f, -0.6f, -0.5f,
};
glVertexAttribPointer ( 1, 2, GL_FLOAT, GL_FALSE, 0, red_left_1 );
glEnableVertexAttribArray (1 );
glDrawArrays ( GL_LINES , 0, 2 );
glLineWidth( width_test );
}
}
void LoadShaders(const char * vShader, const char * pShader)
{
vertShaderNum = glCreateShader(GL_VERTEX_SHADER);
pixelShaderNum = glCreateShader(GL_FRAGMENT_SHADER);
if (CompileShader(vShader, vertShaderNum) == 0)
{
printf("%d: PS compile failed.\n", __LINE__);
return;
}
if (CompileShader(pShader, pixelShaderNum) == 0)
{
printf("%d: VS compile failed.\n", __LINE__);
return;
}
programHandle = glCreateProgram();
glAttachShader(programHandle, vertShaderNum);
glAttachShader(programHandle, pixelShaderNum);
// Bind vPosition to attribute 0
glBindAttribLocation ( programHandle, 0, "vPosition" );
glLinkProgram(programHandle);
// Check if linking succeeded.
GLint linked = 0;
glGetProgramiv(programHandle, GL_LINK_STATUS, &linked);
if (!linked)
{
printf("%d: Link failed.\n", __LINE__);
// Retrieve error buffer size.
GLint errorBufSize, errorLength;
glGetShaderiv(programHandle, GL_INFO_LOG_LENGTH, &errorBufSize);
char * infoLog = (char*)malloc(errorBufSize * sizeof (char) + 1);
if (infoLog)
{
// Retrieve error.
glGetProgramInfoLog(programHandle, errorBufSize, &errorLength, infoLog);
infoLog[errorBufSize + 1] = '\0';
fprintf(stderr, "%s", infoLog);
free(infoLog);
}
return;
}
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glUseProgram(programHandle);
}
Most of the genius peoples suggested to declare one more shader as above but replace uniform sampler2D my_Sampler with uniform vec4 color.
void main()
{
gl_FragColor = color
}
Then switch between these shader programs while showing texture and drawing color lines using glUseProgram.
I tried and absolutely given up as switching to shader program for drawing lines is not working.
Here is code for generating a colored 1x1 texture that you can use for your line (goes in your main or Setup_coordinates). With this solution you won't need another shader.
GLuint lineTexture;
glGenTextures(1, &lineTexture);
unsigned char red[4] = { 255, 0, 0, 255};
glBindTexture(GL_TEXTURE_2D, lineTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, red);
glBindTexture(GL_TEXTURE_2D, 0);
Before calling glDrawArrays, use this to switch to the correct texture.
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, <lineTexture or gTexObj>);
glUniform1i(locSampler, 0);
A more general solution (that I personally implement in my OpenGL projects) is to create a white texture, add a color uniform to your shader and multiply the uniform with the texture2D return value. With this method you can create different colored lines and graphics from the same white texture, only changing the color uniform. For the video frames, you would send in a white color and the pixels will remain unchanged. This will require very few changes to your code, I'm sure you can figure it out if you think it sounds better. :)

128bit texture in openGL does not display (black screen)

I create the texture like this:
GLuint PingPongShader::GenerateTexture()
{
float* pixels = new float[width*height * 4];
for(int i = 0; i < width * height; i+=4)
{
pixels[i] = 1.0;
pixels[i+1] = 1.0;
pixels[i+2] = 1.0;
pixels[i+3] = 1.0;
}
GLuint t;
glGenTextures(1, &t);
glBindTexture(GL_TEXTURE_2D, t);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, width, height, 0, GL_RGBA, GL_FLOAT, pixels);
delete[] pixels;
LogGLError();
return t;
}
My display function looks like this:
void display()
{
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glEnable(GL_TEXTURE_2D);
shader.BindCurrentTexture();
shader.EnableShader();
glBegin(GL_QUADS);
{
glTexCoord2f(0.0f, 0.0f);
glVertex2f(-1.0f, -1.0f);
glTexCoord2f(1.0f, 0.0f);
glVertex2f(1.0f, -1.0f);
glTexCoord2f(1.0f, 1.0f);
glVertex2f(1.0f, 1.0f);
glTexCoord2f(0.0f, 1.0f);
glVertex2f(-1.0f, 1.0f);
}
shader.DisableShader();
glDisable(GL_TEXTURE_2D);
glutSwapBuffers();
}
The shader looks like this:
Vertex:
varying vec2 texture_coordinate;
void main()
{
// Transforming The Vertex
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
// Passing The Texture Coordinate Of Texture Unit 0 To The Fragment Shader
texture_coordinate = vec2(gl_MultiTexCoord0);
}
Fragment:
varying vec2 texture_coordinate;
uniform sampler2D my_color_texture;
void main()
{
// Sampling The Texture And Passing It To The Frame Buffer
gl_FragColor = texture2D(my_color_texture, texture_coordinate);
}
Somehow I'm only getting a black screen, even though the pixels are all 1.0 (white). I don't see where the problem is :/
You are writing code using deprecated functions. In OpenGL 4.4 you can't use glBegin() and friends. Please use VBO for vertex and texture coordinate data. You can look it up in a modern OpenGL tutorial. You can also request older OpenGL context using glutInitContextVersion(). Make sure to use OpenGL 2.1 or enable complitability profile for versions > 3.0.