This week I’ll talk about a cool little visual effect I put in the game: Light shafts. It’s a pretty standard visual effect, so it’s definitely nothing ground breaking, but it does make the game look that much better for very little effort. It was very quick to implement (read: a nice distraction from the monstrous networking task :) ), and I really like the effect.
A while back on our twitch stream I worked on implementing a compute shader to calculate proper volumetric fog, much like the amazing Assassins Creed volumetric fog. I abandoned this approach because it was actually pretty difficult to make it look good in a side scrolling brawler. It was hard to control the fog, created a ton of weird blend mode issues (especially with additive blending) and it also raised the minimum spec because of the compute shader.
Instead of proper volumetric lighting, I’ve opted for a manually placed animated light shafts. This gives us more control over the look, and it also works well in our existing chunk building system.
Last weekend I was playing through Brothers, a tale of two Sons (which is a gorgeous looking game by the way), and they use light shafts in multiple places to create a really cool atmosphere. I wondered how to implement these, as they fluctuate the light over time, yet are always smooth. This blog is about what I came up with.
The light shaft is rendered using alpha blending, using a user-defined color, and using a calculated alpha.
The geometry is 4 triangles, with coded UV’s. the U coordinate is used as the actual U coordinate in the noise texture, and the V coordinate is just used as an alpha multiplier.
The alpha is the product of the user-defined color’s alpha, the sample from the texture, the V coordinate (used as alpha multiplier), and the edge smoothing alpha. The edge smoothing is simply using the (0-1) U coordinate as a parameter for a sine function in the shader to generate a multiplier between 0 and 1 that nicely blends the edges to zero.
The texture coordinates used to render the light shaft is using a thin horizontal sliver of the texture (stretched over the mesh), and is animated over multiple frames to generate the modulation effect in the light shaft.
Here’s a video of the effect in the game:
That’s pretty much it! Simple, but effective. I love putting things like that in the game.
Oh, and as a reminder, we moved our weekly dev-stream to Friday, because Jesse is still in Japan.
In a small development team of three, you often end up having to take care of many different things. In the last week I spent time trying to optimize my rendering, reducing temporary memory allocations in the game-play code, fixing continuity bugs in the network code, as well as updating our build machine to include Mac OSX builds. So many different things that it’s hard to chose one to write a blog about! Well, I’ll just pick the build machine one.
To get Viking Squad working on OSX, I’ve been working on converting the engine code to SDL 2.0 in my ‘spare’ hours over the last few months. Since my engine already supports multiple platforms (Windows, PS4, XBox, iOS, Android, etc), I’ve been able to concentrate platform specific code in a few small areas. Adding another target for SDL2 in those spots was fairly straightforward. Luckily I’d been keeping the OpenGL renderer up to date, so once I was able to create a window in SDL2, getting it to render to the window was easy. We’re using FMod for audio and besides linking the proper libraries there’s nothing I had to do to get it running. Great!
The next hurdle was to get game-pads working on OSX. At first I tried plugging in my wired XBox 360 controller, but I quickly found out that it’s actually not straightforward at all to get that working on a Mac. Third party drivers need to be downloaded and installed, which I don’t like, so I quickly grabbed my PS4 controller, and that worked straight away. I guess I will have to try installing the third party driver to make sure XBox 360 controller work properly at some point. The process of converting to SDL2 was so smooth, that I actually converted my windows build (running D3D11) to SDL2 as well.
Now the build was working properly, the next task was to get it building on a build machine. I had an older 13″ Macbook laying around, which I re-purposed to be our brand new MacOS build drone. I upgraded it to El Capitan, and since all my build tools are written in C#, I installed Mono to see if I would be able to run my console-applications. I was a bit startled that I could simply run windows EXE’s by typing ‘mono SomeProgram.exe’ in a terminal window. It just works. Just like that, I had a working OSX based build drone!
The current build process to upload a new build to Steam is as follows:
1. Build Windows RTM game, and copy to the network drive on completion.
2. Build OSX RTM game (in parallel as step 1), and copy to the network drive on completion.
3. Run a custom Steam uploader tool after both 1 and 2 are complete.
The steam uploader tool is another C# console app that checks if the latest Windows and OSX builds present on the network drive are exactly the same build number, and copies them into the proper place to upload to steam. In this step, the windows data files are copied to the OSX build path. Once everything is in place, and verified, an external tool is called to upload the build to Steam. That’s it!
All in all this took a fair amount of time to get set up, with lots of snags and issues along the way, but it’s all working now. Putting a new build on steam for both Windows and OSX is now only three mouse clicks (one for each step mentioned above). I wanted it to be 1 click, but I guess I can can live with three.
That’s it for this week. Keep those build machines build machining!
And, as usual, today at 4pm there will be another Dev Stream with Jouste the Drawbarian. Don’t miss it!
Last week we didn’t do much developing, but lots of networking. (That is, meeting people, socializing, going to parties, etc. Not actual network programming). Maybe we should have, but we didn’t plan any press meeting for our game this year. We’ve been working so hard getting the game done that it has become a bit of a marathon, so it was nice to take a break and see what everybody else is up to.
On Monday and Tuesday we attended the fantastic Independent Games Summit, which is pretty much the reason we go to GDC every year. It’s a lot of fun seeing talks by people who are in the same boat as you. The main GDC often has talks that don’t really apply to small indies.
On Wednesday I met up with a few new friends who are all into networking, which in this case IS about network programming. It was good to meet them face to face rather than as an icon on our Slack page! Good conversation was had, and I am hopeful that many more good conversations will follow. It’s good to have friends when you’re small.
On Thursday we visited the Twitch headquarters in San Francisco. Our good friend Mos toured us around the office and treated us for lunch. It was great to see the inner workings of Twitch, and man I’m kinda super jealous of their offices. So modern and so much room for activities!!
Coming back from GDC I always have a bit of a weird feeling. Meeting so many friendly peers is super awesome, and going to the parties is always a ton of fun. Seeing all the games people are working on is super neat as well.
Now I feel completely social-ed out though. I feel like I could work alone in an attic for half a year just to recover. Also, every year I come back wishing I was more prolific. Seeing all these cool games people are working on makes me want to make a few small games, instead of working on this giant multi year project we’re working on now. Viking Squad is taking up all of our energy, and I feel guilty when I spend time on anything else. Time to get this baby out the door and start working on toy projects again… soon. Soon. SOON!
Earlier this week I sent out a few vines showing my system to resize textures to their proper size. I thought I’d talk a bit about that in our dev-blog.
When Jesse draws his amazing art, he usually draws it at a resolution that is somewhere in the right ballpark, usually slightly higher res than actual screen pixels. Sometimes we decide to shrink or grow an entity for game-play reasons, and having higher res source art helps in this case. However, when we actually ship the game with the final sizes of entities, it saves quite a bit of texture memory to resize the textures to the actual size they are being used at (on average). Saving texture memory also makes the rendering more efficient, as more textures can fit in one atlas, making batching draw-calls easier. Doing less draw calls is good!
To do this, we added a new icon to our already quite extensive toolbar in the editor, and this tab does all the re-scaling work.
First, it shows every single UI screen we have, including all animations. Then it goes through every level, and pans from the far left to the far right, making sure every part of it is rendered. After this is done, every entity is rendered at the size they appear in the game, including every single skin option, and every animation, which in turn spawns the visual effects that are used. In general the idea is to render everything at least once at the size it appears in game. Here are come vines I posted to twitter earlier this week:
So that’s all fine and dandy, but how does this translate into the texture sizes being used? Well, at the lowest level of rendering, I have a little (editor only) debug routine that calculates how big each triangle is on screen. Everything is rendered to a 1080p target (1920 x 1080 pixels), and for each triangle edge, I calculate the length in pixels being rendered. I also calculate the distance between the UV coordinates in texture pixels. Simply dividing one by the other gives you a scaling factor that you can use to resize the texture! The scaling factor is capped to 1 though, so a texture is never scaled to a bigger size than the original input texture.
For example, say you have a 512 x 512 texture that is rendered to the screen and appears as a 32×32 texture on screen. The screen distance of the top edge would be 32 pixels, but the distance in UV coordinates (in texture pixels) would be 512 pixels. This means you can re-scale the texture by 32/512 = 6.25%, without noticeably degrading the final image. Of course this is a hypothetical example, but hopefully you can see how this would be a huge savings.
One special note is that the blurred background and foreground layers are using a lower resolution render target, which has to be taken into account when calculating the resize factor for textures used only in these layers.
In our game, without texture scaling, we require 36 atlas textures (4096×4096) for all the used textures, totaling about 576mb of compressed textures. With texture scaling this number is currently 21 atlasses with total 336mb of compressed textures, and you can’t tell the difference! HUGE savings!
Alright, that’s it for this week, I have to get back to network programming. :)
Next week we’re at GDC, so there won’t be a dev-blog, but this Saturday at 1:30 EST (10:30 PST), we’re doing an interview and showing our game on the Geeks World Wide. Come check it out!
I have been plugging away on the networking code, while at the same time doing the administration of the company (as an indie, you get to do it ALL! yay!). To have a bit of fun (and partly putting the ‘pro’ in procrastination), I implemented some cinematic effects for our game.
First of all, I improved the vignette shader to give it more tunable values. We used to have only the ‘radius’ to tune how much the vignette was affecting the sides. Now, there’s a radius, a softness and an opacity value.
This next slider box shows the effect of different parameter settings. The hard line is the radius of the vignette, and the opacity is how much darker the outside of the vignette gets. The softness determines how soft the gradient will be between no vignette and full vignette. Slide over the box to see the difference between a softness of 0.0 and 0.2. Click on the image to go to a full screen slider box.
The shader used is pretty straightforward (glsl shader):
// inputColor is the color of the final image for the pixel// texCoord is the coordinate of the pixel on screen, in the range [0,1]. vec4 CalculateVignette(vec4 inputColor, vec2 texCoord)
vec2 dist = (texCoord -0.5);
float len = length(dist);
float vignette = smoothstep(VignetteRadius, VignetteRadius-VignetteSoftness, len);
result.xyz = mix(inputColor.xyz, inputColor.xyz * vignette, VignetteOpacity);
result.w = inputColor.w;
Of course these settings are a bit intense, so here’s the vignette effect in a more real in-game setting:
Next, I added a bit of film grain. Currently I’m calculating a random pixel value using a weird pseudo random generator I bashed together from some code I found online, which results in a super fine grained noise. I might actually change this to a 2d perlin based noise at some point so we can control the size of the film grains.
vec3 Random3D(vec2 uv)
float noiseX = fract(sin(dot(uv, vec2(12.9898,78.233) + FilmGrainSeed)) *43758.5453);
float noiseY = fract(sin(dot(uv, vec2(12.9898,78.233) *1.2345+ FilmGrainSeed)) *43758.5453);
float noiseZ = fract(sin(dot(uv, vec2(12.9898,78.233) *2.1314+ FilmGrainSeed)) *43758.5453);
returnvec3(noiseX, noiseY, noiseZ);
// inputColor is the color of the final image for the pixel the vignette is calculated for// texCoord is the coordinate of the pixel on screen, in the range [0,1] for both x and y. vec4 CalculateFilmGrain(vec4 inputColor, vec2 texCoord)
float luminance = dot(inputColor, vec4(0.299,0.587,0.114,0.0));
float lum = luminance + smoothstep(0.2,0.0,luminance);
vec4 noise =vec4(mix((Random3D(texCoord) -0.5),vec3(0.0), pow(lum,4.0)), 0.0);
return inputColor + (noise * FilmGrainAmount);
The Film Grain effect is hard to see in this small slider box, so click on it to go to full screen image!
Next up, Chromatic Aberration! This effect pulls apart the Red, Green and Blue layers of the screen to create the effect of an old tube TV. I added some settings to tune the amount of chromatic aberration depending on where the pixel is on screen, in much the same way as the vignette. There is a aberration value for the middle (innerAmount), the outer corners (outerAmount), and a curve power value to change how the amount of aberration interpolates between the middle and the corners. Here’s the code. Note that this requires a texturemap as the input. This would be the rendertarget you’re applying the post effects to.
And the effect is shown below (with somewhat extreme values to show what the effect does). Again, it might be hard to notice in this small slider box, so make sure to click it so it opens up a full screen version!
Alright, now that all the components are there, we combine all of it using the following shader code:
Welcome back followers of the fearsome! This week we’ll be taking a look at some areas in background art that we’ve been putting together as we polish up the last few levels in Viking Squad! We’ve decided it’d be really great to show some of our Jarl’s crazy greed in the form of some massive […]