So for the sake of legibility we can swap around a few things on lines 51 and 52 to see if this makes more sense:
// plot our line
float line = plot( st, y );
color = (1.0-line) * color + line * vec3( 0.0, 1.0, 0.0 );
If we do this, than pct only shows up as the variable that’s in the function defined outside of the main loop. Does that help?
main() is a function that’s run for every fragment in your texture - the idea close to a for loop, the big divergence being that we can’t really use the same ideas from for loops when thinking about main(). For loops are sequential, which lets us do things like accumulate values. For example, let’s say that I want to add together all of the values in a list. I could do this in python with something like:
[code]values_to_be_summed = [1, 3, 4, 3, 1]
result = 0
for each_val in values_to_be_summed:
result += each_val
print(result)[/code]
That’s great, and I know that I’m adding to result sequential as I go through the list. It’s easy to take for granted that you have access to the whole list, and the process is ordered.
Shaders don’t function that same way. You’ll execute the main() function on each fragment, but you don’t know what order that’s happening in, nor do you know what’s happening in the fragment next to you, or the one that was evaluated before or after you. All to say that glsl is looking to evaluate the results for each fragment simultaneously and in parallel, rather than sequentially. This matters because it changes the logic we use for how to think about drawing.
and then each time we run that plot() function, it refers to the declaration prior to the main loop to know how the arguments st and y relate to each other?
It’s better to think that you’re running the plot() function for every fragment - and into that function you’re passing a vec2 and a float value. That function is only going to return a value that’s between 0.0 and 1.0. You could do all of this without defining a function outside of main() by changing line 51-52 to be:
float line = smoothstep(y - 0.02, y, st.y) - smoothstep(y, y+0.02, st.y);
color = (1.0-line) * color + line * vec3( 0.0, 1.0, 0.0 );
That does the exact same thing.
Better still, if you want to take out all the arguments from that function, and just look at the smoothstep() function without any other funny business you could re-write that to be:
float line = smoothstep(st.x - 0.02, st.x, st.y) - smoothstep(st.x, st.x + 0.02, st.y);
Okay, so this brings us to your next question -
Looking at line 52:
color = (1.0-line) * color + line * vec3( 0.0, 1.0, 0.0 );
Important to consider here is that our color variable refers to our output color for the texture. Okay, let’s start with (1.0-line). line is float that’s the returned value of our plot() function. It’s going to be a value between 0.0 and 1.0. The operation 1.0 - line will return a result that’s going to be from 1.0 - 0.0. Sanity check:
[code]f(line) = 1 - line
f(0) = 1.0 - 0.0 = 1
f(0.5) = 1.0 - 0.5 = 0.5
f(1.0) = 1.0 - 1.0 = 0.0
[/code]
We’ve essentially just remapped our result to be from 1.0 to 0.0 rather than 0.0 to 1.0. Next we take that result and multiply it by color. Right now color is a variable that’s the x value of our normalized fragment coordinate - we see this in the shader with float y = st.x, and then later when we see color is a vec3 of y vals. Our plot() function, however, uses normalized y value (you’ll also see this called v), we see this as st.y. We’ve gotta go back to smoothstep() for a minute to understand what’s happening.
Smoothstep’s args look like:
smoothstep(start_of_gradient, end_of_gradient, value_we_are_checking);
Try this in a glsl TOP:
[code]out vec4 fragColor;
void main()
{
vec3 color = vec3(1.0);
float alpha = 1.0;
float gradient = smoothstep(0.25, 0.75, vUV.s);
color *= gradient;
vec4 outCol = vec4(color, alpha);
fragColor = TDOutputSwizzle(outCol);
}[/code]
Here are gradient starts at 0.25 and ends at 0.75 - before 0.25 the value returned from smoothstep() is is 0.0, after 0.75 it’s 1.0.
So far so good.
Okay, but what if I want a result that’s a gradient that goes up to white, then back to black? Or from 0.25 to 0.5 is a gradient going up to white, then from 0.5 to 0.75 it ramps back down to black. Try this:
[code]out vec4 fragColor;
void main()
{
vec3 color = vec3(1.0);
float alpha = 1.0;
float gradient = smoothstep(0.25, 0.5, vUV.s) - smoothstep(0.5, 0.75, vUV.s);
color = color * gradient;
vec4 outCol = vec4(color, alpha);
fragColor = TDOutputSwizzle(outCol);
}[/code]
What if instead of white I want that color of that to be green?
[code]out vec4 fragColor;
void main()
{
vec3 color = vec3(1.0);
float alpha = 1.0;
float gradient = smoothstep(0.25, 0.5, vUV.s) - smoothstep(0.5, 0.75, vUV.s);
color = color * gradient;
color = color * vec3(0.0, 1.0, 0.0);
vec4 outCol = vec4(color, alpha);
fragColor = TDOutputSwizzle(outCol);
}[/code]
The biggest difference in getting from the above to the BOS examples is that in the BOS we’re thinking in 2 dimensions instead of just 1. So finally we can get back to your question…
The last thing that’s missing here is order of operations… pink elephants must die at sunrise - PEMDAS:
parenthesis
exponents
multiplication
division
addition
subtraction
It’s implied but not explicit in this:
code * color + pct * vec3( 0.0, 1.0, 0.0 )
[/code]
if we add some parenthesis to clear it up we’ll see:
((1.0-pct) * color) + (pct * vec3( 0.0, 1.0, 0.0 ))
The left hand side is added to the right hand side… the right side gives us a green gradient that’s shaped like our line and that’s added to our left hand side where we’ve created a feathered black gradient for our green to fit into. WHAT?!
Try this - replace line 52 with:
color = (1.0-pct) * color;
now try:
color = pct * vec3( 0.0, 1.0, 0.0 );
Hopefully you’ll be able to see that the combination is about getting the green line over the gradient in the space where we’ve soft black line:
color = (1.0-pct) * color + pct * vec3( 0.0, 1.0, 0.0 );
We actually do declare color up on line 48.
vec3 color = vec3(y);
Shaders are a real brain twister… most of the ideas are grounded more in mathematics so going back to python might help you think about building functions, but going back and brushing up your linear algebra will also be a huge help. You might also look at the glsl2D tutorials that were ported from shader toy… that’s where I started before BOS, and that might help you get grounded a little better.
matthewragan.com/2017/08/02/gls … hdesigner/
Hope that helps at least a little.