-
Hello,
I think I have run into a bug, or at the very least an inconsistency in the behaviour.
The scenenario is the following:
- I have a sprite, with some kind of drawing.
- I want to use a shader on it, using another texture as a mask
I was not able to make it work correctly, and the mask texture seemed to be upside down. I though that this was because I forgot to use the .display() function on my rendertexture after drawing the mask on it. That was not the case, and decided to construct a minimal example, and I found out the following behaviour:
When you pass a texture to a shader, the coordinate system is different depending on the texture loaded:
- If the texture was loaded from a file, the top-left corner of the texture is the point (0,0) and the bottom-right corner is the point (1,1).
- If the texture is from a renderTexture, the bottom-left corner of the texture is the point (0,0) and the top-right corner is the point (1,1).
This means that one of the coordinate system is upside down.
Since this example needs of several files (the code, the shader and a couple of images) I have packed them into a file and attached it to the post.
Here is what is displayed in the example:
(http://i.imgur.com/URRJ3EI.png)
1. The target sprite
2. The renderTexture mask
3. The renderTexture's texture drawn over the target sprite
4. The texture loaded from a file
5. The texture drawn over the target sprite
Is this behaviour expected? Should one of the coordinate system be changed to be consistent between each other?
I hope the example is clear enough. If you have any question about it, please ask so I can clarify it.
[attachment deleted by admin]
-
Are you sure you called ".display()" on the renderTexture after drawing to it?
-
In the vertex shader you must apply the texture matrix to your texture coordinates. Look at the Shader example of the SFML SDK.
-
Are you sure you called ".display()" on the renderTexture after drawing to it?
I checked and double checked, and this was not the case.
In the vertex shader you must apply the texture matrix to your texture coordinates. Look at the Shader example of the SFML SDK.
Shouldn't the default vertex shader apply the appropiate matrix on the target texture?
I have used the following shader, over a file loaded texture and over a renderTexture made texture:
vec2 position = gl_TexCoord[0].xy;
vec4 pixel;
pixel.r=position.y;
pixel.a=1.0;
gl_FragColor = pixel;
The following image is the result of the shader being applied to different textures:
(http://i.imgur.com/WvFWv83.jpg)
The top rectangle is the renderTexture-made texture, and the bottom one the one loaded from a file.
As you can see from the shader, the color red of each pixel is equals the y value of the texture's coordinate.
Only the shader has been cutomized, and in the openGL documentation you can read the following (http://www.opengl.org/sdk/docs/manglsl/ in "gl_FragCoord" under the "Built-in Variables") :
By default, gl_FragCoord assumes a lower-left origin for window coordinates and assumes pixel centers are located at half-pixel centers.
As far as I know, this looks like the default behaviour of gl_FragCoord is not being respected, something is changed in the loading process of the textures in SFML that is modifying the default behaviour on GLSL.
-
Shouldn't the default vertex shader apply the appropiate matrix on the target texture?
It does, but since you didn't give any detail at all about your shader(s), I assumed you had your own vertex shader.
Please give us a complete and minimal code that reproduces the problem. And test the Shader example of SFML, this one works fine and has both textures loaded from file or from render-texture.
-
Here is a complete and minimal code of the issue at hand:
#include <SFML/Graphics.hpp>
int main()
{
sf::RenderWindow m_window;
sf::Sprite maskSprite;
sf::RenderTexture maskRender;
sf::Texture maskTexture;
sf::Shader shader;
m_window.create(sf::VideoMode(800, 600), "Fragment shader!");
//Setup our file-loaded mask
maskTexture.loadFromFile("mask.png");
//Setup our renderTexture-made mask
maskRender.create(maskTexture.getSize().x, maskTexture.getSize().y);
maskRender.clear(sf::Color(0,0,0,255));
maskRender.display();
//Load the shader
shader.loadFromFile("fragment.frag",sf::Shader::Fragment);
while(m_window.isOpen())
{
sf::Event event;
while(m_window.pollEvent(event))
{
if(event.type == sf::Event::Closed)
m_window.close();
}
//We load the mask texture loaded from a file into the sprite and draw it, so we can compare it to the result
maskSprite.setTexture(maskTexture);
maskSprite.setPosition(256,300);
m_window.draw(maskSprite , &shader);
//We load the render-made mask into the sprite and draw it, so we can compare it to the result
maskSprite.setTexture(maskRender.getTexture());
maskSprite.setPosition(0,0);
m_window.draw(maskSprite, &shader);
m_window.display();
}
}
The shader is the following:
void main()
{
vec2 position = gl_TexCoord[0].xy;
vec4 pixel;
pixel.r=position.y;
pixel.a=1.0;
gl_FragColor = pixel;
}
You just need an image with the name "mask.png"
-
Thanks.
The texture coordinates are inverted because the texture produced by sf::RenderTexture is inverted (because of FBO). So I don't know what your initial idea was, but don't do it this way ;)
PS: all SFML resources can be created directly in source code, which is more convenient for me when you submit a minimal example
#include <SFML/Graphics.hpp>
const char* fragmentShader =
"void main()"
"{"
" gl_FragColor = vec4(gl_TexCoord[0].y, 0.0, 0.0, 1.0);"
"}"
;
int main()
{
sf::RenderWindow m_window(sf::VideoMode(800, 600), "Fragment shader!");
// Setup our file-loaded mask
sf::Image image;
image.create(100, 100, sf::Color::Green);
sf::Texture maskTexture;
maskTexture.loadFromImage(image);
// Setup our renderTexture-made mask
sf::RenderTexture maskRender;
maskRender.create(maskTexture.getSize().x, maskTexture.getSize().y);
maskRender.clear(sf::Color(0, 0, 0, 255));
maskRender.display();
// Load the shader
sf::Shader shader;
shader.loadFromMemory(fragmentShader, sf::Shader::Fragment);
sf::Sprite maskSprite;
while (m_window.isOpen())
{
sf::Event event;
while (m_window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
m_window.close();
}
// We load the mask texture loaded from a file into the sprite and draw it, so we can compare it to the result
maskSprite.setTexture(maskTexture);
maskSprite.setPosition(0, 300);
m_window.draw(maskSprite, &shader);
// We load the render-made mask into the sprite and draw it, so we can compare it to the result
maskSprite.setTexture(maskRender.getTexture());
maskSprite.setPosition(0, 0);
m_window.draw(maskSprite, &shader);
m_window.display();
}
return 0;
}
-
The texture coordinates are inverted because the texture produced by sf::RenderTexture is inverted (because of FBO). So I don't know what your initial idea was, but don't do it this way ;)
I see, as long as this behaviour is consistent with sf::renderTexture's texture I can live with it on my code. I ran into this when I decided to draw a mask (renderTexture) I was using over a sprite's texture (loaded from a file), in order to check if it was correctly aligned, just to debug it.
So no real harm. Just wanted to let you guys know, just in case someone ran into this. Loving the library so far, so I was eager to report a bug, knowing I would be of some help.
Thanks!