OpenGL 3D Model Transparency Issues - opengl

I've recently imported a 3D model made in Blender to OpenGL using Assimp. While all of the geometry is fine, I noticed that some transparent planes (the Star) are not rendering correctly in OpenGL.
It seems that OpenGL senses that the plane is transparent, and then forgets to draw any of the model behind it. As I rotate the view, the "transparent" part of the plane (Star) constantly changes texture.
In addition, the model's Staff she's holding in her right hand has a transparency applied from the texture as well. When it intersects over her leg, it seems to be the same effect as the Star, which almost looks like some messed up backface culling.
For reference, I've made sure that:
Backface culling is disabled
The PNG textures have transparent backgrounds
Alpha blending is turned on
Also the first and third pictures are from the Assimp Model Viewer, but the same result happens in my OpenGL program.
My OpenGL texture is loaded by:
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, surface->w, surface->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, surface->pixels);
And my fragment shader looks like:
#version 130
in vec2 texcoord;
uniform sampler2D textureSample;
void main()
{
gl_FragData[0] = texture(textureSample, texcoord).aaaa * texture(textureSample, texcoord);
}
And my blending / depth is:
glEnable(GL_DEPTH_TEST);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDepthFunc(GL_LEQUAL);
Any idea what could be the problem?

Related

OpenGL: Transparent texture issue

I have troubles with Texture transparency in OpenGL. As you can see in the picture below, it doesn't quite work. It's worth noting, that the black is actually the ClearColor, I use to clear the screen.
I use the following code to implement blending:
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
Here's my fragment shader:
#version 330 core
in vec2 tex_coords;
out vec4 color;
uniform vec4 spritecolor;
uniform sampler2D image;
void main(void)
{
color = spritecolor * texture(image, tex_coords);
}
Here is a screenshot of the scene in wireframe mode, in case it helps with the drawn vertices:
If anything else is needed, feel free to ask, I'll add it.
You have to do a Transparency Sorting
If a scene is drawn, usually the depth test (glDepthFunc) is set to GL_LESS. This causes fragments to be drawn only when they are in front of the scene so far drawn.
To draw transparents correctly, you have to draw the opaque objects first. The transparent objects have to be drawn after, sorted by the reverse distance to the camera position.To draw transparents correctly, you have to draw the opaque objects first. The transparent objects have to be drawn after, sorted by the reverse distance to the camera position.
Draw the transparent object first, which has the largest distance to the camera position and draw the transparent object last, which has the lowest distance to the camera position.
See also the answers to the following questions:
OpenGL depth sorting
opengl z-sorting transparency
Fully transparent OpenGL model

OpenGL ES 2.0 : paint in FBO + Texture = gray blending in texture

This is how I render my brush in the fragment shader :
gl_FragColor.rgb = Color.rgb;
gl_FragColor.a = Texture.a * Color.a;
With this Blending function on a (0, 0, 0, 0) texture :
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
This is what I see when I draw my texture ADDED to my white background with glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) :
But this is what I get in my texture :
Can someone help me to understand why I got this grayed stroke in my texture ? Because I need to take a screenshot of this texture, and I want to have the same rendering but without white background.
[1st picture] When I draw my "view" I have a white background
[2nd picture] But I store my stroke in a texture who have a transparent background
You're doing two blending operations, one in your shader, one using the glBlendFunc call. When rendered directly to the screen it doesn't apply the glBlendFunc a second time, however, when rendering to a texture it gets applied when rendering to the texture, and then again when rendering the texture to the screen.
You have two options, 1) don't do blending your shader, 2) use a different blend function (I find glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); to work well). I find option 2 to work the best for me, handling OpenGL blend functions is notoriously annoying.

OpenGL - Is there an easier way to fill window with a texture, instead using VBO,etc?

My OpenGL window is drawn like this:
glClearColor(0.3f, 0.4f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
I want to use a texture to fill up the window.
Is there an easier way to do that, instead of creating another VBO, EBO besides the one I'm already using for my triangles?
Since there is the glClearColor that fills the background..
The most direct and generally most efficient way to draw a texture to the window is by using glBlitFramebuffer().
To use this, you need to create an FBO, and attach your texture texId to it:
GLuint fboId = 0;
glGenFramebuffers(1, &fboId);
glBindFramebuffer(GL_READ_FRAMEBUFFER, fboId);
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, texId, 0);
Note that the code above bound GL_READ_FRAMEBUFFER, since we want to use this as the source of the blit.
Then, to copy the content:
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); // if not already bound
glBlitFramebuffer(0, 0, width, height, 0, 0, width, height,
GL_COLOR_BUFFER_BIT, GL_NEAREST);
This is for the case where texture and window have the same size. Otherwise, you can specify different sizes in the first 8 arguments, and may want to use GL_LINEAR for the last parameter.
Using glBlitFramebuffer() has a few advantages over drawing a window sized textured quad:
It needs fewer API calls.
You don't need to write a shader for the copy operation.
You don't need to bind a different shader program, which can reduce overhead.
The driver may have a more optimized code path for the operation, compared to using an app provided shader and draw call.
Many GPUs have dedicated units for blitting data, which can be more efficient than the programmable shader units. They can also potentially run in parallel to the general purpose programmable part of the GPU, allowing the copy to be executed in parallel with rendering. If that applies, the performance gain can be very substantial.
In one word: No.
Well in legacy OpenGL there'd be glDrawPixels but this function never was very well supported and dead slow on most implementation. You better forget that I told you about it. Also it's been removed from modern OpenGL and never existed in OpenGL-ES.
There are already some answers to this question, but I want to add some more alternatives, for completeness:
1. attributeless rendering
With modern GL, you can render completely without vertex attributes. You can put the 4 2d coordiantes of the full screen rect directly as a const array into the vertex shader and access them via gl_VertexID:
// VERTEX SHADER
#version 150 core
out vec2 v_tex;
const vec2 pos[4]=vec2[4](vec2(-1.0, 1.0),
vec2(-1.0,-1.0),
vec2( 1.0, 1.0),
vec2( 1.0,-1.0));
void main()
{
v_tex=0.5*pos[gl_VertexID] + vec2(0.5);
gl_Position=vec4(pos[gl_VertexID], 0.0, 1.0)
}
// FRAGMENT SHADER
#version 150 core
in vec2 v_tex;
uniform sampler2D texSampler;
out vec4 color;
void main()"
{
color=texture(texSampler, v_tex);
}
If your texture exactly matches the resolution of your viewport (so you are not scaling the texture at all), you can completely remove the v_tex varying and use color=texelFetch(texSampler, ivec2(gl_FragCoord.xy)) in the FS, as #datenwolf suggested in his comment.
In any case, you still need some VAO bound, even if no attributes are enabled in it. So this method requires you to do the following once during intialization:
Create and compile the shaders and link them to the program
Create a new VAO name by a glGenVertexArrays() call
And for drawing, you have to:
Bind the texture you want to draw
Use the program
Bind the (still empty) VAO
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)
You might also be able to simply re-use the currently bound VAO. As the shader does not access any attributes, it does not matter what data your VBOs provide, and which attributes are enabled currently.
This method requires you to switch the shader, which isn't exactly cheap either, so it might be better to just switch the buffer bindigs and keep the current shader.. But you might need to switch the shader anyway.
2. nvidia-specifc extension
NVidia provides a specific extension for the task of drawing a texture to the screen: NV_draw_texture. This introduces the glDrawTextureNV() function which allows drawing a texture without setting changing anything on the GL state. Quoting from the overview section of the extension spec:
While this functionality can be obtained in unextended OpenGL by drawing a
rectangle and using a fragment shader to do a texture lookup,
DrawTextureNV() is likely to have better power efficiency on
implementations supporting this extension. Additionally, use of this
extension frees the application developer from having to set up
specialized shaders, transformation matrices, vertex attributes, and
various other state in order to render the rectangle.
The drawback of this method is of course that it is nvidia-specific, so it is probably of less practical use in a general GL application.
You can render your texture to a fullscreen quad using an ortographic projection:
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, texture);
glDisable(GL_LIGHTING);
// Set up ortographic projection
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glOrtho(0, width, 0, height, -1, 1);
// Render a quad
glBegin(GL_QUADS);
glTexCoord2f(0,0); glVertex2f(0,0);
glTexCoord2f(0,1); glVertex2f(0,width);
glTexCoord2f(1,1); glVertex2f(height, width);
glTexCoord2f(1,0); glVertex2f(height,0);
glEnd();
// Reset Projection Matrix
glPopMatrix();
glDisable(GL_TEXTURE_2D);
glEnable(GL_LIGHTING);
Render this into your framebuffer instead of glClearColor.

What user's framebuffer lacks?

Here is a comparison of same object using framebuffer texture projected onto screen and "main framebuffer"
Left image is bit blured while right is more sharp.Alos some options like glPolygonMode( GL_FRONT_AND_BACK, GL_LINE ) do not work properly while rendering into the framebuffer.
My "pipeline" looks like this
Bind frambuffer
draw all geometry
Unbind
Draw on Quad like as texture.
So I wondering why "main frambufffer" can do this while "mine" can't? What are the differences between those two? Does user framebuffers skips some stages? Is it possible to match the quality of main buffer?
void Fbo::Build()
{
glGenFramebuffers(1, &fboId);
glBindFramebuffer(GL_FRAMEBUFFER, fboId);
renderTexId.resize(nColorAttachments);
glGenTextures(renderTexId.size(),&renderTexId[0]);
for(int i=0; i<nColorAttachments; i++)
{
glBindTexture(format,renderTexId[i]);
glTexParameterf(format, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(format, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(format, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(format, GL_TEXTURE_WRAP_T, GL_CLAMP);
glTexImage2D(format, 0, type, width, height, 0, type, GL_FLOAT, 0);
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i,renderTexId[i], 0);
}
glBindTexture(GL_TEXTURE_2D, 0);
if(hasDepth)
{
glGenRenderbuffers(1, &depthBufferId);
glBindRenderbuffer(GL_RENDERBUFFER, depthBufferId);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height);
//glTexImage2D(GL_TEXTURE_2D, 0,GL_DEPTH_COMPONENT24, width, height, 0,GL_DEPTH_COMPONENT, GL_FLOAT, 0);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthBufferId);
}
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE)
{
printf("FBO error, status: 0x%x\n", status);
}
}
Your "projection" of the FBO onto the screen is subject to sampler state, in particular the texture filter state is to blame here.
By default, if you simply bind the texture attachment you drew into from your FBO to a texture unit and apply it, it is going to use LINEAR sampling. This is different from blitting directly to the screen as would traditionally be the case if you were not using an FBO.
Default State table for Samplers in OpenGL:
http://www.opengl.org/registry/doc/glspec44.core.pdf pp. 541, Table 23.18 Textures (state per sampler object)
If you want to replicate the effect of drawing without an FBO, you would want to stretch a quad (or two triangles) over your viewport and use NEAREST neighbor sampling for your texture filter. Otherwise, it is going to sample adjacent texels in your FBO and interpolate them for each pixel on screen. This is the cause of your smoother image on the left side, which illustrates a form of anti-aliasing. It is worth mentioning that this is not even close to the same thing as MSAA or SSAA, which increase the sample rate when geometry is rasterized to fix undersampling errors, but it does achieve a similar effect.
Sometimes this is desirable, however. Many processing intensive algorithms run at 1/4, 1/8, or lower resolution and then use a bilinear or bilateral filter to upsample to the viewport resolution without the blockiness associated with nearest neighbor sampling.
The polygon mode state should work just fine. You will need to remember to set it back to GL_FILL before you draw your quad over the viewport though. Again, it all comes back to state management here - your quad will require some very specific states to produce consistent results. To render this way effectively you will probably have to implement a more sophisticated state management system / batch processor, you can no longer simply set glPolygonMode (...) once globally and forget it :)
UPDATE:
Thanks to datenwolf's comments, it should be noted that the above discussion of texture filtering was under the assumption your FBO was at a different resolution than the viewport you were trying to stretch it over.
If your FBO and viewport are at the same resolution, and you are still getting these artifacts from LINEAR texture filtering, then you have not setup your texture coordinates correctly. The problem in this scenario is that you are sampling your FBO texture at locations other than the texel centers and this is causing interpolation where none should be necessary.
Fragments are sampled at their centers (non-multisample) in GLSL by default, so if you setup your vertex texture coordinates and positions correctly you will not have to do any texel offset math on your per-vertex texture coordinates. Perspective projection can ruin your day if you are trying to do 1:1 mapping though, so you should either use orthographic projection, or better yet use NDC coordinates and no projection at all when you draw your quad over the viewport.
You can use the following vertex coordinates in Normalized Device Coordinates: (-1,-1,-1), (-1,1,-1), (1,1,-1),(1,-1,-1) for the 4 corners of your viewport if you replace the traditional modelview / projection matrices with an identity matrix (or simply do not multiply the vertex position by any matrix in your vertex shader).
You should also use CLAMP_TO_EDGE as your wrap state, because this will ensure you never generate texture coordinates outside the range of the center of the first texel and the center of the last texel in a given direction (s,t). CLAMP will actually generate values of 0 and 1 (which are not texel centers) for anything at or beyond the edges of the FBO texture attachment.
As a bonus, if you ALWAYS intend to render at 1:1 (FBO vs. viewport), you can avoid using per-vertex texture coordinates altogether and use gl_FragCoord. By default in GLSL, gl_FragCoord will give you the coordinate for the fragment center (0.5, 0.5), which also happens to be the corresponding texel center in your FBO. You can pass gl_FragCoord.st directly to your texture lookup in this special case.

OpenGL Alpha Blending Issue, Blending ignored (maybe)?

EDIT + BETTER SOLUTION:
In case anyone happens to run into the problem I was running into, there are two solutions. One is the solution accepted, but that only applies if you are doing things how I was. Let me explain what I was doing:
1.) Render star background to screen
2.) Render ships, then particles to the FBO
3.) Render FBO to screen
This problem, and therefor the solution to this problem, occurred in the first place because I was blending the FBO with the star background.
The real solution, which is supposedly also slightly faster, is to simply render the star background to the FBO, then render the FBO to screen with blending disabled. Using this method, I do not need to mess with glBlendFuncSeparate...
1.) Render stars, then ships, then particles to FBO
2.) Render FBO to screen with blending disabled
----------ORIGINAL QUESTION:----------
From what I understand of the issue, blending is being ignored somehow. The particle texture with alpha transparency completely overwrites below pixels.
I am creating a top-down game. The camera is slightly angled so that there is some feeling of depth. I am rendering my ships, then rending the particles above them...
After beginning the OpenGL context
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
glCullFace(GL_BACK);
In the render loop, I do this:
glBindFramebuffer(GL_FRAMEBUFFER,ook->fbo);
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
entitymgr_render(ook); //Render entities. Always 1.0 alpha textures.
glDisable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
//glBlendFunc(GL_SRC_ALPHA,GL_ONE); //This would enable additive blending for non-premult
particlemgr_render(ook); //Render particles. Likely always <1.0 alpha textures
//glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
glBindFramebuffer(GL_FRAMEBUFFER,0);
If I run with the above code, I get results like this...
Particle tex:
Screenshot from OGL Profiler (Mac tool):
Screenshot of the FBO without any particle rendered:
Screenshot of the FBO with some particles rendered on top:
As you can see, the particle, despite having alpha transparency, doesn't blend with the ship rendered below. Instead, it just completely overwrites the pixels.
Final notes, setting pixel transparency in the shader blends correctly - the ship appears below. And here is my shader:
#version 150
uniform sampler2D s_tex1;
uniform float v4_color;
in vec4 vertex;
in vec3 normal;
in vec2 texcoord;
out vec4 frag_color;
void main()
{
frag_color=texture(s_tex1,texcoord)*v4_color;
if(frag_color.a==0.0) discard;
}
Let me know if there is anything I can provide.
Looks to me like it is rendering the alpha channel as well into the frame buffer, so when you write the particles, the src alpha channel is getting mixed with the destination alpha channel, which is not what you want here.
This is exactly why the glblendfuncseparate() function was created. Try this...
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE);
So, the alpha channel of your particles will be used to determine the colours of the final pixels, but the alpha channels of the source and destination will be added together.
My guess is that the FBO's rgb channels are being rendered correctly, but because it also has an alpha channel, and it is being drawn with blending enabled, the end result has incorrect transparency where the particle overlaps the spaceship.
Either use glBlendFuncSeparate (described here) to use different blend factors for the alpha channel when you're drawing the particles:
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE);
Or turn off blending altogether when you draw your FBO onto the screen.
In order to obtain texture transparency, other than:
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
you should assure also that:
when creating the particle tex with glTexImage2D, use as format GL_RGBA (or GL_LUMINANCE_ALPHA if you are using gray shaded textures)
when drawing the particle texture, after the glBindTexture command, call
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND);
Instead of GL_BLEND, you could use the correct texture functions as described in
the glTexEnv reference: http://www.opengl.org/sdk/docs/man2/xhtml/glTexEnv.xml