Wednesday, 18 November 2015

Low-poly water on UE4


Here's how to achieve cool low-poly water on UE4

First of all, we need a suitable plane. While Unreal comes with one we can use straight out of the box, I prefer having one that's just right for my purposes. Here's one I made using Blender.
The UV map (to the left) is rather important to get right, or you'll just get wonky results. Make sure it maps the corners to 0,0 and 1,1.

And because I know you're in a hurry, here's the final shader (you can copy and paste this into an empty material to save you a lot of work):

How it works:

Firstly we take the current time and use it as UV coordinate modifiers to sample a nice, smooth and tiled noise texture. I found "LowResBlurredNoise" (make sure you're allowed to view engine assets in order to select it) perfect.

To avoid most issues with occlusion culling I then "normalize" the values between -0,5 and 0,5. Why is this important? It allows us to increase and decrease the vertices' height relative to their original position rather than just adding to it (and thus always skewing them upwards). You could multiply this by 2 to make the values range from -1 to 1 but you can achieve the same thing by tweaking the wave scale value.

Finally, with a suitable noise map, all we have to do it modify the original vertex position. We take the normal (which would point up in the case of your plane), multiply it by the noise values and as time pans the noise texture forward through all the vertices in our plane, we get a wave.

Why use the normal instead of just just an up vector (like 0,0,1)? It allows us to place water sideways and so some weird stuff with other shapes, like this:


The bits connected to the "normal" part of the material are a bit of "dark math magic" which recalculates the vertex normals after we modify their position. If it wasn't there, the lighting on our water would be constant and look completely hideous. I may go into more detail on this one day.

Usage Notes:

  • You CAN put several of these planes beside each other. They will cleanly fit and should have no seams if you've done the uv mapping properly.
  • As with all vertex-deforming materials, this one will become a bit iffy with occlusion culling, especially with huge "Wave Height Scale" values.
  • Make sure the water plane isn't casting shadows on itself or is otherwise baked by Lightmass
  • Keep in mind that due to oscillating height, it may clip through the ground if you're not careful.
  • Unfortunately, due to the way Unreal handles flat shading, this leaves us with a rather high poly count for a simple flat shaded plane. But this has nothing to do with the Water Material itself.

Addendum: If you're into Unity3D, /u/Ro9AM has posted a neat little tutorial (with source code) that does pretty much the same thing :)

Wednesday, 14 October 2015

There is such a thing as too much compiler optimization

After spending the last few years using antiquated subsets of C and C++, I've decided to try my hand at the latest Standards and Proposals.

After some time I found this weird... Bug? I'm willing to bet it's just compiler optimization at work.

The following are screenshots taken after compiling the code. I'm ashamed to say this took me at least 15 minutes to figure out.

This works
This doesn't (std::array is not a defined type)
This works. Wat?

Thursday, 10 September 2015

On moving from Unity to Unreal

The team at Titan is working on a soon-to-be-announced Top Secret Project. We've recently changed from Unity to Unreal and here are some things we've thought about sharing with other Devs that are thinking of making the jump.

The good stuff: 

  • Unreal let's you easily improve the aesthetics of your project. The drag and drop shader editor, the huge amounts of high-quality included content let you easily reach AAA levels of quality, even with short prototypes.
  • Blueprints are amazing for quickly prototyping concepts! Beware: any non-trivial math expressions will be a mess of wires, but thankfully there's a handy math expression node to help you avoid turning your code into a spaghetti mess.
  • The community has a lot of experienced developers, which makes useful content and non-trivial tutorials easier to find.

The bad stuff:

  • Don't get me wrong, Unity can be a bit of a resource hog sometimes. But Unreal seems to be an expert at making even our reasonable Gaming PCs suffer. Compile times will be measured in minutes for anything but the smallest projects.
  • The way Unreal handles project files is simply not as robust as Unity's. Every time you move, a file a redirector is created. 
  • You can't just drop a png file and use it as an asset, there's always going to be some sort of pipeline to get assets into your game.
  • C# is gone. You will be using C++. This can be a curse or a blessing but overall C++ is not as great for quickly prototyping.
  • It's not very stable. Seriously, it's not. The update to 4.9 broke transparent sprites on the UI. This is not a trivial and easily-ignored bug.
  • The huge ingress of novice game developers has made communication with the Epic team and experienced members slightly harder due to a sea of (mostly) trivial questions.

Wednesday, 29 July 2015

Chocolatey is awesome and you should use it

I've recently started using Chocolatey. While the thought of a package manager for Windows has crossed my mind (oh, my long gone Linux fanboy days), I always relegated it to something that would be too hard to maintain and work with.

For anyone who hasn't used a package manager, here's what makes it better than the classic install/uninstall babble.
  • The endless cycle of "Go to website, navigate to download link, wait, double click, click next, uncheck the 'install malware option', press next 2 more times, wait again, press finish" is gone.
  • Update ALL your software with no user input
  • Got a new machine? Run a single-line command and all your favourite programs are now installed.
  • A lot less annoying popups saying an update is available
  • I think avoiding Adobe Flash updates deserves an entire bullet point. 
So how does it work? Suppose you want to install git and Flash. Easy, launch the command line as an administrator and run this command.
 choco install Git flashplayerplugin -y 
After some time both Git and flash will be installed.

Firefox's giving you warnings that your version of Flash is unsafe?

 choco upgrade all -y 
Now both flash and Git are up to date with (nearly) no user input.

Wednesday, 27 May 2015

Unity's new event system

Despite being very poorly documented, Unity's new event system can be incredibly useful. Here's a really simple script that I keep using over and over.

Saturday, 15 February 2014

Regarding Meshes in Unity

Maybe it's just my OpenGL (1.1) background but here's a few roadblocks I've hit while doing procedural Mesh generation in Unity, along with a few tips not many people seem to know of. Some of this knowledge has been useful even outside my procedural experiments.

Quick Overview (stuff you really should know)

  • You will read this until your eyes bleed
  • Mesh.vertices contains your vertices. Mesh.triangles contains indexes to the vertices array. 
  • Normals (and UVs) are per Vertice. E.g.: Mesh.normals[i] has the normal for Mesh.vertices[i] (Blender uses normals for each face, which sometimes confuses people)
  • Different materials means different submeshes (or a new Mesh if that's what you're into).

A few pitfalls and tips

  • If your mesh has more than  one material, you have to use submeshes to define which area has a certain material. When copying meshes, I've found that I have to manually SetTriangles() as no other way of copying submeshes seems to work.
  • The backface is determined by the order of the vertices in each triangle, NOT its normal. If your mesh looks like it's inside out this is probably why.
  • Some of Unity's built-in shaders (namely the bump mapped ones) do not calculate their own tangents. Each Mesh has their own tangent array for this specific purpose. Unfortunately they can't be auto-calculated.
    • Blender files do not have these by default but fortunately Unity can calculate them automatically. Sadly, we don't have access to their algorithm for this.
    • Fortunately there's some code floating around that works. 
  • Don't generate a new Mesh() every time. Use the same mesh instance and .Clear() it. New meshes will get fed to the GPU but won't replace the old data there, causing memory leaks.
  • 99% of the time you can just call Mesh.RecalculateNormals() at the end instead of reinventing the wheel and calculating your own normals. As far as I know this calculates the normals based on the order of the vertices. 
  • Don't forget to Mesh.RecalculateBounds() if having decent bounds is important to you.
  • When you add a collider Unity will automatically fit it to the current Meshfilter's bounds.