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! 🙂

7 thoughts on “Basic Fragment Shader With SFML

  1. Hi, I am writing to you because I have noticed heavy use of the ‘auto’ keyword in your example code and because I would like to ask:
    1 – since local variables in c++ are by default ‘auto’ is there any other specific reason to explicitly declare that the variable must be ‘out of ram buffer’ when the function is used by the caller?
    2 – given that the keyword ‘auto’ is used to avoid making the code auto commented but allows for automatic assignment, is this one of the reasons?

    Since it is just a matter of style, the questions are not for “teasing” but rather trying to understand.

    Thanks for your possible reply.
    Salsa

  2. Heyho,
    Thanks for the questions!
    The use of auto is indeed mostly a stylistic choice. I kind of follow the “almost always auto” style as Herb Sutter likes to call it. I recommend to read his blog post on this topic as well: https://herbsutter.com/2013/08/12/gotw-94-solution-aaa-style-almost-always-auto/

    I feel like something got translated in a way that makes it hard for me to fully understand the question.
    – What do you mean with “out of ram buffer”? Do you just refer to dynamically allocated memory/memory on the “heap”?
    – What do you mean with “auto code commented” and “automatic assignment”?

    Maybe I can provide a short description why/how I use the AAA-style (will probably make it into a slightly longer blog post).
    – Code is here to solve a problem, as such code should be written in a way that makes the solution readable. What exact type is being used, when it can be automatically determined from the interface, distracts from the actual problem solving and making code harder to read.
    – Even though the refactoring tools are generally quite good, but not having to change every type of a usage of a returned value, makes it a lot easier to change something, because “auto” will automatically select the new type.
    – Easy of mind. Not having to think about types all the time and just relying on the interfaces to define the needed type, makes it easier to write code and less information to track in your head.

    1. Well since you declare all of your types as auto, how do you expect a reader to scan your code and know what type you are declaring when using auto. Using auto should be used with templates not for declaring all of your objects. Using it to declare all of your objects like this makes your code horrible to read and is promoting bad c++ coding practices.

      1. Did you once actively try to use it for a while? If one is used to a certain style, I can see how it takes a bit more effort to get used to a different style. But I’m convinced if people try it and use function and variable names that carry meaning, then using ‘auto’ makes code more readable and thus it’s a good C++ coding practice.

        See also my longer post on this topic: https://dev.my-gate.net/2021/11/19/almost-always-auto/

        1. Thx for sharing this!
          Only just starting to dabble with SFML and shaders,
          so very simple examples like this are great.
          What i’m looking for now is how to add two shaders to one draw call.

          As for “always auto”, i don’t but why not?
          for (auto i=0; i<10; ++i) is just as readable as for int=0,
          but its just personal taste.
          In range loops however,
          for (auto elem : json_object.at("material_density"))
          is in to my eye much clearer than:
          for (auto std::string elem : json_object.at("material_density"))

          and the fact that its even a std::string might be totally irrelevant to how it will be used further, thus auto makes sense.
          anyway- ill stop there. Have a nice day!
          -O.X.C.

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.