-
Notifications
You must be signed in to change notification settings - Fork 111
Blending Guide
This article will cover general blending concepts from OpenGL and how they are implemented in Torque 2D. Here is a summarized description from Wikipedia:
Alpha blending is the process of combining a translucent foreground color with a background color, thereby producing a new blended color. The degree of the foreground color's translucency may range from completely transparent to completely opaque. If the foreground color is completely transparent, the blended color will be the background color. Conversely, if it is completely opaque, the blended color will be the foreground color. Of course, the translucency can range between these extremes, in which case the blended color is computed as a weighted average of the foreground and background colors.
With the previous description in mind, let us make a very simple example in TorqueScript to display in the engine:
// Create two ShapeVectors
%shape1 = new ShapeVector();
%shape1.setPosition("5 5");
%shape1.setSize(10);
%shape1.setSceneLayer(2);
%shape1.setLineColor("1 0 0 1");
%shape1.setFillColor("1 0 0 1");
%shape1.setFillMode(true);
%shape1.setPolyPrimitive(4);
%shape2 = new ShapeVector();
%shape2.setPosition("-5 -5");
%shape2.setSize(10);
%shape2.setSceneLayer(1);
%shape2.setLineColor("0 0 1 0.6");
%shape2.setFillColor("0 0 1 0.6");
%shape2.setFillMode(true);
%shape2.setPolyPrimitive(4);
// Add the shapes to your scene
YourSceneName.add(%shape1);
YourSceneName.add(%shape2);
Running the script in a module of your choice will give you a result that looks like this:
The blue colored square is on a higher scene layer, so it is rendered above the red square. The blue square is also partially transparent, so we get a purple color in the region where the two ShapeVectors overlap. This is blending, which is turned on by default for all scene objects. What is Torque 2D doing behind the scenes to make this happen? Let's look at the math involved.
Colors in Torque 2D are represented by floating-point numbers from 0.0 to 1.0, so adding them, subtracting them, and even multiplying them are all perfectly valid operations. Colors can also be defined by integer values from 0 to 255, but T2D will convert them to floating-point numbers when doing any math. The color format is given as "red, green, blue, alpha" or RGBA. In our example above, the red square is below the blue square so the red color can be considered the background or "destination" color. The blue color is then the foreground or "source" color. You will see many fields and methods in T2D using the "source" or "destination" names or an abbreaviation of them: Src and Dst, so keep these meanings in mind.
How the source and destination colors are combined when blending is enabled is controlled by the blending equation. The blending equation looks like this:
Cf = (Cs * S) + (Cd * D)
Here, Cf is the final blended color, Cs is the source color, Cd is the destination color, and S and D are the source and destination blend factors. The blend factors are enumerants and not physical values that you specify directly. The default source blend factor for a SceneObject is "SRC_ALPHA". The default destination blend factor for a SceneObject is "ONE_MINUS_SRC_ALPHA". These factor names come directly from OpenGL. A complete list of available blend factors and what values they represent will be covered in a later section. For now, we will continue with the defaults and explain them.
Our defaults are telling Torque 2D (which is telling OpenGL) to take the source (foreground) color and multiply that color (the RGB values) by the alpha value. Then, it is added to the result of multiplying the destination (background) color by one minus the alpha value from the source. So, in our ShapeVector example the red color values are (1, 0, 0, 1). The blue color values are (0, 0, 1, 0.6). Here is the math:
- Cd = destination color = (1.0, 0.0, 0.0)
- Cs = source color = (0.0, 0.0, 1.0)
- S = source alpha = 0.6
- D = one minus source alpha = 1.0 – 0.6 = 0.4
The original equation:
Cf = (Cs * S) + (Cd * D)
evaluates to
Cf = (Blue * 0.6) + (Red * 0.4) = (0.4, 0.0, 0.6)
The final color is a scaled combination of the background red value and the foreground blue value. The higher the foreground alpha value, the more of the foreground color is added and the less of the background color is retained.
The SceneObject type, and all other types derived from SceneObject, exposes the following fields in relation to blending. Shown are the equivalent methods available that perform the same action in setting and getting the respective field:
- BlendMode
- setBlendMode(bool)
- getBlendMode()
- SrcBlendFactor
- setSrcBlendFactor(enum)
- getSrcBlendFactor()
- DstBlendFactor
- setDstBlendFactor(enum)
- getDstBlendFactor()
- BlendColor
- setBlendColor(float)
- getBlendColor()
- setBlendAlpha(float)
- getBlendAlpha()
- AlphaTest
- setAlphaTest(float)
- getAlphaTest()
The following is a complete description of each of these fields.
Turns blending on or off for this object. The default is true.
Here is a list of the source blending factors that are available to choose from and a description of what goes in the blending equation:
- ZERO
- S = 0
- ONE
- S = 1
- DST_COLOR
- S = destination RGB color values
- ONE_MINUS_DST_COLOR
- S = one minus destination RGB color values
- SRC_ALPHA
- S = source alpha
- ONE_MINUS_SRC_ALPHA
- S = one minus source alpha
- DST_ALPHA
- S = destination alpha
- ONE_MINUS_DST_ALPHA
- S = one minus destination alpha
- SRC_ALPHA_SATURATE
- S = RGB equals (f, f, f) where f = min(source_alpha, 1 - destination_alpha)
Here is a list of the destination blending factors that are available to choose from and a description of what goes in the blending equation:
- ZERO
- D = 0
- ONE
- D = 1
- SRC_COLOR
- D = source RGB color values
- ONE_MINUS_SRC_COLOR
- D = one minus source RGB color values
- SRC_ALPHA
- D = source alpha
- ONE_MINUS_SRC_ALPHA
- D = one minus source alpha
- DST_ALPHA
- D = destination alpha
- ONE_MINUS_DST_ALPHA
- D = one minus destination alpha
This field and its related methods allow us to take a single image and modify its appearance through blending. In terms of the blending equation shown in a previous section of this guide, the default image colors are the destination color and what we specify in "BlendColor" is the source color. With the default T2D blending factors, a white image, RGB = (1, 1, 1), allows the "BlendColor" setting to be the final color as well. For example, if we set our "BlendColor" to green, RGBA = (0, 1, 0, 1):
Cf = (green * 1) + (white * 0) = (0, 1, 0)
Here is this example in TorqueScript:
// Create a Sprite
%example = new Sprite();
%example.setImage("ToyAssets:Particles9");
%example.setFrame(0);
%example.setSize(10);
%example.setBlendColor("0 1 0 1");
// Add the sprite to your scene
YourSceneName.add(%example);
Original Image: now becomes
When using alpha testing, the alpha channel values determine whether or not areas of the image are rendered. The "AlphaTest" value is the threshold in the range of 0.0 to 1.0. A negative value will disable alpha testing. For example, giving this field a value of 0.1 will only render areas of the image with an alpha value above 0.1. The field "BlendColor" and the methods setBlendColor(), setBlendAlpha() can also alter the rendered areas by pushing pixels above or below the "AlphaTest" cutoff threshold. Here is an example:
Disabled AlphaTest: AlphaTest(0.8):
Throughout the engine's scripts, whenever a color is needed, you will usually find a human-readable name for the color such as CornflowerBlue instead of the expected RGBA nomenclature.
You can find the list of stock color names (and add to it!) in the following file: color.cc
Objects can change color over time using the functions fadeTo() and fadeToTime(). When the blendColor of the object reaches the targetColor, onFadeToComplete() is called for the object. This allows you to create cool effects like causing an object to blink red when its life is low or fading out a wall when the player walks behind it. If an object is in the middle of fading to another color and you set the blending color, the object will continue to fade toward the target color. If you want to stop the fade, use cancelFadeTo(). If you want to check to see if an object is in the middle of fading you can use isFadeToComplete() which will return false if it's still fading.
FadeTo() takes a color with four values separated by spaces as its first argument. This is the target color that you want your object to arrive at. The second parameter is the rate of change per second. So if the current color is "1 1 1 0" and the target color is "1 1 1 1" then a rate of 0.5 would take 2 seconds to complete.
FadeToTime() is similar. It takes a target color as the first parameter. Its second parameter is the amount of time in milliseconds that you want the fade to take. So in the example above you could pass in 2000 and the fade would take 2 seconds again. There is actually a subtle difference here. Each of the four values will change at a different rate so that they all arrive at the target at the same time. On the other hand, FadeTo() will cause each of the four values to change at the same rate regardless of how much they need to change. So changing "0.8 0.7 0.6 0.5" to "1 1 1 1" at a rate of 0.1 would cause the red value to reach 1 in 2 seconds, the green value in 3 seconds, the blue value in 4 seconds and the alpha value in 5 seconds. In contrast, using FadeToTime() would cause all values to reach 1 at the same time.
Fading is not persisted in taml. If the object is in the middle of fading when you save it, fading will not be active when you reload it.