Friday, August 16, 2013

Procedural Meshes - Circles

If you're specifically interested in how to create procedural geometry in Unity and not in the mathematical details of creating circle geometry, go here:
http://www.youtube.com/watch?v=3jHe1FzrKD8.   Otherwise, please read on!


In the link above, we created a simple 4 vertex 2d rectangle (“quad”).  Quads are quite useful but sometimes you’re interested in more complex shapes.  How about a circle?  

Ok, that doesn’t sound that much more complex but it does allow us to get our hands dirty with some maths because let’s face it, there was no sexy math involved in creating a quad.  And, as we’ll soon see, a simple circle is more involved that it sounds.

You may remember this equation from school (if not, you can learn about it up with some cool videos from Khan Academy http://www.youtube.com/watch?v=6r1GQCxyMKI ):

x2 + y2 = r2


and it sure does look simple.  It says that the sum of the squares of x and y always have to equal some number, the radius squared.





In the case, it must sum up to be 1.  For example, here we’ve chosen our radius to be equal to 1 and 12 is still equal to 1.  To test this, let’s pick an x value that is less than our radius.  If we pick x to be 0.5 then x2 will be equal to 0.25.  That leaves us with this equation: 0.25 + y2 = 1.  So now we solve for y ("sqrt" means square root): y = sqrt(1 - 0.25).  We could compute that but we don’t really have to because we know that you have to add 0.75 to 0.25 to get 1.

Armed with that knowledge alone and some patience, you could probably get something working. and this seems sufficient to complete the task, but is it the best way?  Let’s explore this a bit further.

We know that creating procedural geometry involves creating a set of points and then connecting those points in sets of three to form triangles.  One way to this using the equation above might be to iterate across each x value and then generate the matching y value.  It might look something like this:



 for (float x = -1.0; x < 1.0; x+=0.1) {  
  float y = Mathf.sqrt(radius * radius - x * x);  
  myVertices.push(new Vector3(x, y, 0);  
 }  


This would get you points laid out across this curve:





But this is only half of what you want due to the fact that the definition of a full circle is not defined by a single mathematical function.  You’d have to run through the loop again and generate the bottom half.




 // Bottom half generated by using negative value  
 float y = -Mathf.sqrt(radius * radius - x * x);  


So it seems this method requires doing the full circles in 2 passes (2 for-loops).  But that’s not the biggest problem here.  Since we’re iterating by a constant value on the x direction (0.1 in the example), we’ll end up with y values that are closer together near x = 0 and y values that get increasingly further apart as we move toward +-1.  You'll get a distribution looking something like this:


If you were to triangulate that you'd notice that it looks blocky toward the left and right and smooth toward the top. Are we happy with that?




We want something evenly distributed.  More like this:






So let’s rethink this... This time, using sacred tools from the game developer’s toolbelt: vectors.  If you’re not already familiar with this extremely important concept, check out Khan Academy: (https://www.khanacademy.org/math/linear-algebra/vectors_and_spaces/vectors/v/linear-algebra--introduction-to-vectors).


Vectors will allow us to generate the vertex data in one convenient loop in an order that makes it easy to triangulate and even more importantly, our points will be evenly spaced out.  The idea is to start with a single vector and rotate it by a constant angle to get the next point.  We do this for the whole circle.  This image sequence will give you the basic idea:



This will give us a set of points around the circle.  But, in order to create triangles for this, we need just one more point.  Take a second and make a guess where it should go. If you guessed that it is needed in the middle of the circle you got it.  With one point in the center and a bunch of other points on the outside, we can then start carving this up like slices of a pie.


Let’s get to the code:



 // The more verts, the more 'round' the circle appears  
 // It's hard coded here but it would better if you could pass it in as an argument to this function  
 int numVerts = 41;  
 Mesh plane = new Mesh();  
 Vector3[] verts = new Vector3[numVerts];  
 Vector2[] uvs = new Vector2[numVerts];  
 int[] tris = new int[(numVerts * 3)];  
  In the beginning we set up for everything we’ll need later. We get an array of Vector3 (3 floats) to use for every point as well as arrays for uv coordinates and triangles.   
  // The first vert is in the center of the triangle  
 verts[0] = Vector3.zero;  
 uvs[0] = new Vector2(0.5f, 0.5f);  
 float angle = 360.0f / (float)(numVerts - 1);  


Here we create our center vertex as the first one in the array and we also figure out how big each slice of pie should be.  The number of pieces of pie (triangle) is equal to the number of vertices - 1.



 for (int i = 1; i < numVerts; ++i) {  
 verts[i] = Quaternion.AngleAxis(angle * (float)(i - 1), Vector3.back) * Vector3.up;  
  float normedHorizontal = (verts[i].x + 1.0f) * 0.5f;  
  float normedVertical = (verts[i].x + 1.0f) * 0.5f;  
  uvs[i] = new Vector2(normedHorizontal, normedVertical);  
 }  


Here we iterate through each slice of pie in similar fashion to that image sequence shown above.  In this code I’m starting with a vector pointing up and rotating it clockwise.  This is a little different from the images above but it doesn’t matter so long as you start somewhere and rotate all the way around.  You could choose other methods of rotation here but the Unity built in Quaternion is what I went with.  If the rotation is a little mysterious to you let me know and I’ll write something up on it.



 for (int i = 0; i + 2 < numVerts; ++i) {  
 int index = i * 3;  
  tris[index + 0] = 0;  
  tris[index + 1] = i + 1;  
  tris[index + 2] = i + 2;  
 }  


Here we create all of our triangles.  Each triangle with start with the vertex in the center and connect to 2 on the outside of the circle.



 // The last triangle has to wrap around to the first vert so we do this last and outside the lop  
 var lastTriangleIndex = tris.Length - 3;  
 tris[lastTriangleIndex + 0] = 0;  
 tris[lastTriangleIndex + 1] = numVerts - 1;  
 tris[lastTriangleIndex + 2] = 1;  

This is kind of a special case where we reuse a vertex so we save it for last and manually compute the final triangle.



 // The last triangle has to wrap around to the first vert so we do this last and outside the lop  
 var lastTriangleIndex = tris.Length - 3;  
 tris[lastTriangleIndex + 0] = 0;  
 tris[lastTriangleIndex + 1] = numVerts - 1;  
 tris[lastTriangleIndex + 2] = 1;  

Make sure you give the mesh everything you just computed and that’s it!  Now you’re thinking with portal....errr, ummm, vectors!

You can download the source package for everything here.

Friday, May 17, 2013

Camera Shake

Before we dive in, I recommend you watch the video here.
First up, let's take a look at how the randomized camera shake is implemented:


The shake animation all takes place within a coroutine which allows us to keep our timing variables local to this function as opposed to declaring them for the entire class.  The shake can be configured to last any length of time using the duration variable and this number will be used to determine what percentage of that duration has elapsed (0% to 100% or in this case 0.0 to 1.0).  If you haven't made best friends with math yet I'll do my best to help you visualize it.  Here's what we're doing:



The graph above corresponds to having set a duration of 2.  The x axis represents the time elapsed and the y axis is the percent complete.  The basic equation is percentComplete = timeElapsed / 2.0.  It's important to note that percentComplete is 0 at timeElapsed equal to 0 and percentComplete is equal to 1 at timeElapsed equal to 2.

So now we have a way to determine how far we are through the shake animation.  We care for 2 reasons.  One is we need to know when to stop shaking and the other is that we'll want to gradually fade the shake out (the damper variable) but we'll get back to that later.

Next we want to compute x and y values for how far we should move.  The idea is that our shake is going to be centered around the camera's starting position and when we're done shaking we should be back where we started.  With that in mind the x and y values are just going to be offsets from the camera's original position.

We make that calculation like this:


The Random.value property will give you something between 0 and 1.  It's always a positive number.  Since we're going to be offset from a starting value, we want to be able to offset in either direction which means what we want is a random value that can be negative as well as positive.  So how about -1 to 1.  That's what that equation does for you and its graph looks like this:

So now if you get a random value of 0.5, the value will be remapped to 0.  Now is a good time to explain why we're choosing values that either go from 0 to 1 or -1 to 1.  Let's say that shaking up to a maximum of 1 unit wasn't enough and we wanted to be able to shake as far as 5 units.  By having a value with a maximum of 1, we can just multiply it by what we want (in this case).  This is shown in the code:


But it's also being multiplied by that damper thing too.  Ok, let's cover that.  Since we want to the camera shake to end where it started but we don't want it to pop back afterward, we have to smoothly bring it back.  That's what damper is all about.

For the first part of the shake animation, the damper is intended to do basically nothing.  That shows up in the math when damper is equal to 1 because anything times 1 is itself.  Then we the animation is getting closer to finishing, it gradually starts multiplying the offsets by a number less than 1 until at the very end is multiplies it be 0.  That brings it back to it's original position.  Here's what the graph looks like:

That covers most of what you need to know to understand the other variations.  The main differences are just that the other version using something better than Random to produce the offset values.

You can download the Unity package shown in the video here.  Enjoy!

PS. If you improve upon my code in any way please let me know!  One thing I could think of would be to make the shake more strongly favor a particular direction.  For instance a ball hitting a wall might want to shake more in the direction it was traveling when it hit the wall.