banner



How To Set A Transparent Color In Opengl

Blending

Avant-garde-OpenGL/Blending

Blending in OpenGL is commonly known as the technique to implement transparency inside objects. Transparency is all about objects (or parts of them) non having a solid color, but having a combination of colors from the object itself and whatsoever other object behind it with varying intensity. A colored glass window is a transparent object; the glass has a color of its ain, but the resulting color contains the colors of all the objects backside the drinking glass also. This is also where the name blending comes from, since we blend several pixel colors (from different objects) to a single color. Transparency thus allows us to see through objects.

Image of full transparent window and partially transparent window

Transparent objects tin be completely transparent (letting all colors through) or partially transparent (letting colors through, but too some of its own colors). The amount of transparency of an object is defined by its colour's alpha value. The alpha color value is the 4th component of a color vector that you've probably seen quite oft now. Up until this chapter, we've always kept this 4th component at a value of 1.0 giving the object 0.0 transparency. An alpha value of 0.0 would result in the object having complete transparency. An alpha value of 0.5 tells us the object'south color consist of fifty% of its ain color and 50% of the colors behind the object.

The textures we've used so far all consisted of 3 color components: ruddy, dark-green and bluish, but some textures likewise have an embedded alpha channel that contains an alpha value per texel. This alpha value tells us exactly which parts of the texture have transparency and past how much. For case, the post-obit window texture has an alpha value of 0.25 at its drinking glass role and an alpha value of 0.0 at its corners. The glass part would ordinarily be completely cherry, simply since it has 75% transparency information technology largely shows the page'southward background through it, making information technology seem a lot less carmine:

Texture image of window with transparency

We'll soon exist adding this windowed texture to the scene from the depth testing affiliate, but first nosotros'll discuss an easier technique to implement transparency for pixels that are either fully transparent or fully opaque.

Discarding fragments

Some effects exercise non care about partial transparency, but either want to show something or nothing at all based on the color value of a texture. Retrieve of grass; to create something like grass with little effort you generally paste a grass texture onto a second quad and identify that quad into your scene. However, grass isn't exactly shaped similar a 2D square and so you merely desire to brandish some parts of the grass texture and ignore the others.

The post-obit texture is exactly such a texture where it either is full opaque (an alpha value of 1.0) or information technology is fully transparent (an alpha value of 0.0) and aught in between. You tin can see that wherever there is no grass, the image shows the page's background colour instead of its ain.

Texture image of grass with transparency

So when adding vegetation to a scene we don't want to see a foursquare image of grass, only rather but show the actual grass and see through the rest of the image. Nosotros desire to discard the fragments that show the transparent parts of the texture, not storing that fragment into the color buffer.

Earlier we get into that we outset need to learn how to load a transparent texture. To load textures with alpha values in that location's not much we demand to change. stb_image automatically loads an image's alpha channel if information technology's bachelor, simply we practice need to tell OpenGL our texture now uses an alpha channel in the texture generation procedure:

                      glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, pinnacle, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);                  

Also brand certain that you retrieve all 4 colour components of the texture in the fragment shader, not but the RGB components:

                      void chief() {     // FragColor = vec4(vec3(texture(texture1, TexCoords)), one.0);     FragColor = texture(texture1, TexCoords); }                  

At present that we know how to load transparent textures information technology'south fourth dimension to put it to the exam by adding several of these leaves of grass throughout the basic scene introduced in the depth testing chapter.

We create a minor vector array where we add several glm::vec3 vectors to represent the location of the grass leaves:

                      vector<glm::vec3> vegetation; vegetation.push_back(glm::vec3(-ane.5f,  0.0f, -0.48f)); vegetation.push_back(glm::vec3( 1.5f,  0.0f,  0.51f)); vegetation.push_back(glm::vec3( 0.0f,  0.0f,  0.7f)); vegetation.push_back(glm::vec3(-0.3f,  0.0f, -2.3f)); vegetation.push_back(glm::vec3( 0.5f,  0.0f, -0.6f));                  

Each of the grass objects is rendered as a single quad with the grass texture fastened to it. It's not a perfect 3D representation of grass, just it'southward a lot more efficient than loading and rendering a large number of complex models. With a few tricks similar adding randomized rotations and scales you can go pretty disarming results with quads.

Considering the grass texture is going to be displayed on a quad object we'll demand to create another VAO once again, make full the VBO, and gear up the advisable vertex attribute pointers. Then later we've rendered the floor and the two cubes we're going to render the grass leaves:

                      glBindVertexArray(vegetationVAO);            glBindTexture(GL_TEXTURE_2D, grassTexture);   for(unsigned int i = 0; i < vegetation.size(); i++)  {     model = glm::mat4(1.0f);     model =            glm::interpret(model, vegetation[i]);				     shader.setMat4("model", model);            glDrawArrays(GL_TRIANGLES, 0, 6); }                  

Running the application will now expect a bit like this:

Not discarding transparent parts of texture results in weird artifacts in OpenGL

This happens because OpenGL past default does not know what to do with blastoff values, nor when to discard them. We take to manually do this ourselves. Luckily this is quite easy thanks to the employ of shaders. GLSL gives us the discard control that (once called) ensures the fragment will non exist further candy and thus non finish up into the colour buffer. Thanks to this command we can check whether a fragment has an alpha value beneath a certain threshold and if so, discard the fragment every bit if it had never been processed:

                      #version 330 core out vec4 FragColor;  in vec2 TexCoords;  uniform sampler2D texture1;  void main() {                  vec4 texColor = texture(texture1, TexCoords);     if(texColor.a < 0.one)         discard;     FragColor = texColor; }                  

Here we bank check if the sampled texture color contains an alpha value lower than a threshold of 0.1 and if so, discard the fragment. This fragment shader ensures united states that information technology only renders fragments that are not (almost) completely transparent. Now information technology'll wait like it should:

Image of grass leaves rendered with fragment discarding in OpenGL

Note that when sampling textures at their borders, OpenGL interpolates the edge values with the next repeated value of the texture (because we gear up its wrapping parameters to GL_REPEAT by default). This is usually okay, but since we're using transparent values, the top of the texture image gets its transparent value interpolated with the lesser border's solid colour value. The result is then a slightly semi-transparent colored border you may see wrapped around your textured quad. To prevent this, ready the texture wrapping method to GL_CLAMP_TO_EDGE whenever y'all use alpha textures that yous don't want to repeat:
                          glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);              glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);                      

You can detect the source lawmaking here.

Blending

While discarding fragments is great and all, it doesn't give us the flexibility to render semi-transparent images; nosotros either return the fragment or completely discard it. To return images with different levels of transparency nosotros take to enable blending. Like almost of OpenGL's functionality we can enable blending by enabling GL_BLEND:

                      glEnable(GL_BLEND);                  

At present that we've enabled blending we need to tell OpenGL how information technology should actually blend.

Blending in OpenGL happens with the following equation:

\begin{equation}\bar{C}_{upshot} = \bar{\color{green}C}_{source} * \color{greenish}F_{source} + \bar{\color{cerise}C}_{destination} * \color{red}F_{destination}\end{equation}

  • \(\bar{\color{green}C}_{source}\): the source color vector. This is the color output of the fragment shader.
  • \(\bar{\color{red}C}_{destination}\): the destination color vector. This is the color vector that is currently stored in the color buffer.
  • \(\colour{green}F_{source}\): the source factor value. Sets the bear on of the blastoff value on the source color.
  • \(\color{red}F_{destination}\): the destination factor value. Sets the impact of the alpha value on the destination colour.

After the fragment shader has run and all the tests have passed, this alloy equation is let loose on the fragment'south color output and with any is currently in the colour buffer. The source and destination colors will automatically exist set by OpenGL, but the source and destination cistron tin can be fix to a value of our choosing. Let's beginning with a simple instance:

Two squares where one has alpha value lower than 1

We accept two squares where we want to draw the semi-transparent green square on acme of the reddish square. The carmine square volition be the destination color (and thus should exist starting time in the color buffer) and nosotros are now going to describe the light-green foursquare over the ruby square.

The question so arises: what do we ready the gene values to? Well, nosotros at least desire to multiply the light-green square with its alpha value so nosotros want to set the \(F_{src}\) equal to the alpha value of the source color vector which is 0.6. Then it makes sense to let the destination square take a contribution equal to the remainder of the alpha value. If the green foursquare contributes lx% to the final color nosotros want the cherry square to contribute 40% of the terminal color e.chiliad. i.0 - 0.six. So nosotros set \(F_{destination}\) equal to 1 minus the blastoff value of the source colour vector. The equation thus becomes:

\begin{equation}\bar{C}_{effect} = \begin{pmatrix} \color{red}{0.0} \\ \color{green}{i.0} \\ \colour{blue}{0.0} \\ \color{purple}{0.6} \stop{pmatrix} * \colour{green}{0.6} + \begin{pmatrix} \color{carmine}{i.0} \\ \color{green}{0.0} \\ \color{blue}{0.0} \\ \color{regal}{i.0} \stop{pmatrix} * (\color{cherry-red}{1 - 0.6}) \end{equation}

The event is that the combined square fragments contain a color that is 60% green and 40% red:

Two containers where one has alpha value lower than 1

The resulting colour is then stored in the color buffer, replacing the previous color.

So this is great and all, just how practise nosotros actually tell OpenGL to apply factors similar that? Well information technology merely so happens that at that place is a role for this chosen glBlendFunc .

The glBlendFunc(GLenum sfactor, GLenum dfactor) function expects two parameters that set the option for the source and destination factor. OpenGL defined quite a few options for us to set of which we'll list the almost common options below. Note that the constant colour vector \(\bar{\color{bluish}C}_{constant}\) can be separately prepare via the glBlendColor function.

Pick Value
GL_ZERO Cistron is equal to \(0\).
GL_ONE Gene is equal to \(one\).
GL_SRC_COLOR Factor is equal to the source color vector \(\bar{\color{greenish}C}_{source}\).
GL_ONE_MINUS_SRC_COLOR Factor is equal to \(1\) minus the source colour vector: \(1 - \bar{\color{greenish}C}_{source}\).
GL_DST_COLOR Gene is equal to the destination color vector \(\bar{\color{red}C}_{destination}\)
GL_ONE_MINUS_DST_COLOR Gene is equal to \(1\) minus the destination color vector: \(1 - \bar{\colour{crimson}C}_{destination}\).
GL_SRC_ALPHA Gene is equal to the \(alpha\) component of the source color vector \(\bar{\color{greenish}C}_{source}\).
GL_ONE_MINUS_SRC_ALPHA Factor is equal to \(1 - alpha\) of the source color vector \(\bar{\color{light-green}C}_{source}\).
GL_DST_ALPHA Factor is equal to the \(blastoff\) component of the destination color vector \(\bar{\color{red}C}_{destination}\).
GL_ONE_MINUS_DST_ALPHA Factor is equal to \(1 - alpha\) of the destination color vector \(\bar{\color{reddish}C}_{destination}\).
GL_CONSTANT_COLOR Factor is equal to the constant colour vector \(\bar{\color{blue}C}_{constant}\).
GL_ONE_MINUS_CONSTANT_COLOR Factor is equal to \(ane\) - the constant color vector \(\bar{\color{blueish}C}_{constant}\).
GL_CONSTANT_ALPHA Cistron is equal to the \(alpha\) component of the constant colour vector \(\bar{\color{bluish}C}_{constant}\).
GL_ONE_MINUS_CONSTANT_ALPHA Factor is equal to \(ane - alpha\) of the constant color vector \(\bar{\color{blue}C}_{constant}\).

To go the blending outcome of our little two foursquare example, we want to take the \(alpha\) of the source colour vector for the source factor and \(i - alpha\) of the same color vector for the destination factor. This translates to glBlendFunc as follows:

                      glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);                  

It is also possible to set unlike options for the RGB and alpha channel individually using glBlendFuncSeparate :

                                    glBlendFuncDivide(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO);                  

This function sets the RGB components as nosotros've set them previously, simply only lets the resulting alpha component be influenced by the source's alpha value.

OpenGL gives us even more flexibility by allowing us to alter the operator between the source and destination function of the equation. Right now, the source and destination components are added together, simply nosotros could besides decrease them if we desire. glBlendEquation(GLenum mode) allows us to gear up this operation and has v possible options:

  • GL_FUNC_ADD: the default, adds both colors to each other: \(\bar{C}_{issue} = \color{dark-green}{Src} + \color{red}{Dst}\).
  • GL_FUNC_SUBTRACT: subtracts both colors from each other: \(\bar{C}_{issue} = \color{green}{Src} - \colour{ruby-red}{Dst}\).
  • GL_FUNC_REVERSE_SUBTRACT: subtracts both colors, but reverses social club: \(\bar{C}_{consequence} = \color{red}{Dst} - \color{green}{Src}\).
  • GL_MIN: takes the component-wise minimum of both colors: \(\bar{C}_{result} = min(\color{red}{Dst}, \color{dark-green}{Src})\).
  • GL_MAX: takes the component-wise maximum of both colors: \(\bar{C}_{effect} = max(\color{carmine}{Dst}, \color{green}{Src})\).

Commonly we can only omit a telephone call to glBlendEquation because GL_FUNC_ADD is the preferred blending equation for near operations, but if yous're really trying your best to suspension the mainstream circuit whatsoever of the other equations could conform your needs.

Rendering semi-transparent textures

Now that we know how OpenGL works with regards to blending it's time to put our knowledge to the test by adding several semi-transparent windows. We'll exist using the same scene every bit in the start of this chapter, merely instead of rendering a grass texture nosotros're at present going to use the transparent window texture from the starting time of this chapter.

First, during initialization nosotros enable blending and set the advisable blending office:

                      glEnable(GL_BLEND);            glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);                  

Since we enabled blending there is no need to discard fragments so we'll reset the fragment shader to its original version:

                      #version 330 core out vec4 FragColor;  in vec2 TexCoords;  uniform sampler2D texture1;  void main() {                  FragColor = texture(texture1, TexCoords); }                  

This fourth dimension (whenever OpenGL renders a fragment) information technology combines the electric current fragment's colour with the fragment color currently in the color buffer based on the alpha value of FragColor. Since the glass part of the window texture is semi-transparent we should be able to see the rest of the scene by looking through this window.

A blended scene in OpenGL where order is incorrect.

If you lot take a closer look yet, you lot may notice something is off. The transparent parts of the front end window are occluding the windows in the background. Why is this happening?

The reason for this is that depth testing works a bit tricky combined with blending. When writing to the depth buffer, the depth test does not care if the fragment has transparency or not, so the transparent parts are written to the depth buffer every bit whatsoever other value. The issue is that the background windows are tested on depth as any other opaque object would exist, ignoring transparency. Fifty-fifty though the transparent part should show the windows behind information technology, the depth examination discards them.

So nosotros cannot merely render the windows even so we want and expect the depth buffer to solve all our issues for usa; this is also where blending gets a piddling nasty. To make certain the windows show the windows backside them, we have to depict the windows in the background commencement. This means we have to manually sort the windows from furthest to nearest and draw them accordingly ourselves.

Annotation that with fully transparent objects similar the grass leaves we take the option to discard the transparent fragments instead of blending them, saving us a few of these headaches (no depth issues).

Don't intermission the lodge

To make blending work for multiple objects we have to draw the virtually distant object first and the closest object terminal. The normal non-blended objects tin can still be drawn every bit normal using the depth buffer so they don't take to be sorted. We do have to make certain they are drawn first before drawing the (sorted) transparent objects. When cartoon a scene with non-transparent and transparent objects the general outline is usually as follows:

  1. Draw all opaque objects offset.
  2. Sort all the transparent objects.
  3. Draw all the transparent objects in sorted order.

One way of sorting the transparent objects is to retrieve the distance of an object from the viewer'due south perspective. This can exist accomplished past taking the distance between the photographic camera'south position vector and the object's position vector. We then store this distance together with the corresponding position vector in a map data structure from the STL library. A map automatically sorts its values based on its keys, so in one case nosotros've added all positions with their distance as the key they're automatically sorted on their distance value:

                      std::map<float, glm::vec3> sorted; for (unsigned int i = 0; i < windows.size(); i++) {     float distance = glm::length(camera.Position - windows[i]);     sorted[distance] = windows[i]; }                  

The result is a sorted container object that stores each of the window positions based on their altitude cardinal value from lowest to highest altitude.

Then, this time when rendering, we take each of the map'south values in contrary social club (from uttermost to nearest) and then draw the corresponding windows in correct order:

                      for(std::map<float,glm::vec3>::reverse_iterator it = sorted.rbegin(); it != sorted.rend(); ++it)  {     model = glm::mat4(1.0f);     model =            glm::interpret(model, it->2d);				     shader.setMat4("model", model);            glDrawArrays(GL_TRIANGLES, 0, 6); }                  

We take a reverse iterator from the map to iterate through each of the items in reverse lodge and then translate each window quad to the corresponding window position. This relatively simple approach to sorting transparent objects fixes the previous problem and now the scene looks like this:

Image of an OpenGL scene with blending enabled, objects are sorted from far to near

You tin observe the complete source code with sorting hither.

While this approach of sorting the objects past their altitude works well for this specific scenario, information technology doesn't take rotations, scaling or any other transformation into business relationship and weirdly shaped objects need a different metric than simply a position vector.

Sorting objects in your scene is a difficult feat that depends greatly on the blazon of scene yous have, let lone the extra processing ability information technology costs. Completely rendering a scene with solid and transparent objects isn't all that easy. In that location are more advanced techniques like society independent transparency but these are out of the telescopic of this affiliate. For now you'll take to alive with normally blending your objects, but if you're careful and know the limitations yous can get pretty decent blending implementations.

Source: https://learnopengl.com/Advanced-OpenGL/Blending

Posted by: drennonowereve.blogspot.com

0 Response to "How To Set A Transparent Color In Opengl"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel