Search Results

Friday, March 18, 2011

Basic OpenGL 2D Screen Filters in the BGE - Part 3

Hey. This time, we'll be looking into something new - a blur filter! What? We did that already? Oh... Well... What if we controlled it with a VARIABLE?! EH?

So... Yeah. That's the new filter that we'll be making - a variable-controlled blur filter. This is actually pretty simple. In fact, the script is mostly the same as the blur script - the only difference is that we interpolate between the blurred texture and the non-blurred texture with a variable. Here's the script...

filter.shaderText = """

            uniform sampler2D bgl_RenderedTexture;

    uniform float percent;

            void main(void)
            {
                        float value = 0.0015;      // Here, value = distance away to check for pixel color
                       
                        vec4 color = texture2D(bgl_RenderedTexture, vec2(gl_TexCoord[0].st.x + value, gl_TexCoord[0].st.y + value)); // Sample area around current pixel
            color += texture2D(bgl_RenderedTexture, vec2(gl_TexCoord[0].st.x - value, gl_TexCoord[0].st.y - value));
            color += texture2D(bgl_RenderedTexture, vec2(gl_TexCoord[0].st.x + value, gl_TexCoord[0].st.y - value));
            color += texture2D(bgl_RenderedTexture, vec2(gl_TexCoord[0].st.x - value, gl_TexCoord[0].st.y + value));
   
            color /= 4.0;         // And average out the final color by number of samples; this could be in a for-loop
   
        vec4 origcolor = texture2D(bgl_RenderedTexture, gl_TexCoord[0].st); // This is a slight difference from the blur filter;
   
            gl_FragColor = origcolor + percent * (color - origcolor); // Here, we use a simple formula where if percent = 0, then we simply use the original color. Otherwise, we use the blurred color;
        // This forumla is called linear interpolation, and is a + scaler * (b - a), so then when scaler = 0, a is returned, and when scaler = 1, then b is returned
        // By using this, we can control how powerful our filter is.

            }
"""
There's really not too much of a difference between the Blur script in part 2, and this script. Let's examine the changes in the code in detail.

vec4 origcolor = texture2D(bgl_RenderedTexture, gl_TexCoord[0].st);
gl_FragColor = origcolor + percent * (color - origcolor);

As you can see, we don't simply set gl_FragColor to be the color variable as we did in the previous part of this series. In the first line, we create a new vector, the original color of the pixel that we're altering. So, we have two vector variables at this point - the blurred color, and the original color. Then, we set gl_FragColor to be the result of a simple formula, one called a linear interpolation.

The formula for a linear interpolation is (a + scaler * (b - a)). This formula essentially is saying that when scaler is 0, then you will get the value of a, and when scaler is 1, you will get the value of b.You can use different number values for a or b, which is what we do in our example, using our two color vectors.

3/21/11 Addition: The linear interpolation formula works, and is a good formula to have around, but you can also use the mix function in a GLSL screen filter script, which takes as arguments for the first a color vector, the second another color, and the third a percentage.

So, we should be able to control how blurry our image is by controlling the scaler variable, which is in this case, percent. Where is percent created in the script, though? Well, it isn't created in the script at all - we actually create it outside of the shader script, and it is passed into the script in the line near the top - the line that says uniform float percent. That is the point of uniform variables - they allow us to pass values from variables elsewhere into our shader script.  Another example of a uniform variable is the bgl_RenderedTexture, which we don't create - the BGE creates and passes it to the shader script for us.

So, in this script, we can control the percent variable in our game code, and the script will automatically update it to blur our screen as much or little as we want. However, we still need to create the percent variable in our game object, as it is a user-defined variable. The BGE handles uniform user-defined variables with object variables - if we have an object variable, then the BGE will use that as the same uniform variable in the shader script. So, all we need to do is create a percent variable in the object, as can be seen in the screenshot. Since the object variable 'percent' is named the same as in our shader script, the script will reference that object variable when it runs. That's all there is to it!

Now, you should be able to control a 2D filter through means of a user-defined uniform variable, one that the BGE automatically passes from an object variable. Download the source code for Part 3 of the Basic OpenGL 2D Screen Filters series here. As always, have fun!

23 comments:

  1. Thats so good! I really nreeded a blur filter controlled by a variable(this will help me very much when I will want to disable it(I will low the values) making a nice transition!can you add variables on all kind of filters?
    You are making so good stuff!

    ---Pr3DaToR---

    ReplyDelete
  2. Thanks - Yes, you can add uniform variables to any filter.

    ReplyDelete
  3. Thanks for this, it's given a much needed little insight into blenders opengl functionality.

    I would point out one thing though:
    it is completely pointless to access the first two components explicitly of a vector that only has two components. as in:

    'gl_TexCoord[0].st' is the same as 'gl_TexCoord[0].st.st' or just 'gl_TexCoord[0]'.

    Also, as this should make obvious, it is also pointless to explicitly access vector components consecutively unless the component accessor coordinate is a variable. Not even sure if that's possible though.

    I assume that your just not familiar with all the access provided for vectors. The most common way is to use these:

    some_vector.xyzw == some_vector.rgba == some_vector.stpq

    They can be used interchangeably, though im not certain if there are exceptions to this somewhere?
    Hope thats usefull, and thanks again ;)

    ReplyDelete
  4. @iisthphir - Thanks for the info. Yeah, I wasn't too familiar with Vectors when I wrote this tutorial, and I still am not very familiar with C++, though I can stumble my way through it. The inability to print out to the console for GLSL scripts is a bit tough to work around.

    I just recently looked at texture coordinate values particularly and what they contain. I should brush up on them for my next tutorial. Thanks!

    ReplyDelete
  5. Thanks so much! This is awesome. These tutorials have given me my introduction into filter/shader coding... I'd like to ask you a question. I'm working with the blur script and trying to make a sort of Bloom filter, but I can't get the code to only use the parts of the texture brighter than a certain amount. Is there a way to do this? I can send you the script, if you'd like to take a look at it... Thanks!

    ReplyDelete
    Replies
    1. Well, you can just use the color gotten from the rendered texture. For example, in the example above, you can just check color.r to see what the value of the color red is for each pixel from 0 to 1.

      Delete
    2. Oh, I see... So I guess "r" is red, "b" blue and "g" green then? So I could use all of these... I hate to ask, but would be the coding for getting and transferring this to the "bloom" layers? I'm sorry, but I'm quite terrible at this... I've only ever written basic python and this is the first c++ I've ever seen. And, of course, thank you!

      Delete
    3. Actually, could one just have the blurred extra offset textures with a standard RGBA, and then at the end multiply the alpha of the compilation of these blurred textures by their brightness (aka rgb, I suppose...) I'm sorry to ask so much. This is my last line before the closing "}":

      gl_FragColor = origcolor.rgba + (flare.rgba * 1.00);

      Is there a way to multiply the "a" in the rgba of "flare" so as to remove any areas in the texture darker than so-and-so just in these last lines? Thank you, of course. I'm here to learn, and thus inadvertently spam your inbox.

      Delete
    4. I'm not sure if this will work for the bloom effect you're going for, but you can just isolate out each value by what you type, i.e.

      flare.r,
      flare.a,

      and so on. The 'a' in RGBA is alpha, though, not brightness. You could sum the colors together and then divide them by three to get a brightness level, but it won't be correct. I believe you need to apply a certain formula to get the correct luminance value. Check out a correct bloom tutorial, because I don't think my method is correct. :p

      Delete
    5. After re-reading your comment, I see what you mean. I'm not certain if multiplying the alpha component to get rid of the bloom will work, since you're adding the colors to each other, not the alphas or images themselves (i.e. adding origcolor.r + flare.r, not adding the two images by their alpha values).

      Delete
    6. I see what you mean... But could you say modify these values, so flare's alpha was multiplied/stenciled by its brightness? Or would there be a way to simply add flare to origcolor through only its brightness, thus making darker areas of it have less of an effect? Like the "add" transparency setting, where it subtracts the darks from the alpha.

      Delete
    7. Yes, you can add 'flare' to 'origcolor'. Adding the colors means that where flare is black, the ending color will be origcolor, and where flare is white (or another color), the ending color will be a combination. You can just multiply flare by an amount to make it less noticeable, but adding flare to origcolor will make it multiplied by its brightness.

      Delete
    8. And by that, I meant literally:

      gl_FragColor = origcolor + (bloom * brightness);

      Delete
    9. :D Thank you! Is there something I need to do to set up a brightness variable previously, or should this just work? Because I can a test and the script simply didn't work... But so do some other bloom filters. However they use a different style altogether.

      Delete
    10. Yes, you would need to set up the brightness variable previously. It should be a float variable.

      Delete
    11. Great. Thank you. I more or less used a brightness variable, but rather than a Float I made it another vec4 variable... Or whatever it's called, in that case... Then modified it following the example of a little "bloom" script that acted more as a contrast script. Then I fine-tuned it and multiplied the flare factor by it, and it looks decent. However, the edges are understandably sharp, and I tried to make a second blurring pass (like the one on your blur tutorial) on this "rough" bloom overlay and THEN add it to the original color for the final. However, I can't seem to modify the coordinates of these new blur passes so as to blur it. This is what it looks like:

      vec4 rough = 1.00 * (flare * (brightness * 5.0));
      rough += rough, vec2(gl_TexCoord[0].st.x - blur, gl_TexCoord[0].st.y - blur);

      Is there a different way to change the variable texcoordinates here? Because this isn't working.

      Delete
    12. Change the texture coordinates how? Make sure that you're using the latest build of Blender from the Blender Buildbot or Graphicall, as there was recently a bug-fix dealing with GLSL filters sampling the edges of the screen. Also, you have 'rough, vec2()' - is that a typo?

      Delete
    13. Oh- by "edges" I meant the edges of the bloom/glare from lights. And your second (and third) tutorials use the line-

      color += texture2D(bgl_RenderedTexture, vec2(gl_TexCoord[0].st.x - value, gl_TexCoord[0].st.y - value));

      -to create the blur, through repetition. And no, that's not quite a typo... I just have no idea what to put there to recreate the effect you used for the blur, but only with this "rough", so as to soften the bloom. This is my final struggle.

      Delete
    14. Oh, now I see how you're doing it. I'm sorry for not reading your posts more thoroughly - if I understand what you're doing, you're basically adding the colors on top and then trying to blur the effect? It would probably be easier to do it at the same time.

      What I mean is that bloom is something of a normal blur filter, but instead of replacing the original color with the blurred color, like in the example filter above, you would add the blur color to it. That should do both at the same time - add the bloom on top of the original color, as well as blur it. Basically, once you get the above filter working, you can just switch it out to add the colors on top. There's more to it, but that's enough to get it working, I think.

      Delete
    15. Yep, that's more or less it. I'm gonna probably take a while to figure this out, so... Yeah. Sorry for the late response... School. I'll hopefully be back to you on this soon. I was trying to use the glare I made and then blur that separately, then add THAT to the original color... If that's what you mean.

      Delete
  6. If you set the percent above 10 you get some nice neon outline effects

    ReplyDelete
  7. hey hello solarlune,

    do you know a way to CREATE a gameproperty for a filter DURING the game ?

    filters with uniforms from gameprops seem to update only if the gameprops exist before the game is run.

    If I try to add a new gameprop while the game is running (ob['spam'] = 'eggs' or something) this does not do anything (if the gameprop spam did not exist before)

    thanks for any pointer,

    Jerome

    ReplyDelete
  8. ah. some pointers :)
    https://blenderartists.org/forum/showthread.php?374488-Bind-custom-uniform-values-to-a-2D-filter-(using-bgl-wrapper)

    ReplyDelete