1
2////////////////////////////////////////////////////////////
3// Headers
4////////////////////////////////////////////////////////////
5#include "Effect.hpp"
6#include <vector>
7#include <cmath>
8
9
10const sf::Font* Effect::s_font = NULL;
11
12////////////////////////////////////////////////////////////
13// "Pixelate" fragment shader
14////////////////////////////////////////////////////////////
15class Pixelate : public Effect
16{
17public:
18
19 Pixelate() :
20 Effect("pixelate")
21 {
22 }
23
24 bool onLoad()
25 {
26 // Load the texture and initialize the sprite
27 if (!m_texture.loadFromFile("resources/background.jpg"))
28 return false;
29 m_sprite.setTexture(m_texture);
30
31 // Load the shader
32 if (!m_shader.loadFromFile("resources/pixelate.frag", sf::Shader::Fragment))
33 return false;
34 m_shader.setUniform("texture", sf::Shader::CurrentTexture);
35
36 return true;
37 }
38
39 void onUpdate(float, float x, float y)
40 {
41 m_shader.setUniform("pixel_threshold", (x + y) / 30);
42 }
43
44 void onDraw(sf::RenderTarget& target, sf::RenderStates states) const
45 {
46 states.shader = &m_shader;
47 target.draw(m_sprite, states);
48 }
49
50private:
51
52 sf::Texture m_texture;
53 sf::Sprite m_sprite;
54 sf::Shader m_shader;
55};
56
57
58////////////////////////////////////////////////////////////
59// "Wave" vertex shader + "blur" fragment shader
60////////////////////////////////////////////////////////////
61class WaveBlur : public Effect
62{
63public:
64
65 WaveBlur() :
66 Effect("wave + blur")
67 {
68 }
69
70 bool onLoad()
71 {
72 // Create the text
73 m_text.setString("Praesent suscipit augue in velit pulvinar hendrerit varius purus aliquam.\n"
74 "Mauris mi odio, bibendum quis fringilla a, laoreet vel orci. Proin vitae vulputate tortor.\n"
75 "Praesent cursus ultrices justo, ut feugiat ante vehicula quis.\n"
76 "Donec fringilla scelerisque mauris et viverra.\n"
77 "Maecenas adipiscing ornare scelerisque. Nullam at libero elit.\n"
78 "Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.\n"
79 "Nullam leo urna, tincidunt id semper eget, ultricies sed mi.\n"
80 "Morbi mauris massa, commodo id dignissim vel, lobortis et elit.\n"
81 "Fusce vel libero sed neque scelerisque venenatis.\n"
82 "Integer mattis tincidunt quam vitae iaculis.\n"
83 "Vivamus fringilla sem non velit venenatis fermentum.\n"
84 "Vivamus varius tincidunt nisi id vehicula.\n"
85 "Integer ullamcorper, enim vitae euismod rutrum, massa nisl semper ipsum,\n"
86 "vestibulum sodales sem ante in massa.\n"
87 "Vestibulum in augue non felis convallis viverra.\n"
88 "Mauris ultricies dolor sed massa convallis sed aliquet augue fringilla.\n"
89 "Duis erat eros, porta in accumsan in, blandit quis sem.\n"
90 "In hac habitasse platea dictumst. Etiam fringilla est id odio dapibus sit amet semper dui laoreet.\n");
91 m_text.setFont(getFont());
92 m_text.setCharacterSize(22);
93 m_text.setPosition(30, 20);
94
95 // Load the shader
96 if (!m_shader.loadFromFile("resources/wave.vert", "resources/blur.frag"))
97 return false;
98
99 return true;
100 }
101
102 void onUpdate(float time, float x, float y)
103 {
104 m_shader.setUniform("wave_phase", time);
105 m_shader.setUniform("wave_amplitude", sf::Vector2f(x * 40, y * 40));
106 m_shader.setUniform("blur_radius", (x + y) * 0.008f);
107 }
108
109 void onDraw(sf::RenderTarget& target, sf::RenderStates states) const
110 {
111 states.shader = &m_shader;
112 target.draw(m_text, states);
113 }
114
115private:
116
117 sf::Text m_text;
118 sf::Shader m_shader;
119};
120
121
122////////////////////////////////////////////////////////////
123// "Storm" vertex shader + "blink" fragment shader
124////////////////////////////////////////////////////////////
125class StormBlink : public Effect
126{
127public:
128
129 StormBlink() :
130 Effect("storm + blink")
131 {
132 }
133
134 bool onLoad()
135 {
136 // Create the points
137 m_points.setPrimitiveType(sf::Points);
138 for (int i = 0; i < 40000; ++i)
139 {
140 float x = static_cast<float>(std::rand() % 800);
141 float y = static_cast<float>(std::rand() % 600);
142 sf::Uint8 r = std::rand() % 255;
143 sf::Uint8 g = std::rand() % 255;
144 sf::Uint8 b = std::rand() % 255;
145 m_points.append(sf::Vertex(sf::Vector2f(x, y), sf::Color(r, g, b)));
146 }
147
148 // Load the shader
149 if (!m_shader.loadFromFile("resources/storm.vert", "resources/blink.frag"))
150 return false;
151
152 return true;
153 }
154
155 void onUpdate(float time, float x, float y)
156 {
157 float radius = 200 + std::cos(time) * 150;
158 m_shader.setUniform("storm_position", sf::Vector2f(x * 800, y * 600));
159 m_shader.setUniform("storm_inner_radius", radius / 3);
160 m_shader.setUniform("storm_total_radius", radius);
161 m_shader.setUniform("blink_alpha", 0.5f + std::cos(time * 3) * 0.25f);
162 }
163
164 void onDraw(sf::RenderTarget& target, sf::RenderStates states) const
165 {
166 states.shader = &m_shader;
167 target.draw(m_points, states);
168 }
169
170private:
171
172 sf::VertexArray m_points;
173 sf::Shader m_shader;
174};
175
176
177////////////////////////////////////////////////////////////
178// "Edge" post-effect fragment shader
179////////////////////////////////////////////////////////////
180class Edge : public Effect
181{
182public:
183
184 Edge() :
185 Effect("edge post-effect")
186 {
187 }
188
189 bool onLoad()
190 {
191 // Create the off-screen surface
192 if (!m_surface.create(800, 600))
193 return false;
194 m_surface.setSmooth(true);
195
196 // Load the textures
197 if (!m_backgroundTexture.loadFromFile("resources/sfml.png"))
198 return false;
199 m_backgroundTexture.setSmooth(true);
200 if (!m_entityTexture.loadFromFile("resources/devices.png"))
201 return false;
202 m_entityTexture.setSmooth(true);
203
204 // Initialize the background sprite
205 m_backgroundSprite.setTexture(m_backgroundTexture);
206 m_backgroundSprite.setPosition(135, 100);
207
208 // Load the moving entities
209 for (int i = 0; i < 6; ++i)
210 {
211 sf::Sprite entity(m_entityTexture, sf::IntRect(96 * i, 0, 96, 96));
212 m_entities.push_back(entity);
213 }
214
215 // Load the shader
216 if (!m_shader.loadFromFile("resources/edge.frag", sf::Shader::Fragment))
217 return false;
218 m_shader.setUniform("texture", sf::Shader::CurrentTexture);
219
220 return true;
221 }
222
223 void onUpdate(float time, float x, float y)
224 {
225 m_shader.setUniform("edge_threshold", 1 - (x + y) / 2);
226
227 // Update the position of the moving entities
228 for (std::size_t i = 0; i < m_entities.size(); ++i)
229 {
230 sf::Vector2f position;
231 position.x = std::cos(0.25f * (time * i + (m_entities.size() - i))) * 300 + 350;
232 position.y = std::sin(0.25f * (time * (m_entities.size() - i) + i)) * 200 + 250;
233 m_entities[i].setPosition(position);
234 }
235
236 // Render the updated scene to the off-screen surface
237 m_surface.clear(sf::Color::White);
238 m_surface.draw(m_backgroundSprite);
239 for (std::size_t i = 0; i < m_entities.size(); ++i)
240 m_surface.draw(m_entities[i]);
241 m_surface.display();
242 }
243
244 void onDraw(sf::RenderTarget& target, sf::RenderStates states) const
245 {
246 states.shader = &m_shader;
247 target.draw(sf::Sprite(m_surface.getTexture()), states);
248 }
249
250private:
251
252 sf::RenderTexture m_surface;
253 sf::Texture m_backgroundTexture;
254 sf::Texture m_entityTexture;
255 sf::Sprite m_backgroundSprite;
256 std::vector<sf::Sprite> m_entities;
257 sf::Shader m_shader;
258};
259
260
261////////////////////////////////////////////////////////////
262// "Geometry" geometry shader example
263////////////////////////////////////////////////////////////
264class Geometry : public Effect
265{
266public:
267
268 Geometry() :
269 Effect("geometry shader billboards"),
270 m_pointCloud(sf::Points, 10000)
271 {
272 }
273
274 bool onLoad()
275 {
276 // Check if geometry shaders are supported
277 if (!sf::Shader::isGeometryAvailable())
278 return false;
279
280 // Move the points in the point cloud to random positions
281 for (std::size_t i = 0; i < 10000; i++)
282 {
283 // Spread the coordinates from -480 to +480
284 // So they'll always fill the viewport at 800x600
285 m_pointCloud[i].position.x = rand() % 960 - 480.f;
286 m_pointCloud[i].position.y = rand() % 960 - 480.f;
287 }
288
289 // Load the texture
290 if (!m_logoTexture.loadFromFile("resources/logo.png"))
291 return false;
292
293 // Load the shader
294 if (!m_shader.loadFromFile("resources/billboard.vert", "resources/billboard.geom", "resources/billboard.frag"))
295 return false;
296 m_shader.setUniform("texture", sf::Shader::CurrentTexture);
297
298 // Set the render resolution (used for proper scaling)
299 m_shader.setUniform("resolution", sf::Vector2f(800, 600));
300
301 return true;
302 }
303
304 void onUpdate(float time, float x, float y)
305 {
306 // Reset our transformation matrix
307 m_transform = sf::Transform::Identity;
308 // Move to the center of the window
309 m_transform.translate(400, 300);
310 // Rotate everything based on cursor position
311 m_transform.rotate(x * 360.f);
312
313 // Adjust billboard size to scale between 25 and 75
314 float size = 25 + std::abs(y) * 50;
315
316 // Update the shader parameter
317 m_shader.setUniform("size", sf::Vector2f(size, size));
318 }
319
320 void onDraw(sf::RenderTarget& target, sf::RenderStates states) const
321 {
322 // Prepare the render state
323 states.shader = &m_shader;
324 states.texture = &m_logoTexture;
325 states.transform = m_transform;
326
327 // Draw the point cloud
328 target.draw(m_pointCloud, states);
329 }
330
331private:
332
333 sf::Texture m_logoTexture;
334 sf::Transform m_transform;
335 sf::Shader m_shader;
336 sf::VertexArray m_pointCloud;
337};
338
339
340////////////////////////////////////////////////////////////
341/// Entry point of application
342///
343/// \return Application exit code
344///
345////////////////////////////////////////////////////////////
346int main()
347{
348 // Create the main window
349 sf::RenderWindow window(sf::VideoMode(800, 600), "SFML Shader",
350 sf::Style::Titlebar | sf::Style::Close);
351 window.setVerticalSyncEnabled(true);
352
353 // Load the application font and pass it to the Effect class
354 sf::Font font;
355 if (!font.loadFromFile("resources/sansation.ttf"))
356 return EXIT_FAILURE;
357 Effect::setFont(font);
358
359 // Create the effects
360 std::vector<Effect*> effects;
361 effects.push_back(new Pixelate);
362 effects.push_back(new WaveBlur);
363 effects.push_back(new StormBlink);
364 effects.push_back(new Edge);
365 effects.push_back(new Geometry);
366 std::size_t current = 0;
367
368 // Initialize them
369 for (std::size_t i = 0; i < effects.size(); ++i)
370 effects[i]->load();
371
372 // Create the messages background
373 sf::Texture textBackgroundTexture;
374 if (!textBackgroundTexture.loadFromFile("resources/text-background.png"))
375 return EXIT_FAILURE;
376 sf::Sprite textBackground(textBackgroundTexture);
377 textBackground.setPosition(0, 520);
378 textBackground.setColor(sf::Color(255, 255, 255, 200));
379
380 // Create the description text
381 sf::Text description("Current effect: " + effects[current]->getName(), font, 20);
382 description.setPosition(10, 530);
383 description.setFillColor(sf::Color(80, 80, 80));
384
385 // Create the instructions text
386 sf::Text instructions("Press left and right arrows to change the current shader", font, 20);
387 instructions.setPosition(280, 555);
388 instructions.setFillColor(sf::Color(80, 80, 80));
389
390 // Start the game loop
391 sf::Clock clock;
392 while (window.isOpen())
393 {
394 // Process events
395 sf::Event event;
396 while (window.pollEvent(event))
397 {
398 // Close window: exit
399 if (event.type == sf::Event::Closed)
400 window.close();
401
402 if (event.type == sf::Event::KeyPressed)
403 {
404 switch (event.key.code)
405 {
406 // Escape key: exit
407 case sf::Keyboard::Escape:
408 window.close();
409 break;
410
411 // Left arrow key: previous shader
412 case sf::Keyboard::Left:
413 if (current == 0)
414 current = effects.size() - 1;
415 else
416 current--;
417 description.setString("Current effect: " + effects[current]->getName());
418 break;
419
420 // Right arrow key: next shader
421 case sf::Keyboard::Right:
422 if (current == effects.size() - 1)
423 current = 0;
424 else
425 current++;
426 description.setString("Current effect: " + effects[current]->getName());
427 break;
428
429 default:
430 break;
431 }
432 }
433 }
434
435 // Update the current example
436 float x = static_cast<float>(sf::Mouse::getPosition(window).x) / window.getSize().x;
437 float y = static_cast<float>(sf::Mouse::getPosition(window).y) / window.getSize().y;
438 effects[current]->update(clock.getElapsedTime().asSeconds(), x, y);
439
440 // Clear the window
441 window.clear(sf::Color(255, 128, 0));
442
443 // Draw the current example
444 window.draw(*effects[current]);
445
446 // Draw the text
447 window.draw(textBackground);
448 window.draw(instructions);
449 window.draw(description);
450
451 // Finally, display the rendered frame on screen
452 window.display();
453 }
454
455 // delete the effects
456 for (std::size_t i = 0; i < effects.size(); ++i)
457 delete effects[i];
458
459 return EXIT_SUCCESS;
460}
461