Posts Tagged ‘information’

[NO SPOILERS] Fun with shaders for LD37, part 2!

Posted by (twitter: @avaskoog)
Saturday, December 17th, 2016 9:45 am

Hey! Posted earlier about some of the custom shaders that were written for our LD37 entry, LOCK AND RELOAD. c:

Since the posts easily get a bit lengthy, I decided to divide this into a series of more bite-sized posts so that I don’t have to sacrifice all too much clarity for the sake of space. This is part two, about one of two shaders that make use of separate cameras and render textures!

(ノ◕ヮ◕)ノ*:・゚✧

Some of the effects are connected to slightly fundamental parts of the story, and since the game ended up being really short due to time constraints, I recommend you play the game first to avoid the spoilers and get the most out of the game with it’s little twists! So I’ll be adding a “read more” tag at to some parts to hide some of the stuff from the front page of the blog.

Note: the game was made in Unity and the shaders were written accordingly and so any code will be given as boiled down versions of the shader code written for that, but obviously everything is readily convertible to something like GLSL with minor to no changes.


 

Inventory inspection

This one is not a spoiler! c:

When picking something up, or clicking something on the inventory bar that was picked up earlier, the player will see a 3D view of the item in question along with some descriptive text.

View post on imgur.com

This view is rendered by a separate camera than the main one to get a nice 3D render of the spinning artefact. The main camera that renders the room (still visible behind the UI) cannot see this model, and the camera that renders the inventory view cannot see anything except the inventory item.

Separate camera setup

This is achieved in Unity by setting inventory items to a render layer of their own, and then unchecking that layer from the main camera, while unchecking everything except that layer on the inventory camera.

ld37_shd_insp_cams

Then you’re free to render your inventory and your game world in the same place, without them actually interfering with each other, like so:

ld37_shd_inv_scene_game

Render texture

Next, we need to create a texture for the inventory camera to render what it sees onto, since we don’t want it to render into the main game view and replace what the main camera sees. A render texture can be added by right-clicking somewhere in Unity’s file inspector and finding the option in the create menu.

It then needs to be connected to the camera, but we’ll not be using the field in the inspector for this, as it won’t work as intended. Instead we’ll be creating a small script that sets the camera up with the texture as the game starts. Something like this at the top:

[SerializeField] private Camera m_cam;
[SerializeField] private RenderTexture m_tex;

This way we’ll be able to drag and drop the camera and the render texture onto our script. Then as the game starts we’ll call this:

m_cam.targetTexture = m_tex;

Simple as that! Finally we’ll need to draw the texture somewhere so that we can see it. We’ll do this by adding a “raw image” to our UI canvas which will take the render texture as its sprite/texture, and that’s it. Now during runtime the UI will show what the inventory camera sees in that image, and we can edit the UI element as usual to get it to appear where we want it!

Remember we can also adjust the resolution of the render texture by opening it up in the inspector, in case the default 256×256 is too small, or has the wrong proportions.

The shader

You may have noticed we haven’t actually done any shader work yet! ヽ(゚Д゚)ノ

We can see there is a white border around the inventory item’s model, and also a circle behind it. But of course that’s not automatic. Without any shader, the inventory would now look like so:

ld37_shd_cabl_0

Not very fanciful, nor stylised. And potentially difficult to see, depending on the background! And of course this game’s UI has a dark overlay, so we want that white border around the model to make it stand out nicely no matter what’s below!

Border shaders can be tricky or not depending on the circumstances. In our case we’ve got a mostly transparent image with some non-transparent pixels in the middle; namely, those that make up the model. This makes it simple: we look for pixels that are transparent but have an adjacent non-transparent pixel at a particular distance from it (which will determine the thickness of the border), in which case we make those pixels as opaque as we wish and then assign the border colour to them! (◕‿◕✿)

Remember that we get the colour of the pixel/fragment at any given position in the texture by sampling a UV coördinate. To get the currently processed pixel, we just use the unmodified x and y that we got from the vertex shader:

fixed4 col = tex2D(_MainTex, i.uv);

To for the alpha value of any pixel around this one, we need to offset the UV coördinate by as much as it takes to move on to the next pixel. We’ll start simple, by only sampling the nearest pixels above, below, to the left and to the right of the current pixel. This gives us a less smooth border, but is easier to keep track of.

UV’s are normalised between 0.0 and 1.0, so to get the size of a pixel at this scale, we will need to divide 1.0 by the width and height of the texture’s resolution (in pixels). If we want our border to be thicker than one pixel, we can multiply the result by some value.

fixed2 one = fixed2(1.0 / width, 1.0 / height) * thickness;

Now we can get the alpha values around the current pixel by sampling the surrounding UV’s and only storing the alpha value and ignoring the RGB components.

fixed left = tex2D(_MainTex, i.uv - fixed2(one, 0.0)).a;
fixed right = tex2D(_MainTex, i.uv + fixed2(one, 0.0)).a;
fixed up = tex2D(_MainTex, i.uv - fixed2(0.0, one)).a;
fixed down = tex2D(_MainTex, i.uv + fixed2(0.0, one)).a;

The case may well be that up actually means positive values rather than negative depending on your coördinate system, but it makes absolutely no difference to the end result of this effect, so we need not worry about it. ᕕ( ᐛ )ᕗ

Now we can accumulate alpha values by adding all of these together, then clamping the result to 1.0 and do some more maths to finish up, which will be explained below the complete line of code.

fixed border = min(1.0, left + right + up + down) - col.a;
col.rgba += border * transparency;

Finally we subtract the alpha value of the original pixel that we are currently processing.

  • If all surrounding pixels as well as the original pixel are transparent, the end result will also be transparent, and we’ll either get no border at all, or a semi-transparent border pixel, which will give a nice and smooth result so long as the original model was also rendered with anti-aliasing.
  • If the original pixel was not transparent, the border value will be reduced (all the way to 0.0 if the original pixel was completely opaque; otherwise a semi-transparent value) and the original colour will overtake the border, since there will be nothing to add when we finally add the border value to all of the channels (RGBA) of the final colour.

We can also multiply the value we add by some factor below 1.0 to only apply a semi-transparent border. Et voilà!

ld37_shd_cabl_1

This border actually also samples pixels diagonally from the original pixel to make it even smoother. Note that this doubles the number of texture lookups, which can be slow, so choose carefully whether to add this extra overhead depending on the needs of your own application.

As you can see we still haven’t added that circle, but this post is getting lengthy, and you can get an idea of how to render a circle all in shader from the previous post. It’s one of the easiest things one can do, actually! Or you could just add a circle image to your UI below the UI image with the render texture if you’re not feeling up to it!

 

That’s it for this part! In the next one (and I won’t be posting it right away as I don’t want to be spammy), we’ll be talking about some UV scrolling shaders (one of which is a spoiler) before we move on to the second effect in the game that required extra cameras and render textures, which will be a SPOILER, so play the game first! c:

Remain at the edge of your seat! ☆

[NO SPOILERS] Fun with shaders for LD37, part 1!

Posted by (twitter: @avaskoog)
Friday, December 16th, 2016 2:34 pm

Hey! They’re not all too advanced, but I still thought I’d share a little something on the custom shaders that were written for our LD37 entry, LOCK AND RELOAD. c:

Since the posts easily get a bit lengthy, I’ll divide this into a series of more bite-sized posts so that I don’t have to sacrifice all too much clarity for the sake of space. We’ll start with the main shader that gives the game its look and feel!

(ノ◕ヮ◕)ノ*:・゚✧

Some of the effects are connected to slightly fundamental parts of the story, and since the game ended up being really short due to time constraints, I recommend you play the game first to avoid the spoilers and get the most out of the game with it’s little twists! So I’ll be adding a “read more” tag at to some parts to hide some of the stuff from the front page of the blog.

Note: the game was made in Unity and the shaders were written accordingly and so any code will be given as boiled down versions of the shader code written for that, but obviously everything is readily convertible to something like GLSL with minor to no changes.


 

Post effect filter

This one won’t spoil a thing as it is the very first thing to see upon starting the game: the wonky colour effects and checkerboard overlay. There’s also a dark vignette as well as some increased contrast.

View post on imgur.com

This effect applies to the whole final rendered image rather than individual objects and is therefore done in one step at the end of each frame, simply applying the shader to each pixel, or fragment.

Let’s go! ᕕ( ᐛ )ᕗ

The low-fi colour effect

This is achieved by first multiplying the colour values (which are all normalised between 0.0 and 1.0) by some factor, then rounding the numbers so that any resulting decimals disappear, and then dividing the values back down again by the original factor. This effectively decreases the colour variation. The smaller the factor, the greater the effect.

col.rgb *= factor;
col.rgb = round(col.rgb);
col.rgb /= factor;

ld37_shd_couleur

Contrast

The contrast effect is fundamentally not so complicated. It can be done in different ways, but for this particular game what I found worked best was to subtract some of the intensity of the colour if the original colour was below some tolerance value, but multiply it by some factor if it were above it.

col.rgb += (col.rgb < tolerance) ? 0.5 : 1.5;

ld37_shd_contr

Checkerboard

Alternating dark and bright squares are all over the screen. This isn’t some texture; just a little bit of maths. By checking the position of each pixel on the screen and figuring out whether it’s even using the modulo operator (which gives the remainder of a division), we can figure out whether the number is evenly divisible by two and thereby whether it’s odd or even.

If we multiply the coördinates by some factor to pretend that the screen is smaller or bigger than it really is, we can control how big the squares get. We apply the effect by darkening odd squares and leaving even ones untouched or vice versa.

col.rgb -= ((floor(x * s) % 2.0) - (floor(y * s) % 2.0)) * darkness;

ld37_shd_chekrs

Vignette

In this case, a vignette is a darkening of the corners of the screen. We’re basically applying darkness to the whole screen except an oval in the middle, leaving the corners dark but the middle unaffected. Circles are easy. We can achieve an oval by pretending that our rectangular canvas is in fact square.

fixed ratio = height / width;
fixed2 middle = fixed2(0.5 * ratio, 0.5);

We can then check the distance from the center to the current fragment, adjusted for the rectangle’s ratio, to get its position inside a circle, which will appear as an oval thanks to the ratio.

fixed2 pos = fixed2(x * ratio, y);
fixed factor = length(pos - middle);
col.rgb -= factor * darkness;

ld37_shd_vign1

You will probably want to adjust some values by multiplying them by various factors to make your vignette look the way you want.

That’s it for this part! In the next one (and I won’t be posting it right away as I don’t want to be spammy), we’ll be talking about the first of the two effects in the game that required extra cameras and render textures!

Stay tooned! ☆

[cache: storing page]