Week 163: Sugarcoat shaders!

Pretty solid week!

Last week I released Apples, but I already wrote about that in the previous recap. I’m really happy with the game and I think you should try it out!

After releasing Apples, I went and fixed a few small issues in Sugarcoat and then finally completed the Sugarcoat shader integration!

bombss

Post-processing shaders are something I really want to add to SUGAR, but since I’m currently using Sugarcoat, I decided to add it to that first. After all, it’s a very good way to test the idea in practical conditions!

If you don’t know, shaders let you get absolute control over how something is rendered, using the very powerful Graphic Prossessing Unit (GPU) of your computer, using either the GLSL or the HLSL language, both very specific to this technology, and which have syntaxes that are difficult to learn, and horrible to read. If you want to see what shaders are capable of, you should definitely take a look at shadertoy.com!

And so, in our case, such a shader would be applied when the simulated low-resolution screen is rendered (generally stretched) to the full size screen. This could be used to generate a very wide variety of effects! Of course the primary use would be to improve the final render, by adding texture to the rendered screen, modifying and/or animating the colors, adding glow effects, separating RGB values from the colors, or even bend the screen to get a CRT effect. But you could also render the game completely inside the shader, using the original simulated screen only as game data. Shaders are crazy.

As it so happens, Sugarcoat already uses shaders! Here’s what I wrote in a previous weekly recap:

This new [palette] system [internally] uses a set of 3 shaders written with love, for:

  • converting a color image to a fake-ish indexed color image
  • drawing in indexed color image to another indexed color image
  • and converting an indexed color image back into a regular color image

And so all we want to do here is to expose that third shader step, which converts our internal indexed color image into the actual color version which you will see on your screen. But we also need that behaviour to be pre-done for us, since the user shader will likely want to use the actual colors directly.

That’s why I made a few things available for the user shader! Here’s an extract of the latest additions to the Sugarcoat documentation:

In your shader code, you can use the following functions and variables:

  - Texel_color(Image texture, vec2 coords) returns the screen's color at those coordinates. You should use this function instead of Texel(...) because the latter will return encoded values.

  - Texel_index(Image texture, vec2 coords) returns the palette index for the color at those coordinates, as an int.

  - SCREEN_SIZE is a vec2 containing the width and the height of the simulated screen.

  - PALETTE is an array of vec4, containing the colors of the palette.

  - SWAPS is an array of int containing the palette swaps made at flip level.

But how even did I make those things available for shader code? I couldn’t find a way to add pre-implemented functions and variables to Love2D’s shader system, so in the end I simply concatenate my own shader code with the user shader code!

So as not to offset error messages, I made it so all my function and variable declarations hold only one line, to be put in front of the user code’s first line. The actual function definitions are added after the user code so it doesn’t matter how much space they take.

And so you can call the Sugarcoat function `screen_shader(shader_code)` with your shader’s code and see the result! (if the shader can’t be compiled, the error message will be written into the debug log)

sugar_postprocess

This shader right there is fairly simple, and so I left it in the Sugarcoat demo in ‘main.lua’. In the shader code, for any pixel, the algorithm takes the x and y coordinates, divides them by the size of a simulated pixel, then squares the x and the y separately and takes the biggest of the two. That value is kept as a multiplayer for the original color at that spot of the screen. Thanks to the profile of the square function, this gives us a very nice textured pixel effect! Here’s the actual code:

  varying vec2 v_vTexcoord;
  varying vec4 v_vColour;
  
  vec4 effect(vec4 color, Image texture, vec2 coords, vec2 screen_coords)
  {
    vec4 col = Texel_color(texture, coords);
    
    vec2 co = mod(coords * SCREEN_SIZE, 1.0);
    float k = 1.0 - max(co.x * co.x, co.y * co.y);
    
    return (0.75*k + 0.75) * col;
  }

That’s exactly the kind of shader I wanted to make possible. It’s a simple effect but it adds so much.

shaderz

After finalizing all this, I went and made a shader for Paul Nicholas and his game Lite Bikez. (temporary title) It’s a game mostly inspired by Tron and so of course it had to look extra glowy and extra retro. What you see above was the first big step towards that.

The glow effect is what gave me the most trouble. Shader code basically runs once per pixel to be rendered, and so if you need data from other pixels around, you have to get it again and again for every single pixel, which can cost performance if you do it too much. But for a glow effect you don’t really have much of a choice, since you want the pixels to be affected by the other pixels around it.

In the final version of my shader, every pixel checks for the value of 40 other pixels around it. This may seem like a bit much but it actually runs fairly well, since it’s only value getting, no complicated maths functions or conditional elements are involved, and those are much more costly.

Of course the glow effect doesn’t do it all and so I also added a scan-line effect, CRT distortion, as well as a sweeping distortion ray which I think really adds a lot!

That’s what it looks like in-game! I’m really happy with it!

Paul’s game isn’t finished yet so I suggest you follow him on Twitter, or even support him on Patreon, to know when it releases!

With Apples released and Sugarcoat in a stable state, I was finally free to start making something new again! On the last day of that week I started coming up with a new multiplayer game idea!

The game would consist of a 2D, vertical arena, where pastries fall from the sky and the players have to eat them to get bigger… bigger than the other players so that they can eat them! And once you ate another player, you gain a point and then regurgitate them and all the other things you ate, becoming small again.

And for this game I’m going to use Ayla (@brdgs)’s multiplayer library Simulsim, which lets you simulate multiple clients in the very same game instance! That sounds very interesting and I will let you know how it goes, next week!

Thank you so much to all my Patreon supporters! If you have content requests of any kind, please let me know!

Here are the names of all my 3$+ supporters!

★Joseph White, ★Spaceling, rotatetranslate, Anne Le Clech, bbsamurai, HJS, Austin East, Meru, Paul Nguyen, Dan Lewis, Dan Rees-Jones, Reza Esmaili, Joel Jorgensen, Marty Kovach, Flo Devaux, Thomas Wright, HERVAN, berkfrei, Tim and Alexandra Swast, Jearl, Michael Leonardi, Johnathan Roatch, Raphael Gaschignard, Eiyeron, Sam Loeschen, Andrew Reitano, amy, Andrea D’Amico, Simon Stålhandske, yunowadidis-musik, slono, Max Cahill, hushcoil, Gruber, Pierre B., Sean S. LeBlanc, Andrew Reist, Paul Nicholas, vaporstack, Jakub Wasilewski

This new week, I’m working on my new game of eating things and people! It seems I’m going to go deep into pixelly fake 3D again, but I’ll tell you more about that in the next recap!

Have a wonderful week!

Take care!

TRASEVOL_DOG

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Blog at WordPress.com.

Up ↑

%d bloggers like this: