3d Hex Grid Outline Shader! Part 2

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey guys i hope you're doing well this tutorial will be part two of my hexagon tutorial series if you haven't followed along with part one go do that now in this video we'll build the shader needed to outline an arbitrary amount of hexes the project will be available for you to download for free on my github a link to my github repo will be in the description down below before we dive in remember to comment like subscribe and ring the notification bell to be notified of when new tutorials go live the first thing i want to do is change the hex colors so we can clearly see where one hex ends and the other begins while they're all at the same height so for now i'll just randomize the color between black and white for each hexagon so open up the hex spawner script from the last tutorial i'll write a quick method that takes a mesh render as a parameter first randomize the value between 0 and 1 to be our shade between black and white create a new material instance that is a copy of the shared material we don't want every hex to be the same color set the base color of this new material to be our randomized shade then assign the material instance to the mesh renderer finally call the method and pass in the mesh renderer from the new hex respawn the hexes awesome looks great time to create the shader create a new pbr shadow graph and open it up i'm going to change the preview to our hex mesh so we can see what's happening to highlight the edge we must first know where the edge is the origin of our hexagon is the center of the bottom face so the center of the top face is the up vector times the height of the hexagon this works because our hexagon is exactly one unit tall so multiplying by point five makes it half height and by two makes it double height we can find a point along our edge by simply multiplying the direction to the edge by the apothem that we calculated before so let's execute this and shade a graph first we need to calculate our apothem so add a radius exposed property remember our formula from last video and recreate it and shade a graph the square root of the radius squared minus one half the radius squared then turn it into a sub graph to keep it clean be sure to name and hook up your output values so let's create a subgraph to calculate our edge it's going to output a 1 for all points along the edge and a 0 for everything else add our inputs as properties up defaults to 0 1 0 direction to edge default to 0 0 1 height default to 1 apothem well a rough estimation of 0.85 is fine here for visualizing we'll feed it in later and thickness default to 0.1 so the height times the up vector plus the apothem times the direction vector will give us the point at the middle of that edge now we don't much care exactly where that point is but more where any given point on the hex is in relation to it so take the object position minus that point on the edge let's look at some math the world up vector cross the direction to the point on edge gives us l l normalized is ln because vectors don't care where they are in space the vector that points left from p our point on edge is the same vector as ln if i is any given position on the hex then i minus p gives us some vector a this is what we just did in shaded graph if we project a on ln we get a1 you can see here that both of these a1 vectors are equal but that's not quite what we want as we need the distance to the edge so a minus a1 gives us the vector a2 this is called rejection you can see here that both of these a2 vectors are equal now we know that the magnitude of a2 is the distance to the edge to calculate the magnitude we would take a2 dot a2 and square root it because any vector a dot itself gives us the magnitude squared of that vector because it's cheaper to square something than to square root something we can instead square our thickness before comparing it to the magnitude of a2 squared so if thickness squared is greater than the magnitude of a2 squared then we know that the point is on the edge essentially we're calculating a cylinder whose radius is thickness and it lies along the edge and then we're checking to see if we're within that cylinder back to shader graph first we need to take up cross direction normalized this is ln then reject a that we calculated before from ln and take the dot product of itself and square the thickness let's compare the two values branchlessly subtract the two take the sign which is either a negative 1 when the value is negative a 0 when they're equal or a 1 when they are positive we only care about when they're positive so take the max of that and 0 to turn any negative ones into zeros i'll put that to our output node and there we go you can see the cylinder drawn at the edge this branch list greater than is actually really helpful so i'm going to turn it into a sub graph as well and go ahead and clean up those notes now we have a sub graph of the edge but we don't want to display these side arcs so let's take the normals into account we don't want to cancel out the up normal or the direction to the edge just the side faces so let's start with up in our main graph add two new exposed properties a vector three up defaulted to zero one zero and a vector one epsilon this is how much wiggle room we will allow so this should be a really low value like 0.001 also create a normal vector node set to object mode this is the normal of any given point on the object first take the dot product of the normal vector and up then feed the epsilon node into a 1 minus then go ahead and reuse the greater than subgraph that we created a moment ago so if the dot product is greater than the wiggle room we paint in with a one else a zero plug it into the albedo awesome since this graph will tell us if any point is facing any normal turn it into a sub graph name the vector3 parameter normal save it as is normal facing clean up the graph and reconnect our properties now that we know the up face let's find the forward face create a vector3 node enter 0 0 1 reuse the is normal facing node that we just created but this time with the forward vector plug that into the albedo channel to see the edge face highlighted now we want to simply or gate these a branchless or gate for two values that are either one or zero is simple start by adding them together this will either be a zero if both were zero a one if either is a one and the other is a zero and a two if both are one since both being one is fine simply take the minimum of that and one turn this into a sub graph called or gate name your properties a and b and then clean up the graph now if we multiply this by our edge then each of the arcs get multiplied by zero to cancel out leaving only our edge highlighted so add in our edge sub graph this is a good time to connect up direction to edge and apothem and add the properties the edge subgraph needs the height and thickness are both vector ones height is defaulted to one while thickness is defaulted to 0.1 now that we have one edge finished we need to highlight the other five edges we can do this with some clever subgraphs let's turn this into a subgraph excluding the apothem the up face and the vector3 forward direction cleanup name the direction to edge apothem and is facing up one or zero properties name your output is on edge marking now because hexagons are mirrored we can calculate the opposite edges really easily and a new output is on opposite edge marking copy paste everything except for the direction to edge feed the direction to edge through a negate node then into the edge subgraph and the is normal facing node and feed it into the new output then save back in the main graph add the two outputs together and feed it into the albedo channel to see the two edges highlighted now that we have one set of edges let's do the diagonals just copy and paste this group of edges two times look up the apothem and is up facing to the respective slots and delete the direction to edge add all the results together and feed it into the albedo channel let's calculate the direction to 1 and 4 first make a new vector3 node feed the apothem into the x-axis and the radius times 0.5 into the z-axis then normalize it the last one is easy do the same thing but use negative 0.5 because we're adding these together the points are adding up to be more than one so just saturated now that we've highlighted all the edges let's add an easy way to turn them on and off add six exposed bool properties one for each edge zero through five be sure to set the reference fields let's start with zero and three add the branch node feed the edge zero bool into the predicate slot and is on edge marking which is a zero or a one to the true node leave the false slot at zero and then do the same for edge three and then replace the add connections do the same thing for edges one two four and five awesome now we can toggle each edge on and off since we randomized the color of our hexagons let's add that functionality in add two new exposed color properties base color and edge color multiply the output of the saturate node with the edge color and take the one minus with the saturate multiply it with the base color add the two together and outputs albedo and the shader portion of this is done now we just have to write a little bit of code to turn on the proper size before we get to the code let's add the shader to each hex on our hex material change the shader drop down to hex outline change the edge color and press spawn hexes dope we want to toggle the outline around a hex when we click on it so to make our lives easy let's use the new unity input system for this you can add it in the package manager doing so will restart your editor now let's add a select hex's mono behavior since we're detecting where the player clicked we need the camera to form array then write a public left click method that takes combat context from the new input system if callback context is anything other than performed return our raycast takes in a screen point two ray from the camera at the mouse position and an out raycast hit if for some reason the hit collider is no just return out get the hex component on the hit collider and call toggle select on it let's define the hex mono behavior it has a toggle select method as we just used if some bool selected is true called deselect else call select for now slack just sets selected to true and deselect sets it to false the selected property should be visible by other classes but only changeable within this class now is a good time to update our hex spawner script to use this new hex type change the prefab update the list change the instantiate and destroy the dot game objects instead you may have to re-serialize the list in the inspector later so remove the read-only attribute if you had it like i did respawn the hexes so they all have the new hex script add the select hexes and play your input components to the hex spawner object create the input actions i'm just going to bind the left mouse button for now assign it to the player input component and send a unit event to call the left click method on the select hexes component since the values we want to change are on the material we need a reference to the mesh renderer let's start with the deselect method unity shader bools are just special floats so loop 0 to less than 6 and use renderer.material.setfloat to set each edge bool to 0. a consistent naming scheme for your reference fields in the shader makes this really easy to make our lives easier for the rest of this tutorial i'm going to create an enum hex neighbor direction for each potential neighbor a hex can have it's important that this enum is in the same order as our edge numbers so 0 is up 1 is upright 2 is downright all the way until 5. we want to be able to loop over this enum easily so make a new script called enum array this is a static generic class where t enum is some struct enum create a private static read-only t-enum array called underscore values and another values that is not read only that returns underscore values then add a static constructor that sets underscore values by getting the values of the enum type which is a low level array type so cast that with a link to an ie numerable of type enum then again use link to convert it to an array back to the hex script we're going to need a reference to the hex spawner so let's just find that in awake and cache it we're also going to need to know what index this hex is so i'm going to create a custom serializable struct with row and column for this but a vector 2 would work as well if we want to be able to view this index in the inspector we must convert our hex script into a serialized mono behavior from odin inspector and add the odin serialize and read only attributes to it then write and then assign index method that takes in an index and sets the hex index property then in the hex spawner script call this assign index method on the new hex and pass in a new index create a method neighbors with direction that returns an i.e numerable of a tuple that contains the hex neighbor and the direction time to loop the direction enum for each direction in enum array of type hex neighbor direction dot values get the neighbor at this hex index in the current direction from the hex spawner then yield return a tuple containing the neighbor and the direction in the hex spawner script let's fill out the get neighbor at method first we need the offsets on the row and column from our current index write a get offset in direction method that switches on the direction and returns the proper offset tuple because of how we generated our hexes if the row is even we may need to use a different column offset so pass that bool in this switch is a bit much so take your time and copy it carefully then return the hex at that hex index plus the offset if that hex is in bounds else return a null to check if a hex is in bounds let's write a simple method on the hex grid struct that takes in a row and a column and returns true if both the row and column are in bounds we can do this mathematically by adding two properties max row and max column back in the hex script let's write the select method loop over every tuple returned by neighbors with direction if the neighbor is null meaning we're on the edge of the grid or if the neighbor is not selected we want to turn the edge in that direction on i write an update edge method that toggles the edge in that direction passed in we want to pass in either a 0 or a 1 for the value parameter so get the existing float subtract 1 take the absolute value of that then floor it to an integer i use an extension method for dot floor but you could just wrap it in a math f dot floor to end similar to the absolute value so if it was a zero we get a one if it was a one we get a zero now we might need to toggle the neighbor hex's edge as well so let's write an update neighbors method and call it at the end of both select and deselect loop over every tuple returned by neighbors with direction if the neighbor is not null and it is selected then call update edge and pass in the opposite direction i extend the hex neighbor direction enum with a dot opposite method cast the direction to an int ab 3 then mod 6 then cast it back to a hex neighbor direction and return it back into unity serialize the mesh renderer on the hex prefab save it and update the material so all of the edges are off by default then respawn the hexes adjust the camera so it's looking down at the grid remember to add a convex mesh collider and a kinematic rigid body to the hex prefab and respawn them again then press play and start clicking on hexes pretty cool right there are so many things that we can do with this be sure you share pictures or videos of any cool things that you make with this technique special thanks to my patrons on patreon you three really make content like this possible thank you from the bottom of my heart i mean it you guys are awesome if you'd like to support my content and see more videos like this please consider supporting me on patreon stop by my live stream on twitch wednesday through saturday 1 p.m to 7 p.m pacific if you enjoyed this tutorial comment like and subscribe for more stop by my discord server we have hundreds of awesome people making awesome things check out my assets on the unity asset store try my games and demos on my itch.io thanks for watching you
Info
Channel: Spherous
Views: 3,922
Rating: undefined out of 5
Keywords: unity, shader, shader graph, hex grid, hex, unity 3d, tutorial, unity tutorial, game dev, game development, unity dev, C#, indie dev, indie game dev, indie development, indie game development, hex tutorial, shader tutorial, C# tutorial, shader graph tutorial, hex grid tutorial, hex outline tutorial, hex outline, selection, new unity input system, unity input system, input system, new input system, hexagon, hexagon grid, hexagon tutorial
Id: ccbYZsF3SZ0
Channel Id: undefined
Length: 20min 7sec (1207 seconds)
Published: Wed Nov 18 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.