-
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]
-
Draw everything with 255 a to render texture then draw sprite using render texture's texture with desired a.
-
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?
-
It's not a preprocessing solution, it's a real-time one. It just involves drawing an extra sprite, so it's very cheap.
-
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)
-
Shaders wouldn't help, you really need an intermediate rendering here.
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.
-
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)
-
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 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
-
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.
-
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
-
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?
-
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.
-
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!
-
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.
-
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.
-
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!
-
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?
-
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?
-
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
-
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?
-
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:
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
-
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.
-
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();