Applying mix() to textures with an alpha channel results in artifacts - opengl

I have two textures: a cat and a wall, both images are loaded using QImage, have the same size(512x512), and are loaded in PNG format (QImage::Format_ARGB32).
//Load texture image
QImage imgWallTexture(":Scenes/Sc5Texture/textures/wall512x512.png");
if(imgWallTexture.isNull()) {
qInfo("[ERROR] initScene: image texture load failed!");
return;
}
imgWallTexture = QGLWidget::convertToGLFormat(imgWallTexture);
//Load texture image
QImage imgCatTexture(":Scenes/Sc5Texture/textures/cat512x512.png");
if(imgCatTexture.isNull()) {
qInfo("[ERROR] initScene: image texture load failed!");
return;
}
imgCatTexture = QGLWidget::convertToGLFormat(imgCatTexture);
//Texture ID(name)
unsigned int wallTexture = 0;
unsigned int catTexture = 0;
//Allocate 1 buffer for textures
glFunctions->glGenTextures(1, &wallTexture);
glFunctions->glGenTextures(1, &catTexture);
//Select wall texture
glFunctions->glBindTexture(GL_TEXTURE_2D, wallTexture);
//Load data to texture
glFunctions->glTexImage2D(GL_TEXTURE_2D, //selected texture type
0, //mipmap level(we generate him using OpenGL)
GL_RGBA, //texture data format
imgWallTexture.width(),
imgWallTexture.height(),
0, //always zero
GL_RGBA, //image format
GL_UNSIGNED_BYTE, //image data format
imgWallTexture.bits() //image data
);
if(glFunctions->glGetError() != GL_NO_ERROR) {
qInfo("[ERROR] initScene: glTexImage2D failed!");
return;
}
//Generate mipmap for texture
glFunctions->glGenerateMipmap(GL_TEXTURE_2D);
//Setting up texture
//Texture draw
glFunctions->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glFunctions->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
//Texture filtration
glFunctions->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
glFunctions->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
//Select cat texture
glFunctions->glBindTexture(GL_TEXTURE_2D, catTexture);
//Select wall texture
glFunctions->glBindTexture(GL_TEXTURE_2D, catTexture);
//Load data to texture
glFunctions->glTexImage2D(GL_TEXTURE_2D, //selected texture type
0, //mipmap level(we generate him using OpenGL)
GL_RGBA, //texture data format
imgCatTexture.width(),
imgCatTexture.height(),
0, //always zero
GL_RGBA, //image format
GL_UNSIGNED_BYTE, //image data format
imgCatTexture.bits() //image data
);
if(glFunctions->glGetError() != GL_NO_ERROR) {
qInfo("[ERROR] initScene: glTexImage2D failed!");
return;
}
//Generate mipmap for texture
glFunctions->glGenerateMipmap(GL_TEXTURE_2D);
//Setting up texture
//Texture draw
glFunctions->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glFunctions->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
//Texture filtration
glFunctions->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
glFunctions->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
//Unselect texture
glFunctions->glBindTexture(GL_TEXTURE_2D, 0);
In my fragment shader code, I use the mix() function to mix the texture.
#version 450 core
out vec4 FragColor;
in vec2 wallTexCoord;
in vec2 catTexCoord;
uniform float mixValue;
//for texture
uniform sampler2D wallTexture;
uniform sampler2D catTexture;
void main()
{
FragColor = mix(texture(wallTexture, wallTexCoord), texture(catTexture, catTexCoord), mixValue);
}
If mixValue = 0, then I see a normal wall.
If mixValue = 1, then I see a normal cat with no artifacts.
But if the mixValue is closer to 1, then I get artifacts:
I thought that there were some artifacts in the pictures, but I ran both images through Photoshop to avoid internal artifacts in the image.
How do I fix this?

The artefacts are caused by unexpected colour data in transparent texels. An easy fix is to use premultiplied alpha.

Related

OpenGL texture function always return 0 on integer data

I'm working on a deferred shading pipeline, and i stored some information into a texture, and this is the texture attached to my gbuffer
// objectID, drawID, primitiveID
glGenTextures(1, &_gPixelIDsTex);
glBindTexture(GL_TEXTURE_2D, _gPixelIDsTex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32UI, _width, _height, 0, GL_RGB_INTEGER, GL_UNSIGNED_INT, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_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);
And this is how i write IDs into it:
// some other gbuffer textures...
layout (location = 4) out uvec3 gPixelIDs;
gPixelIDs = uvec3(objectID, drawID, gl_PrimitiveID + 1);
After the geometry pass, i can read from it using the following code:
struct PixelIDs {
GLuint ObjectID, DrawID, PrimitiveID;
}pixel;
glBindFramebuffer(GL_READ_FRAMEBUFFER, _gBufferFBO);
glReadBuffer(GL_COLOR_ATTACHMENT4);
glReadPixels(x, y, 1, 1, GL_RGB_INTEGER, GL_UNSIGNED_INT, &pixel);
glReadBuffer(GL_NONE);
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
So far, so good. The output is what i need.
But when i try to use this shader to display the object id on the screen(just for debug purpose)
uniform sampler2D gPixelIDsTex;
uint objID = uint(texture(gPixelIDsTex, fragData.TexCoords).r);
FragColor = vec4(objID, objID, objID, 1);
the result is 0 (I used the Snipaste to read the pixel color), which means i cant use the data in my following process.
Other gbuffer textures with data format in floating point (eg. vec4) all be fine, so i dont know why texture always return 0 on it
uniform sampler2D gPixelIDsTex;
Your texture is not a floating-point texture. It's an unsigned integer texture. So your sampler declaration needs to express that. Just as you write to a uvec3, so too must you read from a usampler2D.

Calculating color histogram of framebuffer inside compute shader

As the title suggests, I am rendering a scene onto a framebuffer and I am trying to extract the color histogram from that framebuffer inside a compute shader. I am totally new to using compute shaders and the lack of tutorials/examples/keywords has overwhelmed me.
In particular, I am struggling to properly set up the input and output images of the compute shader. Here's what I have:
computeShaderProgram = loadComputeShader("test.computeshader");
int bin_size = 1;
int num_bins = 256 / bin_size;
tex_w = 1024;
tex_h = 768;
GLuint renderFBO, renderTexture;
GLuint tex_output;
//defining output image that will contain the histogram
glGenTextures(1, &tex_output);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex_output);
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_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_R16, num_bins, 3, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL);
glBindImageTexture(0, tex_output, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_R16UI);
//defining the framebuffer the scene will be rendered on
glGenFramebuffers(1, &renderFBO);
glGenTextures(1, &renderTexture);
glBindTexture(GL_TEXTURE_2D, renderTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, W_WIDTH, W_HEIGHT, 0, GL_RGBA, GL_FLOAT, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glBindFramebuffer(GL_FRAMEBUFFER, renderFBO);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, renderTexture, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
In the main loop I draw a simple square onto the framebuffer and attempt to pass the framebuffer as input image to the compute shader:
glBindFramebuffer(GL_FRAMEBUFFER, renderFBO);
glDrawArrays(GL_TRIANGLES, 0, 6);
glUseProgram(computeShaderProgram);
//use as input the drawn framebuffer
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, renderFBO);
//use as output a pre-defined texture image
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, tex_output);
//run compute shader
glDispatchCompute((GLuint)tex_w, (GLuint)tex_h, 1);
GLuint *outBuffer = new GLuint[num_bins * 3];
glGetTexImage(GL_TEXTURE_2D, 0, GL_R16, GL_UNSIGNED_INT, outBuffer);
Finally, inside the compute shader I have:
#version 450
layout(local_size_x = 1, local_size_y = 1) in;
layout(rgba32f, binding = 0) uniform readonly image2D img_input;
layout(r16ui, binding = 1) uniform writeonly image2D img_output;
void main() {
// grabbing pixel value from input image
vec4 pixel_color = imageLoad(img_input, ivec2(gl_GlobalInvocationID.xy));
vec3 rgb = round(pixel_color.rgb * 255);
ivec2 r = ivec2(rgb.r, 0);
ivec2 g = ivec2(rgb.g, 1);
ivec2 b = ivec2(rgb.b, 2);
imageAtomicAdd(img_output, r, 1);
imageAtomicAdd(img_output, g, 1);
imageAtomicAdd(img_output, b, 1);
}
I defined the output as a 2d texture image of size N x 3 where N is the number of bins and the 3 accounts for the individual color components. Inside the shader I grab a pixel value from the input image, scale it into the 0-255 range and increment the appropriate location in the histogram.
I cannot verify that this works as intended because the compute shader produces compilation errors, namely:
can't apply layout(r16ui) to image type "image2D"
unable to find compatible overloaded function "imageAtomicAdd(struct image2D1x16_bindless, ivec2, int)"
EDIT: after changing to r32ui the previous error now becomes: qualified actual parameter #1 cannot be converted to less qualified parameter ("im")
How can I properly configure my compute shader?
Is my process correct (at least in theory) and if not, why?
As for your questions:
can't apply layout(r16ui) to image type "image2D"
r16ui can only be applied to unsigned image types, thus you should use uimage2D.
unable to find compatible overloaded function ...
The spec explicitly says that atomic operations can only by applied to 32-bit types (r32i, r32ui, or r32f). Thus you must use a 32-bit texture instead.
Your have other issues in your code too.
glBindTexture(GL_TEXTURE_2D, renderFBO);
You cannot bind an FBO to a texture. You should instead bind the texture that backs the FBO (renderTexture).
Also, you intend to bind a texture to an image uniform rather than a sampler, thus you must use glBindImageTexture or glBindImageTextures rather than glBindTexture. With the later you can bind both images in one call:
GLuint images[] = { renderTexture, tex_output };
glBindImageTextures(0, 2, images);
Your img_output uniform is marked as writeonly. However the atomic image functions expect an unqualified uniform. So remove the writeonly.
You can find all the above information in the OpenGL and GLSL specs, freely accessible from the OpenGL registry.

How to draw cv::UMat data with opengl?

I have found a function in order to convert cv::Mat To Texture and it is worked:
GLuint matToTexture(cv::Mat &mat, GLenum minFilter, GLenum magFilter, GLenum wrapFilter)
{
// Generate a number for our textureID's unique handle
GLuint textureID;
glGenTextures(1, &textureID);
// Bind to our texture handle
glBindTexture(GL_TEXTURE_2D, textureID);
// Set texture interpolation methods for minification and magnification
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilter);
// Set texture clamping method
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapFilter);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapFilter);
// Create the texture
glTexImage2D(GL_TEXTURE_2D, // Type of texture
0, // Pyramid level (for mip-mapping) - 0 is the top level
GL_RGB, // Internal colour format to convert to
mat.cols, // Image width i.e. 640 for Kinect in standard mode
mat.rows, // Image height i.e. 480 for Kinect in standard mode
0, // Border width in pixels (can either be 1 or 0)
GL_BGR, // Input image format (i.e. GL_RGB, GL_RGBA, GL_BGR etc.)
GL_UNSIGNED_BYTE, // Image data type
mat.data); // The actual image data itself
glBindFramebuffer(GL_TEXTURE_2D, textureID);
return textureID;
}
// example of usage
GLuint tex = matToTexture(mat, GL_NEAREST, GL_NEAREST, GL_CLAMP);
glBindTexture(GL_TEXTURE_2D, tex);
Is there any way to convert cv::UMat data to opengl texture directly (without converting to cv::Mat)?

OpenGL Depth Texture is the same as Color Texture

I'm trying to visualize the depth texture attached to a custom fbo in opengl. I expected to see a "foggy" looking, greyscale output - the problem is that the output seems like it's the same as the color texture - I mean it's the exact same color texture. Am I actually rendering the same thing into 2 different textures?
Creating the depth texture:
glGenTextures(1, &m_DepthTxId);
glBindTexture(GL_TEXTURE_2D, m_DepthTxId);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, width, height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, (void*)nullptr);
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);
Creating the fbo & attaching the color/depth textures:
glGenFramebuffers(1, &m_Id);
glBindFramebuffer(GL_FRAMEBUFFER, m_Id);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_ColorTxId, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, m_DepthTxId, 0);
m_DrawBuffers[0] = GL_COLOR_ATTACHMENT0;
glDrawBuffers(1, m_DrawBuffers);
The interesting thing is that without adding the depth texture, the objects are displayed in the order they were rendered (scene looks messed up) - but when I add the depth texture, everything looks fine as you would expect with depth testing.
But how come when I give the depth texture to the shader it just displays the color-texture? Am I rendering color data into my depth texture, or..?
Apologies, but I'm fairly new to working with fbos :P
Additional info:
Rendering the fbo-textures onto a quad:
glActiveTexture(GL_TEXTURE0); // Color
glBindTexture(GL_TEXTURE_2D, m_Fbo->getColorTxId()); // ColorTexture ID
glUniform1i(m_Shader->getColorSampler(), 0); // ColorSampler Location
glActiveTexture(GL_TEXTURE1); // Depth
glBindTexture(GL_TEXTURE_2D, m_Fbo->getDepthTxId()); // DepthTexture ID
glUniform1i(m_Shader->getDepthSampler(), 0); // DepthSampler Location
glBindVertexArray(m_Fbo->getVaoId());
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, (void*)0);
glBindVertexArray(0);
Getting the sampler locations from the shader:
m_Location_ColorTxSampler = glGetUniformLocation(m_ProgramId, "colorSampler");
m_Location_DepthTxSampler = glGetUniformLocation(m_ProgramId, "depthSampler");
Shader:
in vec2 uv;
uniform sampler2D colorSampler;
uniform sampler2D depthSampler;
out vec4 color;
void main()
{
color = vec4(texture2D(depthSampler, uv).rgb, 1.0);
}
To me the whole thing seems correct, unless the 2 samplers are at the same location.. I'm sure that's not possible
If you bind the depth texture to unit 1...
glActiveTexture(GL_TEXTURE1); // Depth
glBindTexture(GL_TEXTURE_2D, m_Fbo->getDepthTxId()); // DepthTexture ID
... you should not tell the shader to sample from unit 0:
glUniform1i(m_Shader->getDepthSampler(), 0); // DepthSampler Location

Opengl glsl can't mix 2d textures and 3d textures

[Edit2]: Nothing wrong with this code. My shader class didn't load the uniforms correctly.
[Edit]: It seems like I can only use GL_TEXTURE0/texture unit 0 by some reason.
What I want is to draw a 2d texture and a 3d texture, but only the texture with texture unit 0(GL_TEXTURE_0) will work. And I use both of them at the same time in the shader I can't see anything using that shader.
This is the fragment shader code I want to use:
#version 330 core
// Interpolated values from the vertex shaders
in vec3 fragmentColor;
in vec3 fragmentPosition;
// Ouput data
out vec3 color;
uniform sampler3D textureSampler3D;
uniform sampler2D textureSampler2D;
float getInputLight(vec3 pos);
void main(){
// Get the nearest corner
vec3 cornerPosition = vec3(round(fragmentPosition.x), round(fragmentPosition.y), round(fragmentPosition.z));
float light = getInputLight(cornerPosition);
color = (0.5+16*light)*fragmentColor;
}
float getInputLight(vec3 pos) {
if (pos.z <= 0.f)
return texture2D(textureSampler2D, vec2(pos.x/16, pos.y/16)).r;
return texture(textureSampler3D, vec3(pos.x/16, pos.y/16, pos.z/16)).r;
}
But with that I can't see anything made by that shader. If I use this I can see what the 2d textures does.
float getInputLight(vec3 pos) {
if (pos.z <= 0.f)
return texture(textureSampler2D, vec2(pos.x/16, pos.y/16)).r;
return 0.f;
}
If I use this it will work perfectly except that I only have the 3d texture:
float getInputLight(vec3 pos) {
return texture(textureSampler3D, pos/16).r;
}
That means that I can only use one of the textures on the shaders. When I say that I use the 3d texture then I change getInputLight so I it just used the 3d texture. I do the same thing with 2d textures except that I change it to the other version.
This is the c++ code I use to load the 3d texture:
glGenTextures(1, &m_3dTextureBuffer);
glBindTexture(GL_TEXTURE_3D, m_3dTextureBuffer);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexImage3D(GL_TEXTURE_3D, 0, GL_R8, voxelMatrixWidth, voxelMatrixHeight, voxelMatrixDepth, 0, GL_RED, GL_UNSIGNED_BYTE, (GLvoid*)m_lightData);
This is the code I use to load the 2d texture:
GLuint buffer;
unsigned char *voidData = new unsigned char[256];
// With this I can see if the shader has right data.
for (int i = 0; i < 256; ++i)
voidData[i] = i%16;
glGenTextures(1, &buffer);
glBindTexture(GL_TEXTURE_2D, buffer);
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_R8, 16, 16, 0, GL_RED, GL_UNSIGNED_BYTE, (GLvoid*)voidData);
m_3dTextureBuffer = buffer;
This is the code I run before it draws the vertex buffer:
GLint texture3dId = shader->getUniform(1);
GLint texture2dId = shader->getUniform(2);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_3D, m_3dTextureBuffer);
glUniform1i(texture3dId, 0);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, m_2dTextureBuffer);
//glUniform1i(texture3dId,1);
If I use texture unit 0(TEXTURE0) for both 2d texture and 3d texture I get data/pixels of what I expect.
This is a picture of it and it is what I expect:
http://oi57.tinypic.com/3wrbd.jpg
If I use different units I get this random data and it falshes sometimes(every pixel turns black/0 for a frame). The random data doesn't change either. If you look in some direction it doesn't flash and some directions flash faster than others.
http://oi58.tinypic.com/2ltiqoo.jpg
When I swap texture unit of the 3d texture an the 2d texture the same thing happens, but the 2d texture works an the 3d texture fails.
Do you have any idea what it could be?
There was nothing wrong with this code. My shader class didn't load the uniforms correctly.