I am receiving an screen-shot Palette over the network. Next, I convert the Palette indices to their relevant RGB using my matrix containing different colors.
The problem I have with the code is I cannot create a texture out of this RGB image I receive successfully.
oid GlWidget::createTextureFromBitmap(QByteArray btmp)
{
tex.buf = new unsigned char[bytes.size()];
memcpy(tex.buf, bytes.constData(), bytes.size());
glGenTextures( 1, &tex.id);
glBindTexture(GL_TEXTURE_2D, tex.id);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, 0, 0, 0, GL_RGB, GL_UNSIGNED_BYTE, tex.buf);
gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, 800, 600, GL_RGB, GL_UNSIGNED_BYTE, tex.buf);
delete [] tex.buf;
tex.buf = NULL;
updateGL();
}
void GlWidget::paintGL()
{
shaderProgram.bind();
shaderProgram.setUniformValue("mvpMatrix", pMatrix * vMatrix * mMatrix);
shaderProgram.setUniformValue("texture", 0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex.id);
//glActiveTexture(0);
shaderProgram.setAttributeArray("vertex", vertices.constData());
shaderProgram.enableAttributeArray("vertex");
shaderProgram.setAttributeArray("textureCoordinate", textureCoordinates.constData());
shaderProgram.enableAttributeArray("textureCoordinate");
glDrawArrays(GL_TRIANGLES, 0, vertices.size());
shaderProgram.disableAttributeArray("vertex");
shaderProgram.disableAttributeArray("textureCoordinate");
shaderProgram.release();
}
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, 0, 0, 0, GL_RGB, GL_3_BYTES, tex.buf);
gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, 800, 600, GL_RGB, GL_3_BYTES, tex.buf);
GL_3_BYTES is not a valid data type here, you prpbably mean GL_UNSIGNED_BYTE. Apart from that, there are some redundant calls here. Your glTexImage2D call will specify the data for mipmap level 0, the gluBuild2DMipmaps will specify all the mipmap levels, including 0. Since you use mipmapping, you will only need the latter of those calls. But both calls will fail with GL_INVALID_ENUM due to the wrong data type, so you don't have any texture. So you can replace that two lines by:
gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, 800, 600, GL_RGB, GL_UNSIGNED_BYTE, tex.buf);
Another thing:
glActiveTexture(0);
this is also invalid. Only the GL_TEXTUREn values are valid for that call. You also do not need to "disable" the active texture selector, as you might try to do here. It will always point to some valid texture unit.
In addition to the fixes mentioned above, you can improve the performance by not having to do an additional memcopy (1 in your code, 1 in GL driver), by sending the palette also as a texture, then doing the same indexing that you are doing in the CPU code, inside the shader code. Also, if you are anyway setting the bytes[] array, no need to clear the array first.
Related
I have a RGBA8UI internal format texture attached to a framebuffer, and somehow I need to reset it to a default value. Unfortunately, a simple gl.clear(gl.COLOR_BUFFER_BIT) does not seem to work, (I suspect the issue is the internal format of the texture), giving this error:
[.WebGL-000020EE00AD4700] GL_INVALID_OPERATION: No defined conversion between clear value and attachment format.
The framebuffer status is FRAMEBUFFER_COMPLETE. Here is a minimal example for jsfiddle:
HTML
<canvas id="canvas"></canvas>
JS
const gl = document.getElementById('canvas').getContext('webgl2');
let targetTexture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, targetTexture);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA8UI, 1, 1, 0, gl.RGBA_INTEGER, gl.UNSIGNED_BYTE, null);
let fb = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, targetTexture, 0);
let fstatus = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
console.log(fstatus, gl.FRAMEBUFFER_COMPLETE);
gl.clearColor(0, 0, 0, 0);
gl.clear(gl.COLOR_BUFFER_BIT);
QUESTION
How can I clear/reset the texture?
Well according to the WebGL 2 spec (Section 3.7.9) you can not clear integer color buffers:
If an integer color buffer is among the buffers that would be cleared, an INVALID_OPERATION error is generated and nothing is cleared.
So you have to either call texImage2D again like you already did:
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA8UI, 1, 1, 0, gl.RGBA_INTEGER, gl.UNSIGNED_BYTE, null);
or (maybe more efficient) use a screenspace quad and a fragment-shader to write a constant value of your liking.
I just ran into this myself. clear does not work for integer formats. Instead you can use:
gl.clearBufferuiv(gl.COLOR, 0, [0, 0, 0, 0]);
There's also clearBufferiv for signed integer formats, clearBufferfv for float formats and depth, and clearBufferfi specifically for DEPTH_STENCIL formats.
Docs are here: https://registry.khronos.org/OpenGL-Refpages/es3.0/html/glClearBuffer.xhtml
I want to implement a function on my macOS, with QT.
I have a surface,
writes the result to a texture buffer instead of visualizing it
Then render it in fragment shader.
The size of the result is wrong.
limitFramebuffer is my QOpenGLFramebufferObject.
// Render limit surface to framebuffer
limitFramebuffer->bind();
glClearColor(0, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
renderers["Limit"]->render();
limitFramebuffer->release();
// Set limit framebuffer as current texture
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, limitFramebuffer->texture());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glDrawBuffer(GL_COLOR_ATTACHMENT0);
QImage image0 = limitFramebuffer->toImage();
QDateTime d = QDateTime::currentDateTime();
QString date = d.toString("dd.MM.yyyy.hh:mm:ss.zzz") + ".png";
QString fileName = "./../../../../MeshTool/examples/texture/" + date;
The image saved is
It shoulde be:
As we can see, the texture generated contains only a quarter of the original image from the bottom left corner.
I use
glBindTexture(GL_TEXTURE_2D, limitFramebuffer->texture());
to bind the texture.
How can I make the texture contain all the data in the FBO?
Why does the FBO contain only a quarter of the data?
Thanks!
I am currently working on creating openGL buffers in Qt for some 3D models (cuboids of various sizes depicting buildings).
I've tried to look into some examples, but I've only found some for 2D. I tried using the same for 3D, but at best I end up with blank images when I convert my buffers to images to check.
I reached the max success (at least an image is being created) using: https://dangelog.wordpress.com/2013/02/10/using-fbos-instead-of-pbuffers-in-qt-5-2/
My current code is something like:
QOpenGLFramebufferObject* SBuildingEditorUtils::getFboForBuilding(Building bd)
{
glPushMatrix();
QSurfaceFormat format;
format.setMajorVersion(4);
format.setMinorVersion(3);
QWindow window;
window.setSurfaceType(QWindow::OpenGLSurface);
window.setFormat(format);
window.create();
QOpenGLContext context;
context.setFormat(format);
if (!context.create())
qFatal("Cannot create the requested OpenGL context!");
context.makeCurrent(&window);
// TODO: fbo size = building size
QOpenGLFramebufferObjectFormat fboFormat;
fboFormat.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
//fboFormat.setAttachment(QOpenGLFramebufferObject::Depth);
auto fbo = new QOpenGLFramebufferObject(1500, 1500, fboFormat);
auto res = glGetError();
auto bindRet = fbo->bind();
glEnable(GL_DEPTH_TEST);
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glOrtho(0, 1500, 0, 1500, 0, 1500);
glMatrixMode(GL_MODELVIEW);
glBegin(GL_POLYGON);
glVertex3d(500, 500, 500);
glVertex3d(500, 1000, 1000);
glVertex3d(1000, 1000, 1000);
glVertex3d(1000, 500, 500);
glEnd();
glDisable(GL_DEPTH_TEST);
fbo->toImage().save("uniqueName.png");
fbo->release();
glPopMatrix();
return fbo;
}
Here I've been using the image "uniqueName.png" to text my output. As you can guess most of the code here just for testing. Also, this code is part of a larger code base.
Any advice of what I might be missing. While I have some experience with Qt, I lack any formal education / training in openGL. Any help would be appreciated.
I wish to know how to get the code to work.
Thanks.
With Qt and opengl, I would like draw in a QGlFrameBufferObject ?
I try this :
QGLFrameBufferObject * fbo = new QGLFramebufferObject(200, 200, QGLFramebufferObject::NoAttachment, GL_TEXTURE_2D, GL_RGBA32F);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0.0f, fbo->size().width(), fbo->size().height(), 0.0f, 0.0f, 1.0f);
fbo->bind();
glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
fbo->release();
fbo->toImage().save("test.jpg"));
But I don't get a red image.
OTHER QUESTION
And if I want draw with :
glBegin(GL_QUAD);
glColor3d(0.2, 0.5, 1.0);
glVertex2i( 10, 20);
glColor3d(0.7, 0.5, 1.0);
glVertex2i( 15, 20);
glColor3d(0.8, 0.4, 1.0);
glVertex2i( 15, 25);
glColor3d(0.1, 0.9, 1.0);
glVertex2i( 10, 25);
glEnd();
Do I need also glClear() ?
You never actually clear the framebuffer. glClearColor() only sets the color used for clearing, but does not clear anything. You will need to add the second line here:
glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
The glClear() call will clear the color buffer with the clear color you specified in the first call. The framebuffer content is initially undefined, so you should always clear the framebuffer with a glClear() call before you start drawing. The only exception is if you're certain that the primitives you render will cover every pixel of the drawing surface. Even then, on some architectures it can actually be better for performance to still call glClear() first.
It shouldn't matter yet as long as you only clear the buffer. But once you want to start drawing, you will also need to set the viewport:
glViewport(0, 0, 200, 200);
All figures outside of z-axis: (1;-1) range get clipped. Here is some code:
void MainWindow::initializeGL()
{
glDepthRange(-2,2);
glEnable(GL_TEXTURE_2D);
glDisable(GL_COLOR_MATERIAL);
glEnable(GL_BLEND);
glEnable(GL_POLYGON_SMOOTH);
//glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glClearColor(1, 1, 0, 0);
glDisable(GL_DEPTH_TEST); // Enables Depth Testing
glDepthFunc(GL_LESS); // The Type Of Depth Test To Do
glShadeModel(GL_SMOOTH); // Enables Smooth Color Shading
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
}
void MainWindow::paintGL(){
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
// glEnable(GL_TEXTURE_2D);
// glBindTexture(GL_TEXTURE_2D,texture);
// glTexSubImage2D(GL_TEXTURE_2D, 0, 0,0 , image.width(), image.height(), 0, 0, image.bits() );
//glMatrixMode(GL_MODELVIEW);
//glEnable(GL_DEPTH_TEST);
glTranslated(0.0, 0.0, 1.9);
qglColor(Qt::black);
glBegin(GL_TRIANGLES);
glVertex3d(-0.1,0.1,1);
glVertex3d(-0.1,-0.1,-1);
glVertex3d(0.1,-0.1,0);
glEnd();
}
Any idea why does it happen?
This is actually completely normal behavior.
When you use identity modelview and projection matrices, your coordinates are in clip-space. The default W value for a 3D vertex in OpenGL is 1.0 (vertices are always 4D), and clip-space -> NDC works by dividing each component of a vertex by its W component and then clipping anything with a coordinate outside the range [-1,1].
I think what is confusing you is the glDepthRange (...) call. That does not affect clipping. Depth range is part of the viewport transformation, which happens after clipping.