Rendering to and sampling same texture in one draw call - opengl

Is it possible in OpenGL to sample from, and then write to the same texture in one draw call using GLSL?

I think it actually IS possible. I did it like this for GLSL alpha blending:
vec4 prevColor = texture(dfFrameTexture, pointCoord);
vec4 result = vec4(color.a) * color + vec4(1.0 - color.a) * prevColor;
gl_FragColor = result;
dfFrameTexture is the texture that is being written to with gl_FragColor.
I tested this and it worked. Drawing a white quad onto dfFrameTexture after it's been cleared to black results in a grey quad when I set the color to (0.5, 0.5, 0.5, 0.5)

Related

How to avoid writing to a color attachment in the fragment shader [duplicate]

Ok so I have a framebuffer with a bunch of attachments attached. The attachments are Color, Bloom, Velocity and Depth.
I start by clearing the framebuffer to the values of my choosing with the following code.
// Clear Color Buffer
float colorDefaultValue[4] = { 0.5, 0.5, 0.5, 1.0 };
glClearBufferfv(GL_COLOR, 0, colorDefaultValue);
// Clear Bloom Buffer
float bloomDefaultValue[4] = { 0.0, 0.0, 1.0, 1.0 };
glClearBufferfv(GL_COLOR, 1, bloomDefaultValue);
// Clear Depth Buffer
float depth[1] = { 1.0 };
glClearBufferfv(GL_DEPTH, 0, depth);
Then I proceed to render the scene using my main shader.
As you can see in the code below, Ive specified the outputs.
// Layouts
layout (location = 0) out vec4 o_vFragColor;
layout (location = 1) out vec4 o_vBloomColor;
And then i output values in the fragment shader to them.
o_vFragColor = vec4(color, alpha);
float brightness = dot(o_vFragColor.rgb, vec3(0.2126, 0.2126, 0.2126));
if(brightness > 1.0)
o_vBloomColor = vec4(o_vFragColor.rgb, 1.0);
Now, my question is: If a fragment is not bright enough, why does it output black to the bloom attachment? I haven't specified for it to output anything yet it adjusts it anyway.
For example, if I clear the bloom buffer to green
// Clear Bloom Buffer
float bloomDefaultValue[4] = { 0.0, 1.0, 0.0, 1.0 };
glClearBufferfv(GL_COLOR, 1, bloomDefaultValue);
and I dont output any value to the bloom attachment in the fragment shader, I get a black in the bloom buffer. You can see it it the following image.
BloomBuffer
The parts of the image where the buffer is green is where no geometry was drawn. The black parts are parts of the image that contained geometry that was not bright enough and therefore should have not outputted anything to the bloom buffer, yet they clearly did output something, black in this case.
Purple parts are fragments that are beyind the brightness threshold and working as intended.
What with the black?
You cannot write to a color buffer conditionally from within a fragment shader.
An output fragment always has values for each active color buffer, even if you didn't write them. This is true even if you don't declare a variable for that output value. If no value gets written to a particular fragment output location, then the value used will be undefined. But it will be something.
You can do conditional writing based on color masking. But that is a per-draw-call thing, not something that can be done in the fragment shader. You could employ blending in that color buffer, using an alpha of 0 to mean "don't write", and an alpha of 1 to replace what's there.

Render a texture (decal) with an alpha value?

I tried to draw two textures of decals and background, but only the alpha part of the decals becomes white.
I simply tried the following.
Draw 2 textures (background & decals)
Add glBlendFunc to apply decals alpha value
#version 330 core
in vec2 UV;
out vec3 color;
uniform sampler2D background;
in vec3 decalCoord;
uniform sampler2D decal;
void main(){
vec3 BGTex = texture( background, UV ).rgb;
vec3 DecalTex = texture(decal, decalCoord.xy).rgba;
color =
vec4(BGTex,1.0) + // Background texture is DDS DXT1 (I wonder if DXT1 is the cause?)
vec4(DecalTex,0.0); // Decal texture is DDS DXT3 for alpha
}
// Set below...
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glBlendEquation(GL_FUNC_ADD);
I was able to draw normally but there are the following problems.
  -The alpha part of the decals can not be made transparent.
Is this a problem that can be solved within the fragmentshader?
In that case, how should I rewrite the code?
The texture is DDS, one of which is DXT1 type (Background. Because this texture doesn't need alpha) and the other is DXT3 type (Decal). Is this also the cause?(Both need to be the DXT3 type?)
Also, should I look for another way to put on the decals?
DecalTex should be a vec4, not a vec3. Otherwise the alpha
value will not be stored.
You will also have to change the line at the end to: color =
vec4(BGTex, 1.0) + DecalTex * DecalTex.a As currently it sets the alpha component
to 0.
The DecalTex has an alpha channel. The alpha channel is "weight", which indicates the intensity of the DecalTex. If the alpha channel is 1, then the color of DecalTex has to be used, if the alpha channel is 0, then the color of BGTex has to be used.
Use mix to mix the color of BGTex and DecalTex dependent on the alpha channel of DecalTex. Of course the type of the DecalTex has to be vec4:
vec3 BGTex = texture( background, UV ).rgb;
vec4 DecalTex = texture(decal, decalCoord.xy).rgba;
color = vec4(mix(BGTex.rgb, DecalTex.rgb, DecalTex.a), 1.0);
Note, mix linear interpolates between the 2 values:
mix(x, y, a) = x * (1−a) + y * a
This is similar the operation which is performed by the blending function and equation:
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glBlendEquation(GL_FUNC_ADD);
But Blending is applied to the fragment shader output and the current value in the framebuffer.
You don't need any blending at all, because the textures are "blended" in the fragment shader and the result is put in the framebuffer. Since the alpha channel of the fragment shader output is >= 1.0, the output is completely "opaque".

Shader not drawing black with no light?

I'm trying to implement specular reflection using values sampled from a grayscale, 1D texture as the multiplicative term.
I've implemented a toggle so I can see the difference between the two, but for some reason when the sampled color is enabled, the areas of the scene with no light display as a light gray, where without the sampling those same areas display as black.
Why is this? Here's the area where I'm setting the fragment color.
if(u_specularRamp == 1)
{
specularAmount = clamp(specularAmount, 0.0, 1.0);
vec2 texCoords = vec2(specularAmount, 0.5);
vec4 sampledColor = texture(u_ramp_tex, texCoords);
specularReflection = vec3(0.3 * sampledColor.x);
}
else
{
specularReflection = vec3(0.3 * specularAmount);
}
FragColor = vec4(specularReflection, 1.0);
u_specularRamp is an integer uniform I'm passing in to toggle the sampled color on and off.
I've fixed the issue, in case anyone runs into this problem, this behaviour was caused by the texture parameter being set to GL_REPEAT. If you want black where there isn't any light, set that parameter to GL_CLAMP_TO_EDGE

OpenGL 3.0 Framebuffer outputting to attachments without me specifying?

Ok so I have a framebuffer with a bunch of attachments attached. The attachments are Color, Bloom, Velocity and Depth.
I start by clearing the framebuffer to the values of my choosing with the following code.
// Clear Color Buffer
float colorDefaultValue[4] = { 0.5, 0.5, 0.5, 1.0 };
glClearBufferfv(GL_COLOR, 0, colorDefaultValue);
// Clear Bloom Buffer
float bloomDefaultValue[4] = { 0.0, 0.0, 1.0, 1.0 };
glClearBufferfv(GL_COLOR, 1, bloomDefaultValue);
// Clear Depth Buffer
float depth[1] = { 1.0 };
glClearBufferfv(GL_DEPTH, 0, depth);
Then I proceed to render the scene using my main shader.
As you can see in the code below, Ive specified the outputs.
// Layouts
layout (location = 0) out vec4 o_vFragColor;
layout (location = 1) out vec4 o_vBloomColor;
And then i output values in the fragment shader to them.
o_vFragColor = vec4(color, alpha);
float brightness = dot(o_vFragColor.rgb, vec3(0.2126, 0.2126, 0.2126));
if(brightness > 1.0)
o_vBloomColor = vec4(o_vFragColor.rgb, 1.0);
Now, my question is: If a fragment is not bright enough, why does it output black to the bloom attachment? I haven't specified for it to output anything yet it adjusts it anyway.
For example, if I clear the bloom buffer to green
// Clear Bloom Buffer
float bloomDefaultValue[4] = { 0.0, 1.0, 0.0, 1.0 };
glClearBufferfv(GL_COLOR, 1, bloomDefaultValue);
and I dont output any value to the bloom attachment in the fragment shader, I get a black in the bloom buffer. You can see it it the following image.
BloomBuffer
The parts of the image where the buffer is green is where no geometry was drawn. The black parts are parts of the image that contained geometry that was not bright enough and therefore should have not outputted anything to the bloom buffer, yet they clearly did output something, black in this case.
Purple parts are fragments that are beyind the brightness threshold and working as intended.
What with the black?
You cannot write to a color buffer conditionally from within a fragment shader.
An output fragment always has values for each active color buffer, even if you didn't write them. This is true even if you don't declare a variable for that output value. If no value gets written to a particular fragment output location, then the value used will be undefined. But it will be something.
You can do conditional writing based on color masking. But that is a per-draw-call thing, not something that can be done in the fragment shader. You could employ blending in that color buffer, using an alpha of 0 to mean "don't write", and an alpha of 1 to replace what's there.

What exactly happens with alpha colors after blending?

I am working on a test project where one big 3D quad is intersected by several other 3D quads (rotated differently). All quads have transparency (ranging from fully opaque to fully transparent). The small quads never overlap, well, they might, but the camera placement and the Z-buffer make sure that only those parts that may overlap actually overlap.
To render this, I first render the big quad to a different rgba framebuffer for later lookup.
Now I start rendering to the screen. A skybox is first rendered, then the small quads are rendered with alpha blending enabled: GL_SRC_ALHPA and GL_ONE_MINUS_SRC_ALPHA. After that I render the big quad again with alpha blending also enabled this time. Of course, only the pixels in front of the quads will be rendered because of the Z-buffer.
That's why, in the fragment shader of the small quads, I do a raytrace to find the intersection with the big quad. If the intersection point is BEHIND the small quad, I fetch the texel from the originally created framebuffer and blend this texel manually with the calculated small quad texel color.
But the result is not the same: the colors in front are rendered correctly (GPU handles the blending there), the colors behind them are "weird". They are lighter or darker but I never get the same result. After consideration, I think this must be because I am not emitting the correct alpha value from my small-quads shader, so that the GPU performed blending changes the colors even more.
So, how exactly is the alpha value calculated when blending on the GPU when using the above mentioned blending method. In the opengl manual, I find: A * Srgba + (1 - A) * Drgba. When I emit that result, the blending is not the same. I am quite sure that this is because the result is again passing through the GPU blending again.
The ray tracing is correct, I'm certain of that.
So, what should I do with my manual blending?
Or, should I use another method to get the right effect?
Not really necessary I believe, but here is some code (optimization is for later):
vec4 vRayOrg = vec4(0.0, 0.0, 0.0, 1.0);
vec4 vRayDir = vec4(normalize(vBBPosition.xyz), 0.0);
vec4 vPlaneOrg = mView * vec4(0.0, 0.0, 0.0, 1.0);
vec4 vPlaneNormal = mView * vec4(0.0, 1.0, 0.0, 0.0);
float div = dot(vRayDir.xyz, vPlaneNormal.xyz);
float t = dot(vPlaneOrg.xyz - vRayOrg.xyz, vPlaneNormal.xyz) / div;
vec4 pIntersection = vRayOrg + t * vRayDir;
vec3 normal = normalize(vec4(vCoord, 0.0, 0.0)).xyz;
vec3 vLight = normalize(vPosition.xyz / vPosition.w);
float distance = length(vCoord) - 1.0;
float distf = clamp(0.225 - distance, 0.0, 1.0);
float diffuse = clamp(0.25 + dot(-normal, vLight), 0.0, 1.0);
vec4 cOut = diffuse * 9.0 * distf * cGlow;
if (distance > 0.0 && t >= 0.0 && pIntersection.z <= vBBPosition.z)
{
vec2 vTexcoord = (vProjectedPosition.xy / vProjectedPosition.w) * 0.5 + 0.5;
vec4 cLookup = texture(tLookup, vTexcoord);
cOut = cLookup.a * cLookup + (1.0 - cLookup.a) * cOut;
}
vFragColor = cOut;