SFML community forums

Help => Graphics => Topic started by: danikaze on September 22, 2012, 08:39:19 pm

Title: [SOLVED] How to do (composed) transparency? shaders?
Post by: danikaze on September 22, 2012, 08:39:19 pm
Hello!

I've searched for this question in this forum but I couldn't find it so, I want to know how to apply a % of transparency to several sprites/images at the same time.

Let me explain this:

Think about Photoshop (or gimp if you like). While the alpha channel of each sprite is like the opacity of each layer in Photoshop, and it's easily modificable, what I want to do is change the opacity of a group/folder of layers in Photoshop. That is, draw several images and then and only then apply the opacity value.

Here's an example:
Think of a playable character composed by 3 images (head/body/feet).
Let's say I do (pseudo-code)
player.alpha(60%)
I want to show this:
(http://en.sfml-dev.org/forums/index.php?action=dlattach;topic=9227.0;attach=265)

But applying alpha to each image, it would draw this:
(http://en.sfml-dev.org/forums/index.php?action=dlattach;topic=9227.0;attach=267)

So my question is, how to do this? Do I need to play with OpenGL (I never touched it and I don't know how) or there's a way in SFML?

Thanks!

[attachment deleted by admin]
Title: Re: How to do transparency? shaders?
Post by: FRex on September 22, 2012, 09:21:03 pm
Draw everything with 255 a to render texture then draw sprite using render texture's texture with desired a.
Title: Re: How to do transparency? shaders?
Post by: danikaze on September 22, 2012, 11:45:41 pm
Draw everything with 255 a to render texture then draw sprite using render texture's texture with desired a.

Yeah that's the simple way... "pregenerate graphics"...
But if the character is updating animations every frame... is there any way to do this propertly?
Title: Re: How to do transparency? shaders?
Post by: Laurent on September 23, 2012, 10:16:25 am
It's not a preprocessing solution, it's a real-time one. It just involves drawing an extra sprite, so it's very cheap.
Title: Re: How to do transparency? shaders?
Post by: danikaze on September 23, 2012, 07:38:40 pm
It's not a preprocessing solution, it's a real-time one. It just involves drawing an extra sprite, so it's very cheap.
I see... so there's no need for shaders or anything else?

I thought this would be a heavy solution... and now that I see exploit3r sentence makes me think it again:
But keep in mind drawing to the render texture every frame is quite a heavy task, it's like drawing two frames at once, so if it's not really needed don't do this every frame iteration.
I would need to do it every frame (that's why I thought about shaders, to let the GPU do the heavy task)
Title: Re: How to do transparency? shaders?
Post by: Laurent on September 23, 2012, 08:11:06 pm
Shaders wouldn't help, you really need an intermediate rendering here.

Quote
I thought this would be a heavy solution... and now that I see exploit3r sentence makes me think it again
He's wrong ;)

Let me show you why:
- drawing to a render-texture rather than to a render-window is the same operation, it's not more (or less) expensive
- drawing the render-texture to the render-window involves nothing special, it's just a single sprite to draw

And that's all.
Title: Re: How to do transparency? shaders?
Post by: danikaze on September 24, 2012, 10:27:22 pm
That's very nice then.

Anyway, taking further the question and talking about optimization now (if you don't mind), this way would need to create a renderTexture, therefore you need to know the total size of the sprites (ok, something easy), but you also need to clear it every frame before rendering other frames into the texture, and then draw it.

Ok ok, I know it's not a very heavy operation (and probably it will be the way I'll start with), but I think it could be optimized using shaders. Is it too hard/not worthable to do it? Everything that could save a few FPS is great (specially when you have to do it every frame, multiple times -20/30- per frame)
Title: Re: How to do transparency? shaders?
Post by: model76 on September 25, 2012, 12:32:42 am
I would advise you to write your software first, as simply and straight forward as you can, and then look at optimizing it later. Otherwise you could easily be spending your time optimizing the wrong thing, and complicating your code unnecessarily.
Title: Re: How to do transparency? shaders?
Post by: danikaze on September 25, 2012, 12:43:05 am
I would advise you to write your software first, as simply and straight forward as you can, and then look at optimizing it later. Otherwise you could easily be spending your time optimizing the wrong thing, and complicating your code unnecessarily.

I know I know, it's not the first game I write ;)
But I want to plan my architecture for the next one as complete as possible
Title: Re: How to do transparency? shaders?
Post by: Laurent on September 25, 2012, 08:20:36 am
Quote
I think it could be optimized using shaders
Shaders are not a magic solution to every problem.

Before talking about technical details, you should first find the right algorithm, i.e. how to draw 3 transparent sprites as a single uniform one. Chances are that an intermediate texture cannot be avoided.

Once you find a solution/idea/algorithm, we can start talking about whether shaders can help you to implement it or not.
Title: Re: How to do transparency? shaders?
Post by: danikaze on September 25, 2012, 12:22:20 pm
Yeah probably.
I have to do a research first about openGL and shaders, but I'll stick to this solution for now.

Thanks for everything
Title: Re: How to do transparency? shaders?
Post by: danikaze on December 02, 2012, 03:48:34 am
I tested it, finally!

Actually, drawing into a RenderTexture is about 15x slower than drawing directly into the RenderWindow...

Main code (shared part)
#include <SFML/Graphics.hpp>
#include <iostream>

using namespace std;

/*
 *
*/

int main()
{
        sf::RenderWindow window(sf::VideoMode(800, 600), "SFML tests");
        sf::RenderTexture screenBuffer;
        screenBuffer.create(window.getSize().x, window.getSize().y);
        sf::CircleShape shape(100.f);
        shape.setFillColor(sf::Color::Green);
        shape.setOutlineColor(sf::Color::Yellow);
        shape.setOutlineThickness(5);
        shape.setPointCount(shape.getPointCount()*1.5);
        shape.setOrigin(100, 100);
        shape.setPosition(400, 300);
        sf::Clock clock;

        sf::Texture texture;
        texture.loadFromFile("bin/img.png");
        sf::Sprite sprite(texture);

        while (window.isOpen())
        {
                sf::Event event;
                while (window.pollEvent(event))
                {
                        switch (event.type)
                        {
                        case sf::Event::Closed:
                                window.close();
                                break;
                        default:
                                break;
                        }
                }
               
                /* TEST PART, LOOK BELOW */

                window.display();
                cout << clock.restart().asMicroseconds() << endl;
        }

        return 0;
}
 

RenderWindow test part: (output: ~14800 microseconds)
        for(int i=0; i<1000; ++i)
        {
                window.clear();
                sprite.setColor(sf::Color(255, 255, 255));
                window.draw(sprite);

                window.draw(shape);

                sprite.setColor(sf::Color(255, 255, 255, 100));
                window.draw(sprite);
        }

 

RenderTexture test part: (output: ~222000 microseconds)
        for(int i=0; i<1000; ++i)
        {
                screenBuffer.clear();
                sprite.setColor(sf::Color(255, 255, 255));
                screenBuffer.draw(sprite);

                screenBuffer.draw(shape);

                sprite.setColor(sf::Color(255, 255, 255, 100));
                screenBuffer.draw(sprite);
                screenBuffer.display();

                window.draw(sf::Sprite(screenBuffer.getTexture()));
        }
 

Am I doing something wrong?
Title: Re: How to do transparency? shaders?
Post by: Laurent on December 02, 2012, 08:31:26 am
What takes time is to switch from one target to another.So if these 1000 iterations are only for testing purpose, you can ignore the results.
Title: Re: How to do transparency? shaders?
Post by: danikaze on December 02, 2012, 09:00:07 pm
Yes, that's for testing purpose, but in a real situation wouldn't be the same?
I mean, switching need to be done, whether it is a test or not.

SFML looks great, but I'd like to know its limitations, and I'm seeing it's not the same writing to RenderTexture than directly to the screen, and 15 times slower it's something to take into account ;)

What's exactly the switch it needs to do? From target texture? (RenderWindow/RenderTexture).
In a game probably will be more switching than in this test, so if that's the case, I'd design my architecture in a different way.

Thanks for all!
Title: Re: How to do transparency? shaders?
Post by: masskiller on December 02, 2012, 09:02:12 pm
Quote
SFML looks great, but I'd like to know its limitations, and I'm seeing it's not the same writing to RenderTexture than directly to the screen, and 15 times slower it's something to take into account ;)

This is definitely not normal, they last about the same time in normal circumstances.
Title: Re: How to do transparency? shaders?
Post by: Laurent on December 02, 2012, 10:37:48 pm
When you were drawing to a render-target (RenderTexture or RenderWindow) and then draw to another one, internally SFML has to change the active OpenGL context (there's one for each target). Changing the current context means to reset all the OpenGL states, flush the rendering pipeline, etc. This is extremely expensive.

I can't imagine why you'd need more than 1000 context switches every frame, but this is definitely not a good way to go.
Title: Re: How to do transparency? shaders?
Post by: danikaze on December 03, 2012, 12:04:18 am
I was just "stressing" the system to see the different performance ;)
Obviously I hope I won't need to draw 1000 times every frame, but I think I will need to do this several times per frames (drawing sprites composed by several images and applying transparency or other effects to the whole thing as I described in the example of the 1st message).

Nice to know how and why happens this ;)
Thanks Laurent for everything. Keep going!
Title: Re: How to do transparency? shaders?
Post by: danikaze on December 03, 2012, 12:42:37 am
Just another test to see about the switching target.. I removed the

window.draw(sf::Sprite(screenBuffer.getTexture()));

line so there's only screenBuffer.draw calls (therefore, shouldn't switch the active OpenGL context, right?), but the performance is still much slower than drawing directly to the screen.

Am I using RenderTextures in the proper way?
Title: Re: How to do transparency? shaders?
Post by: Laurent on December 03, 2012, 08:08:39 am
Quote
Am I using RenderTextures in the proper way?
Yes. It's hard to say what's wrong, could you write a complete and minimal code that reproduces the problem, so that we can test it?
Title: Re: How to do transparency? shaders?
Post by: danikaze on December 04, 2012, 02:22:55 am
Quote
Am I using RenderTextures in the proper way?
Yes. It's hard to say what's wrong, could you write a complete and minimal code that reproduces the problem, so that we can test it?
Here's a beautiful code to test.
#include <SFML/Graphics.hpp>
#include <SFML/System.hpp>
#include <iostream>

using namespace std;

int main()
{
        // define and init stuff
        int iterations = 10000;

        sf::RenderWindow window(sf::VideoMode(640, 480), "RenderWindow vs RenderTexture performance test");

        sf::Texture texture;
        texture.loadFromFile("img/hotel2.png");

        sf::Sprite sprite(texture);

        sf::RenderTexture screenBuffer;
        screenBuffer.create(window.getSize().x, window.getSize().y);

        sf::Clock clock;
        sf::Event event;
        sf::CircleShape shape(100.f);

        int time1, time2;

        // drawing directly to the RenderWindow
        cout << "RenderWindow.draw x " << iterations << " = ";
        clock.restart();
        for(int i=0; i<iterations; ++i)
        {
                window.clear();
                sprite.setColor(sf::Color(255, 255, 255, 100));
                sprite.setPosition(20, 10);
                window.draw(sprite);
                sprite.setPosition(60, 10);
                window.draw(sprite);
                window.display();
        }
        time1 = clock.getElapsedTime().asMilliseconds();
        cout << time1 << " ms." << endl;

        // drawing to the RenderTexture
        cout << "RenderTexture.draw x " << iterations << " = ";
        clock.restart();
        for(int i=0; i<iterations; ++i)
        {
                screenBuffer.clear();
                sprite.setColor(sf::Color(255, 255, 255, 255));
                sprite.setPosition(20, 10);
                screenBuffer.draw(sprite);
                sprite.setPosition(60, 10);
                screenBuffer.draw(sprite);
                screenBuffer.display();

                sf::Sprite sp(screenBuffer.getTexture());
                sp.setColor(sf::Color(255, 255, 255, 100));

                window.clear();
                window.draw(sp);
                window.display();
        }
        time2 = clock.getElapsedTime().asMilliseconds();
        cout << time2 << " ms." << endl;

        // display results
        if(time1 < time2)
        {
                cout << endl << "RenderWindow is " << time2/static_cast<double>(time1) << " times faster than RenderTexture." << endl;
        }
        else
        {
                cout << endl << "RenderWindow is " << time1/static_cast<double>(time2) << " times slower than RenderTexture." << endl;
        }

        // wait for key pressed and close screen
        cout << endl << "Push any key to exit..." << endl;
        while(window.isOpen())
        {
                while (window.pollEvent(event))
                {
                        if(event.type == sf::Event::KeyPressed || event.type == sf::Event::Closed)
                        {
                                window.close();
                        }
                }
        }

        return 0;
}
 

BUT! If you comment the 62nd line (window.display(); after window.draw(sp);), that is, the 2nd display in the 2nd test, the times are equals.

So, in this new test everything seems right, I don't know why in the old code was 15 times slower... maybe because of using and drawing shapes? :-/

At least, the effect of transparency works fine :D
Title: Re: How to do transparency? shaders?
Post by: Laurent on December 04, 2012, 08:20:54 am
Quote
So, in this new test everything seems right, I don't know why in the old code was 15 times slower...
Everything is right only without the call to display(), or even with it? If it's slower with it, is it approximately as slow as in your app (x15)?

What's your graphics card? Are your drivers up-to-date?
Title: Re: How to do transparency? shaders?
Post by: danikaze on December 04, 2012, 04:16:09 pm
Quote
So, in this new test everything seems right, I don't know why in the old code was 15 times slower...
Everything is right only without the call to display(), or even with it? If it's slower with it, is it approximately as slow as in your app (x15)?

What's your graphics card? Are your drivers up-to-date?

This is the output of the test program:
Code: [Select]
RenderWindow.draw x 10000 = 1720 ms.
RenderTexture.draw x 10000 = 3591 ms.

RenderWindow is 2.08779 times faster than RenderTexture.

Press any key to exit...

Which is totally legit, because in the RenderTexture part we need to call twice to .display() (one for the RenderTexture and one for the RenderWindow), while in the RenderWindow part we only need to call it once.

My graphic card is a nVidia GTX 460 with updated drivers
sf::ContextSettings context = window.getSettings();
cout << "Currently using OpenGL: " << context.majorVersion << "." << context.minorVersion << endl;
 
displays Currently using OpenGL: 4.2
Title: Re: How to do transparency? shaders?
Post by: Laurent on December 04, 2012, 04:25:12 pm
Quote
Which is totally legit, because in the RenderTexture part we need to call twice to .display() (one for the RenderTexture and one for the RenderWindow), while in the RenderWindow part we only need to call it once.
Hmm yeah, you should probably test the exact same code for each type of target.
Title: Re: How to do transparency? shaders?
Post by: danikaze on December 04, 2012, 06:49:44 pm
So, the thing that consumes time is actually the display :)

By the way, regarding to the original question of this post, I'll mark as solved this post and write here the solution to my problem:

// prepare everything:
sf::Texture texture1, texture2;
texture1.loadFromFile("img1.png");
texture2.loadFromFile("img2.png");

sf::Sprite sprite1(texture1);
sf::Sprite sprite2(texture2);

sf::RenderTexture buffer;
buffer.create(w, h);

// draw things with 255 alpha into RenderTexture
buffer.draw(sprite1);
buffer.draw(sprite2);
// and then, draw the buffer with the desired alpha into the screen
sf::Sprite bufferSp(buffer.getTexture());
bufferSp.setColor(sf::Color(255, 255, 255, alpha));
window.draw(buffer);
window.display();