Java-Gaming.org    
Featured games (78)
games approved by the League of Dukes
Games in Showcase (429)
Games in Android Showcase (89)
games submitted by our members
Games in WIP (467)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1]
  ignore  |  Print  
  blendFunc woes  (Read 1527 times)
0 Members and 1 Guest are viewing this topic.
Offline seismic

Senior Member


Medals: 3
Projects: 1



« Posted 2013-07-05 11:35:07 »

Ok I'm just testing out something and I'm very confused about the whole blendFunc thing.

I have my level which I just render (did not touch blendFunc yet), with let's say color(0.3f, 0.3f, 0.3f, 1f);. Pretty dark...

Then this guy comes:


Now I want that cool looking light-effect, so I set the blendFunc to what exactly? GL_SRC_ALPHA, GL_DST_ALPHA?

Reading about blendFunc, I feel I'm getting only more confused. Why is it even possible to set something like GL_DST_* in the src part of the blendFunc? Shouldn't the src part only allow for GL_SRC_* stuff? Does it even make a difference?

Can anyone explain blendFunc to me like I'm five years old? Sad

edit: Damn, I chose the wrong subforum
Offline Redocdam

Senior Member


Medals: 17



« Reply #1 - Posted 2013-07-06 00:09:48 »

Alright, I'll give it a shot.

Explain Like I'm 5 Version:
When a mommy pixel loves a daddy pixel, they sometimes make a baby pixel.
This might get confusing, let's try another approach

Explained How I Understand it:
The Blend Function, as you may have guessed, is a function which defines how two colors combine to make the final color.
It helps if you think of this as a per-pixel process. So don't think of the entire image in the buffer, just consider one pixel.

First off we need to understand which parts of the functions apply to which color.

Source Color: This is the color you want to draw to the surface (the one you're currently drawing with).
Destination Color: This is the color which already exists on the surface.

So we have the basic idea:
Source Color + Destination Color = Baby-I mean Final Color.

Blend Function
Well, the above is great and all, but simply adding two colors together isn't always going to produce the results we want. Thus along comes the Blend Function.
Instead of just adding the two colors together, we're now going to mangle both colors and then add them together to create the final result. So we're going to define a function
for how we're going to mangle each pixel, by using the blend function.

BlendFunc(Mangle Source Function, Mangle Destination Function);

And once we plug the colors into the blending process, we get:
Mangle Source Function * Source Color + Mangle Destination Function * Destination Color = Final Color

Essentially, the function is a scaling factor for the specified color (think multiply each element in a color).

Of course this isn't the exact math under the hood, but we can overlook that right now.

Blend Function Parameters
You're probably already familiar with this, but just in case: elements of a color are defined in a 0 to 1 range.
The blend function defines how those elements should be scaled.

Let's take some simple examples, we'll be using RGB format:
You already drew your scene and now you want to add some lighting by drawing a circle/sphere over a specific area.

Circle/Sphere = Source Color
Scene = Destination Color

We'll go ahead and keep this simple by making a medium light completely white. So
Source = (0.5, 0.5, 0.5)

Let's assume all the colors under the light are dark blue. Thus
Destination = (0, 0, 0.3)

Since we're making a light, we obviously want to make the dark blue color a lighter shade of blue. So how should we do this?
Easy enough, just slap those two bad boys together with an add function and BAM instant lighting.
How do we tell OpenGL to do this? Well, we us the blending parameter called GL_ONE.

glBlendFunc(GL_ONE, GL_ONE);

This means the source and the destination are going to be multiplied by 1.
And here's essentially what happens:

Source Color: 1 * (0.5, 0.5, 0.5) = (0.5, 0.5, 0.5)
Destination Color: 1 * (0, 0, 0.3) = (0, 0, 0.3)
Final Color: (0.5, 0.5, 0.5) + (0, 0, 0.3) = (0.5, 0.5, 0.8 )

Note: This actually answers your question about which blend function to use for your lighting. Also, you can change the color and strength of the light
by manipulating the Source Color elements. IE: change to (0,1,0) if you want a really strong green light etc...
I recommend you play with it to get the effect you want, but start with lower numbers and work your way up. Once a color goes out of the 0 to 1 range, it's clamped to either 0 or 1.

In this example, we'll use RGBA format. And we'll tackle a common question when trying to do transparencies.
How do you draw a transparent object on top of a scene?
You're going to need a transparency value (the alpha element of course). Keep in mind the destination color does not need an alpha value, but the source does.
I think the magic of this one is easier understood by just looking at an example.
The function we'll use is

glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)

This means we're going to multiply the Source Color by the value of the Alpha channel on the source value.
Then we're going to take 1 and subtract the Source Color Alpha channel. The result is then multiplied against the Destination Color.

So let's say we want to blend a dark red over a blue Scene. But we only want to blend it by half (in other words, 50% transparency).
Source = (0.3, 0, 0, 0.5)
Destination = (0, 0, 0.3)

And essentially what happens:

Source Color: 0.5 * (0.3, 0, 0, 0.5) = (0.15, 0, 0, 0.25)
Destination Color: (1 - 0.5) * (0, 0, 0.3) = (0, 0, 0.15)
Final Color: (0.15, 0, 0, 0.5) + (0, 0, 0.15) = (0.15, 0, 0.15, 0.5)

The end result is a purple-ish looking color.
If the final destination doesn't contain alpha values, then to the best of my understanding the alpha channel of the final color is taken to a farm out in the country where it lives the
rest of its days - and wasn't by any means taken out back and shot.

Other Blend Function Parameters
There are a lot of other parameters you can set. To be honest, you'll likely see the above two example used the most.
The reason you can use things like GL_DST_ALPHA in the Source parameter, is because sometimes you feel life just isn't hard enough. No no no, that can't be right.
I haven't run into a case where I've needed a value from the destination channel, so I can't really provide an example. But I'm sure there are specific cases where it comes in useful.


Offline ra4king

JGO Kernel


Medals: 322
Projects: 2
Exp: 4 years


I'm the King!


« Reply #2 - Posted 2013-07-06 05:25:18 »

I fucking love you. I never bothered to learn about glBlendFunc before but now I finally know it.

Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline seismic

Senior Member


Medals: 3
Projects: 1



« Reply #3 - Posted 2013-07-06 11:29:25 »

Oh wow, bookmarked Cheesy
Thank you.
Offline ShuKen

Junior Member


Medals: 4



« Reply #4 - Posted 2013-07-22 04:43:12 »

Hi guys! I want to do the exact same effect posted here. The explanation about BlendFunc its awesome! but I still can't make it work properly!

I draw a background image:

1  
2  
3  
4  
batch.begin();

batch.setColor(1, 1, 1, 1f);
batch.draw(texture, 0, 0);


Then I draw a Rectangle (or Image, whatever) completely black with a little transparency (let's say, alpha 0.8f). That's the "shadow of the environment".

1  
2  
3  
4  
5  
Gdx.gl.glEnable(GL10.GL_BLEND);
Gdx.gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);

batch.setColor(1, 1, 1, 0.8f);
batch.draw(light_back, 0, 0);


And then, I want to draw the "light", which is just a png image with a white circle and transparent background. I also use an alpha value here.

1  
2  
3  
4  
batch.setColor(1, 1, 1, 0.7f);
batch.draw(light1, light_position_x, light_position_y);

batch.end();


And thats all. It works but...the result is kinda sad. Besides, the "light" is not very "transparent" and I guess that depends of the alpha of the "light_back" previously rendered. Here's a pic:



Im not sure what I have to do exactly but, I guess that what I need is to render the "light1" and get a final pixel color with alpha equal to "light1", independently of the previously alpha of the pixel. Tried a lot of things and combinations of the BlengFunc but i always get the same result (or worst).

Any idea? Sad
Offline davedes
« Reply #5 - Posted 2013-07-22 16:08:29 »

Something to note; you need to call flush() or end() on the sprite batch before changing GL states (such as blend function).

There is a working LibGDX example of blend func masking (feathered circle) here:
https://gist.github.com/mattdesl/5957929

However, this has some performance implications and seems to be buggy with certain Android devices.

Another approach is to apply the masking effect in a shader. The following shows the entire game with a dark "fog of war", with a feathered circle as the light:
http://pastebin.com/QeNgdqUt

Offline Redocdam

Senior Member


Medals: 17



« Reply #6 - Posted 2013-07-22 19:18:16 »

I think I understand what you are doing and why you're getting the less than stellar lighting result.
Unfortunately the end answer isn't a simple solution.

The problem with using a flat color as a light is what you're seeing. It's prone to saturation and really not good for main lighting.
That being said, I think you've done a pretty nice job mangling it to a point where it's not too bad.

I left it out of my above explanation, but the way to do lighting is a bit more complex. Instead of blanketing the light area with a single color, you actually want to use the exact color of every pixel for every pixel in the lit area.
Take your light as an example. The steps would be roughly
1.) Copy pixels covered by the light and save them.
2.) Draw your shadow texture
3.) Draw the pixels you saved back to the main screen using glBlend(GL_ONE, GL_ONE).

The end result is a perfectly round circle with all the pixels accurately lit without saturation.
But now you have a new problem. It's all lit, but now you're missing the nice feathered lighting. So in order to apply the attenuation, you'll either need to redraw a dark circle over the light, or get down into the shader code and apply an attenuation calculation based on the position of the light. The later being the optimal of course.

This approach is similar to something called a G-Buffer, where you draw parts of your scene to a off screen textures called Frame Buffers. For example, you'd draw all the objects to something named a diffuse buffer (note: I'm simply naming it diffuse), which contains everything without any lighting or shadowing. Then you can easily sample from the diffuse buffer and apply it to your output.
Think of it similar to a copy paste approach. Only the user never sees the source picture and when you paste the picture to the main view port, you can make it paste in special ways.

At this point, you're probably thinking "Wow... that's a lot of shi-" yes, it is.
Of course this isn't the only approach, but it's an effective one which mirrors the way many modern 3D games handle lighting.

Fortunately, you appear to be using LibGDX and davedes has done a nice job of pulling up some tutorials which should make your life a lot easier.
I don't use LibDGX, so I can't say for sure, but I believe the second link is more similar to my above approach. Only it's made easier through LibGDX (lucky you).
Offline ShuKen

Junior Member


Medals: 4



« Reply #7 - Posted 2013-07-22 23:13:57 »

Thanks davedes! Your links helped a lot! I made it Smiley



Anyway, is not very nice. The best solution is to use shaders, obviously. This implementation can be used only in particular cases...very particular cases I would say xD

Thanks for the help!
Offline ShuKen

Junior Member


Medals: 4



« Reply #8 - Posted 2013-07-22 23:31:23 »

I think I understand what you are doing and why you're getting the less than stellar lighting result.
Unfortunately the end answer isn't a simple solution.

The problem with using a flat color as a light is what you're seeing. It's prone to saturation and really not good for main lighting.
That being said, I think you've done a pretty nice job mangling it to a point where it's not too bad.

I left it out of my above explanation, but the way to do lighting is a bit more complex. Instead of blanketing the light area with a single color, you actually want to use the exact color of every pixel for every pixel in the lit area.
Take your light as an example. The steps would be roughly
1.) Copy pixels covered by the light and save them.
2.) Draw your shadow texture
3.) Draw the pixels you saved back to the main screen using glBlend(GL_ONE, GL_ONE).

The end result is a perfectly round circle with all the pixels accurately lit without saturation.
But now you have a new problem. It's all lit, but now you're missing the nice feathered lighting. So in order to apply the attenuation, you'll either need to redraw a dark circle over the light, or get down into the shader code and apply an attenuation calculation based on the position of the light. The later being the optimal of course.

This approach is similar to something called a G-Buffer, where you draw parts of your scene to a off screen textures called Frame Buffers. For example, you'd draw all the objects to something named a diffuse buffer (note: I'm simply naming it diffuse), which contains everything without any lighting or shadowing. Then you can easily sample from the diffuse buffer and apply it to your output.
Think of it similar to a copy paste approach. Only the user never sees the source picture and when you paste the picture to the main view port, you can make it paste in special ways.

At this point, you're probably thinking "Wow... that's a lot of shi-" yes, it is.
Of course this isn't the only approach, but it's an effective one which mirrors the way many modern 3D games handle lighting.

Fortunately, you appear to be using LibGDX and davedes has done a nice job of pulling up some tutorials which should make your life a lot easier.
I don't use LibDGX, so I can't say for sure, but I believe the second link is more similar to my above approach. Only it's made easier through LibGDX (lucky you).


Yeah, all this with alpha mask to use as light sounds weird and also not pretty nice on the end (at least what I'm capable of get).

Your approach its interesting, but Im starting to believe that Lighting and shaders are like cousins Tongue. The results of light effects using shaders its very good. For the moment, I will not look into shaders but in the near future, if I really want to implement light, I believe that I should look into shaders directly. :/

Seem that coding it's like life, it gets more and more complicated at every step LOL

Thanks for the reply!
Offline davedes
« Reply #9 - Posted 2013-07-23 02:23:31 »

Shaders are not too difficult to learn, they are just intimidating when you first look at them. Smiley IMHO it's pretty crucial to learn how they work if you want to make some "fancy" graphics (lighting, VFX, etc) rather than just a simple sprite game.

It seems you might already have seen this links given the placeholder graphics you're working with, but if not...

Here is a Java-based intro & tutorial to shaders:
https://github.com/mattdesl/lwjgl-basics/wiki/Shaders

And this one covers lighting in depth, albeit using a more involved technique (for "3D" style diffuse) than simple alpha maps:
https://github.com/mattdesl/lwjgl-basics/wiki/ShaderLesson6

Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline ShuKen

Junior Member


Medals: 4



« Reply #10 - Posted 2013-07-23 17:23:58 »

Shaders are not too difficult to learn, they are just intimidating when you first look at them. Smiley IMHO it's pretty crucial to learn how they work if you want to make some "fancy" graphics (lighting, VFX, etc) rather than just a simple sprite game.

It seems you might already have seen this links given the placeholder graphics you're working with, but if not...

Here is a Java-based intro & tutorial to shaders:
https://github.com/mattdesl/lwjgl-basics/wiki/Shaders

And this one covers lighting in depth, albeit using a more involved technique (for "3D" style diffuse) than simple alpha maps:
https://github.com/mattdesl/lwjgl-basics/wiki/ShaderLesson6


Hehe yep been looking at them. Are pretty cool and very well explained. I understand shaders a bit more now, but I still have to do some tests, moving from lwjgl-basics to normal api and then yes, I'll be able to use shaders Smiley

Thanks for the support! You guys rock!
Online Danny02
« Reply #11 - Posted 2013-07-23 19:56:06 »

give this excelent tool a try:
http://www.andersriggelsen.dk/glblendfunc.php

it let you try out all the different blend funcs in the browser
Offline ShuKen

Junior Member


Medals: 4



« Reply #12 - Posted 2013-07-23 23:15:14 »

give this excelent tool a try:
http://www.andersriggelsen.dk/glblendfunc.php

it let you try out all the different blend funcs in the browser

Wow very nice tool!

But I have some problems with it. Im triying to reproduce some effects in a libgdx app. Probably Im doing something wrong but, for example, the combination of: GL_ZERO and GL_ONE_MINUS_SRC_ALPHA doesn't seems to work as expected.

This is the output from the browser:

This is what I get:

This is the render method:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
     
batch.begin();
     
Gdx.gl20.glEnable(GL20.GL_BLEND);
Gdx.gl20.glBlendFunc(GL20.GL_ZERO, GL20.GL_ONE_MINUS_SRC_ALPHA);
Gdx.gl20.glBlendEquation(GL20.GL_FUNC_ADD);  
     
batch.draw(bg, 0, 0);
//batch.flush();          //<-- its the same flushing or not :(
 
batch.draw(sprite, 0, 0);
//batch.flush();          //<-- its the same flushing or not :(

batch.end();


Tried flushing the batch after every draw, and draw in differents orders, it simply doesn't do the same effect.

Alto tried other things and they doesn't work either :S
Offline davedes
« Reply #13 - Posted 2013-07-23 23:41:38 »

Try using spriteBatch.setBlendFunction(...) and spriteBatch.enableBlending() instead of directly calling Gdx.gl.glBlendFunc and Gdx.gl.glEnable.

Alternatively, you can set SpriteBatch's blend function to (-1, -1) and use your own glBlendFunc calls.

If you are using spriteBatch.setBlendFunction, you won't need to flush the batch (it will call flush() for you, if necessary). And you only need to flush the batch before setting a different blend function.

Offline ShuKen

Junior Member


Medals: 4



« Reply #14 - Posted 2013-07-24 00:56:42 »

Try using spriteBatch.setBlendFunction(...) and spriteBatch.enableBlending() instead of directly calling Gdx.gl.glBlendFunc and Gdx.gl.glEnable.

Alternatively, you can set SpriteBatch's blend function to (-1, -1) and use your own glBlendFunc calls.

If you are using spriteBatch.setBlendFunction, you won't need to flush the batch (it will call flush() for you, if necessary). And you only need to flush the batch before setting a different blend function.


Doing this:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
batch.begin();
     
batch.enableBlending();
batch.setBlendFunction(GL10.GL_ZERO, GL10.GL_ONE_MINUS_SRC_ALPHA);         //GL10 or GL20, same result
Gdx.gl20.glBlendEquation(GL20.GL_FUNC_ADD);             //with or without this, same result

//Background
batch.draw(bg, 0, 0);

//Ball
batch.draw(sprite, 0, 0);

batch.end();


I get an entirely black screen Sad
Tried setting batch.setBlendFunction(-1, -1) and then calling to Gdx.gl.setBlendFunction(...) and got same result.
Offline ShuKen

Junior Member


Medals: 4



« Reply #15 - Posted 2013-07-24 20:02:14 »

Ok I just can't let this problem go. I wanted a better result using alpha blending and I guess that I got something useful here.

With what I call my "lovely environment shadow":

And without my lovely shadow, which looks even better in movement:

This is the render code:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
batch.begin();

//Draw background and "environment shadow"...
batch.setBlendFunction(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
     
batch.setColor(1f, 1f, 1f, 1f);                 //<-- just to reset color
batch.draw(backgroundImage, 0, 0);
     
batch.setColor(0f, 0f, 0.02f, 0.8f);
batch.draw(shadowEnvirontment, 0, 0);
     
     
//Draw white light (it follows the mouse position, updated outside of this method)
batch.setBlendFunction(GL10.GL_SRC_ALPHA, GL10.GL_ONE);
batch.setColor(1f, 1f, 1f, 1f);
batch.draw(light, light_position_x, light_position_y, light.getWidth(), light.getHeight());
     
//Static lights
batch.setColor(1f, 0f, 0f, 1f);
batch.draw(light, 0 - light.getWidth()/2, game.height - light.getHeight()/2, light.getWidth(), light.getHeight());
batch.setColor(0f, 1f, 0f, 1f);
batch.draw(light, 0 - light.getWidth()/2, 0 - light.getHeight()/2, light.getWidth(), light.getHeight());
     
batch.end();


Something to note: the light file must be kinda specific. I used this one created with photoshop+paint.net:

http://postimg.org/image/gwrpe9ldt/

It doesn't need to be that size Tongue

And now, Im happy with the result Smiley

Thanks guys for the help!
Pages: [1]
  ignore  |  Print  
 
 
You cannot reply to this message, because it is very, very old.

 

Add your game by posting it in the WIP section,
or publish it in Showcase.

The first screenshot will be displayed as a thumbnail.

xsi3rr4x (81 views)
2014-04-15 18:08:23

BurntPizza (73 views)
2014-04-15 03:46:01

UprightPath (84 views)
2014-04-14 17:39:50

UprightPath (67 views)
2014-04-14 17:35:47

Porlus (84 views)
2014-04-14 15:48:38

tom_mai78101 (107 views)
2014-04-10 04:04:31

BurntPizza (167 views)
2014-04-08 23:06:04

tom_mai78101 (263 views)
2014-04-05 13:34:39

trollwarrior1 (214 views)
2014-04-04 12:06:45

CJLetsGame (223 views)
2014-04-01 02:16:10
List of Learning Resources
by SHC
2014-04-18 03:17:39

List of Learning Resources
by Longarmx
2014-04-08 03:14:44

Good Examples
by matheus23
2014-04-05 13:51:37

Good Examples
by Grunnt
2014-04-03 15:48:46

Good Examples
by Grunnt
2014-04-03 15:48:37

Good Examples
by matheus23
2014-04-01 18:40:51

Good Examples
by matheus23
2014-04-01 18:40:34

Anonymous/Local/Inner class gotchas
by Roquen
2014-03-11 15:22:30
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!