Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?

[SFML 2.X] Motion Blur - rozmycie sprajta w ruchu

Ostatnio zmodyfikowano 2025-04-12 13:44
Autor Wiadomość
tBane
Temat założony przez niniejszego użytkownika
[SFML 2.X] Motion Blur - rozmycie sprajta w ruchu
» 2025-04-11 06:26:23
Cześć. Próbuję osiągnąć efekt Motion Blur czyli rozmycie podczas przesuwania obiektu. Jak na razie napisałem shader oraz klasę pod renderer. W zasadzie algorytm działa tak, że wszystkie sprajty są rysowane na ostatniej teksturze renderera, a suma wagowa tych tekstur daje nam teksturę finalną, czyli tą którą renderuje. Moje pytanie brzmi, jak poprawnie wyrenderować efekt Motion Blur, bo u mnie migają sprajty :-/

Poniżej zamieszczam kod shadera oraz Renderera.

shaders\motion_blur.frag
uniform sampler2D render_texture_0;
uniform sampler2D render_texture_1;
uniform sampler2D render_texture_2;

void main()
{
    vec4 c0 = texture2D(render_texture_0, gl_TexCoord[0].xy);
    vec4 c1 = texture2D(render_texture_1, gl_TexCoord[0].xy);
    vec4 c2 = texture2D(render_texture_2, gl_TexCoord[0].xy);

    gl_FragColor = c0*0.7 + c1*0.2 + c2*0.1;
}

Camera.hpp
C/C++
#ifndef Camera_hpp
#define Camera_hpp
float screenWidth = 1280;
float screenHeight = 720;

class Camera {
public:
   
sf::Vector2f position;
   
sf::View view;
   
   
Camera() {
       
position = sf::Vector2f( 0, 0 );
       
view.setSize( sf::Vector2f( screenWidth, screenHeight ) );
       
view.setCenter( position );
   
}
}
;
#endif

Renderer.hpp
C/C++
#ifndef Renderer_hpp
#define Renderer_hpp


class Renderer {
public:
   
const short frames = 3;
   
std::vector < sf::RenderTexture * > render_textures;
   
sf::Shader * sh;
   
sf::Color frame_color = sf::Color( 0, 0, 0, 128 );
   
   
Renderer() {
       
       
for( short i = 0; i < frames; i++ ) {
           
sf::RenderTexture * r = new sf::RenderTexture();
           
r->create( screenWidth, screenHeight );
           
r->clear( frame_color );
           
r->setView( cam->view );
           
render_textures.push_back( r );
       
}
       
       
sh = new sf::Shader();
       
sh->loadFromFile( "shaders\\motion_blur.frag", sf::Shader::Fragment );
       
       
   
}
   
   
~Renderer() {
       
for( auto & r: render_textures )
           
 delete r;
       
       
render_textures.clear();
   
}
   
   
sf::RenderTexture * getTheCurrentFrame() {
       
return render_textures.back();
   
}
   
   
void update() {
       
// delete the oldest texture
       
delete render_textures.front();
       
render_textures.erase( render_textures.begin() );
       
       
// add the new texure
       
sf::RenderTexture * rtex = new sf::RenderTexture();
       
rtex->create( screenWidth, screenHeight );
       
rtex->clear( frame_color );
       
rtex->setView( cam->view );
       
render_textures.push_back( rtex );
   
}
   
   
void draw() {
       
       
getTheCurrentFrame()->display();
       
       
sh->setUniform( "render_texture_0", render_textures[ 0 ]->getTexture() );
       
sh->setUniform( "render_texture_1", render_textures[ 1 ]->getTexture() );
       
sh->setUniform( "render_texture_2", render_textures[ 2 ]->getTexture() );
       
       
sf::Sprite spr;
       
spr.setTexture( getTheCurrentFrame()->getTexture() );
       
spr.setOrigin( screenWidth / 2, screenHeight / 2 );
       
spr.setPosition( cam->position );
       
       
window->draw( spr, sh );
   
}
}
;

Renderer * renderer = nullptr;

#endif


main.cpp
C/C++
#include "Camera.hpp"
#include "Renderer.hpp"

sf::RenderWindow * window;
Camera * cam;

int main() {
   
   
window = new sf::RenderWindow( sf::VideoMode( screenWidth, screenHeight ), "test motion blur" );
   
   
cam = new Camera();
   
renderer = new Renderer();
   
   
sf::Texture texture;
   
texture.loadFromFile( "assets\\natures\\tree9.png" );
   
   
sf::Sprite tree;
   
tree.setTexture( texture );
   
tree.setOrigin( texture.getSize().x / 2, texture.getSize().y / 2 );
   
   
while( window->isOpen() ) {
       
       
sf::Vector2i mousePosition = sf::Mouse::getPosition( * window ); // Pobierz aktualną pozycję myszy względem bieżącego okna
       
sf::Vector2f worldMousePosition = window->mapPixelToCoords( mousePosition );
       
       
cam->update();
       
window->setView( cam->view );
       
       
// EVENTS
       
sf::Event event;
       
while( window->pollEvent( event ) ) {
           
           
if( event.type == sf::Event::Closed ) {
               
window->close();
           
}
           
        }
       
       
// UPDATES
       
renderer->update();
       
tree.setPosition( worldMousePosition );
       
       
// RENDER
       
window->clear();
       
       
renderer->getTheCurrentFrame()->draw( tree );
       
renderer->getTheCurrentFrame()->display();
       
renderer->draw();
       
       
window->display();
   
}
   
   
   
return 0;
}
P-182224
pekfos
» 2025-04-11 21:19:26
Podajesz do shadera te tekstury w złej kolejności, poza tym nie widzę żadnego migania.
C/C++
#include <SFML/Graphics.hpp>

float screenWidth = 1280;
float screenHeight = 720;

class Camera {
public:
   
sf::Vector2f position;
   
sf::View view;
   
   
Camera() {
       
position = sf::Vector2f( 0, 0 );
       
view.setSize( sf::Vector2f( screenWidth, screenHeight ) );
       
view.setCenter( position );
   
}
}
;

sf::RenderWindow * window;
Camera * cam;

class Renderer {
public:
   
const short frames = 3;
   
std::vector < sf::RenderTexture * > render_textures;
   
sf::Shader * sh;
   
sf::Color frame_color = sf::Color( 0, 0, 0, 128 );
   
   
Renderer() {
       
       
for( short i = 0; i < frames; i++ ) {
           
sf::RenderTexture * r = new sf::RenderTexture();
           
r->create( screenWidth, screenHeight );
           
r->clear( frame_color );
           
r->setView( cam->view );
           
render_textures.push_back( r );
       
}
       
       
sh = new sf::Shader();
       
//sh->loadFromFile( "shaders\\motion_blur.frag", sf::Shader::Fragment );
       
sh->loadFromMemory( R"( uniform sampler2D render_texture_0; uniform sampler2D render_texture_1; uniform sampler2D render_texture_2; void main() { vec4 c0 = texture2D(render_texture_0, gl_TexCoord[0].xy); vec4 c1 = texture2D(render_texture_1, gl_TexCoord[0].xy); vec4 c2 = texture2D(render_texture_2, gl_TexCoord[0].xy); gl_FragColor = c0*0.7 + c1*0.2 + c2*0.1; } )", sf::Shader::Fragment );
       
       
   
}
   
   
~Renderer() {
       
for( auto & r: render_textures )
           
 delete r;
       
       
render_textures.clear();
   
}
   
   
sf::RenderTexture * getTheCurrentFrame() {
       
return render_textures.back();
   
}
   
   
void update() {
       
// delete the oldest texture
       
        // Po co w kółko niszczyc i tworzyć te tekstury???????????
       
auto rtex = render_textures[ 0 ];
       
render_textures.erase( render_textures.begin() );
       
       
// add the new texure
       
rtex->clear( frame_color );
       
rtex->setView( cam->view );
       
render_textures.push_back( rtex );
   
}
   
   
void draw() {
       
       
getTheCurrentFrame()->display();
       
       
sh->setUniform( "render_texture_0", render_textures[ 2 ]->getTexture() ); // odwrotnie
       
sh->setUniform( "render_texture_1", render_textures[ 1 ]->getTexture() );
       
sh->setUniform( "render_texture_2", render_textures[ 0 ]->getTexture() );
       
       
sf::Sprite spr;
       
spr.setTexture( getTheCurrentFrame()->getTexture() );
       
spr.setOrigin( screenWidth / 2, screenHeight / 2 );
       
spr.setPosition( cam->position );
       
       
window->draw( spr, sh );
   
}
}
;

Renderer * renderer = nullptr;



int main() {
   
   
window = new sf::RenderWindow( sf::VideoMode( screenWidth, screenHeight ), "test motion blur" );
   
   
cam = new Camera();
   
renderer = new Renderer();
   
   
   
sf::CircleShape tree( 30 );
   
tree.setFillColor( sf::Color::White );
   
tree.setOrigin( 30, 30 );
   
   
window->setFramerateLimit( 20 );
   
   
while( window->isOpen() ) {
       
       
sf::Vector2i mousePosition = sf::Mouse::getPosition( * window ); // Pobierz aktualną pozycję myszy względem bieżącego okna
       
sf::Vector2f worldMousePosition = window->mapPixelToCoords( mousePosition );
       
       
//cam->update();
       
window->setView( cam->view );
       
       
// EVENTS
       
sf::Event event;
       
while( window->pollEvent( event ) ) {
           
           
if( event.type == sf::Event::Closed ) {
               
window->close();
           
}
           
        }
       
       
// UPDATES
       
renderer->update();
       
tree.setPosition( worldMousePosition );
       
       
// RENDER
       
window->clear();
       
       
renderer->getTheCurrentFrame()->draw( tree );
       
//renderer->getTheCurrentFrame()->display();  // już robione w draw() poniżej.
       
renderer->draw();
       
       
window->display();
   
}
   
   
   
return 0;
}
Wrzucaj takie przykłady jako jeden ciągły kod, najlepiej bez zależności do osobnych plików.
P-182232
tBane
Temat założony przez niniejszego użytkownika
» 2025-04-12 10:52:36
Ok. Następnym razem wrzucę jako jeden kod. Podmieniłem kolejność tekstur w shaderze i migotanie nadal występuje. Szczególnie mocno to widac z meshem, który robie się cieńszy/węższy. Nie wiem czy ten motion blur to dobry pomysł podczas przesuwania mapy ... jak myślicie? Czy może jakoś inaczej napisać ten motion blur ? Najgorzej, że to przykład z książki "OpenGL Programowanie Gier" i nie działa tak jak oczekiwałem :-/


uniform sampler2D render_texture_0;
uniform sampler2D render_texture_1;
uniform sampler2D render_texture_2;

uniform float brightness;
uniform float contrast;
uniform float gamma;

void main()
{
    vec4 c0 = texture2D(render_texture_0, gl_TexCoord[0].xy);
    vec4 c1 = texture2D(render_texture_1, gl_TexCoord[0].xy);
    vec4 c2 = texture2D(render_texture_2, gl_TexCoord[0].xy);

    vec4 color = c2*0.7 + c1*0.2 + c0*0.1; // podmieniona kolejność
   
    color.rgb += brightness;
    color.rgb = (color.rgb - 0.5) * contrast + 0.5;
    color.rgb = pow(color.rgb, vec3(1.0 / gamma));

    gl_FragColor = color;
}

P-182235
DejaVu
» 2025-04-12 13:40:13
Ja bym na Twoim miejscu dążył do maksymalnej ostrości, tj. nie robił żadnych 'motion blur' bo:
1. monitor zrobi to za Ciebie jako side effect refresh rate (obecnie rzadziej spotykane)
2. oko doda 'monition blur'
3. Takie efekty to raczej nie są zbyt przyjemne w grach (chyba, że znasz grę, w której to dobrze się sprawdza?)
P-182240
tBane
Temat założony przez niniejszego użytkownika
» 2025-04-12 13:44:49
Ok. W takim razie zrezygnuję z tego efektu. Dzięki za rady :-)
P-182241
« 1 »
  Strona 1 z 1