Kandria
A look at Kandria's glitch effect
Alright, I took a bit of a break writing these weekly updates during the Kickstarter campaign, and while I was on holidays, but now we're back to work fully, so I think it's time for me to resume!
We're in the polish and testing phase of the game now, so there isn't too much to talk about current progress that would be interesting to summarise. Instead, this time I want to walk you through a particular effect in Kandria: the glitch/corruption effect!
You encounter this if you get hit, or if you get killed. In case you haven't played the demo yet or were too good to encounter it, here it is in video form:
The effect itself is purely post-processing, meaning it's a shader that runs on the output game image, modifies it pixel by pixel, and then spits it out again. Here's the code for it:
vec2 pos = floor(tex_coord*num);
int r = int(rand(pos+seed)*glyph_count);
float scalar = 1-clamp(strength,0,1);
float val = 1;
if(scalar*glyph_count < r){
vec2 idx = vec2(r%glyphs.x, r/glyphs.x);
vec2 sub = mod(tex_coord*num, 1);
val = texelFetch(pixelfont, ivec2((sub+idx)*7), 0).r;
}
float scale = clamp(r,scalar*30,glyph_count)*scalar*scalar;
pos = floor(tex_coord*num*scale)/scale;
if(val == 1){
color = texture(previous_pass, pos/num);
color = mix(color, vec4(0.2,0.3,0.7,1), clamp(strength*4-3,0,1));
}else{
color = texture(previous_pass, (pos+1)/num);
color = mix(color, vec4(1), clamp(strength*4-3,0,1));
}
We'll go through the code in detail, but first let's talk about the inputs:
previous_pass
The texture holding the image of the game that was rendered.pixelfont
A texture atlas with a font in it. Every glyph is 7x7 pixels.seed
A random integer that changes every 10 seconds to add some variance.strength
A number from 0 to 1 that determines how strong the effect should be. At 0 the effect is disabled, and at 1 it should be fully blue.tex_coord
The texture coordinate of the pixel we're currently rendering.
We also have some constants defined:
num
The number of glyphs the screen should be divided up into.glyphs
The number of glyphs (horizontally, and vertically) in thepixelfont
.glyph_count
The total number of glyphs in thepixelfont
.
Alright, with that out of the way, let's look at the actual code step by step. In the first three lines we set up some variables:
pos
The "absolute" position we're currently rendering within the glyph grid that we want to output.r
A sufficiently random integer.scalar
The inverse of thestrength
, so 1 if it should not be displayed, and 0 if it should not.val
The value of the glyph at the current pixel. Should be 1 if there's background, and 0 if it's within a glyph.
In the following if
we decide whether we are drawing inside a glyph or not. We don't do it if strength
is 0 (and thus scalar
is 1), or if our randomised test fails. This means the number of glyphs will increase with the strength, but will always remain somewhat random.
To actually look up the glyph in the if
body, we first pick a random glyph from our atlas, then break down the tex_coord
to get the coordinate within the glyph, and finally look up the pixel within the atlas. This gives us the correct val
of whether we're within the glyph we're drawing, or outside.
Next we compute another pos
. This time we want to offset our lookup slightly depending on how strong we want the effect to be. This causes the colours we output to be shifted and clamped more and more.
Finally, depending on whether we're drawing the foreground or background of a glyph we mix the colour of the rendered game with either a blue or a white, again depending on the strength of the effect.
If the strength is 1, we get a fully blue screen with the glyphs rendered with a white text. If the strength is 0, the pos
offset is zero, and we also don't mix in any blue or white, so the original game colour gets output. Anything in between, and the number of glyphs, the offset of the lookup, and the tinting of the colours increases as the strength goes towards 1.
And that's all there is to it.
This code, along with the rest of Kandria, will be open-sourced at some point soon, probably sometime in September or October. If you're interested in more deep-dives, I hope you're looking forward to that!
If there's any other bits of the game you'd like me to explain in detail like this, please let me know. Cheers!