I am having difficulty making a square fade to black using exclusively the shaders. I am using OpenGL combined with C++ and Qt. What I need to do is send the vertex list to the GPU one time and only have one draw call. Using that one vertex list, I need the square to fade to black over a certain amount of time. Is this possible by using only OpenGL shaders? I do not want to do this on the CPU side because it is part of a larger application and makes it extremely slow. How do I make a shader fade without sending it new faded values?
The fade out effect is easy done in the fragment shader.
You have a timer in the CPU. Every time it fires you drecrease a var remainingPercent meaning how much of full "white" (as opposed to full black) you want to see in the window. Let this value be in range [0, 1.0].
Each time the timer fires you just bind the VAO's and call glDrawXX, no need to fill the buffers again. And call swapbuffers.
You pass this remainingPercent value by a glUniform to shader. The last step is mixing the current color with the full black by the amount of this uniform. Something like:
float uniform fadeOut;
in vec4 fragColor;
out vec4 finalColor;
....
finalColor = mix(vec4(0.0), fragColor, fadeOut);
Related
I'm using OpenGL to draw a basic UI with knobs and buttons, which I'm trying to performance optimize as much as possible. The UI only needs to update on user input, but at the moment I'm updating it # 60fps. Because of this, I want to keep the amount of OpenGL calls per frame to an absolute minimum.
The knobs are mapped as textures onto squares in an 2D orthogonal projection, via a fragment shader. The rotation of the knob is stored as a uniform float rotation in the shader.
Everything works just fine after I set the uniform value with glUniform1f(…); and call glDrawArrays(GL_TRIANGLES, …); but it seems wasteful to redraw the triangles every time since the position will be the same every time.
Ideally, I would just draw the geometry once, and only update the uniform values with glUniform() calls every time a value changed - but when I remove glDrawArrays from my render loop and update the uniform values before triggering a glFlush(), the fragment shaders stay the same.
Is this by design, and am I supposed to redraw the triangles to trigger a repaint of the fragment shader? Or is there a more performant workaround?
This question already has an answer here:
(How) can a shader view the current render-buffer?
(1 answer)
Closed 3 years ago.
I'm trying to basically "add" framebuffers, or better the colortexture attachmement of framebuffers. I found a way to do this is by having a shader which gets all the textures and renders their combination.
But to improve the performance wouldn't it be better to just have one shader and framebuffer, and then through instanced drawing the shader draws onto the framebuffer colortexture attachement it is using for drawing?
A bit better explained:
I have 2 framebuffers: Default and Framebuffer1.
I bind Framebuffer1
and give the colortexture attachment of Framebuffer1 as uniform "Fb1_cta" to the following fragment shader:
out vec4 FragColor;
in vec2 TexCoords;
uniform sampler2D Fb1_cta;
void main()
{
vec3 text = texture(Framebuffer1, TexCoords).rgb;
FragColor = vec4(vec3(0.5) + text, 1.0);
}
So i draw into Framebuffer1, but also use the current colortexture attachement for the drawing.
Now I call glDrawArraysInstanced with instancecount 2.
The first renderpass should draw the whole texture in grey (rgb = (0.5, 0.5, 0.5)) and the second should add another vec3(0.5) to that, so the result will be white. That however didn't really work so I split the glDrawArraysInstanced into 2 glDrawArrays and checked the 2 results.
Now while the first pass works as intended:Result of first rendering
The second didn't (btw this is the same result as with the glDrawArraysInstanced):Result of second rendering
To me this pretty much looks like the two renderpasses aren't done sequentially, but in parallel. So I did rerun my code but this time with a bit of time passing between the calls and that seemed to have solved the issue.
Now I wonder is there any way to tell OpenGL that those calls should truly be sequential and might there even be a way to do it with glDrawArraysInstanced to improve the performance?
Is there in general a more elegant solution to this kind of problem?
In general, you cannot read from a texture image that is also being rendered to. To achieve the level of performance necessary for real-time rendering, it is essential to take advantage of parallelism wherever possible. Fragment shader invocations are generally not processed sequentially. On a modern GPU, there will be thousands and thousands of fragment shader invocations running concurrently during rendering. Even fragment shader invocations from separate draw calls. OpenGL and GLSL are designed specifically to enable this sort of parallelization.
From the OpenGL 4.6 specification, section 9.3.1:
Specifically, the values of rendered fragments are undefined if any shader stage
fetches texels and the same texels are written via fragment shader outputs, even
if the reads and writes are not in the same draw call, unless any of the following
exceptions apply:
The reads and writes are from/to disjoint sets of texels (after accounting for
texture filtering rules).
There is only a single read and write of each texel, and the read is in
the fragment shader invocation that writes the same texel (e.g. using
texelFetch2D(sampler, ivec2(gl_FragCoord.xy), 0);).
If a texel has been written, then in order to safely read the result a texel fetch
must be in a subsequent draw call separated by the command
void TextureBarrier( void );
TextureBarrier will guarantee that writes have completed and caches have
been invalidated before subsequent draw calls are executed.
The OpenGL implementation is allowed to (and, as you have noticed, will actually) run multiple drawcalls concurrently if possible. Across all the fragment shader invocations that your two drawcalls produce, you do have some that read and write from/to the same sets of texels. There is more than a single read and write of each texel from different fragment shader invocations. The drawcalls are not separated by a call to glTextureBarrier(). Thus, your code produces undefined results.
A drawcall alone does not constitute a rendering pass. A rendering pass is usually understood as the whole set of operations that produce a certain piece of output (like a particular image in a framebuffer) that is then usually again consumed as an input into another pass. To make your two draw calls "truly sequential", you could call glTextureBarrier() between issuing the draw calls.
But if all you want to do is draw two triangles, one after the other, on top of each other into the same framebuffer, all you have to do is draw two triangles and use additive blending. You don't need instancing. You don't need separate drawcalls. Just draw two triangles. OpenGL requires blending to take place in the order in which the triangles from which the fragments originated were specified. Be aware that if you happen to have depth testing enabled, chances are your depth test is going to prevent the second triangle from ever being drawn unless you did change the depth testing function to something other than the default.
The downside of blending is that you're limited to a set of a few fixed functions that you can select as your blend function. But add is one of them. If you need more complex blending functions, there are vendor-specific extensions that enable what is typically called "programmable blending" on some GPUs…
Note that all of the above only concerns drawcalls that read-from and write to the same target. Drawcalls that read from a target that ealier drawcalls rendered to are guaranteed to be sequenced after the drawcalls that produced their input.
I have a texture array (~512 layers).
Some of the textures I upload have 4 channels (RGBA), some have only one (RED).
When creating individual textures, I can do this:
GLint swizzleMask[] = { GL_ONE, GL_ONE, GL_ONE, GL_RED };
glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask);
Can I do this for specific layers of my texture array? (Swizzling should apply to one texture in the array only, not the others).
I suspect this is not possible, and if so, what's the preferred method? (Vertex attributes would be my last resort option).
(i) EDIT: Looking preferably for an OpenGL 3.3 or below solution.
(ii) EDIT: The idea is that I have RGBA bitmaps for my game (grass, wall, etc...) and I also have font bitmaps. I'm trying to render these in the same draw call.
In my fragment shader, I have something like:
uniform sampler2DArray TextureArraySampler;
out vec4 FragmentColor;
in VertexOut
{
vec2 UV;
vec4 COLOR;
flat uint TEXTURE_INDEX;
} In;
void main(void)
{
FragmentColor = In.COLOR * texture(TextureArraySampler, vec3(In.UV.x, In.UV.y, In.TEXTURE_INDEX));
}
So, when rendering fonts, I would like the shader to sample like:
FragmentColor = In.COLOR * vec4(1, 1, 1, texture(TextureArraySampler, vec3(In.UV.x, In.UV.y, In.TEXTURE_INDEX)).r);
And, when rendering bitmaps:
FragmentColor = In.COLOR * texture(TextureArraySampler, vec3(In.UV.x, In.UV.y, In.TEXTURE_INDEX)).rgba;
To start with, no, there's no way to do what you want. Well, there is a way, but it involves sticking a non-dynamically uniform conditional branch in your fragment shader, which is not a cost worth paying.
I'm trying to render these in the same draw call.
Performance presentations around OpenGL often talk about reducing draw calls being an important aspect of performance. This is very true, particularly for high-performance applications.
That being said, this does not mean that one should undertake Herculean efforts to reduce the number of draw calls to 1. The point of the advice is to get people to structure their engines so that the number of draw calls does not increase with the complexity of the scene.
For example, consider your tile map. Issuing a draw call per-tile is bad because the number of draw calls increases linearly with the number of tiles being drawn. So it makes sense to draw the entire tile map in a single call.
Now, let's say that your scene consists of tile maps and font glyphs, and it will always be exactly that. You could rendering this in two calls (one for the maps and one for the glyphs), or you could do it in one call. But the performance difference between them will be negligible. What matters is that adding more tiles/glyphs does not mean adding more draw calls.
So you should not be concerned about adding a new draw call to your engine. What should concern you is if you're adding a new draw call per-X to your engine.
I want to be able to (in fragment shader) add one texture to another. Right now I have projective texturing and want to expand on that.
Here is what I have so far :
Im also drawing the viewfrustum along which the blue/gray test image is projected onto the geometry that is in constant rotation.
My vertex shader:
ProjTexCoord = ProjectorMatrix * ModelTransform * raw_pos;
My Fragment Shader:
vec4 diffuse = texture(texture1, vs_st);
vec4 projTexColor = textureProj(texture2, ProjTexCoord);
vec4 shaded = diffuse; // max(intensity * diffuse, ambient); -- no shadows for now
if (ProjTexCoord[0] > 0.0 ||
ProjTexCoord[1] > 0.0 ||
ProjTexCoord[0] < ProjTexCoord[2] ||
ProjTexCoord[1] < ProjTexCoord[2]){
diffuse = shaded;
}else if(dot(n, projector_aim) < 0 ){
diffuse = projTexColor;
}else{
diffuse = shaded;
}
What I want to achieve:
When for example - the user presses a button, I want the blue/gray texture to be written to the gray texture on the sphere and rotate with it. Imagine it as sort of "taking a picture" or painting on top of the sphere so that the blue/gray texture spins with the sphere after a button is pressed.
As the fragment shader operates on each pixel it should be possible to copy pixel-by-pixel from one texture to the other, but I have no clue how, I might be googling for the wrong stuff.
How can I achieve this technically? What method is most versatile? Suggestions are very much appreciated, please let me know If more code is necessary.
Just to be clear, you'd like to bake decals into your sphere's grey texture.
The trouble with writing to the grey texture while drawing another object is it's not one to one. You may be writing twice or more to the same texel, or a single fragment may need to write to many texels in your grey texture. It may sound attractive as you already have the coordinates of everything in the one place, but I wouldn't do this.
I'd start by creating a texture containing the object space position of each texel in your grey texture. This is key, so that when you click you can render to your grey texture (using an FBO) and know where each texel is in your current view or your projective texture's view. There may be edge cases where the same bit of texture appears on multiple triangles. You could do this by rendering your sphere to the grey texture using the texture coordinates as your vertex positions. You probably need a floating point texture for this, and the following image probably isn't the sphere's texture mapping, but it'll do for demonstration :P.
So when you click, you render a full screen quad to your grey texture with alpha blending enabled. Using the grey texture object space positions, each fragment computes the image space position within the blue texture's projection. Discard the fragments that are outside the texture and sample/blend in those that are inside.
I think you are overcomplicating things.
Writes to textures inside classic shaders (i.e. not compute shader) are only implemented for latest hardware and very latest OpenGL versions and extensions.
It could be terribly slow if used wrong. It's so easy to introduce pipeline stalls and CPU-GPU sync points
Pixel shader could become a terribly slow unmaintainable mess of branches and texture fetches.
And all this mess will be done for every single pixel every single frame
Solution: KISS
Just update your texture on CPU side.
Write to texture, replacing parts of it with desired content
Update is only need to be done once and only when you need this. Data persists until you rewrite it (not even once per frame, but only once per change request)
Pixel shader is dead brain simple: no branching, one texture
To get target pixels, implement ray-picking (you will need it anyway for any non-trivial interactive 3D-graphics program)
P.S. "Everything should be made as simple as possible, but not simpler." Albert Einstein.
I've written a simple GL fragment shader which performs an RGB gamma adjustment on an image:
uniform sampler2D tex;
uniform vec3 gamma;
void main()
{
vec3 texel = texture2D(tex, gl_TexCoord[0].st).rgb;
texel = pow(texel, gamma);
gl_FragColor.rgb = texel;
}
The texture paints most of the screen and it's occurred to me that this is applying the adjustment per output pixel on the screen, instead of per input pixel on the texture. Although this doesn't change its appearance, this texture is small compared to the screen.
For efficiency, how can I make the shader process the texture pixels instead of the screen pixels? If it helps, I am changing/reloading this texture's data on every frame anyway, so I don't mind if the texture gets permanently altered.
and it's occurred to me that this is applying the adjustment per output pixel on the screen
Almost. Fragment shaders are executed per output fragment (hence the name). A fragment is a the smallest unit of rasterization, before it's written into a pixel. Every pixel that's covered by a piece of visible rendered geometry is turned into one or more fragments (yes, there may be even more fragments than covered pixels, for example when drawing to an antialiased framebuffer).
For efficiency,
Modern GPUs won't even "notice" the slightly reduced load. This is a kind of microoptimization, that's on the brink of non-measureability. My advice: Don' worry about it.
how can I make the shader process the texture pixels instead of the screen pixels?
You could preprocess the texture, by first rendering it through a texture sized, not antialiased framebuffer object to a intermediate texture. However if your change is nonlinear, and a gamma adjustment is exactly that, then you should not do this. You want to process images in a linear color space and apply nonlinear transformation only as late as possible.