My problem is getting more than one texture accessible in a GLSL shader.
Here's what I'm doing:
Shader:
uniform sampler2D sampler0;
uniform sampler2D sampler1;
uniform float blend;
void main( void )
{
vec2 coords = gl_TexCoord[0];
vec4 col = texture2D(sampler0, coords);
vec4 col2 = texture2D(sampler1, coords);
if (blend > 0.5){
gl_FragColor = col;
} else {
gl_FragColor = col2;
}
};
So, I simply choose between the two color values based on a uniform variable. Simple enough (this is a test), but instead of the expected behavior, I get all black when blend <= 0.5.
OpenGL code:
m_sampler0location = m_shader.FindUniform("sampler0");
m_sampler1location = m_shader.FindUniform("sampler1");
m_blendlocation = m_shader.FindUniform("blend");
glActiveTexture(GL_TEXTURE0);
glEnable(GL_TEXTURE_2D);
m_extensions.glUniform1iARB(m_sampler0location, 0);
glBindTexture(GL_TEXTURE_2D, Texture0.Handle);
glActiveTexture(GL_TEXTURE1);
glEnable(GL_TEXTURE_2D);
m_extensions.glUniform1iARB(m_sampler1location, 1);
glBindTexture(GL_TEXTURE_2D, Texture1.Handle);
glBegin(GL_QUADS);
//lower left
glTexCoord2f(0, 0);
glVertex2f(-1.0, -1.0);
//upper left
glTexCoord2f(0, maxCoords0.t);
glVertex2f(-1.0, 1.0);
//upper right
glTexCoord2f(maxCoords0.s, maxCoords0.t);
glVertex2f(1.0, 1.0);
//lower right
glTexCoord2f(maxCoords0.s, 0);
glVertex2f(1.0, -1.0);
glEnd()
The shader is compiled and bound before all this. All the sanity checks in that process indicate that it goes ok.
As I said, the value of col in the shader program reflects fragments from a texture; the value of col2 is black. The texture that is displayed is the last active texture - if I change the last glBindTexture to bind Texture0.Handle, the texture changes. Fixed according to Bahbar's reply.
As it is, the scene renders all black, even if I add something like gl_FragColor.r = blend; as the last line of the shader. But, if I comment out the call glActiveTexture(GL_TEXTURE1);, the shader works again, and the same texture appears in both sampler0 and sampler1.
What's going on? The line in question, glActiveTexture(GL_TEXTURE1);, seems to work just fine, as evidenced by a subsequent glGetIntegerv(GL_ACTIVE_TEXTURE, &anint). Why does it break everything so horribly? I've already tried upgrading my display drivers.
Here's a basic GLUT example (written on OS X, adapt as needed) that generates two checkerboard textures, loads a shader with two samplers and combines them by tinting each (one red, one blue) and blending. See if this works for you:
#include <stdio.h>
#include <stdlib.h>
#include <GLUT/glut.h>
#include <OpenGL/gl.h>
#include <OpenGL/glu.h>
#define kTextureDim 64
GLuint t1;
GLuint t2;
/* adapted from the red book */
GLuint makeCheckTex() {
GLubyte image[kTextureDim][kTextureDim][4]; // RGBA storage
for (int i = 0; i < kTextureDim; i++) {
for (int j = 0; j < kTextureDim; j++) {
int c = ((((i & 0x8) == 0) ^ ((j & 0x8)) == 0))*255;
image[i][j][0] = (GLubyte)c;
image[i][j][1] = (GLubyte)c;
image[i][j][2] = (GLubyte)c;
image[i][j][3] = (GLubyte)255;
}
}
GLuint texName;
glGenTextures(1, &texName);
glBindTexture(GL_TEXTURE_2D, texName);
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_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kTextureDim, kTextureDim, 0, GL_RGBA, GL_UNSIGNED_BYTE, image);
return texName;
}
void loadShader() {
#define STRINGIFY(A) #A
const GLchar* source = STRINGIFY(
uniform sampler2D tex1;
uniform sampler2D tex2;
void main() {
vec4 s1 = texture2D(tex1, gl_TexCoord[0].st);
vec4 s2 = texture2D(tex2, gl_TexCoord[0].st + vec2(0.0625, 0.0625));
gl_FragColor = mix(vec4(1, s1.g, s1.b, 0.5), vec4(s2.r, s2.g, 1, 0.5), 0.5);
}
);
GLuint program = glCreateProgram();
GLuint shader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(shader, 1, &source, NULL);
glCompileShader(shader);
GLint logLength;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &logLength);
if (logLength > 0) {
GLchar* log = (GLchar*)malloc(logLength);
glGetShaderInfoLog(shader, logLength, &logLength, log);
printf("Shader compile log:\n%s\n", log);
free(log);
}
glAttachShader(program, shader);
glLinkProgram(program);
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &logLength);
if (logLength > 0) {
GLchar* log = (GLchar*)malloc(logLength);
glGetProgramInfoLog(program, logLength, &logLength, log);
printf("Program link log:\n%s\n", log);
free(log);
}
GLuint t1Location = glGetUniformLocation(program, "tex1");
GLuint t2Location = glGetUniformLocation(program, "tex2");
glUniform1i(t1Location, 0);
glUniform1i(t2Location, 1);
glUseProgram(program);
}
void init()
{
glClearColor(0.0, 0.0, 0.0, 0.0);
glEnable(GL_DEPTH_TEST);
glShadeModel(GL_FLAT);
t1 = makeCheckTex();
t2 = makeCheckTex();
loadShader();
}
void display()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
glActiveTexture(GL_TEXTURE0);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, t1);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, t2);
glBegin(GL_QUADS);
//lower left
glTexCoord2f(0, 0);
glVertex2f(-1.0, -1.0);
//upper left
glTexCoord2f(0, 1.0);
glVertex2f(-1.0, 1.0);
//upper right
glTexCoord2f(1.0, 1.0);
glVertex2f(1.0, 1.0);
//lower right
glTexCoord2f(1.0, 0);
glVertex2f(1.0, -1.0);
glEnd();
glutSwapBuffers();
}
void reshape(int w, int h)
{
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-2, 2, -2, 2, -2, 2);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
int main(int argc, char **argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_DEPTH | GLUT_RGBA);
glutInitWindowSize(512, 512);
glutInitWindowPosition(0, 0);
glutCreateWindow("GLSL Texture Blending");
glutReshapeFunc(reshape);
glutDisplayFunc(display);
glutIdleFunc(display);
init();
glutMainLoop();
return 0;
}
Hopefully the result will look something like this (you can comment out the glUseProgram call to see the first texture drawn without the shader):
Just to help other people who might be interested in using multiple textures and will feel hopeless after days searching for an answer. I found that you need to call
glUseProgram(program);
GLuint t1Location = glGetUniformLocation(program, "tex1");
GLuint t2Location = glGetUniformLocation(program, "tex2");
glUniform1i(t1Location, 0);
glUniform1i(t2Location, 1);
In that order for it to work (glUseProgram is the last instruction for 99% of the sample code I've found online). Now it may be only the case for me and not affect anyone else on Earth, but just in case I thought I'd share.
Quite late reply, but for anybody encountering this - I encountered same problem and after short fiddling, I realized that calling glActiveTexture(GL_TEXTURE0) fixes the issue.
It seems something down the line gets confused if active texture unit is not 'reset' to zero.
This could be system-dependent behavior; I'm running 64bit Archlinux with Mesa 8.0.4.
When compiling your shader to test, I found two errors:
coords should be assigned the st portion of the 4-component gl_TexCoord, e.g.
vec2 coords = gl_TexCoord[0].st;
The shader should not end with a semicolon.
Are you checking anywhere in your main program that shader compiles correctly? You may want to look at GL_COMPILE_STATUS via glGetShader and glGetShaderInfoLog.
It sounds like your glActiveTexture call does not work. Are you sure you set up the function pointer correctly ?
Verify by calling glGetIntegerv(GL_ACTIVE_TEXTURE, &anint) after having called your glActiveTexture(GL_TEXTURE1).
Also glEnable(GL_TEXTURE_2D) are not useful. The shader itself specifies what texture units to use, and which target of each unit to "enable".
Edit to add:
Well, your new situation is peculiar. The fact that you can't even show red is weird, in particular (well, did you set alpha to 1 just to make sure ?).
That said, you should restore GL_TEXTURE0 as the glActiveTexture after you're done setting the texture unit 1 (i.e. after the glBindTexture call).
Related
Here is the code:
int main(){
//init gl environment
//...
//create textures for pass 1
GLuint normal_color_output;
glCreateTextures(GL_TEXTURE_2D_MULTISAMPLE, 1, &normal_color_output);
glTextureStorage2DMultisample(normal_color_output, 8, GL_RGBA32F, 1000, 800, GL_TRUE);
GLuint high_color_output;
glCreateTextures(GL_TEXTURE_2D_MULTISAMPLE, 1, &high_color_output);
glTextureStorage2DMultisample(high_color_output,8, GL_R11F_G11F_B10F, 1000, 800,GL_TRUE);
//init framebuffer
GLuint render_buffer;
glCreateRenderbuffers(1, &render_buffer);
glNamedRenderbufferStorageMultisample(render_buffer, 8, GL_DEPTH24_STENCIL8, 1000, 800);
GLuint framebuffer;
glCreateFramebuffers(1, &framebuffer);
glNamedFramebufferTexture(framebuffer, GL_COLOR_ATTACHMENT0, normal_color_output,0);
glNamedFramebufferTexture(framebuffer, GL_COLOR_ATTACHMENT1, high_color_output, 0);
glNamedFramebufferRenderbuffer(framebuffer, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, render_buffer);
const GLenum drawbuffers[] = {GL_COLOR_ATTACHMENT0,GL_COLOR_ATTACHMENT1};
glNamedFramebufferDrawBuffers(framebuffer, 2, drawbuffers);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
//init another framebuffer
//What I want to do is trying to achieve implementing my own msaa color resolve solution.
GLuint mix_framebuffer;
glCreateFramebuffers(1, &mix_framebuffer);
GLuint mix_renderbuffer;
glCreateRenderbuffers(1, &mix_renderbuffer);
glNamedRenderbufferStorage(mix_renderbuffer, GL_DEPTH24_STENCIL8, 1000, 800);
GLuint normal_antialiasing_texture, hdr_antialiasing_texture;
glCreateTextures(GL_TEXTURE_2D, 1, &normal_antialiasing_texture);
glTextureStorage2D(normal_antialiasing_texture, 1, GL_RGBA32F, 1000, 800);
glCreateTextures(GL_TEXTURE_2D, 1, &hdr_antialiasing_texture);
glTextureStorage2D(hdr_antialiasing_texture, 1, GL_RGBA32F, 1000, 800);
glNamedFramebufferTexture(mix_framebuffer, GL_COLOR_ATTACHMENT0, normal_antialiasing_texture, 0);
glNamedFramebufferTexture(mix_framebuffer, GL_COLOR_ATTACHMENT1, hdr_antialiasing_texture, 0);
glNamedFramebufferDrawBuffers(mix_framebuffer,2, drawbuffers);
glNamedFramebufferRenderbuffer(mix_framebuffer, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, mix_renderbuffer);
glBindFramebuffer(GL_FRAMEBUFFER, mix_framebuffer);
//....
//draw commands
while (!glfwWindowShouldClose(window)) {
// pass 1
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
glUseProgram(program);
glUniformMatrix4fv(3, 1, GL_FALSE, glm::value_ptr(camera.GetViewMat()));
model.Render(program);
glPointSize(20.f);
glUseProgram(light_shader);// I draw a point to show the light's position
glUniformMatrix4fv(0, 1, GL_FALSE, glm::value_ptr(camera.GetViewMat()));
glDrawArrays(GL_POINTS, 0, 1);
//pass 2
glBindFramebuffer(GL_FRAMEBUFFER, mix_framebuffer);
glUseProgram(mix_program);
glBindTextureUnit(0, normal_color_output);
glBindTextureUnit(1, high_color_output);
glClear(GL_COLOR_BUFFER_BIT);
glNamedFramebufferDrawBuffers(mix_framebuffer, 2, drawbuffers);
glDrawArrays(GL_POINTS, 0, 1);
//...
}
}
I use geometry shader to model a square, here is the code:
//mix_gs.glsl
#version 450 core
layout(points) in;
layout(triangle_strip) out;
layout(max_vertices = 4) out;
void main(){
gl_Position = vec4(-1,1,-1,1);
EmitVertex();
gl_Position = vec4(-1,-1,-1,1);
EmitVertex();
gl_Position = vec4(1,1,-1,1);
EmitVertex();
gl_Position = vec4(1,-1,-1,1);
EmitVertex();
EndPrimitive();
}
here is the mix_fs.glsl:
#version 450 core
layout(location = 0)out vec4 color;
layout(location = 1)out vec4 hdr_color;
layout(binding = 0) uniform sampler2DMS color_sdms;
layout(binding = 1) uniform sampler2DMS hdr_sdms;
void main(){
/*
for(int i=0;i<8;i++){
color += texelFetch(color_sdms,ivec2(gl_FragCoord.xy),i);
hdr_color += vec4(texelFetch(hdr_sdms,ivec2(gl_FragCoord.xy),i).xyz,1);
}
*/
color = vec4(1,0,0,1);//I just output a color
hdr_color = vec4(0,1,0,1);
}
I encount a problem that I find during the draw pass 2, gl cannot outout any color to textures bind to mix_framebuffer.
Here is the debugging info in RenderDoc:
draw pass 1 texture output:
draw pass 2's geometry output:
draw pass 2 texture input:
draw pass 2 texture output:
You can see, draw-pass 1's output was passed to draw-pass2's pipeline successfully, but there is no output to draw-pass2's textures. I don't know why.
If you don't see even the plain color, the first I'd recommend to check how it was discarded. There are no so many options:
glColorMask. Highly likely it's not your case, since pass 1 works;
Wrong face culling and polygon winding order (CW, CCW). By your geometry shader, it looks like CW;
Blending options;
Depth-stencil state. I see you use glNamedRenderbufferStorage(mix_renderbuffer, GL_DEPTH24_STENCIL8, 1000, 800); What are depth-stencil settings?
If everything above looks good, any glGetError messages? If it's ok, try to remove MRT for a debugging purposes and output the only color in the second pass. If it would work, probably some error in MRT + Depth buffer setup.
Here is my code:
modify from qt example: Examples\Qt-5.14.2\quick\scenegraph\openglunderqml
void SquircleRenderer::init()
{
unsigned char* data = (unsigned char*)malloc(1200*4);
for(int i=0;i<600;i++)
{
data[i*4] = 0;
data[i*4+1] = 255;
data[i*4+2] = 0;
data[i*4+3] = 255;
}
for(int i=600;i<1200;i++)
{
data[i*4] = 0;
data[i*4+1] = 0;
data[i*4+2] = 255;
data[i*4+3] = 255;
}
if (!m_program) {
QSGRendererInterface *rif = m_window->rendererInterface();
Q_ASSERT(rif->graphicsApi() == QSGRendererInterface::OpenGL || rif->graphicsApi() == QSGRendererInterface::OpenGLRhi);
initializeOpenGLFunctions();
if (texs[0])
{
glDeleteTextures(1, texs);
}
glGenTextures(1, texs);
glBindTexture(GL_TEXTURE_2D, texs[0]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 30, 40, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
m_program = new QOpenGLShaderProgram();
m_program->addCacheableShaderFromSourceCode(QOpenGLShader::Vertex,
"attribute highp vec4 vertices;"
"varying highp vec2 coords;"
"void main() {"
" gl_Position = vertices;"
" coords = vertices.xy;"
"}");
m_program->addCacheableShaderFromSourceCode(QOpenGLShader::Fragment,
"varying highp vec2 coords;"
"uniform sampler2D inputImageTexture;"
"void main() {"
" gl_FragColor = texture2D(inputImageTexture, coords);"
"}");
m_program->bindAttributeLocation("vertices", 0);
m_program->link();
arrUni[0] = m_program->uniformLocation("inputImageTexture");
}
}
//! [4] //! [5]
void SquircleRenderer::paint()
{
// Play nice with the RHI. Not strictly needed when the scenegraph uses
// OpenGL directly.
m_window->beginExternalCommands();
m_program->bind();
m_program->enableAttributeArray(0);
float values[] = {
-1, 1,
1, 1,
-1, -1,
1, -1
};
// This example relies on (deprecated) client-side pointers for the vertex
// input. Therefore, we have to make sure no vertex buffer is bound.
glBindBuffer(GL_ARRAY_BUFFER, 0);
m_program->setAttributeArray(0, GL_FLOAT, values, 2);//values
//m_program->setUniformValue("t", (float) m_t);
qDebug()<<m_viewportSize.width()<<m_viewportSize.height()<<"\n";
glViewport(0, 0, m_viewportSize.width(), m_viewportSize.height());
glDisable(GL_DEPTH_TEST);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texs[0]);
glUniform1i(arrUni[0], 0);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
m_program->disableAttributeArray(0);
m_program->release();
m_window->endExternalCommands();
}
As the picture you can see,it produces 4 same pictures,could you tell me how to produce 1 picture fill the whole window?:
I tried so many methods, but it didn't work, I guess the problem exists in the values array or the glTexImage2D function.
Textures are mapped accross the [0, 1] range, and values outside of that range are modulo-looped back into it, which creates a repeating pattern. Interpreting the texture over the [-1, 1] range leads to what you are seeing since you are mapping exactly twice the UV range in both axises.
There's a few ways to fix this. But my personal preference for a full-framebuffer pass like this is to have the attribute be normalized, and then have it converted to the expected [-1, 1] range for the clip-space coordinate in the vertex shader:
float values[] = {
0.f, 1.f,
1.f, 1.f,
0.f, 0.f,
1.f, 0.f
};
gl_Position = vertices * 2.0 - vec4(1.0, 1.0,0.0,1.0);
Another common technique is to do away with the attribute buffer altogether, and use gl_VertexID to directly generate both the UVs and coordinates.
I'm trying to draw some graphics using OpenGL.
I'm using VMWare Fusion Version 10.1.3 with Ubuntu 16.04 and QtCreator 4.6.2.
My task is to play recording with some drawing in OpenGL.
In case of that, I need FBO to increment the content of my drawing and create an image on the end of the recording and I also need texture to display on screen during playing the recording.
My problem is that I have a permanent crash during destroying an object in which I use OpenGL.
Here is my code:
void DrawingView::paint(QPainter *painter) {
painter->beginNativePainting();
if(_needsErase) {
QOpenGLContext *context = QOpenGLContext::currentContext();
_frameBuffer = new QOpenGLFramebufferObject(QSize(_rect.width(), _rect.height()), _format);
glBindTexture(GL_TEXTURE_2D, _frameBuffer->texture());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_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);
glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_FALSE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, _rect.width(), _rect.height(), 0, GL_RGBA , GL_UNSIGNED_BYTE, 0);
glBindTexture(GL_TEXTURE_2D, 0);
_frameBuffer->bind();
{
glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
_frameBuffer->release();
_needsErase = false;
}
glEnable(GL_BLEND);
_frameBuffer->bind();
{ // here I'm drawing to my fbo, this part is irrelevant. }
_frameBuffer->release();
_shaderProgram->release();
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glUniform1i(_tex0Uniform, 0);
_copyBuffer->bind();
glActiveTexture(GL_TEXTURE0);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, _frameBuffer->texture());
glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, 0, 0, squareVertices);
glEnableVertexAttribArray(ATTRIB_VERTEX);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glActiveTexture(GL_TEXTURE0);
glBindTexture( GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
_copyBuffer->release();
glDisable(GL_BLEND);
painter->endNativePainting();
}
App crashes during destroying my DrawingView Object after line delete _frameBuffer; with log:
context mismatch in svga_surface_destroy
VMware: vmw_ioctl_command error Invalid argument.
Here is how I'm freeing my DrawingView Object.
void DrawingView::cleanupGL() {
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
_shaderProgram->release();
_frameBuffer->release();
_copyBuffer->release();
delete _shaderProgram;
delete _copyBuffer;
delete _frameBuffer;
glDeleteBuffers(1, &_ebo);
glDeleteBuffers(1, &_vbo);
}
I noticed that app stops crashing when I erase line with glDrawArrays(...) command, but I need this command to display drawing during playing the recording.
I was using this code also on macOS and there everything was working PERFECTLY, but unfortunately, I need to use this code on a virtual machine with Ubuntu.
Here is also the code of my vertex and fragment shaders:
Vertex shader:
uniform vec2 screenSize;
attribute vec2 position;
varying vec2 coord;
void main(void)
{
coord = position;
vec2 halfScreenSize = screenSize * 0.5f;
vec2 pos = halfScreenSize * position + halfScreenSize;
gl_Position = gl_ModelViewProjectionMatrix * vec4(pos, 0.0, 1.0);
}
Fragment shader:
uniform sampler2D tex0;
varying vec2 coord;
void main(void)
{
vec2 coords = coord * 0.5 + 0.5;
gl_FragColor = texture2D(tex0, coords.xy);
}
Do anyone have any idea why this doesn't want to work on a virtual machine?
I really wanted to be specific describing my problem, but if You have any further questions please ask.
You create a (new) QOpenGLFramebufferObject instance only if _needsErase ever is set true.
if(_needsErase) {
QOpenGLContext *context = QOpenGLContext::currentContext();
_frameBuffer = new QOpenGLFramebufferObject(…
If that doesn't happen, it will be an uninitialized pointer, and calling member functions on it will invoke undefined behaviour. This particular code it wrong anyway, because it never deletes whatever instance may have been pointed to by _frameBuffer before overwriting the pointer.
Instead of manual memory management I strongly advise, to make use of automatic constructs. I appreciate the need for dynamic instance creation in this particular case. The way to go about this is through either std::shared_ptr or std::unique_ptr. I strongly suggest to start with std::unique_ptr and change it to a shared pointer only if absolutely needed so.
In your DrawingView class
#include <memory>
class DrawingView : public ... {
...
protected:
std::unique_ptr<QOpenGLFramebufferObject> _frameBuffer;
...
};
in the DrawingView::paint method:
if(_needsErase) {
QOpenGLContext *context = QOpenGLContext::currentContext();
_frameBuffer = std::make_unique<QOpenGLFramebufferObject>(…
Do not use new or delete.
So I've recently been learning some openGL. I've initially been using the SDL library to print images on screen but I figured it would be interested to try and achieve something similar with openGL and thus also being able to apply shaders to my images for neat effects such as lighting effects and night/day cycles and such. What I'm doing right now is simply loading a texture, then applying that texture to a quad with the same size of the texture. This works well.
Now I want to apply some shaders. This is an example of a vertex and fragment shader that I could apply to one of my textured quads:
in vec2 LVertexPos2D;
void main()
{
gl_Position = vec4( LVertexPos2D.x, LVertexPos2D.y, 0, 1);
}
which does nothing, then my fragment shader:
out vec4 LFragment;
void main()
{
LFragment = vec4(1.0, 1.0, 1.0, 1.0);
}
Which obviously just turns the texture I'm applying it on into a white block, which isn't exactly what I want. Somehow I need to retrieve the current texel data so I can modify that instead of simply changing it.
I've read that the function call to texture2D is supposed to return a vec4 of the current pixel data but I haven't gotten this to work. (Having a hard time finding a good explanation of the function inputs and how it works). Furthermore texture2D is supposedly deprecated but I can't get its replacement (texture()) to work either. Any nudges in the right direction would be greatly appreciated!
Edit: I'll throw in some more info on how I'm doing things, this is the function that loads my textures:
texture makeTexture(std::string fileLocation)
{
texture tempTexture;
SDL_Surface *mySurface = IMG_Load(fileLocation.c_str());
if (mySurface == NULL)
{
std::cout << "Error in loading image at: " << fileLocation << std::endl;
return tempTexture;
}
GLuint myTexture;
glGenTextures(1, &myTexture);
glBindTexture(GL_TEXTURE_2D, myTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, mySurface->w, mySurface->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, mySurface->pixels);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, 0);
SDL_FreeSurface(mySurface);
tempTexture.texture_id = myTexture;
tempTexture.h = mySurface->h;
tempTexture.w = mySurface->w;
return tempTexture;
}
Where this is my texture struct:
struct texture
{
int w;
int h;
GLuint texture_id;
};
and this function draws any texture to a given x and y coordinate:
void draw(int y, int x, texture &tempTexture)
{
glBindTexture(GL_TEXTURE_2D, tempTexture.texture_id);
glBegin(GL_QUADS);
glTexCoord2f(0, 1);
glVertex2f(-1 + ((float)(x) / SCREEN_WIDTH) * 2, 1 - ((float)(y + tempTexture.h) / SCREEN_HEIGHT) * 2); //Bottom left
glTexCoord2f(1, 1);
glVertex2f(-1 + ((float)(x + tempTexture.w)/SCREEN_WIDTH)*2, 1 - ((float)(y + tempTexture.h) / SCREEN_HEIGHT) * 2); //Bottom right?
glTexCoord2f(1, 0);
glVertex2f(-1 + ((float)(x + tempTexture.w) / SCREEN_WIDTH) * 2, 1.0 - ((float)y / SCREEN_HEIGHT) * 2); //top right
glTexCoord2f(0, 0);
glVertex2f(-1 + ((float)(x) / SCREEN_WIDTH) * 2, 1.0 - ((float)y / SCREEN_HEIGHT) * 2); //Top left (notification: Coordinates are (x,y), not (y,x).
glEnd();
glBindTexture(GL_TEXTURE_2D, 0);
}
then in my main render function I'm now doing:
draw(0, 0, myTexture);
glUseProgram(gProgramID);
glUniform1i(baseImageLoc, myTexture2.texture_id);
draw(100, 100, myTexture2);
glUseProgram(NULL);
where myTexture is just a meadow of grass and myTexture2 is a player character that I want to apply some shading shenanigans to. gPriogramID is a program that has my two aformentioned shaders loaded to it.
In order to access texture data in a shader you have to do the following:
First you need to glBind your texture to a specific texture unit (change active texture unit using glActiveTexture.
Pass the texture unit index as a uniform sampler to the shader.
Access the texture in the shader like the following.
// tex holds the value of the texture unit to be used (not the texture)
uniform sampler2D tex;
void main()
{
vec4 color = texture(tex,texCoord);
LFragment = color;
}
You also need to pass texCoords to the shader as in vertex attribute.
I've been trying to learn about modern OpenGL/GLSL recently, and that has taken me to the subject of FBOs, and I've encountered a mysterious problem.
In my program, I initialize an FBO, then unbind it. Everything works so far. I then bind the FBO, and render my scene to it. I get a blank screen now when I run this program, but that's normal - I'm not rendering the output. However, when I try to do that, or attempt to render anything at all, I still get the black screen. I can make it work only if I don't bind the FBO during the main loop.
I have had FBOs working in Java, using LWJGL, and this is the second time I've attempted to render an FBO in C++, with SDL, and both times I came across the same bug.
What it isn't:
The texture - I've already tried drawing other textures after using the FBO
The shader - I've tried my other shader on this
My wrapper classes used - they have all been proven to work when using other systems.
Also, glChackFramebufferStatus(GL_FRAMEBUFFER) returns GL_FRAMEBUFFER_COMPLETE, and glGetError() returns 0.
Also, I understand that there are a few things I haven't done which would be sensible (depth buffer for example), that don't affect this bug. I will fill those in later.
Here's the code:
Main.cpp:
#include <iostream>
#include "GLee.h"
#define GLM_FORCE_RADIANS
#include "matrices.h" //Replaces built-in matrices, functions behave the same as legacy OGL
#include "functions.h" //Camera, works very well
#include <SDL/SDL.h>
#include <SDL/SDL_image.h>
#include <SDL/SDL_opengl.h>
#include <SDL/SDL_mouse.h>
#include <math.h>
#include <SDL/SDL_opengl.h>
#include <vector>
#include <string>
#include <algorithm>
#include <fstream>
#include <cstdio>
#include <cstdlib>
#include <GL/gl.h>
#include <GL/glu.h>
//#include <SDL/SDL_video.h>
using namespace std;
unsigned int grass, crate, FBOTex;
int ground, monkey;
bool mousein = true;
matrices pipeline;
int frame;
#include <GL/gl.h>
#include <GL/glu.h>
#include "TexLoader.h" //Loads textures - I've never had issues with this header
#include "ModelLoader.h" //Loads models (OBJ), again, works like a treat
#include "shader.h" //Includes the shader class, which has been proven to work
#include "Material.h" //Used in lighting calculations
texProperties linear = texProperties(GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, GL_REPEAT, 16);
texProperties pixellated = texProperties(GL_NEAREST, GL_LINEAR_MIPMAP_LINEAR, GL_REPEAT, 16); //Properties for textures loaded by the texture loader.
shader* mainShader;
shader* postProcess; //The FBO shader
material simpleMaterial = material(0.2, 0.2, 0.2, 0.6, 0.6, 0.6, 0.1,0.1,0.1, 1000),
littleShine = material(0.2, 0.2, 0.2, 0.6, 0.6, 0.6, 0.1,0.1,0.1, 5),
monkeyMtl = material(0.2, 0.2, 0.2, 0.6, 0.6, 0.6, 0.3,0.3,0.3, 50);
//monkeyMtl = material(0.2, 0.2, 0.2, 0.6, 0.6, 0.6, 1,1,1, 5);
SDL_Surface* loadTextureData(const char* fileName){ //For creating icons
SDL_Surface* tex;
if((tex = IMG_Load(fileName))){
cout<<"Texture Found! Tex: "<<fileName<<endl;
}
return tex;
}
unsigned int createTexture(int w, int h, bool isDepth){ //Create texture for FBO
unsigned int textureID;
glGenTextures(1, &textureID);
glBindTexture(GL_TEXTURE_2D, textureID);
glTexImage2D(GL_TEXTURE_2D, 0, isDepth?GL_DEPTH_COMPONENT:GL_RGBA8, w, h, 0, isDepth?GL_DEPTH_COMPONENT:GL_RGBA, GL_FLOAT, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
int i;
i = glGetError();
if(i != 0){
cout<<"Texture creation error! Error code: "<<i<<endl;
}
glBindTexture(GL_TEXTURE_2D, 0);
return textureID;
}
unsigned int fbo; //The FBO ID
unsigned int renderTexture; //The ID of the output texture for the FBO
void init(){
//glEnable(GL_CULL_FACE);
//glCullFace(GL_BACK);
const unsigned char* text = glGetString(GL_VERSION); //OpenGL 4
cout<<"GL version: "<<text<<endl;
SDL_WM_GrabInput(SDL_GRAB_ON);
glEnable(GL_MULTISAMPLE);
glEnable(GL_DEPTH_TEST);
glEnable(GL_TEXTURE_2D);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 8);
SDL_WM_SetCaption("OGL!", "OGL!!");
//SDL_WM_IconifyWindow();
SDL_Surface* icon = loadTextureData("Logo.png");
SDL_WM_SetIcon(icon, NULL);
glClearColor(0.0, 0.0, 0.0, 1);
pipeline.matrixMode(PROJECTION_MATRIX);
pipeline.loadIdentity();
pipeline.perspective(45, 1280.0/720.0, 0.1, 5000.0); //Similar to legacy OGL
pipeline.matrixMode(MODEL_MATRIX);
SDL_FreeSurface(icon);
mainShader = new shader("vertex.vert", "fragment.frag"); //These shader files load+compile fine
postProcess = new shader("PostProcess.vert", "PostProcess.frag");
grass = getTexture("Textures/Grass.png", linear);
crate = getTexture("Textures/Crate.png", linear);
ground = loadModel("Models/Plane.obj");
monkey = loadModel("Models/Monkey.obj");
float amb[3] {0.2, 0.2, 0.2};
float diff[3] {0.6, 0.6, 0.6};
float spec[3] {1, 1, 1};
//////////FBO Creation///////////
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);//This bind function works
renderTexture = createTexture(1920, 1080, false);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, renderTexture, 0);
int i = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if(i != GL_FRAMEBUFFER_COMPLETE){//I get no errors here...
cout<<"FBO is not complete!"<<endl;
}
cout<<"glGetError() = "<<glGetError()<<endl; //Prints 0
glBindFramebuffer(GL_FRAMEBUFFER, 0); //This unbind works perfectly
}
void display(){ //The drawing part of the main loop
cout<<"FBO Number: "<<fbo<<endl; //Returns 1, as it should
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
mainShader->bindShader();
mainShader->setUniformFloat3("lspecular", 1,1,1); //Lighting stuff...
mainShader->setUniformFloat3("lambient", 0.2,0.2,0.2);
mainShader->setUniformFloat3("ldiffuse", 0.6,0.6,0.6);
mainShader->setUniformFloat3("lightPos", 0,500,0);
mainShader->setUniformSampler("texture", 0);
mainShader->setUniformFloat("constantAttenuation", 0.0);
mainShader->setUniformFloat("linearAttenuation", 0.0);
mainShader->setUniformFloat("quadraticAttenuation", 0.000002);
littleShine.addToShader(*mainShader); //Sets material for lighting
pipeline.matrixMode(PROJECTION_MATRIX);
pipeline.loadIdentity();
pipeline.perspective(45, 1280.0/720.0, 0.1, 5000.0); //3D projection
pipeline.matrixMode(VIEW_MATRIX);
pipeline.loadIdentity();
control(0.2, 0.2, mousein, pipeline); //Controls camera
updateCamera(pipeline); //Updates matrices
pipeline.updateMatrices(mainShader->getHandle()); //Updates shader matrices
glBindTexture(GL_TEXTURE_2D, grass);
pipeline.matrixMode(MODEL_MATRIX);
//pipeline.loadIdentity();
glCallList(ground);
pipeline.translate(0, 1, -5);
pipeline.rotatef(frame, 0, 1, 0);
pipeline.updateMatrices(mainShader->getHandle());
monkeyMtl.addToShader(*mainShader);
glBindTexture(GL_TEXTURE_2D, crate);
glCallList(monkey);
glBindFramebuffer(GL_FRAMEBUFFER, 0); ///////DOESN'T UNBIND!! Still get black screen
//glClear(GL_COLOR_BUFFER_BIT);
pipeline.matrixMode(MODEL_MATRIX);
pipeline.loadIdentity();
pipeline.matrixMode(VIEW_MATRIX);
pipeline.loadIdentity();
//pipeline.translate(0, 0, -5);
pipeline.matrixMode(PROJECTION_MATRIX);
pipeline.loadIdentity();
pipeline.ortho(1920, 0, 0, 1080, 1, -1);//Clear matrices, set orthographic view
//mainShader->unbindShader();
postProcess->bindShader(); //Binds the post-processing shader
postProcess->setUniformSampler("texture", 0); //Texture sampler
pipeline.updateMatrices(postProcess->getHandle());
glBindTexture(GL_TEXTURE_2D, renderTexture); //Bind the FBO's output as a texture
glBindFramebuffer(GL_FRAMEBUFFER, 0); //Tried a second time to unbind FBO, to no avail...
glBegin(GL_QUADS);
glColor3f(1.0, 1.0, 1.0); //Makes no difference
glTexCoord2f(0,0); /////Draw FBO texture to screen, fails
glVertex2f(0,0);
glTexCoord2f(1,0);
glVertex2f(1920,0);
glTexCoord2f(1,1);
glVertex2f(1920,1080);
glTexCoord2f(0,1);
glVertex2f(0,1080);
glEnd();
}
void update(){
frame++;
}
int main(int args, char* argv[]){
SDL_Init(SDL_INIT_EVERYTHING);
IMG_Init(IMG_INIT_PNG);
SDL_Delay(150);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 8);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_Surface* screen = SDL_SetVideoMode(1920, 1080, 32, SDL_SWSURFACE|SDL_OPENGL|SDL_FULLSCREEN);
bool running = true;
Uint32 start;
SDL_Event event;
init();
while(running){
start = SDL_GetTicks();
while(SDL_PollEvent(&event)){
switch(event.type){
case SDL_QUIT:
running = false;
break;
case SDL_KEYDOWN:
switch(event.key.keysym.sym){
default:
break;
case SDLK_ESCAPE:
running = false;
break;
case SDLK_p:
mousein = false;
SDL_ShowCursor(SDL_ENABLE);
SDL_WM_GrabInput(SDL_GRAB_OFF);
break;
}
break;
case SDL_KEYUP:
break;
case SDL_MOUSEBUTTONDOWN:
mousein = true;
SDL_ShowCursor(SDL_DISABLE);
SDL_WM_GrabInput(SDL_GRAB_ON);
break;
}
}
update();
display();
SDL_GL_SwapBuffers();
if(1000/60 > (SDL_GetTicks() - start)){
SDL_Delay(1000/60 - (SDL_GetTicks() - start));
}
}
delete mainShader;
SDL_Quit();
//cout << "Hello world!" << endl;
return 0;
}
PostProcess.vert:
#version 130
varying vec2 texCoord;
varying vec3 position;
varying vec3 normal;
//Matrices, passed in to shader
uniform mat4 modelMatrix;
uniform mat4 viewMatrix;
uniform mat4 projectionMatrix;
uniform mat4 modelViewMatrix;
uniform mat4 modelViewProjectionMatrix;
uniform mat3 normalMatrix;
void main(){
gl_Position = modelViewProjectionMatrix * gl_Vertex;
normal = normalMatrix * gl_Normal;
texCoord = gl_MultiTexCoord0.st;
position = vec3(modelViewMatrix * gl_Vertex);
}
PostProcess.frag:
#version 130
uniform sampler2D texture;
varying vec2 texCoord;
varying vec3 position;
varying vec3 normal;
void main(){
gl_FragColor = texture(texture, texCoord); //Draw the textur, also tried texture2D...
//gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); //I've tried drawing white to the screen - it failed
}
I really don't know what's happening here. I'd guess it's something simple, but how did I miss it twice?
I've solved it!
For some reason, the material.cpp file was messing it all up. For some reason, when I used the material.addToShader(shader) function, it would not render, but when I added the uniforms myself, or changed the parameter to a pointer to a shader, it seemed to work perfectly fine.
There was a 1281 error caused by this issue. Why this was the case I have no idea, but at least it's solved now!
You don't call glClear when the window framebuffer is bound. Try:
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);