Hello,
I'm currently working on a small game project to learn SFML. I would like (for stylistic purposes) for the background of every frame to be visual noise rather than a flat color. To do this, I am clearing the window with sf::Color::Black, and then drawing my chosen noise color to each pixel of an sf::Image with a pseudorandom alpha value, then copying that image into a texture and then finally into a sprite which is drawn to the window.
This seems to be the intended way to draw something to the screen that we want to be able to manipulate pixel by pixel, as sf::Image is not drawable. However, it is very slow. I am only managing ~20 FPS. Is there something I'm missing here that would greatly speed up the process of drawing visual noise?
Code:
#include <SFML/Graphics.hpp>
#include <SFML/System.hpp>
#include <SFML/Audio.hpp>
#include <SFML/Window.hpp>
#include <sstream>
struct RenderSettings
{
bool Watermark;
bool Wireframe;
int xres;
int yres;
int depth;
sf::Color DefaultText;
uint32_t WindowMode;
};
int main()
{
float Runtime = 0.0;
//SETTINGS
RenderSettings ini;
ini.Watermark = true;
ini.Wireframe = true;
ini.xres = 1920;
ini.yres = 1080;
ini.depth = 32;
ini.DefaultText.r = 230;
ini.DefaultText.g = 210;
ini.DefaultText.b = 210;
ini.DefaultText.a = 200;
ini.WindowMode = sf::Style::Fullscreen;
//VERSION NUMBER
sf::String versionno = "HEAT DEATH 0.00";
sf::String watermark;
sf::String sFPS;
int FPS;
//Seed Random Number Generator
srand(time(NULL));
//Initialize Text
sf::Font CourierNew;
CourierNew.loadFromFile("CourierNew.ttf");
sf::Text watermarktext;
watermarktext.setPosition(10.0f, 10.0f);
watermarktext.setFont(CourierNew);
watermarktext.setCharacterSize(25);
watermarktext.setFillColor(ini.DefaultText);
//Initialize Sound
//Define Monochrome Tint
sf::Color heat(230, 210, 210, 20);
//Initialize Background Static
sf::Image backim;
backim.create(ini.xres, ini.yres, heat);
sf::Texture backtex;
sf::Sprite background;
backtex.loadFromImage(backim);
//Open Window
sf::RenderWindow mywindow(sf::VideoMode(ini.xres, ini.yres), versionno, ini.WindowMode);
//Begin Measuring Time
sf::Clock clock;
//Run Program Until User Closes Window
while (mywindow.isOpen())
{
//Check All New Events
sf::Event event;
while (mywindow.pollEvent(event))
{
//If an event is type "Closed", close the window
if (event.type == sf::Event::Closed)
{
mywindow.close();
}
}
//Get Time Since Last Frame
sf::Time delta = clock.restart();
Runtime = Runtime + delta.asSeconds();
//Reset Window to Blank Background
mywindow.clear(sf::Color::Black);
for (int i = 0; i < ini.xres; i++) {
for (int j = 0; j < ini.yres; j++) {
backim.setPixel(i, j, sf::Color::Color(230,210,210,rand() % 51));
}
}
backtex.update(backim);
background.setTexture(backtex);
mywindow.draw(background);
//Update Game Logic
//Draw Watermark
if (ini.Watermark) {
FPS = 1;
FPS /= delta.asSeconds();
sFPS = std::to_string(FPS);
watermark = versionno;
watermark += " (";
watermark += sFPS;
watermark += " FPS)";
watermarktext.setString(watermark);
mywindow.draw(watermarktext);
}
//Draw Frame to Window
mywindow.display();
}
return 0;
}
Much of game making boils down to 'faking it' quite often ;) However the fastest way to do something like this programatically (and most fun way imo, cos shaders are awesome 8) ) is to use a noise shader like this:
namespace
{
const std::string GreyFrag = R"(
#version 120
#define GREY
)";
const std::string ColourFrag = R"(
#version 120
)";
const std::string NoiseFrag = R"(
uniform float u_time;
float rand(vec2 pos)
{
return fract(sin(dot(pos, vec2(12.9898, 4.1414) + u_time)) * 43758.5453);
}
void main()
{
#if defined (GREY)
gl_FragColor.rgb = vec3(rand(floor((gl_FragCoord.xy))));
#else
// these are arbitrary numbers just to provide an offset.
// you could use uniform values here for a variable effect
gl_FragColor.r = rand(floor((gl_FragCoord.xy + 17.034765)));
gl_FragColor.g = 1.0 - rand(floor((gl_FragCoord.xy)));
gl_FragColor.b = rand(floor((gl_FragCoord.xy + 0.145)));
#endif
gl_FragColor.a = 1.0;
})";
}
int main()
{
sf::RenderWindow window(sf::VideoMode(400, 200), "SFML works!");
sf::CircleShape shape(100.f);
sf::Shader greyShader;
greyShader.loadFromMemory(GreyFrag + NoiseFrag, sf::Shader::Fragment);
sf::Shader colourShader;
colourShader.loadFromMemory(ColourFrag + NoiseFrag, sf::Shader::Fragment);
sf::Clock shaderClock;
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
{
window.close();
}
}
float shaderTime = shaderClock.getElapsedTime().asSeconds();
greyShader.setUniform("u_time", shaderTime);
colourShader.setUniform("u_time", shaderTime);
window.clear();
shape.setPosition(0.f, 0.f);
window.draw(shape, &greyShader);
shape.setPosition(200.f, 0.f);
window.draw(shape, &colourShader);
window.display();
}
return 0;
}