In the following example I am trying to have a trailing glow by using sf::RenderTexture. Unfortunately, the texture seem to be mirrored. I don't understand why.
Main program
#include <SFML/Graphics.hpp>
#include <iostream>
#define WIDTH 800
int main() {
sf::RenderWindow window(sf::VideoMode(WIDTH, WIDTH), "Test");
sf::Shader buffer;
buffer.loadFromFile("buffer.frag", sf::Shader::Fragment);
sf::Texture texture;
texture.create(WIDTH, WIDTH);
sf::Sprite sprite(texture);
sf::RenderTexture renderTexture;
renderTexture.create(WIDTH, WIDTH);
sf::Vector2f mousePos = sf::Vector2f(-1.f, -1.f);
buffer.setUniform("iChannel0", texture);
buffer.setUniform("iResolution", sf::Glsl::Vec2(WIDTH, WIDTH));
auto time = sf::Clock();
window.clear();
while(window.isOpen()) {
sf::Event currEvent;
while(window.pollEvent(currEvent))
if (currEvent.type == sf::Event::Closed) window.close();
buffer.setUniform("iTime", (float)time.getElapsedTime().asSeconds());
renderTexture.clear();
renderTexture.draw(sprite, &buffer);
renderTexture.display();
texture.update(renderTexture.getTexture());
window.clear();
window.draw(sprite);
window.display();
}
}
Fragment Shader
#version 140
out vec4 out_Colour;
uniform sampler2D iChannel0;
uniform vec2 iResolution;
uniform float iTime;
void main(void) {
vec2 spot = vec2(sin(iTime*5)*200+400, cos(iTime*15.0)*70+150);
vec3 color = vec3(sin(iTime*5), sin(iTime*1), sin(iTime*10)) * 0.5 + 0.5;
vec2 uv = (gl_FragCoord.xy - spot.xy) / iResolution.xy;
vec3 blob = 1.0 - vec3(smoothstep(length(uv), 0, .02));
vec3 stack = texture(iChannel0, gl_FragCoord.xy / iResolution.xy).xyz;
stack -= vec3(0.003);
out_Colour = vec4(stack + color * blob* 0.1, 1.0);
}
I get this weird result where half the screen is mirrored:
But if I render directly the buffer I don't get this result:
window.clear();
window.draw(sprite, &buffer);
window.display();
texture.update(window);
Related
I would like to replicate this in SFML, so I wrote this:
Main.cpp
#include <SFML/Graphics.hpp>
#define WIDTH 800
int main() {
sf::RenderWindow window(sf::VideoMode(WIDTH, WIDTH), "Test");
sf::Shader shader, buffer;
shader.loadFromFile("shader.frag", sf::Shader::Fragment);
buffer.loadFromFile("buffer.frag", sf::Shader::Fragment);
sf::Shader *shaders[] = {&shader, &buffer};
sf::Texture texture;
texture.create(WIDTH, WIDTH);
sf::RenderTexture renderTexture;
sf::Vector2f mousePos = sf::Vector2f(-1.f, -1.f);
for(auto &shader : shaders) {
shader->setUniform("iChannel0", texture);
shader->setUniform("iResolution", sf::Glsl::Vec2(WIDTH, WIDTH));
}
auto time = sf::Clock();
sf::Sprite sprite(texture);
while(window.isOpen()) {
sf::Event currEvent;
while(window.pollEvent(currEvent)) {
switch(currEvent.type) {
case(sf::Event::Closed): window.close(); break;
case(sf::Event::MouseMoved):
mousePos = sf::Vector2f(
currEvent.mouseMove.x, currEvent.mouseMove.y);
break;
}
}
buffer.setUniform("iTime", time.getElapsedTime().asSeconds());
buffer.setUniform("iMouse", sf::Glsl::Vec2(
mousePos.x, WIDTH - mousePos.y));
sf::RenderStates states;
states.blendMode = sf::BlendAdd;
states.shader = &buffer;
renderTexture.clear(sf::Color::Transparent);
renderTexture.draw(sf::Sprite(texture), states);
renderTexture.display();
sprite.setTexture(renderTexture.getTexture());
window.clear(sf::Color::Black);
window.draw(sprite, &shader);
window.display();
}
}
Buffer.frag
#version 140
out vec4 out_Colour;
uniform sampler2D iChannel0;
uniform vec2 iResolution;
uniform vec2 iMouse;
uniform float iTime;
void main(void){
vec2 mouse = iMouse.xy;
if(mouse.x <= 0.)
mouse = vec2(iResolution.x * (sin(iTime)+1.)/2., iResolution.y/2.);
vec3 blob = vec3(.11-clamp(
length((
gl_FragCoord.xy-mouse.xy)/iResolution.x),0.,.11))*2.;
vec3 stack = texture(
iChannel0,gl_FragCoord.xy/iResolution.xy).xyz * vec3(0.99,.982,.93);
out_Colour = vec4(stack + blob, 1.0);
}
Shader.frag
#version 140
out vec4 out_Colour;
uniform sampler2D iChannel0;
uniform vec2 iResolution;
void main(void){
out_Colour = vec4(texture(
iChannel0, gl_FragCoord.xy / iResolution.xy).xyz, 1.0);
}
I am probably missing something with the buffers.
Similar questions without clear answer:
shadertoy to SFML
I am new to shaders, and I want to animate an object with the vertex shader.
Right now I just want to move it with a constant. For some reason, instead of going in the x-direction of the world, it moves in the x-direction of the camera. (So whenever I turn the camera, the object rotates with me)
The project is in processing, but I don't think it affects the shader.
THE PROCESSING CODE:
PShader sdr;
void setup() {
size(1000, 1000, P3D);
noStroke();
sdr = loadShader("shdFrag.glsl", "shdVert.glsl");
}
void draw() {
background(200);
// Set camera
camera(0, -300, 700, mouseX-500, 0, 200, 0, 1, 0);
// Ground
resetShader();
beginShape();
fill(100);
vertex(-500, 0, 500);
vertex( 500, 0, 500);
vertex( 500, 0, -500);
vertex(-500, 0, -500);
endShape();
// Red Sphere
shader(sdr);
fill(255, 0, 0);
sphere(100);
}
VERTEX SHADER:
uniform mat4 transform;
attribute vec4 position;
attribute vec4 color;
out vec4 vertColor;
void main() {
vec4 pos = position;
pos.x += 300;
vertColor = color;
gl_Position = transform * pos;
}
FRAGMENT SHADER:
#ifdef GL_ES
precision mediump float;
precision mediump int;
#endif
in vec4 vertColor;
void main() {
vec4 color = vertColor;
gl_FragColor = vec4(color.xyz, 1);
}
A GIF of what is happening:
the scene with a sphere
I want to create a glow effect for my game. In order to keep this minimalistic let's say I want to glow an Image. :)
Starting with this one:
To get something like this:
It's a three step way.
save all bright pixels from the scene (= luminescence)
Apply a blur effect on those pixels (= blur)
draw original picture and the blur texture ontop (= assemble)
Step 1 and 3 are no Problem. The blur part just doesn't want to work correctly.
Before I explain further, here's my luminescence result:
(threshold = 0.67f)
An now when I blur this, I get some unlucky results:
This black edge comes from the fact, that any transparent color is black vec4(0.0, 0.0, 0.0, 0.0). It's not an unkown Problem within SFML/GLSL, and the suggestion was to use SFML's sf::BlendMode for this and multiply the .rgb value of the final pixel color in the fragment shader with its alpha value. So I did and now this my result:
It's better, but definetely not good. The blur shader now also avarages out the surrounding pixels of the luminescence mask. After assembling it's just a blurry picture:
.. I tried "fixing" this in the shader files by checking if the pixel's alpha is zero. This way I don't value them when avaraging out. But since sf::BlendMode is activated, I don't know how alpha behaves now - So I deactivated the blendmode but I still have weird results. (at the very of this question I provided the code and a result from this attempt)
none of my attempts to fix this work. I really could use some help here. Maybe I'm doing something fundamentally wrong in the shaders.. here's the full code -
If you want to compile it, make a folder resources with the 2 Fragment shaders and the background.jpg (in 1280x720).
luminescence.frag
#version 120
uniform sampler2D texture;
uniform float threshold;
void main(void){
vec3 current_color = texture2D(texture, gl_TexCoord[0].xy).rgb;
vec4 pixel = vec4(current_color.rgb, 0.0);
float brightness = dot(current_color.rgb, vec3(0.2126, 0.7152, 0.0722));
if (brightness >= threshold){
pixel = texture2D(texture, gl_TexCoord[0].xy);
}
gl_FragColor = pixel;
}
boxblur.frag
#version 120
uniform sampler2D texture;
uniform float texture_inverse;
uniform int blur_radius;
uniform vec2 blur_direction;
void main(void){
vec4 sum = texture2D(texture, gl_TexCoord[0].xy);
for (int i = 0; i < blur_radius; ++i){
sum += texture2D(texture, gl_TexCoord[0].xy + (i * texture_inverse) * blur_direction);
sum += texture2D(texture, gl_TexCoord[0].xy - (i * texture_inverse) * blur_direction);
}
vec4 pixel = vec4(sum / (blur_radius * 2 + 1));
pixel.rgb *= pixel.a;
gl_FragColor = pixel;
}
main.cpp
#include <SFML/Graphics.hpp>
#include <iostream>
#include <exception>
void run() {
const sf::Vector2f SIZE(1280, 720);
sf::Texture background_tex;
background_tex.loadFromFile("resources/background.jpg");
sf::Sprite background(background_tex);
sf::Shader luminescence;
luminescence.loadFromFile("resources/luminescence.frag", sf::Shader::Fragment);
luminescence.setUniform("texture", sf::Shader::CurrentTexture);
luminescence.setUniform("threshold", 0.67f);
sf::Shader blur;
blur.loadFromFile("resources/boxblur.frag", sf::Shader::Fragment);
blur.setUniform("texture", sf::Shader::CurrentTexture);
blur.setUniform("texture_inverse", 1.0f / SIZE.x);
sf::RenderStates shader_states;
shader_states.blendMode = sf::BlendMode(sf::BlendMode::One, sf::BlendMode::OneMinusSrcAlpha);
sf::ContextSettings context_settings;
context_settings.antialiasingLevel = 12;
//draws background
sf::RenderTexture scene_render;
scene_render.create(SIZE.x, SIZE.y, context_settings);
//draws luminescence and blur
sf::RenderTexture shader_render;
shader_render.create(SIZE.x, SIZE.y, context_settings);
sf::RenderWindow window(sf::VideoMode(SIZE.x, SIZE.y), "glsl fun", sf::Style::Default, context_settings);
while (window.isOpen()) {
sf::Event event;
while (window.pollEvent(event)) {
if (event.type == sf::Event::Closed) {
window.close();
}
}
scene_render.clear();
scene_render.draw(background);
scene_render.display();
//apply luminescence
shader_states.shader = &luminescence;
shader_render.clear(sf::Color::Transparent);
shader_render.draw(sf::Sprite(scene_render.getTexture()), shader_states);
shader_render.display();
//apply two pass gaussian blur 3 times to simulate gaussian blur.
shader_states.shader = &blur;
float blur_radius = 30.0f;
for (int i = 0; i < 3; ++i) {
blur.setUniform("blur_radius", static_cast<int>(blur_radius));
//vertical blur
blur.setUniform("blur_direction", sf::Glsl::Vec2(1.0, 0.0));
shader_render.draw(sf::Sprite(shader_render.getTexture()), shader_states);
shader_render.display();
//horizontal blur
blur.setUniform("blur_direction", sf::Glsl::Vec2(0.0, 1.0));
shader_render.draw(sf::Sprite(shader_render.getTexture()), shader_states);
shader_render.display();
//decrease blur_radius to simulate a gaussian blur
blur_radius *= 0.45f;
}
//assembly
window.clear();
window.draw(sf::Sprite(scene_render.getTexture()));
window.draw(sf::Sprite(shader_render.getTexture()));
window.display();
}
}
int main() {
try {
run();
}
catch (std::exception e) {
std::cerr << "caught exception - - - " << e.what() << '\n';
return 1;
}
return 0;
}
This is the boxblur.frag where I tried to exclude zero alpha values: (I removed shader_states.blendMode = sf::BlendMode(sf::BlendMode::One, sf::BlendMode::OneMinusSrcAlpha);on line 29 in main.cpp of course):
#version 120
uniform sampler2D texture;
uniform float texture_inverse;
uniform int blur_radius;
uniform vec2 blur_direction;
void main(void){
float div = 0.0;
vec4 sum = vec4(0.0, 0.0, 0.0, 0.0);
vec4 temp_color = texture2D(texture, gl_TexCoord[0].xy);
if (temp_color.a > 0.0){
sum += temp_color;
div += 1.0;
}
for (int i = 0; i < blur_radius; ++i){
temp_color = texture2D(texture, gl_TexCoord[0].xy + (i * texture_inverse) * blur_direction);
if (temp_color.a > 0.0){
sum += temp_color;
div += 1.0;
}
temp_color = texture2D(texture, gl_TexCoord[0].xy - (i * texture_inverse) * blur_direction);
if (temp_color.a > 0.0){
sum += temp_color;
div += 1.0;
}
}
vec4 pixel;
if (div == 0.0){
pixel = vec4(texture2D(texture, gl_TexCoord[0].xy).rgb, 0.0);
}
else{
pixel = vec4(sum / div);
}
gl_FragColor = pixel;
}
Resulting in:
[I am using Visual Studio 2017 Community] - Thanks for any help!
I posted this question also on en.sfml-dev.org (here) and fallahn showed me the correct approach.
Before tackling that, here are the picture results:
Luminescence (threshold = 0.24f):
Blur (4 layers):
Assembled:
Yay! The solution is to set all transparent pixels to solid black vec4(0.0, 0.0, 0.0, 1.0) and than after they've been blurred, just add them ontop the Scene:
vec4 tex_color = texture2D(texture, gl_TexCoord[0].xy);
vec4 add_color = texture2D(add_texture, gl_TexCoord[0].xy);
gl_FragColor = tex_color + add_color;
This way, if add_color is black ("transparent"), we add tex_color + vec4(0.0, 0.0, 0.0, 1.0) which results in no change!
This is great, because now you can ignore the alpha channel completely.
To understand why I find this so great, you can read this little rant here (feel free to skip it):
Without worrying about alpha you can ignore any sf::BlendMode like the confusing sf::BlendMode::OneMinusSrcAlpha which caused me headaches for 2 solid days. Try calculating any reasonable "true" alpha value when you know they are all premultiplied. Of course you also have to multiply all rgb values with the pixel's alpha to reverse the prumultiplication… the formulas escalate quite quickly from here. Also subtract 1 from alpha because it's OneMinusSrcAlpha... and don't forget to check for cases where the sum of all alphas (yes, you need to sum that) is 0 (or in OneMinusSrcAlpha matter, something else), because otherwise you get division by 0 (or in OneMinusSrcAlpha matter a division by 0 when all surrounding pixels are solid). Also sometimes weird alpha values might work, but only for a single pass of blur, but in my case I have multiple passes.. etc.
Here's the final code:
luminescence.frag
#version 120
uniform sampler2D texture;
uniform float threshold;
void main(void){
vec3 current_color = texture2D(texture, gl_TexCoord[0].xy).rgb;
vec4 pixel = vec4(0.0, 0.0, 0.0, 1.0);
float brightness = dot(current_color.rgb, vec3(0.2126, 0.7152, 0.0722));
if (brightness >= threshold){
pixel = texture2D(texture, gl_TexCoord[0].xy);
}
gl_FragColor = pixel;
}
boxblur.frag
#version 120
uniform sampler2D texture;
uniform float texture_inverse;
uniform int blur_radius;
uniform vec2 blur_direction;
void main(void){
vec4 sum = texture2D(texture, gl_TexCoord[0].xy);
for (int i = 0; i < blur_radius; ++i){
sum += texture2D(texture, gl_TexCoord[0].xy + (i * texture_inverse) * blur_direction);
sum += texture2D(texture, gl_TexCoord[0].xy - (i * texture_inverse) * blur_direction);
}
gl_FragColor = sum / (blur_radius * 2 + 1);
}
multiply.frag
#version 120
uniform sampler2D texture;
uniform float multiply;
void main(void){
gl_FragColor = texture2D(texture, gl_TexCoord[0].xy) * multiply;
}
assemble.frag
#version 120
uniform sampler2D texture;
uniform sampler2D add_texture;
uniform float add_weight;
void main(void){
vec4 tex_color = texture2D(texture, gl_TexCoord[0].xy);
vec4 add_color = texture2D(add_texture, gl_TexCoord[0].xy) * add_weight;
gl_FragColor = tex_color + add_color;
}
main.cpp
#include <SFML/Graphics.hpp>
#include <iostream>
#include <array>
void run() {
const sf::Vector2f SIZE(1280, 720);
sf::Texture background_tex;
background_tex.loadFromFile("resources/background.jpg");
sf::Sprite background(background_tex);
sf::Shader luminescence_shader;
luminescence_shader.loadFromFile("resources/luminescence.frag", sf::Shader::Fragment);
luminescence_shader.setUniform("texture", sf::Shader::CurrentTexture);
luminescence_shader.setUniform("threshold", 0.24f);
sf::Shader blur_shader;
blur_shader.loadFromFile("resources/boxblur.frag", sf::Shader::Fragment);
blur_shader.setUniform("texture", sf::Shader::CurrentTexture);
blur_shader.setUniform("texture_inverse", 1.0f / SIZE.x);
sf::Shader assemble_shader;
assemble_shader.loadFromFile("resources/assemble.frag", sf::Shader::Fragment);
assemble_shader.setUniform("texture", sf::Shader::CurrentTexture);
sf::Shader multiply_shader;
multiply_shader.loadFromFile("resources/multiply.frag", sf::Shader::Fragment);
multiply_shader.setUniform("texture", sf::Shader::CurrentTexture);
sf::RenderStates shader_states;
//no blendmode! we make our own - assemble.frag
sf::ContextSettings context_settings;
context_settings.antialiasingLevel = 12;
//draws background
sf::RenderTexture scene_render;
scene_render.create(SIZE.x, SIZE.y, context_settings);
sf::RenderTexture luminescence_render;
luminescence_render.create(SIZE.x, SIZE.y, context_settings);
//draws luminescence and blur
sf::RenderTexture assemble_render;
assemble_render.create(SIZE.x, SIZE.y, context_settings);
//addding multiple boxblurs with different radii looks really nice! in this case 4 layers
std::array<sf::RenderTexture, 4> blur_renders;
for (int i = 0; i < blur_renders.size(); ++i) {
blur_renders[i].create(SIZE.x, SIZE.y, context_settings);
}
const int BLUR_RADIUS_VALUES[] = { 250, 180, 125, 55 };
float blur_weight = blur_renders.empty() ? 0.0 : 1.0 / blur_renders.size();
sf::RenderWindow window(sf::VideoMode(SIZE.x, SIZE.y), "glsl fun", sf::Style::Default, context_settings);
while (window.isOpen()) {
sf::Event event;
while (window.pollEvent(event)) {
if (event.type == sf::Event::Closed) {
window.close();
}
}
//first draw the scene
scene_render.clear();
scene_render.draw(background);
scene_render.display();
//apply luminescence
shader_states.shader = &luminescence_shader;
luminescence_render.clear();
luminescence_render.draw(sf::Sprite(scene_render.getTexture()), shader_states);
luminescence_render.display();
//apply two pass gaussian blur n times to simulate gaussian blur.
shader_states.shader = &blur_shader;
for (int i = 0; i < blur_renders.size(); ++i) {
blur_shader.setUniform("blur_radius", BLUR_RADIUS_VALUES[i]);
blur_renders[i].clear();
blur_renders[i].draw(sf::Sprite(luminescence_render.getTexture()));
blur_renders[i].display();
//vertical blur
blur_shader.setUniform("blur_direction", sf::Glsl::Vec2(1.0, 0.0));
blur_renders[i].draw(sf::Sprite(blur_renders[i].getTexture()), shader_states);
blur_renders[i].display();
//horizontal blur
blur_shader.setUniform("blur_direction", sf::Glsl::Vec2(0.0, 1.0));
blur_renders[i].draw(sf::Sprite(blur_renders[i].getTexture()), shader_states);
blur_renders[i].display();
}
//load blur_renders[0] into assemble_render so we can add the other blurs ontop of it
shader_states.shader = &multiply_shader;
multiply_shader.setUniform("multiply", blur_weight);
assemble_render.clear();
assemble_render.draw(sf::Sprite(blur_renders[0].getTexture()), shader_states);
assemble_render.display();
//adding the rest ontop creating a final blur
shader_states.shader = &assemble_shader;
assemble_shader.setUniform("add_weight", blur_weight);
for (int i = 1; i < blur_renders.size(); ++i) {
assemble_shader.setUniform("add_texture", blur_renders[i].getTexture());
assemble_render.draw(sf::Sprite(assemble_render.getTexture()), shader_states);
assemble_render.display();
}
//final result; scene + blur
assemble_shader.setUniform("add_weight", 1.0f);
assemble_shader.setUniform("add_texture", assemble_render.getTexture());
assemble_render.draw(sf::Sprite(scene_render.getTexture()), shader_states);
assemble_render.display();
window.clear();
window.draw(sf::Sprite(assemble_render.getTexture()));
window.display();
}
}
int main() {
try {
run();
}
catch (std::exception e) {
std::cerr << "caught exception - - - " << e.what() << '\n';
return 1;
}
return 0;
}
Try to make a tiny example where you just want to average TWO pixels. Left (L) and right (R). Then the left pixel is made up of R(L), G(L), B(L), A(L) and the right pixel is made up of R(R), G(R), B(R) and A(R).
Without alpha, the averaging of Blue would just be:
(B(L)+B(R)) / 2
Taking alpha into account, it becomes:
(B(L)*A(L)+B(R)*A(R)) / (A(L)+A(R))
We can directly see that in the case of completely solid pixels (alpha=1) we get exactly the same formula as above:
(B(L)*1+B(R)*1) / (1+1) = (B(L)+B(R)) / 2
Furthermore, let's say that the right pixel is completely transparent and the left is solid, then the right pixel's color component won't affect anything, leaving the result exactly like the left pixel, which is exactly what we want:
(B(L)*1+B(R)*0) / (1+0) = (B(L)) / 1 = B(L)
Both pixels being completely transparent becomes a degenerate case which must be handled in some elegant way.
Now all you have to do is to extend this beyond two pixels. :-)
I'm trying to create a outline effect to my buttons when they are clicked but atm I'm just testing stuff with shaders... whenever I draw something with my shader tho it renders the shape completely black
#include <SFML\Graphics.hpp>
int main(){
sf::RenderWindow window(sf::VideoMode(500, 500), "hello world", sf::Style::Close);
sf::RectangleShape rect;
rect.setSize(sf::Vector2f(100, 100));
rect.setFillColor(sf::Color::Red);
sf::Shader blur;
if (!blur.loadFromFile("blur.frag", sf::Shader::Fragment)){
return 1;
}
if (!blur.isAvailable()){
return 1;
}
blur.setUniform("texture", sf::Shader::CurrentTexture);
blur.setUniform("blur_radius", 0.002f);
while (window.isOpen()){
sf::Event evnt;
while (window.pollEvent(evnt)){
if (evnt.type == sf::Event::Closed)
window.close();
}
window.clear(sf::Color(0, 100, 100));
sf::RenderStates state;
state.shader = &blur;
window.draw(rect, state);
window.display();
}
return 0;
}
and my shader code.
uniform sampler2D texture;
uniform float blur_radius;
void main()
{
vec2 offx = vec2(blur_radius, 0.0);
vec2 offy = vec2(0.0, blur_radius);
vec4 pixel = texture2D(texture, gl_TexCoord[0].xy) * 4.0 +
texture2D(texture, gl_TexCoord[0].xy - offx) * 2.0 +
texture2D(texture, gl_TexCoord[0].xy + offx) * 2.0 +
texture2D(texture, gl_TexCoord[0].xy - offy) * 2.0 +
texture2D(texture, gl_TexCoord[0].xy + offy) * 2.0 +
texture2D(texture, gl_TexCoord[0].xy - offx - offy) * 1.0 +
texture2D(texture, gl_TexCoord[0].xy - offx + offy) * 1.0 +
texture2D(texture, gl_TexCoord[0].xy + offx - offy) * 1.0 +
texture2D(texture, gl_TexCoord[0].xy + offx + offy) * 1.0;
gl_FragColor = gl_Color * (pixel / 16.0);
}
I would expect a blurred red rectangle to appear in the top left cornor but instead theres a black solid rectangle.
Because rect does not have a texture attach to it, all the pixels used in blur.frag are invalid, I guess in this case it use black pixels, hence the black rectangle.
You should create a texture (use sf::RenderTexture), draw whatever you want on it, then create a sf::Sprite on top of it and draw this sprite. Alternatively you may load a texture from an image.
#include <SFML/Graphics.hpp>
int main(){
sf::RenderWindow window(sf::VideoMode(200, 200), "hello world", sf::Style::Close);
window.setFramerateLimit(60);
sf::RectangleShape rectBlue;
rectBlue.setSize(sf::Vector2f(100, 50));
rectBlue.setFillColor(sf::Color::Blue);
sf::RectangleShape rectYellow;
rectYellow.setSize(sf::Vector2f(100, 50));
rectYellow.setFillColor(sf::Color::Yellow);
rectYellow.setPosition(0, 50);
sf::RenderTexture renderTexture;
if (!renderTexture.create(100, 100)){ //create a render texture
return 1;
}
renderTexture.clear();
renderTexture.draw(rectBlue); //draw blue rect on the render texture
renderTexture.draw(rectYellow); //draw yellow rect
renderTexture.display();
sf::Sprite sprite(renderTexture.getTexture()); //create a sprite from the created texture
sf::Shader blur;
if (!blur.loadFromFile("blur.frag", sf::Shader::Fragment)){
return 1;
}
if (!blur.isAvailable()){
return 1;
}
blur.setUniform("texture", sf::Shader::CurrentTexture);
blur.setUniform("blur_radius", 0.05f);
while (window.isOpen()){
sf::Event evnt;
while (window.pollEvent(evnt)){
if (evnt.type == sf::Event::Closed)
window.close();
}
window.clear(sf::Color(0, 100, 100));
window.draw(sprite, &blur); //draw the sprite with blur shader
window.display();
}
return 0;
}
Result:
I have a problem with a multisampled texture. It seems after blitting it to another surface for rendering, it's flipped upside down. What might cause that ? Should I provide some code ?
edit: Well, it's gonna be a lot of code, but here we go. This is how I create my surfaces / textures:
protected override void Create(int width, int height, SurfaceFormat format)
{
this.format = format;
bool multisample = format.Multisampling > 0;
int samples = Math.Max(0, Math.Min(format.Multisampling, 4));
format.TextureTarget = multisample ? TextureTarget.Texture2DMultisample : format.TextureTarget;
format.MipMapping = format.MipMapping && format.TextureTarget == TextureTarget.Texture2D;
Width = width;
Height = height;
textureHandle = GL.GenTexture();
//bind texture
GL.BindTexture(format.TextureTarget, textureHandle);
Log.Error("Bound Texture: " + GL.GetError());
if (format.TextureTarget == TextureTarget.Texture2D)
{
GL.TexParameter(format.TextureTarget, TextureParameterName.TextureMinFilter, (int)(format.MipMapping ? TextureMinFilter.LinearMipmapLinear : TextureMinFilter.Linear));
GL.TexParameter(format.TextureTarget, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
GL.TexParameter(format.TextureTarget, TextureParameterName.TextureWrapS, (int)format.WrapMode);
GL.TexParameter(format.TextureTarget, TextureParameterName.TextureWrapT, (int)format.WrapMode);
}
Log.Debug("Created Texture Parameters: " + GL.GetError());
if (samples < 1)
GL.TexImage2D(format.TextureTarget, 0, format.InternalFormat, Width, Height, 0, format.PixelFormat, format.SourceType, format.Pixels);
else
GL.TexImage2DMultisample(TextureTargetMultisample.Texture2DMultisample, samples, format.InternalFormat, Width, Height, true);
if (format.MipMapping)
GL.GenerateMipmap(GenerateMipmapTarget.Texture2D);
Log.Debug("Created Image: " + GL.GetError());
//unbind texture
GL.BindTexture(format.TextureTarget, 0);
//create depthbuffer
if (format.DepthBuffer)
{
GL.GenRenderbuffers(1, out dbHandle);
GL.BindRenderbuffer(RenderbufferTarget.RenderbufferExt, dbHandle);
if(multisample)
GL.RenderbufferStorageMultisample(RenderbufferTarget.RenderbufferExt, samples, RenderbufferStorage.DepthComponent24, Width, Height);
else
GL.RenderbufferStorage(RenderbufferTarget.RenderbufferExt, RenderbufferStorage.DepthComponent24, Width, Height);
}
//create fbo
fboHandle = GL.GenFramebuffer();
GL.BindFramebuffer(FramebufferTarget.FramebufferExt, fboHandle);
GL.FramebufferTexture2D(FramebufferTarget.FramebufferExt, FramebufferAttachment.ColorAttachment0Ext, format.TextureTarget, textureHandle, 0);
if (format.DepthBuffer)
GL.FramebufferRenderbuffer(FramebufferTarget.FramebufferExt, FramebufferAttachment.DepthAttachmentExt, RenderbufferTarget.RenderbufferExt, dbHandle);
Log.Debug("Framebuffer status: " + GL.CheckFramebufferStatus(FramebufferTarget.FramebufferExt));
Log.Debug("Created Framebuffer: " + GL.GetError());
GL.BindFramebuffer(FramebufferTarget.FramebufferExt, 0);
}
creation:
var sf = SurfaceFormat.Surface2D;
sf.Multisampling = 4;
multisampler = new Surface(Window.Width, Window.Height, sf);
Now in the render loop I do the following:
//Render entire scene to multisampler
SceneRenderer.RenderMultisampled(ActiveCamera, multisampler, time);
//blit sampler to my material input texture
multisampler.CloneTo(postEffect.Textures["_tex"]);
//blit this texture to my "Canvas" (basically a surface with additional drawing methods. The canvas material is use as a texture for a quad in my scene, thus rendering a copy of the output image to a plane.
postEffect.Textures["_tex"].CloneTo(canvas.Surface);
//This would be the same but via rendering with a quad instead of blitting. Has the same result
//canvas.Clear();
//canvas.DrawMaterial(postEffect);
//clear framebuffer
GL.BindFramebuffer(FramebufferTarget.Framebuffer, 0);
//Set viewport
GL.Viewport(0, 0, Window.Width, Window.Height);
//use material (bind shader & shader params) and draw the scene.
postEffect.Use();
Helper.DrawScreenQuad();
GL.UseProgram(0);
If this is not enough, I can also post the shaders & mesh code.
EDIT2: Okay everything is now working as expected EXCEPT when I use canvas.draw() instead of blitting the texture. The draw method looks like this:
public void DrawMaterial(Material material)
{
GL.Viewport(0, 0, Surface.Width, Surface.Height);
Surface.BindFramebuffer();
material.Use();
Helper.DrawScreenQuad();
GL.UseProgram(0);
GL.BindFramebuffer(FramebufferTarget.Framebuffer, 0);
}
Draw screen quad:
public static void DrawScreenQuad()
{
GL.Begin(PrimitiveType.Quads);
GL.TexCoord2(0, 1);
GL.Vertex2(-1, -1);
GL.TexCoord2(1, 1);
GL.Vertex2(1, -1);
GL.TexCoord2(1, 0);
GL.Vertex2(1, 1);
GL.TexCoord2(0, 0);
GL.Vertex2(-1, 1);
GL.End();
}
Shader used:
[Shader vertex]
#version 150 core
in vec2 _pos;
out vec2 texCoord;
uniform float _time;
uniform sampler2D tex;
void main() {
gl_Position = vec4(_pos, 0, 1);
texCoord = _pos/2+vec2(0.5,0.5);
texCoord.y = 1 - texCoord.y;
}
[Shader fragment]
#version 150 core
#define PI 3.1415926535897932384626433832795
out vec4 outColor;
uniform float _time;
uniform sampler2D tex;
in vec2 texCoord;
//
void main() {
outColor = texture2D(tex, texCoord);
}
Somehow the rendered scene gets turned upside down by this. Why ?
I think I found my mistake. I had the texture coordinates AND the camera inverted. It seems to be fixed now. Why I still don't undestand is, why this works:
[Shader vertex]
#version 150 core
in vec2 _pos;
out vec2 texCoord;
uniform float _time;
uniform sampler2D tex;
void main() {
gl_Position = vec4(_pos, 0, 1);
texCoord = _pos/2+vec2(0.5,0.5);
//texCoord.y = 1 - texCoord.y;
}
[Shader fragment]
#version 150 core
#define PI 3.1415926535897932384626433832795
out vec4 outColor;
uniform float _time;
uniform sampler2D tex;
in vec2 texCoord;
//
void main() {
outColor = texture2D(tex, texCoord);
}
I would've expected that the y coordinate of the tex coord would need to be inverted.