Ava Skoog

LD 38

JAM ENTRY: Blomst

Help the little astronaut give life to a barren rock by planting, harvesting, buying and selling seeds and building up an atmosphere! ♥︎

Ava and Marte made an LD jam game for the 9th time in a row! Woah!

* GO HERE TO PLAY: * https://ldjam.com/events/ludum-dare/38/blomst

Skjermbilde 2017-04-25 kl. 05.03.06-kopi 2.png

Great experience as always! Looking forward to playing everyone's games after a bit of relaxation. c':

Blomst : procedural planets & shaders!

Hey! Last time around I wrote a bunch of posts about shader stuff in our LD37 entry, LOCK AND RELOAD, which can be found on the old website:

This time around, I thought I'd do something similar for our LD38 game, Blomst 🌻, which also featured some procedural generation in it—something I've rarely done—so there's a lot of new concepts this time! c:

Go here if you want to play Blomst 🌻!

Procedural planets

Skjermbilde 2017-04-25 kl. 05.03.06-kopi 2.png

The main mechanic of the game is to sow seeds to let different kinds of plants spring up and build up an atmosphere to terraform the planet. 🌱

We wanted to associate different kinds of plant with different "biomes", so that some could only be placed on the green greens, others only in the sandy deserts and the rest in watery puddles. So I had to make all of these areas show up on the planet!

Generating the sphere

First things first. There are different ways to go about producing a round 3D object. We could use an icosphere/polyhedron (left) or we could use a UV sphere (right).

sfaer2_litn.png

There are pros and cons to both depending on the application. Without really considering the other option at the time, I just went ahead and generated a UV sphere, but in retrospect an icosphere would probably have been better for the tiled nature of the planets, to keep each tile about the same size, either as hexagons by joining five triangles, or as quads by joining two. UV spheres have a problem of pinching at the poles, with faces getting increasingly smaller the closer to the poles they are.

As a nice touch, to make the planets not look so perfectly round, I also displaced random vertices a bit to give a rougher, more potato-like shape to it! 🍠

Dealing with pinched poles

Well, I didn't, really! To cheat around the problematic poles, I ended up just covering them entirely in ice, much like the poles on Earth, and made it so that nothing could grow on those areas. One of them later became the designated spawn point for the main character, and the other one the designated spot for the shop, so it worked out in the end! c;

Skjermbilde 2017-04-25 kl. 04.52.33-kopi.png

But of course if this kind of cheating doesn't work for your application, just do the smart thing, unlike me, and use an icosphere! I should have also probably used fewer slices horizontally than vertically to give the tiles more of a square size.

Generating the biomes

So the next thing to do was to figure out how to create the different areas on the planet. I wanted it to be procedural and randomised each time. I ended up generating a very low-resolution texture (if the planet had 20x20 slices, I made the texture 20x20 pixels, so as to assign each pixel on the texture to a tile on the sphere) by use of a seamless simplex noise algorithm so that it would loop nicely around the planet, and assigned different colours to the pixels depending on the noise values, while also hardcoding in the icy poles.

I only needed four types of terrain (greens, sand, water and ice), so I decided to encode each as a full RGBA value in one of the four channels of each pixel, leaving the other channels zeroed. So for example the sand would have an RGBA colour value of (1, 0, 0, 0) (or (255, 0, 0, 0)), i.e. fully transparent red. This way I would only have to read one value for each type in the shader.

A resulting texture looks something like this, with the alpha values filled in again and poles represented by white; in reality of course most pixels were fully transparent:

tex.png

And on the planet, with the usual interpolation of textures, it would look less pixellated and more rounded (note that this was before I added poles):

texplanet.png

Rendering higher-resolution terrain

So now both I and my shader code could see where the different biomes were on the planet, but of course I wanted it to look nicer than a bunch of smudged pixels. Instead of actually displaying the terrain texture on the sphere, I used it to determine which high-res texture to use in a particular spot on it. Again the interpolation would make sure that it didn't look blocky.

texplanet2.png

Essentially I would read the colour of the terrain texture at the current fragment, something like so:

fixed4 terrain = tex2D(texTerrain, UV);

Then I would read each high-res texture, like so:

fixed3 colSand = tex2D(texSand, UV).rgb; fixed3 colGreen = tex2D(texGreen, UV).rgb; fixed3 colWater = tex2D(texWater, UV).rgb; fixed3 colIce = tex2D(texIce, UV).rgb;

I could then separate the terrain values into one intensity for each biome at the current fragment:

fixed valSand = terrain.r; fixed valGreen = terrain.g; fixed valWater = terrain.b; fixed valIce = terrain.a;

As you can see, which colour channel I chose to use for which type of terrain was somewhat symbolic. Then it was just a matter of multiplication, multiplying intensities by colours to get at the final result!

Optimising and enabling palettes

Four different RGB images really weren't necessary to get the desired results, however, so I decided to optimise things a bit. Instead I turned each of the three first textures (excluding ice) into grayscale images, and encoded each as a single channel into one RGB image, so that the red channel represented the sand pattern, the green the grassy pattern and the blue the watery pattern. I didn't read the ice from a texture at all in the end, but just used pure white, which I didn't need an image for.

kanalar2.png

Of course, as you can see, the water texture ended up being completely unnecessary since it was just one colour too, but at the time I had planned to actually put some kind of pattern in there as well and reserved the space for it. In the end I actually alpha blended the grass pattern on top in a parallax manner instead, to give the water a little sense of depth as well as reflection.

Now the sampling could be simplified to just one:

fixed3 patterns = tex2D(texPatterns, UV).rgb; fixed intSand = patterns.r; fixed intGreen = patterns.g; fixed intWater = patterns.b;

So now I could read the intensities of each channel, rather than the colours of the whole texture, for each type of terrain, to get its pattern. That would make everything turn black and white, though. To get colours back in, I created a palette texture where the UV position read would correspond to the intensity of the pattern on the horizontal axis and the channel on the vertical one, and so the correct pixel colour would be red.

Here's what a palette looks like:

palette0.png

The left side corresponds to pixels with no intensity ("black") in the pattern texture, "white" on the right, and "greys" in the middle. For a smoother transition between colours I could've used a gradient instead.

In the end we didn't actually end up using different palettes for different planets, like we had initially planned on, however.

Moving on

There's more shader stuff to look at, like the parallax water, the atmosphere around the planet, or how the planet turns from sepia to full colour as the atmosphere grows! But I don't want this one post to get too lengthy, so I'll save it for the next one! Until then! 🎮

Blomst : atmosphere & saturation fx

Another technical post to talk about some of the shader stuff and related things done for our jam game, Blomst 🌻! There's also part one about procedural planets and their shaders.

Go here if you want to play Blomst 🌻!

This time, I'll talk about two more minor effects that play into the game's mechanics: the rendering of the atmosphere that builds up around the planet as you plant more things on it, and the change from a brownish, dead planet coloured in sepia to a lush, green world with sharp colours!

The transformation

planet-brun-til-groen.png

This picture shows clearly the contrast between what the planet looks like at the start of the game and nearing the end of a completed level. Initially, the colours just blend together into a brown, lifeless mush, and not much of an atmosphere is there; what little there is is also brown.

As the atmosphere builds up over the course of the mission to terraform the planet with various plants, the sepia effect tones down and the true, radiant colours emerge, while the atmosphere also grows, and reveals its blue colour. 🌎

Atmosphere

Let's start with the simplest effect to pull off. I've seen a bunch of other LD38 games with planets use the same trick, actually! As you can see, the atmosphere is at all times rendered behind the planet itself and everything on it, no matter how it twists and turns:

ld38ematmos/emsmolopt.gif

Normals

The answer lies in the surface normals, and how front/back face culling works. The normal vector of a surface (generally a triangle) in 3D space is perpendicular to that surface, as illustrated by the blue arrows in this figure from the Blender documentation:

normalar.png

These basically tell us in which direction a surface is facing.

Culling

As a way to optimise rendering, most rendering systems have a system where faces that are facing away from the camera are completely disregarded and not rendered, as they can generally be safely assumed to be invisible to the camera, as generally 3D objects are properly filled in without any holes, and are not usually just flat triangles.

Here's a wireframe example to illustrate what it means. To the left is a sphere with the back faces (the ones facing away from the camera, that are supposed to be invisible due to being obscured by front facing geometry) rendered, and to the right is one with these faces culled, as they need not be rendered:

backcull.png

The trick to the atmosphere is inverting these normals, so that all the faces of the (atmo)sphere point inwards. This has the interesting effect that faces that are closer to the camera are actually pointing away from it, and are therefore culled, while the faces far from the camera are facing towards it, and therefore being rendered! Another way to achieve this could've been to actually cull front faces in the shader for the atmosphere, but this allowed me to use existing back face culling shaders.

Essentially, with the regular back face culling, we would get this, with the planet obscured by the atmosphere:

camcullback.png

But instead we get this, where we see into the atmosphere like a bowl, rendered only behind the planet and not in front:

camcullfront.png

Mission accomplished!

Sepia to saturation

So this one involves shader programming. Shaders allow us to mess with pixels before they get rendered to the screen, and so changing the colour of them is one of the simplest things one can do! The little shader program works on each pixel individually and applies the same operation to each.

So we start out with this fully saturated planet before applying any effects, of course:

planet-fullfarge.png

Greyscale

The first thing we need to do in order to achieve a sepia effect is to simply convert the image to greyscale, giving us a single intensity value for each pixel instead of three values (red, green and blue) to encode a colour.

We can convert an RGB triad to a single greyscale value by treating the values as a three-dimensional vector and extracting the dot product between this vector and another vector containing NTSC conversion weights, a set of values chosen on the basis that the human eye experiences different wavelengths of colour differently; we are especially good at greens, which is clearly reflected in the weights vector:

fixed grey = dot(col.rgb, fixed3(0.299, 0.587, 0.114));

Now we get this:

planet-graa.png

Sepia

Once we have this, we can convert the greyscale into sepia, which needs the three colour channels again, so we take a vector where the red, green and blue values respectively are all equal to the single grey value we extraced, and multiply this by a sepia vector, which boosts up the red, leaves the green as is, and decreases the blue a bit, giving us that characteristic brownish tint:

fixed3 sepia = fixed3(grey, grey, grey) * fixed3(1.2, 1.0, 0.8);

Done! Voilà:

planet-sepia.png

Interpolation

Finally, I needed to make the planet interpolate from full sepia to full colour throughout the course of the game depending on the current atmosphere levels.

The game presents the player with the goal to make the atmosphere 100% completed, so 0% would mean full sepia and 100% full colour. Thus I passed a value between 0 and 1 into the shader to use for blending. Easy-peasy!

The blending is a regular alpha blending algorithm. This one:

source colour * source alpha + destination colour * (1 - source alpha)

Or, in actual code:

source.rgb * source.a + destination.rgb * (1.0 - source.a)

In our case, source colour would be the full colour (the colour to fade in depending on the atmosphere percentage), and the destination colour would be the full sepia. The alpha value is simply the number between 0 and 1 representing the atmosphere levels. Thus:

result.rgb = full.rgb * atmosphere + sepia.rgb * (1.0 - atmosphere);

And so we get a smooth transition:

ld38ematmos/eminterpol_2.gif

Then it was just a matter of applying the same effect to the atmosphere as well as the bar showing how much of it is complete, and presto:

Skjermbilde 2017-04-25 kl. 05.03.06-kopi 2.png

Moving on

Next writeup will be about lighting, including the rim lighting of the planet and all the stuff on it, as well as the revolving sun that gives plants the power to grow as it shines upon them each new day on the very small world! 💚 See you around soon!

Blomst : sunlight and fresnel

All right! This will be the third, and probably final, post in the serious on various technical stuff and graphical effects in our jam game, Blomst 🌻. You can also check out part one about procedural planets and part two about shader effects!

Go here if you want to play Blomst 🌻!

In this one, the subject is lighting! Plays both visual and functional roles in this game.

The fresnel (or rim light) looks nice, but it also makes it a little easier to see where things are on the dark side of the planet.

The sunlight not only illuminates the planet and brings out its eventual colours, but also makes plants grow and produce seeds when facing them throughout the cycling days on the sphere.

Rim light

No matter how dark the planet itself is, there is always a little bit of "backlighting" on the plants and the character to make them pop out and help the player find their way around. And it looks neat too! There is also one on the whole planet itself.

fresnel.png

The effect is actually quite simple in theory, but requires a little bit of shader work. What it basically boils down to is shining a light at the object from behind, exactly towards the camera, so that some light ends up creeping up along the sides, giving that characteristic look with glowing edges:

fresnel2.png

And indeed, if we look at it from a different angle than that of the camera that the rim light is pointing toward, we can see how the opposite side is just entirely washed out by light:

fresnel3.png

The trick lies in the fact that the rim light always points straight at the camera, so if the camera moves and rotates, so does the rim light, and we never actually see the washed out side, only the glowing edges, making the effect work. Cheap but pretty trick!

Sun

This is even simpler. The sun is just a regular directional light, and works with a little bit of cheating, as it is actually the sky and sun circling the planet rather than the other way around, the planet's transform being the parent of these two, so that they follow along with it as it turns (another cheat here, you see—it's technically the planet rotating while the character stays in place as the player moves!).

Every time the sun completes a full 360° rotation, the game counts a new day, and as the sun passes over plants on each day, they grow or might produce seeds depending on their current state.

The solution was to use the same maths that are also used in shader to calculate the actual lighting. The N•L calculation does the trick, as the dot product between the surface normal and the direction of the light represents how directly the light is shining upon the geometry, and how much it should be lit, as demonstrated by this figure from here:

lightequation.gif

Using the same calculation in the game logic, I could check whether the sun was passing over a plant, and have something happen to it, such as this "sapling" flower growing into its fullest as a new day dawns upon it:

ld38emsolveks/emoptimert.gif

LD 39

We're in and we're done!

Didn't make an "in" post before the jam, so here we go, presenting the entry at the same time~

Ava and Morten made another space game based on an idea by Marte, who otherwise didn't join in this time. c:


logo-black.png

ld39emskjerm/em0-kopi.png

* 💓 GO HERE TO PLAY/RATE/COMMENT! 💓 *


Hope you like it! c: Feeling a bit sickly right now, but will be playing your amazing games in the days to come, and write a couple of technical blog posts as usual.

SPACEJAMMED: technobabble

My last two jams have been followed up by a couple of ~technical blog posts~, mostly going into how I did various graphical things (shaders and so on), so let's not break the tradition now! c:

Examples of the old posts/games, in case anybody remembers them:

LD38: Blomst 🌻LD37: Lock and reload - | - ezgif-4-0dc7b137a0.gif | ezgif-4-6be4dc66a9.gif


logo-black-kopi-2.png

This time we'll be looking at SPACEJAMMED (made by me and Morten with a little input from Marte who couldn't join in fully this time) which is a slightly less ambitious project gameplaywise due to a bit less intense participation than usual, but has a bunch of graphical trickery to look at nonetheless! 👀

ld39emskjerm/em2-kopi2-2.png

Do have a play first to get acquainted with it! 💙


Painted background and sharp foreground

There are some three major graphical considerations to blog about for this game, so let's do one for each post so as not to make it all too lengthy. Today we'll be talking about the major effect that gives the game its primary graphical style.

effekt1-kopi 2.png

In this image, zooming in on the selected areas, we can see that while the gameplay elements are crisp and clear, the background, especially the farther it gets from the camera, is getting increasingly smudged, so as to give off a slightly "painted" effect, inspired a bit by the style of The Legend of Zelda: Skyward Sword, which has a similar thing going for it, if you compare Link to the background in this screenshot:

10yqpok.png

Mine is a bit of a cheap version which doesn't look quite as pretty. 🐻 A side effect of time constraints and a desire not to press the GPU too hard, since there was little time to optimise the effect properly, meaning I had to water it down instead, but I'm quite happy with it, and the higher the resolution, the more noticeable it is, so I do highly recommend playing the game in fullscreen!


Paint effect

Let us split this post up into the two parts. Before I could consider separating the game elements from each other, I needed to implement the paint effect in the first place, which would originally just cover the entire scene and make the character and the puzzle pieces smudged as well, which I had to fix later. It involved more than one step to get right, and so it would be too much to glomp it all together into one section.

Brush

I cannot take all the credit for myself. The implementation was inspired by the description in this forum post on TIGSource, in a thread discussing precisely the Skyward Sword brushstroke effect. The author described it using two images which I will reüse here, so that you don't have to click away from here. c:

The first is this, showing the texture sample pattern necessary for the effect, essentially representing the shape of the "brush". The middle represents the current fragment, and then a couple of extra samples are taken around it; in this case, six more.

pix1.png

Next, the goal is to turn this into a stroke, so that the final effect can be described like so:

pix2.png

In the case of SPACEJAMMED, I opted to simplify the brush and take fewer samples in order to go a little easier on the GPU and speed up rendering, and also moved things around a bit, so that my final brush looked like this:

pix3.png

Shader

The game was made in Unity, and so Unity shader syntax will be used, but the same methodology of course applies when using something like GLSL. The effect was added as a single pass over the entire rendered scene as a post-effect on the camera. The shader code for the sampling looks something like this, where UV is a texture coördinate with an x and y value:

``` // The o is for original sample. fixed3 o = tex2D(tex, UV);

// These are the offset samples. fixed3 a = tex2D(tex, UV + fixed2(0, y * 0.5)); fixed3 b = tex2D(tex, UV + fixed2(x, -y)); fixed3 c = tex2D(tex, UV + fixed2(0, -y * 0.5)); fixed3 d = tex2D(tex, UV + fixed2(x, y)); ```

The x and y values would depend on the distance intended between the samples, which can of course be played with to achieve different degrees of smudging.

Stroke

Next, following the forum post's instruction to render using the pattern, and using a blend algorithm that only picks the brightest of the source and destination colours, I set out to get it working.

Intensity

The brightest indeed. So first I needed a way to calculate the intensity of each fragment's colour. Just like when I had to convert colours to grayscale like I wrote about in my second blog post on Blomst 🌻—see there for details—it needs to be borne in mind that there are conversion weights to consider for each of the colour components (red, green and blue) in order to get the proper value. Rounding the values a bit, this is the code:

fixed intensity(fixed3 col) { return col.r * 0.3 + col.g * 0.59 + col.b * 0.11; }

Blending

Next, I needed to blend each offset brush sample with the previous values by keeping only the brightest fragment, in order to get that soft halo around things. Unfortunately I couldn't think of a good way to solve this without branching (which is generally a bad thing in shaders), but should you try and implement this effect, that may be worth trying to figure out.

The simplest implementation would thus be to simply check if the current pixel being tested is brighter than the previous one in the same place and if so replacing the old one or otherwise keeping it:

``` fixed3 blended(fixed3 a, fixed3 b) { fixed ai = intensity(a); fixed bi = intensity(b);

return (ai > bi) ? ai : bi;

} ```

However, I wanted to go slightly more fancy and only do partial blending to accent the strokes a bit more, and my algorithm ended up being a slightly more complex version of one of the more common methods of alpha blending (which was also explained in more detail in the just aforementioned blog post), with an alpha value based on the highest intensity.

I could then go through all the samples and blend them together:

o = blended(o, a); o = blended(o, b); o = blended(o, c); o = blended(o, d);

Depending on the distance between samples, the end result could now be something like so:

foer-etter-1-kopi.png

Distance from camera

The next thing I did to make the effect a little less overwhelming was to also sample the depth buffer (which is easily done in Unity by getting the built-in sampler variable _CameraDepthTexture) and multiplied the intensity of the effect by the depth, so that things deeper into the scene would be affected more intensely by the smudging than those nearer the camera. I also modified the camera's near clip plane in order to get a more highly contrasted depth buffer.

Here's the depth buffer before and after a little shader code to blow up the effect a lot more in the distance:

dbuf-kopi.png

Current result

At this point, the "end" result was now something this, with the smudge exaggerated for clarity, where there would be little to no smudging near the camera, and a lot more in the distance:

resultat-1.png

Of course, the foreground/gameplay elements were still smudged too, which was not desirable, neither from a playability perspective nor an æsthetic one. There's also a little bit of a lie going on here; as there were light sources on the sides of the scene, the shadows below the puzzle pieces were not actually straight below them, which made gameplay unnecessarily confusing, and this was fixed with a bit of trickery which will be explained in a future post, as will also the solution for the foreground elements.

Note on textures

To really give the shader something to work with, it was also important to make sure the textures weren't completely flat, but had irregularities that the shader could catch onto and amplify with the brush strokes, so I applied a little extra texture on top of the main texture so to speak, as for example in this subsection of the spaceship interior's texture:

texex.png


Next time

Like I said, at this point the effect had only been applied to the entire image, without making gameplay elements such as the character and the puzzle pieces clear and fully distinct from the background, and we have yet to touch upon how the shadows of these were forced to be cast right below them even tho there were light sources coming in from the sides. We'll get to these things in future posts! c:

Until then, have a good one! 🔥

SPACEJAMMED: selective post-effects

Hello again! I wrote my last technobabble on a paint effect shader for our LD39 jam entry, SPACEJAMMED, a few days ago, so do have a read to stay up to date on where I left off! c:

ld39emskjerm/em2-kopi2-2.png

Quick update before we start: I set up an itch.io yesterday—and have officially joined the ranks of cool indie kids™—so now SPACEJAMMED along with eight earlier LD games can be found there. I'll dig up some more on my old PC later. I've fixed up old games to work on web too, and most work in fullscreen, so you can play in the comfort of your own browser! 🐱


Today I'll be talking about how I got the foreground elements, or gameplay elements, like the character and the puzzle pieces, to remain clear and unaffected by the painted and smudgy shader effect I'd applied to the image as a post-effect but really only wanted to have on the background. c:

And please do give SPACEJAMMED a play 🎮 to acquaint yourself with it first. ε: It's not as ambitious as our last entry, Blomst 🌻, but hopefully you'll like it anyway!


Painted background

So last time we gave the whole image a bit of a funky, smudgy effect like so:

resultat-1.png

But smudging out the character and the interactable puzzle is not that nice. It might be a good idea to clearly distinguish the immutable background from the gameplay elements, not just from a gameplay perspective, but an æsthetic one as well.


Clear foreground

The solution is more or less complex depending on what we need, and I did initially go for the more complex one before I realised this particular game didn't need it, and ended up simplifying it not to waste unnecessary render time and framerate. Let's consider the simple version first, and then explore why there can be issues with it and how to solve those as well.

Simple solution

The absolute easiest way to do something like this in Unity, which was used for this game, is to slap on another camera linked to the transform of the main camera and set up some render layers. This way we can have one camera render only the background elements and apply the painted post-effect to those, and then have a second camera render only the gameplay elements on top, without applying an effect, or at least not the same one.

Camera setup

The first camera should work as usual, clearing to a skybox or a colour or what have you, and draw everything considered part of the background, but not the render layer for the foreground elements (in my project called Clearer):

kamera-1.png

Note the setup for the culling mask, excluding this layer:

kameramaske.png

The second camera, for the foreground, renders only this layer, and has been set to clear depth only, so that it renders right on top of whatever was below:

kamera-2.png

Finally, the foreground camera has a higher depth value than the background camera so that it draws last, and of course only the background camera has the MainCamera tag and an audio listener.

Object/mesh setup

Then the objects with the 3D meshes themselves need to be marked accordingly. I simply left the background elements on whatever layer they were supposed to be anyway, while all the foreground elements, such as the character model, were set to the Clearer render layer, like so:

karmod.png

Result

Et voilà! Clearer indeed:

tokamera.png

Problems

However, this only works because our game happened to have a fixed camera where all the foreground elements are in front of the background elements! Look what happens when I move the character behind one of the parts of the spaceship interior sticking out from the wall, compared to how it's supposed to look, when looking at the editor where the character is rendered properly:

in-game | editor - | - kar-spel.png | kar-editor.png

Since everything on the foreground layer is drawn after everything on the background layer, it doesn't matter what's actually in front of what; foreground elements always end up on top! Again, not a problem for this particular game since this situation would never happen, but let's look at how to solve it anyway, since I actually did solve it initially before I realised it wasn't necessary.

Complex solution

To fix this, we need to do what is actually done to solve problems like this for each individual camera anyway, as the objects rendered are not necessarily drawn in order of their distance from the camera either, and even if one object has its centre farther away than another, there may still be parts sticking out of it that should occlude objects whose centres are nearer the camera, so that solution doesn't work either.

Depth buffer

Instead, a depth buffer is used to encode the depth of any pixel, or fragment, rendered to the screen, and whenever something new is to be rendered in the same position, it is first checked whether the depth of the new pixel is less than the old value for that pixel, and only then will it be considered in front and replace the old value.

Here's an example of the depth buffer of the scene, with both the foreground and the background elements:

dypta-2.png

White pixels, which have a value of 1.0, represent pixels far away from the camera and black, 0.0, ones very close. Then there's a greyscale in between. We can use these values to compare depths and figure out whether to override an old pixel with a new one whenever we draw something, based on whether it's actually in front, i.e. has a smaller value.

Getting depths

Luckily for us, Unity has made it quite easy to get to depth buffers, tho we need to do it two ways. First, we need to change up the pipeline a bit. Now we want the foreground camera to draw before the background camera instead so that it's all done when we get to the background camera, because we're going to manually be drawing what the foreground camera sees on top of what the background camera sees in the same post-effect shader that applies the painted effect.

To get the background camera's depth buffer in the post-effect shader attached to it is easy as pie! 🍰 Unity already has a built-in sampler, _CameraDepthTexture, which we can get like any other sampler by doing this somewhere in the pass block:

sampler2D _CameraDepthTexture;

We can then use this in the fragment function. But hold on for a moment. We need the foreground camera's depth as well. This is slightly more complicated, but not terribly so. First we need to add two texture properties to the shader that are going to be filled in by a script later.

_ForegroundTex ("Base (RGB)", 2D) = "white" {} _ForegroundDepth ("Base (RGB)", 2D) = "white" {}

We'll have to add samplers for those as well:

sampler2D _ForegroundTex; sampler2D _ForegroundDepth;

The first, _ForegroundTex, will represent what's rendered by the foreground camera. _ForegroundDepth will hold its depth buffer. Now let's attach a script to the foreground camera, so that we can fill these values in.

``` public class ClearCam : MonoBehaviour { [SerializeField] private Camera m_maincam;

private RenderTexture m_tex, m_depth;
private int m_widthLast = 0, m_heightLast = 0;

void Update()
{
    int w = Screen.width;
    int h = Screen.height;

    if (m_tex == null || m_widthLast != w || m_heightLast != h)
    {
        var cam = GetComponent<Camera>();
        cam.depthTextureMode = DepthTextureMode.Depth;

        m_widthLast  = w;
        m_heightLast = h;

        if (m_tex != null)
        {
            cam.targetTexture = null;

            m_tex.Release();
            m_depth.Release();
        }

        m_tex = new RenderTexture(w, h, 8);
        m_depth = new RenderTexture(w, h, 8, RenderTextureFormat.Depth);

        cam.SetTargetBuffers(m_tex.colorBuffer, m_depth.depthBuffer);

        var mat = m_maincam.GetComponent<CamPost>().material;
        mat.SetTexture("_ClearTex", m_tex);
        mat.SetTexture("_ClearDepth", m_depth);
    }
}

} ```

This is quite a bit of code, but fear not! I shall explain. 🙀 First, we need an inspector value where we can pop in the other camera, the background camera. I've called this variable m_maincam, since the background camera has the MainCamera tag. So in the inspector, drag that there:

clearcamskript.png

Then there are two private render texture variables to hold the colour buffer (simply what the camera sees) and the depth buffer, which correspond to the two texture properties and samplers we just added to the shader. Finally two variables to keep track of the last width and height of the screen so that we can recreate these textures in case the resolution of the game changes, so that they're always the same size as that.

In the update function we get the foreground camera's own camera component, and we check if we need to update the buffers, or create them for the first time. If we update them, it's important to manually release the old buffers, or we'll be leaking memory and the computer is going to have a bad day eventually. Then comes the important part: use SetTargetBuffers() to be able to specify colour and depth buffers separately as render targets for our camera, and then finally get the post-effect shader material from the background camera and pass the buffers into it.

Compositing and comparing

Now we can finally use those samplers in the shader with the correct data in them, and do the final composite. After applying all the paint effects to the fragment from the background camera's colour buffer, at the very end of the fragment function, we'll do a few more things.

First, let's get the depth values for the background and the foreground respectively:

fixed depthBG = tex2D(_CameraDepthTexture, UV).r; fixed depthFG = tex2D(_ForegroundDepth, UV).r;

We can use the same UV coördinates we used to sample the background's colour buffer, as in the post-effect shader, all the values simply correspond to the entire screen. These are only single scalars/values since depths are greyscale and we don't need multiple colour channels, so we're only grabbing the first value, i.e. the "red" value. Then we need the colour buffer of the foreground:

fixed colFG = tex2D(_ForegroundTex, UV);

Assuming our background colour buffer variable has been correspondingly named colBG, we can now alpha blend the foreground texture onto the background texture, but only if the depth is greater. One way to do it would be like so:

if (depthFG < depthBG) colBG.rgb = colFG.a * colFG.rgb + (1.0 - colFG.a) * colBG.rgb;

However, branching is discouraged in shaders, and it might be faster with a convoluted hack like this, tho I haven't done any benchmarking:

float alpha = colFG.a * ceil(clamp(depthBG - depthFG, 0.0, 1.0)); colBG.rgb = alpha * colFG.rgb + (1.0 - alpha) * colBG.rgb;

And there we go!

kar-ferdy-2.png

⚠️ Note that to get the complex method to work, at least for me, it's necessary to set the cameras to forward rendering instead of deferred or no foreground will show up! ⚠️


Next time

That is it! Like I said at the end of last post, there is still a bit of a lie in this image: at this point the shadows from the gameplay elements were not actually cast right below them as there are light sources coming in from the sides in the scene. This was confusing from a gameplay perspective, so a little trickery was taken to in order to force those shadows to be cast right below instead. That's for next time!

Until then, stay safe! 💓

LD 40

Eleventh jam in a row!

I might not be able to get any of my usual collaborators, Marte (maybe) and Morten (can't), onboard with me this time, but I'm not about to break my personal streak since LD30! 😎

hype.gif

Day one!

Thought I was going to bed, but had to spend about half an hour optimising this GIF for you so that the LD website would accept it, amidst internet disconnections. 😥 Hope it was worth it!

So this is where we are after a day! I'm mostly working on my own this time, but Marte is chiming in with ideas and helped me come up with the concept. Right now all I have are some mechanics and a mostly motionless cast of characters. Tomorrow I'll make 'em move and there'll be Christmas shopping chaos galore! ❄️

ld40_dag1.png

malliokartemdag1/em4_optimert.gif

Jam entry: MALLIO CART

logo.png

Grab your trolley 🛒 and your shopping list 📝! It's Christmas 🎄 Eve and everyone's desperate to get the last remaining stock before closing time in just three minutes! ⏰

👉 PLAY HERE 👈

skjerm0-kopi.png skjerm1-kopi-2.png

Collage!

With Mallio Cart, my combo (but not compo, because I always jam!) streak has increased to eleven! 😎

Thanks to everyone for being such a cool community making so many cool games! 💚

ldemkollasje/em40.png

Mallio Cart tech: gfx & audio ḑi̶śto͝r̴t҉io̡ns̴ pt.1

Hey! I usually write ~techy 🤖 stuff~ on how I did effects and so on in my LD games (see here and here), like for these games:

LD39: SPACEJAMMED | LD38: Blomst 🌻LD37: Lock and reload - | - ezgif-4-2a3a7addb4.gif | ezgif-4-0dc7b137a0.gif | ezgif-4-6be4dc66a9.gif

This time around I made Mallio Cart. There wasn't that much focus on that kind of stuff, as the focus was mainly on the gameplay idea and content actually ended up somewhat lacking and empty in the end, altho this game too had its visual flair:

Of course this GIF has been heavily shrunk and optimised to fit this website's minuscule limit of 4MB, but you get the idea.

malliocartemgif/emoptimert4.gif

You can PLAY Mallio Cart here if you haven't yet!

There's also a video if you just want to watch it:

https://www.youtube.com/watch?v=OsZ4hs6MYEQ


Crying BABBY visual and auditory effects

While I did at least make modifications to it, I didn't write the toon shader from scratch this time but found one. Perhaps the most elaborate graphical (and auditory) effect this time around was the one activated when too close to a crying 👶 bab(b)y:

malliocartemgif/embabby_optimert.gif

To also hear the audio distortion that goes along with it, watch this video:

https://www.youtube.com/watch?v=iBBdr6mlmMM

Therefore, I thought we'd talk about that this time around! Since the audio stuff was sort of new to me, as someone who has mostly been focusing on having fun with graphics, I thought I might cover both this time, to see how the effects tie together and how they were done! c:


Visuals

This was made in Unity. I applied a post-effect, which is something I've written about before under getting depths in the second post on my LD39 game, SPACEJAMMED. This means a shader that is getting applied to the entire rendered image as a 2D effect after the whole scene has been rendered by the camera. Since all I needed to do was add some tinting, distortions and noise, I could do all of the relevant work in this shader.

If you're totally new to shaders, what it basically means in this particular case is some code where I can modify the colours of (or completely discard) the pixels (or "fragments") that are about to be drawn before they are drawn. I can basically "hook in" and modify them before they go to the screen. This way, for example, I can apply a tint. I can also accept input data which I can control through regular non-shader code, which is how I passed a noise texture into the shader as well as a number representing the level of distortion, so that I could fade it in instead of just toggling it on and off.

Tinting

Simplifying a bit, thus the tinting was no more complicated than something like this:

``` // Gets the colour from the image about to be rendered // to screen at the current pixel/fragment as specified // by the UV (texture coördinate) value passed to us. // The 'fixed4' means a vector of four values, one for // each colour channel (red, green, blue and alpha). fixed4 col = tex2D(_MainTex, UV.xy);

// Increases the red channel by the distortion factor. col.r *= 1.0 + _Factor;

// Pass the new colour of the picture on for rendering! return col; ```

In reality the exact maths for tinting the right way looked a bit different and I also decreased the green and blue channels, but it's just something one has to play around with until it looks right!

Noise

Then there's what looks a bit like television static fading in as the effect increases. This is just a texture passed into the shader and added on top of the image, with a little bit of animation going on to make it appear all erratic and constantly changing, like real static. The technique was essentially the exact same as I used on the actual television set in my LD37 game, Lock and reload, as described here, so I won't really go over it again.

The only new thing was alpha compositing on top of the original image, which was done using the same old blending algorithm I've also described before under interpolation when talking about my LD38 game, Blomst 🌻!

Vignette

The edges around the screen also get darker (and redder) when this happens altho it can be hard to see with everything else that's going on. When a thief approaches, this is also the only effect that gets activated. This is simply done by tinting the pixels based on their distance from the middle of the image. We can get the position of the current pixel the same way we did when sampling the original texture in the code example under tinting, by reading the UV value, which is a pair of positions (x and y) between 0.0 and 1.0 and so the middle is at (0.5, 0.5). The function I use to calculate the "vignette factor" looks like this:

``` // Takes in two values (xy) and returns a single one. fixed vignette(fixed2 pos) { // Just for clarity. fixed2 middle = fixed2(0.5, 0.5);

// length() gives distance between two points.
return length(fixed2(pos.x, pos.y) - middle);

} ```

To calculate the vignette for any given pixel, which like anything else is also influenced by the distortion factor, I simply use it like so:

fixed v = vignette(UV.xy) * _Factor;

Then I simply combine this resulting value with the original pixel's colour channels in various ways to get the result I want, like increasing red and decreasing overall colour.

Di̶śto͝r̴t҉io̡n

Here's the big one! See how everything wobbles and waves around? It all comes back to those texture coördinates (UV's). By offsetting them a little bit, we can sample pixels further away from those that we are actually "supposed" to sample to get the original image and depending on how we offset them we can get the pixels to move around the way we want to.

I simply used sine waves for this. The function sin() returns a value between -1.0 and 1.0 depending on the value that is given to it, and just repeats the pattern over and over, creating a nice waveform:

sine.png

Just by looking at the wave, I think it's already possible to see what we're getting at here. We can also use cosine, cos(), which does the same thing, but slightly offset.

So what we want to do is replace our old code for sampling the original texture, tex2D(_MainTex, UV.xy) with something a little more fancy, where UV.xy gets offset by some kind of value returned by the wave functions. For each pixel, we need to supply a different value to the function or else they will all get offset by the same amount and there won't really be any distortion, just the whole image shifted to the left or the right, up or down. So what easier value to use than the position of the pixel itself?

``` // x and y for the offset will be stored here. fixed2 offset;

// Start by offsetting according to position. offset.x = sin(UV.x); offset.y = cos(UV.y);

// We also want to multiply by the factor to fade it in. // This modifies x and y at the same time. offset *= _Factor;

tex2D(_MainTex, UV.xy + offset.xy) ```

If you try this, you'll notice that the results are a bit wacky. Remember that UV's range from 0.0 to 1.0 and the wave functions from -1.0 to 1.0. This means the waves used here will ripple by the size of the entire image and create enormous distortions. So we'll have to tune that number down a lot:

offset *= 0.02;

Play with the number for desired results. You can also use different factors for x and y respectively.

Animating the distortion

Of course, this is completely static. The waves aren't moving, because the only input variable is the position of each pixel, and that never changes. So we need to introduce something that does. The easiest thing to do is use the runtime of the game, which is constantly updating. Unity gives us the built-in variable _Time which is actually four values (read details in the manual). The second value, y, is the actual time, so we'll use that. We'll have to stick it in the wave functions so we need to update those:

offset.x = sin(UV.x + _Time.y); offset.y = cos(UV.y + _Time.y);

The time as is might move the waves too slowly (or fast!) for your tastes (it did for mine) so you may want to multiply the time by some factor for desired results. I multiplied by ten. Finally, you may find that the steps between each pixel passed into the wave functions are too small, since they'll only range from 0.0 to 1.0, so you may want to multiply those UV values by something quite large. My final code looked like this:

offset.x = sin(UV.x * 50.0 + _Time.y * 10.0); offset.y = cos(UV.y * 40.0 + _Time.y * 10.0);


Audio

I'll cover this in the next post, not to make it too lengthy! Won't post that today since I don't want to be spammy, but if you're interested, look out for that! Have fun playing and rating more amazing LD games; I know I will! <3

LD 41

JAM ENTRY: Shutin

Forgot to post about it on the LD blog!

logo.png

Survival horror meets life simulator. Then again, what's the difference between the two? How long will you last, seeing to your needs, working to pay your food and bills, until it all comes crashing down?

https://ldjam.com/events/ludum-dare/41/shutin

shutin8.png multi-litn.png

'Shutin' tech: / cycle & gradients

My tradition of doing a little post-jam write-up on some technical aspect (usually graphics) of my game continues with LD41! This time I'll talk about two ways I used gradients for my game Shutin, for the draining bars and this day/night cycle:

syklusemlitn/emoptimert.gif

👉 You can PLAY 'Shutin' here! 👈


Here are some of the earlier jams I've done write-ups on:

LD40: Mallio Cart | LD39: SPACEJAMMED | LD38: Blomst 🌻 | LD37: Lock and reload - | - mallioemlitn.gif | space/emlitn.gif | blomstemlitn.gif | ld37/emlitn.gif


Day/night cycle 🌗

It's a little weird to have one, considering I didn't actually put any windows in the apartment, but as the game keeps ticking day by day, it was a good way to represent the change!

skjerm.png

Maybe you can see it, or maybe not, but there are two different light colours being used to illuminate the scene: the ambient light and the directional light. Each has a different gradient that it cycles through during the 24 hours of the day. Both of these gradients are baked into one image, like so:

ramp-light.png

This has been scaled up eight times from the actual image. Top row is for the ambient light and bottom for the directional one. Figuring out the exact colours was just a matter of running the cycle, looking at it, and making adjustments until it looked good!

Implementation 🔧

The most obvious way to do this would perhaps be to pass the texture to the shader and getting the interpolation for free on the GPU but it felt unnecessary to sample that for every single pixel of every single object rendered with lighting instead of just finding the right colour once per frame on the CPU and passing the precalculated value along as a uniform. Since I made this game in Unity, I just modified the colour property of the actual lights so that I didn't have to modify the shader already using those.

Interpolation 🔀

This meant that I had to calculate the tweening myself since I didn't want the colours to just snap from one pixel to the next on the texture. Unity's colour struct has a nice lerp method built in, so I just used that.

Using somewhat simplified pseudocode:

``` // Calculate the daytime progress between 0.0 and 1.0. float progressTime = timeCurrent / timeTotal;

// Calculate how far into the texture this is. float pixel = progressTime * countPixels;

// Get the closest two pixels. int i = floor(pixel); int j = ceil(pixel);

// Save the second value and wrap the index. float progressTimeCeiled = (float)j; if (j >= countPixels) j = 0;

// Find the progress between the two pixels. float progressPixels = 1.0f - (progressTimeCeiled - progressTime);

// Do the lerp! return lerp(pixels[i], pixels[j], progressPixels); ```

Turning the sun 🌞

Finally, to top it off, the light is rotated 360 degrees every cycle to make the shadows move around!

ljos.gif

Might not be how everything realistically works, but it's good enough for me! 💃


Bars for stats 📶

Nothing much to add here, but I did basically the same thing for those stats at the top left corner of the HUD:

bars.gif

They go from greenish to reddish through a yellowish passage. The gradient looks like so:

ramp-stat.png

Unlike the light gradients, this one of course does not wrap around since the bars stop at zero.


That's it for now! Might write something up about the paint shader later. In the meantime, please do try the game! 💜

Touting Shutin and seeking non-violent games to play!

I'm finally playing a bunch of LD games after having a bunch of computer trouble, so I'm inviting everyone one last time to try my game, and I will try to make sure to play yours after seeing your comments because that makes it a little easier for me to find games to play outside of the front page and the games page that I might miss otherwise.

Feel free to leave a comment here too! As long as it's not another dating sim or a particularly graphic game, I'm all down! Especially interested in playing non-violent/non-combative games!

Play Shutin here!

A survival horror life simulator!

shutin8.png multi-litn.png

Give me your gamepad games!

I've got an Xbox 360 controller here and would love to use it to play more LD games. Does your game support gamepad? Comment with your link and I'll try to play it before time runs out!

If you want to play my survival horror life simulator: https://ldjam.com/events/ludum-dare/41/shutin

multi-litn.png

LD 42

Jam status update

Nearing the end of day two and it's finally starting to look like a game!

Skjermbilde 2018-08-12 kl. 21.20.37-kopi.png

Skjermbilde 2018-08-12 kl. 21.19.27-kopi.png

13th LD in a row!

Can't quite believe it. So much fun with LD over the years. Thanks, everyone! 💜

ldemkollasje/em42.png

Encroaching

Our latest game is Encroaching, a stealthy game where you play as a deer.

You can play here

SKJERMSAMLING.png

LD 43

'Permanent damage'

I see everyone downvoting this but I actually found it to be probably the most interesting one of all. 👀

I can see it being interpreted in a lot of different ways besides the obvious "game with damage and health system but no heals"… The damage doesn't have to be to an individual but could be to an item or to the world, for example. It can even be completely thematic as opposed to mechanical, such as a game about dealing with trauma.

Just some food for thought! 😶

THE INNEST

All set up for the 14th time in a row! 😨 Let's do this!

ldgit.png

Gan your battest!! 💪