Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: New Shader class in SFML 2 to replace PostFx  (Read 42348 times)

0 Members and 1 Guest are viewing this topic.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
New Shader class in SFML 2 to replace PostFx
« on: November 03, 2009, 10:17:56 am »
Hi

I've rewrote the sf::PostFx class, which is now sf::Shader. It's a more general purpose pixel shader (or fragment shader) class. I've not updated the API documentation yet, so here is a short description of the new features, and of what to change for people using shaders.

Its interface is basically the same as PostFx (Load, SetParameter, SetTexture), but it no longer inherits from sf::Drawable. Here is the new usage:
Code: [Select]
target.Draw(object, shader);
Shaders can be used on any type of drawable, but for sf::String and sf::Shape it is more limited than for sf::Sprite because:
- the texture used by a sf::String is not what you see on screen
- sf::Shape doesn't use a texture
You have to take this in account in your shaders.

Post-effects are now achieved using a combination of RenderImage and Shader:
Code: [Select]
sf::Shader postEffect;
...
sf::RenderImage renderImage;
renderImage.Create(window.GetWidth(), window.GetHeight());
...
renderImage.Clear();
renderImage.Draw(everything);
renderImage.Display();

sf::Sprite screen(renderImage.GetImage());
window.Draw(screen, postEffect);
window.Display();


Images are no longer passed by pointer to Shader::SetTexture, and the texture of the object being drawn is referred as Shader::CurrentTexture:
Code: [Select]
sf::Image image;
...
shader.SetTexture("image", image);
shader.SetTexture("texture", sf::Shader::CurrentTexture);


The special preprocessing step has been removed, shaders are now pure GLSL programs. So what was previously this:
Code: [Select]
texture tex

effect
{
_out = tex(_in);
}

is now this:
Code: [Select]
uniform sampler2D tex;

void main()
{
gl_FragColor = texture2D(tex, gl_TexCoord[0].xy);
}


Shaders can also be used directly, with custom OpenGL rendering. However it is not possible to have a vertex shader if you use sf::Shader.
Code: [Select]
window.SetActive();
shader.Bind();

... render OpenGL stuff ...

shader.Unbind();


If I have forgotten anything, or if something is not clear enough, ask your question here :)
Laurent Gomila - SFML developer

l0calh05t

  • Full Member
  • ***
  • Posts: 200
    • View Profile
New Shader class in SFML 2 to replace PostFx
« Reply #1 on: November 03, 2009, 02:08:02 pm »
If you are using OpenGL3, which SFML2 will do automatically if it is available AFAIK, the predefined varyings like gl_TexCoord etc no longer exist, which might turn out to be a problem (depending on how your predefined vertex shader is set)

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
New Shader class in SFML 2 to replace PostFx
« Reply #2 on: November 03, 2009, 02:17:39 pm »
Quote
If you are using OpenGL3, which SFML2 will do automatically if it is available AFAIK

It will probably be explicitely specified with an additional parameter in sf::ContextSettings in the future.

Quote
the predefined varyings like gl_TexCoord etc no longer exist, which might turn out to be a problem (depending on how your predefined vertex shader is set)

I hadn't thought about this potential issue. I don't know much about the new predefined variables, I'll have to take a look into this.

To avoid problems I think I'll first implement the explicit selection of the OpenGL version :)
Laurent Gomila - SFML developer

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
New Shader class in SFML 2 to replace PostFx
« Reply #3 on: November 03, 2009, 05:21:31 pm »
By the way, it is still possible to do post-effects the old way:
Code: [Select]
sf::Shader postEffect;
sf::Image screen;
...
window.Clear();
window.Draw(everything);
screen.CopyScreen(window);
window.Draw(sf::Sprite(screen), postEffect);
window.Display();

This one has the benefit of not impacting the main rendering process, and can be added/removed easily.
Laurent Gomila - SFML developer

l0calh05t

  • Full Member
  • ***
  • Posts: 200
    • View Profile
New Shader class in SFML 2 to replace PostFx
« Reply #4 on: November 03, 2009, 05:41:10 pm »
Quote from: "Laurent"
It will probably be explicitely specified with an additional parameter in sf::ContextSettings in the future.


Good idea. Will window creation fail if a specific version is requested and isn't available, or will it work, but report it in some way? (I would prefer the first variant)

Quote
I hadn't thought about this potential issue. I don't know much about the new predefined variables, I'll have to take a look into this.

Which is why I brought it up. BTW, there are no "new predefined" variables, as OpenGL3 simply removes all of the predefined ones (even position iirc)

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
New Shader class in SFML 2 to replace PostFx
« Reply #5 on: November 03, 2009, 07:51:43 pm »
Quote
Good idea

Implemented and commited ;)

Quote
Will window creation fail if a specific version is requested and isn't available, or will it work, but report it in some way? (I would prefer the first variant)

My rule is that context settings are only a hint, and if any of them is not supported then SFML will try to find the closest valid match. So no, no failure will happen. And you can request the actual settings that were used with Window::GetSettings().

Quote
BTW, there are no "new predefined" variables, as OpenGL3 simply removes all of the predefined ones (even position iirc)

Oh, I see. That will not be a big issue then :)
Laurent Gomila - SFML developer

OniLinkPlus

  • Hero Member
  • *****
  • Posts: 500
    • View Profile
New Shader class in SFML 2 to replace PostFx
« Reply #6 on: November 04, 2009, 04:09:32 am »
So we can draw the shader to specific sprites on the screen with Bind and Unbind? Exciting, now I can make my 2D Water distort everything inside of it. Before, it would've taken drawing the sprites, then after the water is drawn, copy that region covered by water to a sprite, render the shader to it, then copy that BACK to the screen.
I use the latest build of SFML2

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
New Shader class in SFML 2 to replace PostFx
« Reply #7 on: November 04, 2009, 08:48:23 am »
Quote
So we can draw the shader to specific sprites on the screen with Bind and Unbind?

Bind and Unbind is for using the shader with OpenGL. To use it with sprites, you just have to pass the shader to the Draw function.

Quote
Exciting, now I can make my 2D Water distort everything inside of it. Before, it would've taken drawing the sprites, then after the water is drawn, copy that region covered by water to a sprite, render the shader to it, then copy that BACK to the screen.

You could already do it with a render image ;)
Laurent Gomila - SFML developer

OniLinkPlus

  • Hero Member
  • *****
  • Posts: 500
    • View Profile
New Shader class in SFML 2 to replace PostFx
« Reply #8 on: November 04, 2009, 04:38:25 pm »
So I'm trying this out, right? I updated to build 1262, cded to the sfml2 directory, did a make sfml, then a sudo make install, changed my game to use the new shader class, compiled, and get this:
/usr/local/lib/libsfml-graphics.so  undefined reference to `sf::EnsureGlewInit()'
I use the latest build of SFML2

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
New Shader class in SFML 2 to replace PostFx
« Reply #9 on: November 04, 2009, 04:50:29 pm »
I think you have to "make clean" before :)

If you were previously using a very old revision, be careful because the default install path has changed, so you probably have to remove what's left in /usr/include and /usr/lib.
Laurent Gomila - SFML developer

heishe

  • Full Member
  • ***
  • Posts: 121
    • View Profile
New Shader class in SFML 2 to replace PostFx
« Reply #10 on: November 04, 2009, 08:54:17 pm »
Already made up your mind about giving us the ability to apply textures to sf::Shape's ?

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
New Shader class in SFML 2 to replace PostFx
« Reply #11 on: November 04, 2009, 09:03:33 pm »
Almost.

I won't think about it until the next major version (maybe SFML 3, maybe not), and here is why: a sf::Shape with texture would make sf::Sprite almost useless, I think I'd have to merge them into a more general purpose "2D entity with texture" class that would be as easy as sf::Sprite to use, and as powerful as sf::Shape. You see, it's not a trivial modification and it will make me rewrite some core parts of the graphics module.

Another problem is that a lot of people will want to be able to use texture-repeat with shapes, and I can't provide it.
Laurent Gomila - SFML developer

OniLinkPlus

  • Hero Member
  • *****
  • Posts: 500
    • View Profile
New Shader class in SFML 2 to replace PostFx
« Reply #12 on: November 05, 2009, 12:18:48 am »
Ignore this post, it was an error on my end.
I use the latest build of SFML2

OniLinkPlus

  • Hero Member
  • *****
  • Posts: 500
    • View Profile
Differences between PostFX and Shader
« Reply #13 on: November 05, 2009, 05:25:25 am »
Alright, so I got everything working with sf::Shader, and went to test the differences. Here's what I've found:
-Native Linux sf::Shader is very laggy
-Emulated Windows game with sf::PostFX runs perfectly at full speed
Why is it that an Emulated Game can run more efficiently than a Native Test? Here's another problem. When I used sf::PostFX, I used this shader:
Code: [Select]
texture screen
float time

effect
{
vec2 offset = vec2(cos(time/500+_in.x*10)/50, sin(time/500+_in.y*10)/50);
_out = vec4(screen(_in+offset));
}

This was meant to make a watery effect by stretching and shrinking the screen. It worked perfectly, every part of the screen was stretched and shrunk, and there were no blank spaces in the screen. I used this code:
Code: [Select]
#include <SFML/Graphics.hpp>
int main(){
 sf::RenderWindow Window(sf::VideoMode(512, 512, 32), "Test");
 sf::Image imgBackground;
 imgBackground.LoadFromFile("Random512x512image.png");
 sf::Sprite sprBackground;
 sprBackground.SetImage(imgBackground);
 sf::PostFX WaterShader;
 WaterShader.LoadFromFile("WaterShader.sfx");
 while (Window.IsOpened()){
  // Process Events here, close on sf::Event::Close
  Window.Clear();
  Water.SetTexture("screen", NULL);
  Water.SetParameter("time", (double)clock());
  Screen.Draw(sprBackground);
  Screen.Draw(Water);
  Screen.Display();
 }
}

It works like a charm. However, with sf::Shader, I translated the Shader file to this:
Code: [Select]
uniform sampler2D screen;
uniform float time;

void main(){
vec2 offset = vec2(cos(time/500+gl_TexCoord[0].x*10)/50, sin(time/500+gl_TexCoord[0].y*10)/50);
gl_FragColor = vec4(texture2D(screen, gl_TexCoord[0].xy+offset));
}

And the test's code to this:
Code: [Select]
#include <SFML/Graphics.hpp>
int main(){
 sf::RenderWindow Window(sf::VideoMode(512, 512, 32), "Test");
 sf::Image imgBackground;
 imgBackground.LoadFromFile("Random512x512image.png");
 sf::Sprite sprBackground;
 sprBackground.SetImage(imgBackground);
 sf::Shader WaterShader;
 WaterShader.LoadFromFile("WaterShader.sfx");
 while (Window.IsOpened()){
  // Process Events here, close on sf::Event::Close
  Window.Clear();
  Water.SetTexture("screen", sf::Shader::CurrentTexture);
  Water.SetParameter("time", (double)clock());
  Screen.Draw(sprBackground, Water);
  Screen.Display();
 }
}

And it works, but I get blank spots on the sides of the screen, and there is much lag. What could be causing this change?
I use the latest build of SFML2

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
New Shader class in SFML 2 to replace PostFx
« Reply #14 on: November 05, 2009, 07:27:27 am »
Quote
And it works, but I get blank spots on the sides of the screen

Can you show a screenshot? Have you tried to disable smoothing on your image?

Quote
and there is much lag

You didn't have it before?
Laurent Gomila - SFML developer