Basic Fragment Shader with SFML

Today I had the idea to try and generate some “art” using semi-random vertices, shapes and colors, but to achieve that I knew, I had to finally learn a bit more about shaders, to get nice glow or similar effects. As I started reading through The Book of Shaders I created in parallel an example application in SFML with which I can follow along. That’s what I’m putting out here in addition to having made a pull request so it could be added to the book repository itself.

The Structure

If you want to use the presented code as is, you’ll need the following structure:

├─CMakeLists.txt
├─data
│ └─shader.frag
└─src
  └─main.cpp

CMake the Builder

As with most of my projects these days, CMake doesn’t let you down in setting up a basic application and it integrates so well with Visual Studio and all sorts of other IDEs.

cmake_minimum_required(VERSION 3.15)

project(TheBookOfShaders LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

find_package(SFML 2.5 COMPONENTS graphics REQUIRED)

add_executable(TheBookOfShaders
	src/main.cpp
)

target_link_libraries(TheBookOfShaders sfml-graphics)

install(TARGETS TheBookOfShaders
		RUNTIME DESTINATION .)
install(DIRECTORY data
		DESTINATION .)

The Source

Since we can’t apply the shader directly to the screen/window, we use a sf::RectangleShape with the same dimensions as the window. The code is also written in my (currently) preferred way of almost-always auto and uniform initialization.

#include <SFML/Graphics.hpp>

#include <iostream>

int main()
{
    auto window = sf::RenderWindow{ { 800U, 800U }, "The Book of Shaders" };
    window.setFramerateLimit(144);

    auto clock = sf::Clock{};

    auto shape = sf::RectangleShape{ sf::Vector2f{ window.getSize() } };

    auto shader = sf::Shader{};
    if (!shader.loadFromFile("data/shader.frag", sf::Shader::Fragment))
    {
        std::cerr << "Couldn't load fragment shader\n";
        return -1;
    }

    auto mouse_position = sf::Vector2f{};

    while (window.isOpen())
    {
        for (auto event = sf::Event{}; window.pollEvent(event);)
        {
            if (event.type == sf::Event::Closed)
            {
                window.close();
            }
            else if (event.type == sf::Event::MouseMoved)
            {
                mouse_position = window.mapPixelToCoords({ event.mouseMove.x, event.mouseMove.y });
            }
        }

        shader.setUniform("u_resolution", sf::Glsl::Vec2{ window.getSize() });
        shader.setUniform("u_mouse", sf::Glsl::Vec2{ mouse_position });
        shader.setUniform("u_time", clock.getElapsedTime().asSeconds());

        window.clear();
        window.draw(shape, &shader);
        window.display();
    }
} 

The Magic

This is directly copied from the book and while u_mouse and u_time aren’t used in this example, they are used in other examples and/or allow you to do some fun modifications of your own.

#ifdef GL_ES
precision mediump float;
#endif

uniform vec2 u_resolution;
uniform vec3 u_mouse;
uniform float u_time;

void main() {
	vec2 st = gl_FragCoord.st/u_resolution;
	gl_FragColor = vec4(st.x,st.y,0.0,1.0);
} 

The Result

If everything works out, you should end up with a window like this:

Hope this can be useful for someone else as well! 🙂

Leave a Comment

Your email address will not be published. Required fields are marked *

 

This site uses Akismet to reduce spam. Learn how your comment data is processed.