Unite 2015 - A coder's guide to spline-based procedural geometry

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
can you all hear me well yes okay good hello and welcome to this talk thank you all for joining me here so this talk is going to be a coder's guide to spline-based procedural geometry which means that we're going to take a look at how we can generate a mesh along a spline and how we would Implement that in code so it's going to be a very technical talk and it's going to be quite practical and feel free to take notes at any time so I'm not offended if you have your laptop typing so yeah by the way I run a very very small Indie studio in Stockholm Sweden called neat Corporation we've been doing freelance work contract work and Tool development for a while uh most the two of you probably heard about most is jadaforge so that's the one I've been working on lately so while we support shaderforge right now we're mostly looking into doing VR game development at the moment so that's sort of what we're doing right now so that's sort of completely unrelated to what I'm going to talk to you about but all right so let's jump into it um so procedural mesh generation is quite a common way to enable the player server game to be more creative with it you can it enables people to create stuff destroy stuff alter stuff and change it to their liking so for instance a classic example is Minecraft where you can reshape the world you can create tunnels buildings and and so forth and Minecraft is procedural geometry as well there is not just toggling of cubes and it has actual geometry that's altered as the player plays this game this is another game that's using procedural geometry Little Big Planet they have this huge chunk of shape that you can create and then you can reshape that to pretty much whatever you like and then you can put that in your level and you can shape your level however you like and sort of make an entire game in a sense using Little Big Planets so people have been very creative with how they use those tools it's not the game this is from the worms series probably one of the later ones and this one is using procedural geometry as well I think it's kind of hard to tell but I think it's doing that and here's another game that you're many of you are probably familiar with this this is from series guidelines this is a super intricate intersection that someone has built that looks very nice foreign I'm not sure what it's like to drive through that but it looks nice from above um so this is probably the closest to what we're going to take a look at how to create today so in City skylines you can create roads using splines so when you want to make a road you then extend a piece of road and then you can bend that road to the sides and you can also elevate it and sink it down so that way you can create you can create the roads in pretty much any shape you like that is at least a realistic to roadshape and here's another example this is a small 24-hour game Jam game I created with a few friends and in this we also had procedural mesh generation and this was in unity as well so in this case we created this track in the editor and then when you played the game you have a small stock car that drives along this track sort of the the fun thing with this project was that the track was controlled by the audience so the audience would scream and then the car would accelerate which was kind of interesting um so then an advantage of having an actual having actual spline data to generate this track is that you can use that spline data for the gameplay as well which we did in this case so if you drive too fast then the car is going to go off track but if you drive carefully it's going to go on track and it's also going to like pivot around the shape of the track and so on so we were using that data for the gameplay as well so we're pretty much taking a look at how to do this we have some 2D shape and then we want to change that into an extruded shape along this plane but before we get started we should be asking ourselves quite a deep question what is a mesh so you all probably know that we're using this meshes to have visual geometry in our games but we need to know what the components of a mesh actually are so we have vertices that's all the positions of every vertex in your mesh you have triangles that's the connection data for how those vertices are connected to each other then you have normals which is a direction every vertex is looking there is a normal direction from the surface so if you imagine a surface here the normal is always pointing out from that surface then you have UVs so UVS are 2D coordinates on your 3D objects and then you have vertex colors which is a color that you can assign to every vertex in the mesh then you also have tangent so if if this is a Surface this isn't normal but this is a tangent so it's sort of laying down flat on the surface all right and then there's other there are quite quite a few other things in a mesh as well you have buying poses you have skin weights and so on so that's all most of it is related to animation and skinning but we're not going to take a look at that so here's a very simple mesh this is a quad so you have two triangles in this one so we're going to take a look at how we just create that in code so we have four vertices but since this is a coders guide we should start at zero because it's indexed um so that's all four vertices and if we wanted to find that in code we simply make an array of vector three so we have a vector three array we give it the coordinates we want for every vertex and that's about it so then we're done with the vertices then we also have normals in this case it's quite trivial to define the normals all of them are pointing upwards so we can just make a make an array like this an array of vector threes and all of them are pointing upwards you could also type vector3. up in that case by the way all right so then we'll have UVS let's say we have a texture and we want to map this to our objects then we need to write 2D coordinates on this mesh to Define what part of the texture should go where on our 3D object so we need to explicitly Define that so the texture coordinates are generally from zero to one unless you want to tile it or stretch it so if you you can then just write those coordinates to every vertex so if we look at the top bottom left corner we have zero zero and then a top right corner we have one one so then we just write those coordinates into these vertices so have one there and one zero there and so on so then we just type these coordinates into it as a vector 2 array like this so then you have the U coordinates here and you have the V coordinates here that's sort of like X and Y so then we have triangles triangles is probably the most tricky one to get right because you need to really understand how they work and how they're defined in code because otherwise you're going to end up with like a tangly mess of shards sticking everywhere which looks pretty cool but it's not really useful and it's really hard to debug so if you want to create triangles you first need to think how should the triangles be defined and in this case it's quite trivial we just need to split it into two triangles so in this case you have a red triangle and a blue triangle so then you need to pick a point that you started so let's just pick a point of zero and then we go to a second point so we go from zero to two and then we go from two to three and then that implicitly tells us that there's going to be a triangle between 0 2 and 3. so that's how you define triangles so the triangles array for the mesh looks like this so you have an integer array that defines the triangle structure of the mesh where they are in sets of three so the first three integers Define the first triangle in the mesh and then the next pair or set of three indices are is a second triangle all right so that's very straightforward so then when you want to apply to your objects you first need to get your mesh filter component of the object so you just access it and if the mesh filter.shared mesh is null you create a new mesh and then you just grab the mesh and then you can keep moving on from there so you can get you need to Define your vertices so normals UVS and all the data you need to need to or want to Define and then you can run mesh.clear and then you just assign them to the mesh so mesh dot vertices equals vertices that's all the positions mesh.normal SQL to normal through all the normal directions and the same for the UVs and you also do the same for triangles and that's about it so if you run this code in unity it's going to generate that quad and that's about it I would also say that this depends on how you want to structure your game but in general so try to make sure that each object has their own unique mesh so if you do get component meshfilter dot shared mesh you can assign it a new mesh but if you just read from the existing mesh there's a risk that you're altering the original which may affect other objects so if you have a piece of mesh you're generating and then you duplicate that object they're both going to refer to the same mesh so then you're going to at least just edit just one asset so you often want to make sure that you have unique meshes and there's a bit of a shortcut you can do I wrote a little script for this so you can inherit from this class in order to make things a bit more straightforward so this is I'm just calling it unique mesh so if you if you extend from this on your Model Behavior you can then just type mesh and you will access that mesh and it will ensure that it's Unique and it will ensure that it's Unique even if you duplicate the object in the editor so if it if the owner of the mesh changes it creates a new mesh and you can keep altering that um yeah but I'm not going to go into the details of this one so um before we move on I want to go through quite an important thing we should probably ask ourselves some deep questions about space so what is space well we have these axes in unity we have x y and z they're all pointing in three different directions and they're always red green and blue and it's always the same thing you have a red X a green y a blue Zed and so on so they're always in the same orders RGB it's quite good to remember that but it's so here's another tool you can use so you can use your hand to Aid thinking about space so if you use the left hand it's important that it's your left hand by the way you can just hold your fingers like this and you can imagine your fingers being the axes so if I just color them you can see that we have a red green and blue axis and I can just overlay this one so if you're dealing with meshes it's very useful to use this tool to to think about how everything is going to be oriented and stuff like that so yep another one is this if you do like a thumbs up with your left hand you the the direction your thumb is pointing that is the axis of rotation if you're doing a rotation and the directions your fingers of the rest of your fingers are pointing that's the positive direction of rotation so if you want to do a rotation on something in unity then you can use your left hand to you know figure out which direction is positive so these are very handy tools I would say sorry but there are like this this is not like applicable for everything like if you if you try to use this in Maya you will probably be very confused so this is because unity's coordinate system is Left-Handed where Y is up like this is not the same everywhere you can have like four different sets of coordinate system at least these are the common ones and it's not the same across applications so you have Unity is Left-Handed with Y up but unreal is Left-Handed with Z up and source and cryengine is right-handed with Z up then have Maya which is right-handed wire up C brush is Left-Handed wire up Max is right-handed Z up blender is the same and then you have DirectX which defaults to Y up left-handed and open DL defaults the right-handed yup so this is a bit of a mess um but we can deal with it in any case let's do a mini quiz so how many triangles does this Cube have I'll give you a few seconds to think of that just the mesh asset not with occlusion all right so so how many of you would say that this Cube has 12 faces or triangles right good so a cube has six faces and each face has two triangles so that gives us 6 by 12 so that's 12 and that gives us 12 triangles all right another one how many vertices does this cube have and again just give a few seconds to think about that all right raise your hand if you would say that this has eight all right and raise your hand if you would say 24. okay good um so you're both right in your own way but those of you who said 24 are a bit more right uh so you can think of it like this you have four vertices at the top you have four vertices at the bottom so then that should give us eight vertices in total right but that makes sense mathematically but not technically so we can say that this has eight corners and we have three normals per corner so that gives us eight by three which is 24. so that's 24 vertices so this is quite a crucial thing to know when you're dealing with mesh generation so that's technically how many vertices it has so this is actually unity's default Cube if you look at the inspector of the cube you will see that it actually has 24 vertices so yeah that's the three different normals we have at the corner so the crucial thing here is that you should probably think about vertices in a bit different way than you might intuitively do so thinking about vertices try to think of them as a container of data so a Vertex can only have a single position a single normal a single tangent and so on so you can see the vertex is sort of like a the type and then all it has properties like position UV normal tangents and so on so that's you you should think about this when you're generating meshes so the crucial thing is that a Vertex has to split into multiple ones if any of the data above needs to be unique for any of the connected triangles so what that means is that if you have hard edges in your mesh it creates additional vertices which which we solve in Cube because you have multiple normals at the same point so by the same logic if you have UV seams that also create additional vertices because you need to have multiple UVS at the same point all right so that's a bit about space in general and meshes so we're not going to take a look at busier's planes and more specifically cubic ones so these splines are the most probably the most common I would say if you're using things like Adobe Photoshop illustrator or anything using splines they're almost always cubic per serious points so that's where you have a starting point you have an end point and then you have two tangents attached to those points and then you can drag those around to reshape the spline so we're going to take a look at how you would write code for a spline like that and we're also going to take a look at how the splines actually work so here you can see a quick visualization of the spline so we can drag these points around you can see that it reshapes according to depending on where these points are so we're going to take a look at how we can construct this from code so the first thing we're going to do is to make a line between P1 and P2 so we have four points we have P0 P1 P2 and P3 now let's see right so we're going to first enable some points here so if I drag this slider you can see that there are now three points we have a b and c and if I as I drag this slider that is a t slider it's just a value between zero and one it's sort of like a percentage value so as I drag this slider you can see that these points move between their their respective points so even if I make this one shorter you can see that c the last Point here is also moving um and reaching its destination at the same time so all of these are all consistent with each other they don't have the same speed but they always have the same percentage value all right so the next thing you can do is to draw a line between these points as well so now we have a line between A and B and B and C and these lines follow along as you drag this slider as well and then we can enable some more points here we have a point d and e added to those two lines and these points follow the same rules you only drag this slider and those points also begin at their starting point and then lurp towards their ending points so that's the way they move to the ending point and then you can enable the final line here and then you have a line between d and e and again it follows along with those two points and then finally we can enable this so this is sort of the and the final point in the entire calculation so now we can see that this big Point here it starts at P0 and then it curves up here and ends up at P3 so that point is actually what's tracing the busier curve so the code to do this is quite straightforward because all of this is essentially a lerp so just to get a point in the spline we make a function called get points and we pass it along a points array which is the three the Four Points we had in the cubic Basir curve which is P0 P1 P2 P3 and then a percentage value for where we want to get the points so it between 0 and 1. so we first get a that's a lerp between P0 P1 based on the T value the same for B but it's between P1 and P2 same for C but with P2 and P3 and then we do the same but with a b and c so we learn between a and b and we'll learn between B and C and then we'll learn between d and e so it's just a bunch of lurps to do this so that's pretty straightforward but we should be asking ourselves at this point because we're doing something that we want to do for every point along the way when we're generating a mesh so this is going to be quite performance heavy so can we optimize this so before we start trying to optimize this we should we should we should probably ask ourselves some deep questions about what is a lerp well you have a b and t that's a start starting value and end value and a percentage if T equals zero we should return a if T equals one we should return B if T is 0.5 it should be the average of those two points so this is essentially a weighted sum so the code for alert is very straightforward you do 1 minus t multiplied by a and then you add a t multiplied by B and that's it so that's how you write the lurp mathematically all right so this is the code version and this is the math version and of course that's usually with math you can move these terms around however you like depending on how you want to use it we're mostly going to use the second one here all right so the code version and the math version so here we have the entire everything you need to do to calculate the cubic bissier curve in math so now we just need to simplify this all right so we just move all of these terms away and then we just insert all of them into one huge equation like this and then whoops like this so this is a very pretty equation so then of course you should probably you know expand this so you get this instead um and at this point you can probably see that you can start isolating some of these terms so if you do that you end up getting this and this looks quite clean like you have P0 P1 P2 and P3 entirely separate and then they are all multiplied by a an equation and then you add all of those together and turns out if you plot this on a graph you oh you can simplify them by the way to this looks a bit cleaner so if you plot these to a graph you get this and these are the point basis functions for every point in this Messier curve and it turns out some mathematician did this years ago so this is not new unfortunately I wish it was um but yeah so so these are apparently called The Bernstein polynomial functions I believe but this this is just four of those so there are plenty of them so this is specifically for the cubic researcher curve all right and that's all fine so this would be a more optimized version of get point you can probably optimize this further by the way but this is just how far I wanted to go for this talk all right so you might also want to get this so this is the tangent of the Curve and the tangent of the curve is the direction it's going for some point in the Curve so if we go back to this one you can see that we pretty much already have the tangent the tangent is between d and e and if I move this slider the D and E line is always tangent to the point so if we want to get the tangent we can simply do e minus D and then normalize that but again since we started optimizing it earlier we can do the same thing here so if we look at the math for E minus D we get this and then we can expand that as well and then we end up with this and again you have the points multiplied by some equation and it's rather clean and we can also plot that on the graph as well and I haven't seen these before so I was quite happy when I saw this but um so so that's sort of the tangent basis functions rather than the points so then we can do the same thing here for the tangent we use these functions instead of just doing a bunch of lurps in the normalizing it so yeah that's just the code for that all right so we have the tangent but that's only one direction we have the Z direction of a point but there are two other directions we might want to take a look at and one of them is the normal so how do we get the normal from the tangent or in general how do we get the normal with this data so we get normal function is a bit more complex if you do a 2d game like a little big planet where you have 3D geometry but 2D gameplay it's quite straightforward so if you do that you can simply do get tangent which is the function we'll look at earlier and then you rotate it by 90 degrees and then that's it so in that case it's very straightforward how do you get the normal but the 3D case is much more complex so with the 3D version you need to have a reference vector so the problem here I'm gonna use my handy tool again so if you have the Gizmo here for all of the different directions and this is the tangent and this one is moving along the Curve the problem is that this direction can be different even though the tangent is the same so we can rotate it like this with the tangents being the same so which should it be should should the normal point this way or this way or down or how do we want to orient this so the way you generally do that is you have a reference Vector for what is the upper Direction so this is likely what city skylines did because in skylines the cars are always driving on top of the roads and you can't have like inverted roads or anything so even if you have a slope like this if it's moving forward and then it's going up for a slope if you have a reference Vector you use the upper Vector in general so if you have the up Vector you do the cross product between the tangent and the up Vector so that's between this one and the upper vector and then you get the binormal which is pointing towards you so that's my thumb and then you do the cross product between the binormal and the tangent and then you get the normal so yeah so that's how you would get the normal in 3D and the code for that looks like this all right so it's a bit more complex but it's quite straightforward once you once you know the math behind it all right so that also led us to the binormal so now we have x y and z but we might also want to represent this as a quaternion rather than three different directions and that's also super straightforward so we can make a function called get orientation so we get to orientation 2D would be like this we have a tangent and then we get the normal then we do a quaternion.look rotation with a tangent that are normal and it's actually the same for the 3D so you just do a look rotation and it's done so now we sort of talked about the the basics of how you make a spline and the basics of how meshes work so now we're going to take a look at how to generate the actual geometry in this plane so we should probably first take a look at the 2D shape so if you look here the small shapes beneath between or next to these splines that's the shape we want to extrude along the path so let's say we want to extrude this one this is sort of like a skateboard ramp it has some a very shallow skateboard ramp with a flat top so we want to extrude it in this direction so then we need to look at the 2D shape and we need to Define that in code as well so we need to then analyze how it's how it looks and how all the different parts of the geometry works together so you have a soft edge here and you have a soft edge here then you have a hard edge here then I have a soft Edge or not really an edge but and so on so you have a Vertex here that's vertex zero you have one and two here because again it's a hard Edge you have two normals so you need to have two points so you have zero one and two and then three four and five so that's the six points in this shape so that will be our vertices and in this case since it's a 2d shape we just do a vector 2 array of vertices and then we'll make a vector two array of normal since we have 2D normals in this case two and then we have use since we're not really UV mapping this we need a u array for just the U coordinate so I'm calling it use kind of a bad name but it's sort of descriptive and then we have lines instead of triangles so we make a new integer array and it in this this time it's going to be in pairs rather than three in every part so we have zero and one for a line between 0 and 1 we have a line between two and three and a line between three and four and four and five so this is sort of like a 2d version of a mesh all right so then we want to extrude it along a spline so we'll get a result sort of like this so this is our extrude shape or our 2D shape and then we have a cubic besser 3D that's just the name of the class I used and so so the next step you're going to want to do is to convert your cubic basier 3D into a set of oriented points so that's a location and an orientation sort of like transforms but it's a separate class um so you can then use those oriented points and the extrude shape to generate the final mesh so yeah just an array of oriented points and the oriented point class is super simple it's just a position a rotation and a Constructor and then a few functions to convert between local and World space so that's yeah that's about it so when we want to generate the mesh we need to have some terminology rights so we we're going to want to look at the segments so that's every segment of this mesh that's sort of the groups of quads that you generate between each Edge Loop and these are all the edge loops and then you have the vertex counts and remember the vertex count is double for every hard Edge and then you have the triangle count so we need to to Define all of this in code so let's say we want to make an extrude function where we pass along a mesh an extrude shape and an oriented point so the oriented point is the path it's going to take over the oriented Point array by the way not just a single one so the number of vertices in the shape is shape dot vertices.length the amount of vertices and then we have segments which is path dot length minus one we have Edge Loops which is path.length the vertex count is the number of vertices in the 2D shape multiplied by the number of edge loops then the triangle count is shape dot lines dot length that's the amount of line indices we have and then we'll multiply that by the number of segments we have and then we have the triangle index count which is the triangle count multiplied by three because we need three integers for every triangle all right so then we need to Define arrays for all of these we'll make a triangle indices array we make a vertex vertices array normals array UV array and so on and then the mesh generation code goes here and my talk is not finished here by the way so then you do mesh.clear and you assign all the stuff you want to assign to your mesh all right so the mesh generation code I'm just going to talk a bit about pseudo code for how you do that so in this case we do a for Loop so for each oriented point in the path we're going to extrude along and for each vertex in the 2D shape we add the vertex position based on the oriented points so we use the local to World Transformations there then we add the normal Direction in the very same way and then we'll add the UV so this is where it gets a bit messy so U is based on the 2D shape because we had our U array but V is a bit more tricky but a very simple straightforward way you can do that is simply make V be based on the distance along the path so you can use the same T value as you had when you made the busier spline and then you end those two for Loops so that's enough to Define all of the points normals and so on and then to define the triangles you you make another for Loop so you do for each segment and for each line in the 2D shape so the line is the just the line connections in the 2D shape you add two triangles with vertex indices based on the line indices and then you end those two loops and that's it that's the code you need to generate a mesh um so the actual code looks like this so this is not the pseudocode this is the code that's running and actually works um but yeah if you want this it's going to be on YouTube later all right so that's actually enough to generate this so you can generate something like this where you have a 2d shape and you extrude it along a spline and then you get this however there is a problem here if you look closely you might see that one of them is actually kind of stretched so if we place three of these you can easily see the problem here so the one at the in the top right is very stretched compared to the one in the bottom left that's simply because we we base the texture coordinate values based on how far it has come along the spline in the sense of the T value so we have 0 0.5 and 1 as UV coordinates here or V coordinates and the same goes for the other ones so we're going to get stretching so this is caused by incorrect V coordinates and we need to compensate by the length of this plane so then we can simply multiply the V coordinate by the length of it so then we end up with this which is also incorrect um so you can see that now they're very tightly packed but they're at least consistent among each other they have the same tight packing so what we need to do next is to look at the u-span I'm not entirely sure what to call it but new spine is probably the most descriptive so the u-span is sort of the same thing as the total length of the lines you have in the 2D shape it could be different so it depends on how the mesh is viewed I guess um yeah so if you if you then divide by the use band you end up with this so now they're all consistent it looks nice and they have the same spacing even though they have a different length but that's actually not quite enough either so here's another problem that you tend to bump into with vessier curves especially if you're dealing with animation along busier curves so if you look at this one you can you can probably see that the texture stretching is quite it's a lot more stretched in the far right than it is in the bottom left and that's simply because the stretching of this depends on the length of the tangent so an important thing to note is that t is not the same thing as a percentage of distance along the spline and this is crucial to have correct V coordinates because T moves faster the longer the distance between the control points so again if we look at this one if I make it really short between P2 and P3 and I drag this slider with a roughly uniform speed I'm trying and you can see that it moves a lot slower by the end and if I enable curve spacing here you can see that the distance between the points here they're much shorter than they are here and this is all based on the T value so if I increment the T value by something like 0.1 for every step the distance between those are going to be different so we need to compensate for that in some way so one way to do that is to create a lookup table containing the cumulative Point distances so the code for that would be something like this where you have where you calculate a length table information where you pass it an array that is already set to the size you want it in and then you pass along the cubic wester3d class and then what you do is that you Loop through every point in the bezier curve that you want to have so you just sample the bezier curve at some T value then keep incrementing it like that and for every point you calculate the distance with you increment it for for everyone so it gets bigger and bigger so that means that the final Point has a value that is the same as a distance along the whole spline where the first one is just zero and after you created this lookup table you should then sample the length array at T and if in order to sample an array of floats we need to to make a sample extension method for float so the problem here is that we we have an array so we have a bunch of elements here and they all have a bunch of float values it's two 1.5010.5 for instance and then we have the T values here so the T value is the percentage for how far along this array you want to be and then we can just plot these points here so that's every point in the array for the the float value it has and let's say we want to get the value where T is 0.65 so that's between two points right here so just a simple way of doing that is to blend between the points so you blend between the nearest neighbors and then you get the value there and you can you can probably use like by cubic interpolation here if you like but I'm not doing this for this demo all right so you get that points so the extension method looks like this so it's an a float array extension method where you can just if you have a float array you can just type dot sample and then you can just input the T value you want to have and so then you get an interpolated value in that flow three and this is the code for that and again it's going to be on YouTube later all right so instead of multiplying by length we then sample the length array at T so here's the before and here's after so before after before after so now it's much more uniform there's no stretching by the end and everything looks nice and pretty and that's about it actually so some final notes updating mesh colliders is expensive so try to not update mesh colliders every frame so if you have a level editor where you can shape roads or something like that try to not update the member frame just do it when you need to do that and you can also use separate simplified and smooth Collision geometry so you have a much more simpler Collision geometry which is faster to process reading method of vertices and mesh dot triangles is extremely expensive so if you if you want to alter a mesh and you have a for Loop where you sample the mesh.vertices array inside the loop it's going to be extremely slow so you just want to sample it once get all the vertices and then you work on that that set that you got from it already so this whole thing or the whole spline generation thing can be quite easily modified to be used as a deformer rather than a mesh generator so you can use that for titling things like railroad fences roller coaster tracks and so on all right so that's actually about it so are there any questions you can walk up to the microphones by the way there are some in these alleys here so if you have a question you can just go there no actually actually I can actually have the the code here if you want to see how it works so if I move these around you can see that the shape of the the track is changing as well and this is the the one that is not length compensated by the way so you can see that we get a lot of stretching as I move this so like right now it's super stretched but if I go to this one and do length compensation so this is the one that's compensated about the length of the whole spline so even if I change the tangent here you can see that it's it sort of gets stretching here anyway and it's very compressed here but in total it's quite compensated for how long it is and then finally I can enable the Delta compensated one that looks like this so this is the one that has proper uniform spacing across the whole thing so this one looks quite nice when you move it around and there are of course other issues you might run into if you want to have like banking curves so one quite common example if if you have it upside down if you want to make like a half Loop but yeah you can also make compensation code to fix those problems as you tend to run into if you do that all right so question so curve with the center of the mesh geometry but in the demo you did before you did it on the edge the curve was on the edge of the geometry what are the differences is there a benefit to one or the other not really it just depends on how you want to shape the the final mesh that you want to achieve like uh like if you're doing like the pivots of the 2D shape can be anywhere it doesn't really matter um so the code that we we've gone through just works for pretty much any 2D shape but I might if you're doing like a roller coaster track you're probably going to want to have the pivot of the track where the Riders of the carts are sitting because in that case if you rotate it it's going to go in a spiral shape rather than like just being linear so that way it's going to like sort of compensate for all the torque that the user is going to feel when they ride the roller coasters so but again it just depends on what you're going to use it for thanks for the talk another question um actually I can see the answer to my question now that you turn on the mesh so the they're not compensating on the uh for the mesh for the mesh geometry um I guess it's by Design so you have more you have more detail at the point of curve yeah you can use the same technique to compensate for the mesh um for the mesh generation as well but yeah like you said in this case it actually makes more sense to have it more dense wherever it's very curved but you could actually make proper code that checks for curvature rather than just naively doing the mesh generation but thank you another question hi uh I just wanted your general thoughts on uh two issues that I see with this one is um the stitching of pieces together to form a larger track and the second is uh pieces backing up onto previously past tracks uh what was the second part okay let's say if you uh turn back around on the track and you intersect a previous piece yeah um just general a general approach to maybe addressing those two issues when generating a larger track yeah um well if you generate a larger chart you should probably of course if you so if I move this point you can see that it's wait this one is not complicated hold on uh there we go so so the the UV coordinates for the final Point here is actually moving and changing I presume that you mean that if you want to tile an entire track you should then make sure that this UV coordinate is consistent so in that case you the way you do that is that you pass the information from this curve to the next plane and tells you to start at this UV offset so you keep passing along UV offsets along the track but of course it's it's somewhat impossible to make it tile perfectly around an entire track uh so what you can also do is that if you have a piece like this you might want to round the UV coordinates a bit so that you squash the entire textures to make sure that it Tiles at the start point on the end point so that way it's going to tile completely around the whole track the problem with that is is that you're going to get like tiny stretching here and there and it's going to be either stretched or compressed because it's going to like approximate the the proper stretching um yeah did that answer your question yes thanks and also um pieces going back in and intersecting of other pieces let's say because that the user just create whatever they want or if it gets procedurally generated um using something that's random yeah what kind of strategies would you suggest in terms of preventing track from you know you know like hitting the pieces of track yeah yeah um I'm actually not sure I guess it depends on the game and how complex the shape is I guess you could Ray cost along the entire spline and then just see if it hits another road or you could have some sort of like bounding box or voxel based method where you try to see where all the other tracks are placed and whether or not it's intersecting one of those thank you yeah another question uh so we actually have a game called cosmonauts which is based on this kind of thing it's not bezier curves it's just a a spline um and uh so the two biggest problems that we ran into were a performance in generating the actual mesh so like that the time it takes to copy the vertex data the GPU and trying to spread that load out over multiple frames as opposed to just single frame and LEDs in general and when I've talked to people with LEDs they're like yeah long skinny objects are tough for LEDs and I just wondered if you had any thoughts on like a just doing this stuff in real time and performance concerns and B how you'd LOD some something like this uh well doing lods is quite straightforward I guess you could just compensate for the length of the curve I'm just like assuming that it's a very very long you know like you're gonna have a piece of it way out the distance and pieces go up close yeah so we ended up breaking it up into lots of little chunks oh like a like a dynamic LOD on one spline exactly yeah I would not have it be one spline in that case right like just try to work around that problem because it's going to be quite tricky to make its LOD on a single spline I'm not entirely sure how you would do that properly it sounds hard um what was the other question did you see the question was like um sort of how to spread out the load you know there's a single blocking call to say copy the array to the the geometry so copy and geometry and like if you had any advice about like good ways to um partition the spline into smaller objects or like if you can update like half of the mesh at a time or something like that um yeah you could definitely update half of the mesh along the way uh I guess the most important part at least if you have meshes with a consistent vertex count is to just cache the vertex array like never accessing mesh dot vertices because that's again it's super expensive so if you would have all of the data of the mesh on the c-sharp side yeah you can then just apply it to the mesh so that's much faster than having to re-read them every frame okay so I guess that would be my advice thanks yeah yeah yes about your final notes you said yeah I agree that you doing mesh based Collision uh Mass space collider always be expensive yeah I was trying to work on a better way to do to handle that and when he said separating can you give an example like what do you mean by that like separating the the collider so it can be efficient um so basically what I mean by that is that if we go somewhere here here so basically if you want to make a mesh quality for this one you could in theory just have a 2d shape for that as well but you remove this vertex and you remove this vertex and then you keep this one and this one and this one and you merge this one so that it's no longer a hard Edge so then you have a simplified geometry for for that shape and then you use the very same code you use for extruding the visual shape you extrude the collider as well and then you assign that separately and you can also have the the extruded one for the collider be less detailed than the one for the visuals as well but again it of course depends on the game and so on but if you if you have a game like serious guidelines you don't really need accurate Collision because I'm guessing the cars are not physically simulated they're rather animated along a track so if you have Siri skylines the collider is most likely only going to be used for like cursor interaction and shaking collision between roads when you're placing them so that can be quite much simpler than the Collision mesh you would have for a like a racing game for instance everyone uh hi uh first of all I want to thank you for this wonderful talk and thank you Shader Forge it's awesome and my question is basically on behalf of those who might struggle with math works out might be a while since school days and it might be rusty so what gonna be your personal recommendation on how to refresh it maybe learn it for those how to what how to learn the mass because like you show a couple of stuff like yes like we're gonna just re uh rotate this Vector for 90 degrees just uh swiping the chords and change your sign and basically you need to know that which Matrix should apply so for those who find it not clear what would you recommend as a learning material maybe books links courses how would you what would you comment for someone who want to learn this Mass I'm not entirely sure I understand the question my name Alex I'm sorry I'm not an native speaker oh the math what uh what resources would you suggest to learn the also required mathematics for like like what math do you need to know to make games in general or no no no no just for this one just for this one yes oh books oh uh I would recommend not reading any books uh that sounds like an awful suggestion um I would I would rather recommend just making things like try to do Vector stuff in general uh like come up with small experiments see if you can figure out how I like like let's say you wanted to have a system where an object falls and it hits the ground and you want to make that the sound is louder if it hits a surface like this rather than this so that's a classic way that you can like try to think of how the Dodge product works works for instance like you you don't really need that much math you need like you need to understand vectors you need to understand dot product cross product and that's sort of about it if you're going to do like mostly for positioning and stuff like that so that's yeah it's mostly that I would thank you yeah yes awesome yeah thank you um this was absolutely one of the talks that I started I was like must go to this talk in this conference and you're absolutely delivered the uh thank you part of the bezier uh freaking mind-blowing thank you cool thanks um so you mentioned how uh get vertices is um expensive uh so I guess when you uh generate um procedurally you would instead of um giving them to Unity and getting back all the coordinates you would save them as an array of of vector threes um I guess what is is that how what other optimizations uh do you think that that um there are to uh kind of work with your coordinates after you you've uh render them well I would just again you I would keep copies of all the data of the mesh on the c-sharp side so as long as you have if you have your vector three array on the c-sharp side you can just edit that array and then reapply it to the mesh rather than just reading it back from the mesh so that of course gets a bit trickier if you're having a very Dynamic mesh where you want to change the number of vertices all the time but you could actually in theory you can have a mesh that has more vertices than you can you actually use with the triangles so you can cache even more vertices in that mesh but of course there's some processing overhead for that as well but if you have a if you have a max amount of vertices you can then always keep that on the c-sharp side that would probably make things a lot faster it's beautiful thank you yeah thank you yes hi um what uh my question refers to your um your length lookup table yep and I was just curious you were showing an example where you had four points in that array and then you were sampling it in between two points and you were obviously when you were following your bezier curve you were sampling at some point in between then have you found a sweet spot with the number of points in a museum curve that you should add to a lookup table like is six points for every busier curve normally enough to get you uh decent results that's it it's sort of like I actually tried to make that as a tweakable value in the inspector but I could drag the detail level of that of that um and you can actually be quite slow on it and still have it look nice but like if you sit there tweaking it you're like oh but it looks so much better I want to make it super high but like if you you never need to go more accurate than the amount of oriented points you sample in the spline like you never need to go above that it doesn't matter but you can keep it like quite low I mean you can probably do with half of it and it looks just fine um yes hi there um my question goes around creating uh huge measures yeah okay with this technique and uh when we import any asset any 3D model into Unity there's a number of optimization that they do things like if you go up over 65 000 vertices on your mesh then they subdivided and stuff like that do you have any ideas of uh of what kind of immunizations that you need to do because uh the mesh object wanted won't allow some things but we allow other things to when you create that program actually uh well the one of the advantages you have with the procedural mesh is that you can set the quality level yourself so in I made a game earlier called flowstorm where you could create like a like a 2d shape but it was a 3D mesh which was a spline that you could drive on and in that case I could in the level editor add restrictions that would disallow the user from making that very long so I would either limit the user in that sense or a more proper solution would be to just split the mesh up into multiple meshes so it's I'm guessing it's quite straightforward to do that especially if it's procedural but there's not really any way around that limit so you're gonna have to do some some solution like that where you just chop it up into multiple meshes yeah if can you still use things like bezier curves into a four-dimensional uh representational space and they're not limited to any like any Dimension you can use it in any Dimension and the cubic Messier curve uh with that we looked at with all the lurps like we had you can see that we have a bunch of lines here we can add another point to this specific curve and we would just have one more layer of lerps that we can do and again the data that we're lurp in between doesn't really matter it can we can we can learn between Vector Force we can slurp between quaternions you could you could learn between colors and so on so you can use bezier curves for pretty much any data like the only thing you need is that the data needs to be you need to be able to multiply it and add it and that's it yeah yes good question hello I have a question about uh doing this on the flight is this is this fast enough to do it while you're like writing down a racetrack and it'll compute your generate in front of you yes you can do that but the problem is again Collision so I would I would recommend splitting up Collision into smaller meshes and sorry for interrupting your question you can keep that's okay I was just like kind of I didn't yeah I thought if you're generating mesh collider I don't know if you can extend the mesh collider or I've never really done uh Dynamic mesh colliders or extending them or creating them while you're going I don't know if you run into any issues like uh when you're doing that uh no it's the only issues I run into is that there's a lot of performance issues when you're having uh when you're having large mesh colliders generated that's there's a big performance hit when you apply the mesh collider so I would I would split it up into multiple mesh colliders but that has another problem which is really frustrating so the game I worked on had you could like drive around a spaceship where you could slide on bestier curves uh so it was a 2d game where you could slide on on things um so sort of like a Super Meat Boy racing game in a way um and the problem I ran into is that if you have a curve going like this and then another curve here and these are two separate mesh colliders the the spaceship you could drive a slide on this curve with it it got a hiccup here in the edge between the two colliders so we're just sometimes trip and fall over and explode so I don't know if there's a good solution to that the the semi solution I did was to make the mesh collider super simple just a like an extruded line a single line that I extrude for the actual track that you drive on so then I could make that very long and like span several curves so if you have a racing game you would then for the racetrack you would expand that to be across multiple visitors as far as possible so then you have less Collision issues yeah sort of a tangent but yeah yeah all right thanks yeah no problem yes uh your editor script for managing the bezier curves um do you have like a custom component for handling the drag points there or do you just have a whole bunch of game objects that you drag around um I did want to make a lot of gizmos but the problem is that it's quite tricky to make a custom transform guest mode that you can switch between rotate and scale and move so I just ended up making game objects here so yeah it's not bulletproof like if I duplicate this it gets a bit messy but yeah it's definitely possible to do that I just haven't bothered right now but I just move around this object I read the tangent by the scale of this object so if I rotate it it moves along like this um yeah so it's quite a quite a simple setup yes have you um is this the same technique work if you're um trying to do something with the path branches like for example the ramps that were going onto the freeway could you say that again in the mic oh yeah sorry does this technique work also if you uh want your path to Branch like for example those on-ramps that were going onto the freeway in the uh cities say example uh like uh I guess your question is like how do you handle intersections between yeah yeah um there's yeah that's it's a bit trickier to do that because you can't really intersect at any angle so if you have a piece of road here and you have an intersection here this one has to be oriented in a way that it meshes with the orientation of this one but since you have the 2D shape you could in theory make another tool that generates an intersection using that 2D shape so it would be quite straightforward to make something like that as well so I guess the the one limitation would be that they have to be planar with each other where they intersect um yeah um all right so we're running out of time so that's it and thank you very much [Applause]
Info
Channel: Unity
Views: 179,746
Rating: undefined out of 5
Keywords: Unity (Software), Video Game (Industry), Video Game Development, Unite 2015 Boston, Geometry (Field Of Study)
Id: o9RK6O2kOKo
Channel Id: undefined
Length: 59min 37sec (3577 seconds)
Published: Wed Oct 21 2015
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.