Reading the GLSL 1.40 specification:
Fragment outputs can only be float,
floating-point vectors, signed or
unsigned integers or integer vectors,
or arrays of any these. Matrices and
structures cannot be output. Fragment
outputs are declared as in the
following examples:
out vec4 FragmentColor;
out uint Luminosity;
The fragment color is defined writing gl_FragColor... is it right? Somebody could clear my ideas about these outputs? May I write only 'FragmentColor' of the example to determine fragment color? May I read back them ('Luminosity' for example)?
The global output variable gl_FragColor is deprecated after GLSL version 120.
Now you have to give it a name and type by yourself, as in your example.
Regarding several outputs,
this link gives you information about the mapping: http://www.opengl.org/wiki/GLSL_Objects#Program_linking
(And I found that link at: http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&Number=270999 )
Hope this helps! :D
Ooops! I see that kvark gave the relevant information. Anyway, maybe you got something out of my text too.
Your example has 2 outputs. They have corresponding FBO slots associated after GLSL program linking. You can redirect them using glBindFragDataLocation.
Once you activated the shader and bound FBO, it all depends on a draw mask, set by glDrawBuffers. For example, if you passed GL_COLOR_ATTACHMENT0 and GL_COLOR_ATTACHMENT2 there, that would mean that output index 0 will go to the attachment 0, and output index 1 would go to the color attachment 2.
i want to give some examples:
void add(in float a, in float b, out float c)
{
//you can not use c here. only set value
//changing values of a and b does not change anything.
c = a + b;
}
void getColor(out float r, out float g, out float b)
{
//you can not use r, g, b here. only set value
r = gl_FragColor.r;
g = gl_FragColor.g;
b = gl_FragColor.b;
}
void amplify(inout vec4 pixelColor, in value)
{
//inout is like a reference
pixelColor = pixelColor * value;
}
Related
I am trying to calculate a morph offset for a gpu driven animation.
To that effect I have the following function (and SSBOS):
layout(std140, binding = 7) buffer morph_buffer
{
vec4 morph_targets[];
};
layout(std140, binding = 8) buffer morph_weight_buffer
{
float morph_weights[];
};
vec3 GetMorphOffset()
{
vec3 offset = vec3(0);
for(int target_index=0; target_index < target_count; target_index++)
{
float w1 = morph_weights[1];
offset += w1 * morph_targets[target_index * vertex_count + gl_VertexIndex].xyz;
}
return offset;
}
I am seeing strange behaviour so I opened renderdoc to trace the state:
As you can see, index 1 of the morph_weights SSBO is 0. However if I step over in the built in debugger for renderdoc I obtain:
Or in short, the variable I get back is 1, not 0.
So I did a little experiment and changed one of the values and now the SSBO looks like this:
And now I get this:
So my SSBO of type float is being treated like an ssbo of vec4's it seems. I am aware of alignment issues with vec3's, but IIRC floats are fair game. What is happenning?
Upon doing a little bit of asking around.
The issue is the SSBO is marked as std140, the correct std for a float array is std430.
For the vulkan GLSL dialect, an alternative is to use the scalar qualifier.
I try to optimize a working compute shader. Its purpose is to create an image: find the good color (using a little palette), and call imageStore(image, ivec2, vec4).
The colors are indexed, in an array of uint, in an UniformBuffer.
One color in this UBO is packed inside one uint, as {0-255, 0-255, 0-255, 0-255}.
Here the code:
struct Entry
{
*some other data*
uint rgb;
};
layout(binding = 0) uniform SConfiguration
{
Entry materials[MATERIAL_COUNT];
} configuration;
void main()
{
Entry material = configuration.materials[currentMaterialId];
float r = (material.rgb >> 16) / 255.;
float g = ((material.rgb & G_MASK) >> 8) / 255.;
float b = (material.rgb & B_MASK) / 255.;
imageStore(outImage, ivec2(gl_GlobalInvocationID.xy), vec4(r, g, b, 0.0));
}
I would like to clean/optimize a bit, because this color conversion looks bad/useless in the shader (and should be precomputed). My question is:
Is it possible to directly pack a vec4(r, g, b, 0.0) inside the UBO, using 4 bytes (like a R8G8B8A8) ?
Is it possible to do it directly? No.
But GLSL does have a number of functions for packing/unpacking normalized values. In your case, you can pass the value as a single uint uniform, then use unpackUnorm4x8 to convert it to a vec4. So your code becomes:
vec4 color = unpackUnorm4x8(material.rgb);
This is, of course, a memory-vs-performance tradeoff. So if memory isn't an issue, you should probably just pass a vec4 (never use vec3) directly.
Is it possible to directly pack a vec4(r, g, b, 0.0) inside the UBO, using 4 bytes (like a R8G8B8A8) ?
There is no way to express this directly as 4 single byte values; there is no appropriate data type in the shader to allow you to do declare this as a byte type.
However, why do you think you need to? Just upload it as 4 floats - it's a uniform so it's not like you are replicating it thousands of times, so the additional size is unlikely to be a problem in practice.
I am trying to pass an array of floats (in my case an audio wave) to a fragment shader via texture. It works but I get some imperfections as if the value read from the 1px height texture wasn't reliable.
This happens with many combinations of bar widths and amounts.
I get the value from the texture with:
precision mediump float;
...
uniform sampler2D uDisp;
...
void main(){
...
float columnWidth = availableWidth / barsCount;
float barIndex = floor((coord.x-paddingH)/columnWidth);
float textureX = min( 1.0, (barIndex+1.0)/barsCount );
float barValue = texture2D(uDisp, vec2(textureX, 0.0)).r;
...
If instead of the value from the texture I use something else the issue doesn't seem to be there.
barValue = barIndex*0.1;
Any idea what could be the issue? Is using a texture for this purpose a bad idea?
I am using Pixi.JS as WebGL framework, so I don't have access to low level APIs.
With a gradient texture for the data and many bars the problems becomes pretty evident.
Update: Looks like the issue relates to the consistency of the value of textureX.
Trying different formulas like barIndex/(barsCount-1.0) results in less noise. Wrapping it on a min definitely adds more noise.
Turned out the issue wasn't in reading the values from the texture, but was in the drawing. Instead of using IFs I switched to step and the problem went away.
vec2 topLeft = vec2(
paddingH + (barIndex*columnWidth) + ((columnWidth-barWidthInPixels)*0.5),
top
);
vec2 bottomRight = vec2(
topLeft.x + barWidthInPixels,
bottom
);
vec2 tl = step(topLeft, coord);
vec2 br = 1.0-step(bottomRight, coord);
float blend = tl.x * tl.y * br.x * br.y;
I guess comparisons of floats through IFs are not very reliable in shaders.
Generally mediump is insufficient for texture coordinates for any non-trivial texture, so where possible use highp. This isn't always available on some older GPUs, so depending on the platform this may not solve your problem.
If you know you are doing 1:1 mapping then also use GL_NEAREST rather than GL_LINEAR, as the quantization effect will more likely hide some of the precision side-effects.
Given you probably know the number of columns and bars you can probably pre-compute some of the values on the CPU (e.g. precompute 1/columns and pass that as a uniform) at fp32 precision. Passing in small values between 0 and 1 is always much better at preserving floating point accuracy, rather than passing in big values and then dividing out.
I'm trying to test colour values and change them if they're greater than 0.5.
I started out with this test which didn't compile:
if(colourIn.b > 0.5){
colourIn.b=0.0;
}
I read through some post on here and found this post which explains relational operators only work on scalar integer and scalar floating-point expressions.
So after finding out a good way to efficiently test frag values and using the built in functions, changed it to:
float mixValue = clamp(ceil(colourIn.b * 2.0) - 1.0, 0.0, 1.0);
if(greaterThan(colourIn.b,0.5)){
colourIn.b = mix(colourIn.b, 0.0, mixValue);}
Unfortunately it still doesn't compile; it gives the following errors:
ERROR: 0:15 error(202) No matching overloaded function found greaterThan
ERROR: 0:16 error(164) 1-value required assigned "colourIn" (can't modify an input)
ERROR: 0:15 error(179) Boolean expression expected
For this I get that the greatThan function is being used wrong? (I can't find an example in similar circumstances) and that the colourIn value cannot be changed?
I may be wrong... Please help me figure this out.
Basically, I want to change any pixels with Blue values greater than 0.5 to white (0.0).
Yes, it is true that relational operators only work on scalars... what on Earth is colourIn declared as (bvec)? Considering boolean colors do not make a lot of sense, usually colourIn.b would be a scalar component from a vecN or ivecN type. Please include the actual body of the shader you are trying to compile.
Additionally, greaterThan (...) does not work on scalar types, only vector. What it returns is a boolean vector containing the result of the test v1 > v2 for each component in v1 and v2.
So for instance consider the following pseudo-code,
vec3 v1 = vec3 (1,2,3);
vec3 v2 = vec3 (3,2,1);
bvec3 gt = greaterThan (v1, v2);
Then the boolean vector gt would have the following form:
gt.x = false;
gt.y = false;
gt.z = true;
However, the biggest problem you have is you are trying to modify an input value. You cannot do this, fragment shader inputs are interpolated from vertex transform (vertex shader, geometry shader, tessellation shader) outputs during rasterization and are read-only. Vertex shader inputs are from your vertex buffer and are also read-only. The only thing shaders are capable of doing is computing the output for the next stage in the pipeline.
In a fragment shader, the next stage would be blending for final pixel output. In a vertex shader it would be tessellation (GL4+) and primitive assembly (geometry shader) and rasterization (fragment shader).
I have a GLSL shader that reads from one of the channels (e.g. R) of an input texture and then writes to the same channel in an output texture. This channel has to be selected by the user.
What I can think of right now is to just use an int uniform and tons of if-statements:
uniform sampler2D uTexture;
uniform int uChannelId;
varying vec2 vUv;
void main() {
//read in data from texture
vec4 t = texture2D(uTexture, vUv);
float data;
if (uChannelId == 0) {
data = t.r;
} else if (uChannelId == 1) {
data = t.g;
} else if (uChannelId == 2) {
data = t.b;
} else {
data = t.a;
}
//process the data...
float result = data * 2; //for example
//write out
if (uChannelId == 0) {
gl_FragColor = vec4(result, t.g, t.b, t.a);
} else if (uChannelId == 1) {
gl_FragColor = vec4(t.r, result, t.b, t.a);
} else if (uChannelId == 2) {
gl_FragColor = vec4(t.r, t.g, result, t.a);
} else {
gl_FragColor = vec4(t.r, t.g, t.b, result);
}
}
Is there any way of doing something like a dictionary access such as t[uChannelId]?
Or perhaps I should have 4 different versions of the same shader, each of which processes a different channel, so that I can avoid all the if-statements?
What is the best way to do this?
EDIT: To be more specific, I am using WebGL (Three.js)
There is such a way, and it is as simple as you actually wrote it in the question. Just use t[channelId]. To quote the GLSL Spec (This is from Version 3.30, Section 5.5, but applies to other versions as well):
Array subscripting syntax can also be applied to vectors to provide numeric indexing. So in
vec4 pos;
pos[2] refers to the third element of pos and is equivalent to pos.z. This allows variable indexing into a
vector, as well as a generic way of accessing components. Any integer expression can be used as the
subscript. The first component is at index zero. Reading from or writing to a vector using a constant
integral expression with a value that is negative or greater than or equal to the size of the vector is illegal.
When indexing with non-constant expressions, behavior is undefined if the index is negative, or greater
than or equal to the size of the vector.
Note that for the first part of your code, you use this to access a specific channel of a texture. You could also use the ARB_texture_swizzle functionality. In that case, you would just use a fxied channel, say r, for access in the shader and what swizzle the actual texture channels so that wahtever channel you want to access becomes r.
Update: as the target platform turned out to be webgl, these suggestions are not available. However, a simple solution would be to use a vec4 uniform in place of uChannelID which is 1.0 for the selected component and 0.0 for all others. Say this variable is called uChannelSel. You could use data=dot(t, uChannelSel) in the first part and gl_FragColor=(vec4(1.0)-uChannelSel) * t + uChannelSel*result for the second part.
as i'm sure you know, branching can be expensive in shaders. however, it sounds like it'll always be the same channel in a pass (yes?), so you might maintain enough cohesion to see good performance.
it's been a good while since i've used GLSL, but if you're using a newer version, maybe you could do some bitwise shifting (<< or >>) magic? you would read the texture into int instead of vec4, then shift it a number of bits depending on which channel you want to read.