Decals & Stickers in Unity Shader Graph and URP

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
decals can be used to plaster all kinds of bonus textures over objects in your game such as stickers or graffiti we could theoretically just use textured quads and place them on flat surfaces to achieve this effect but if we want the pattern to stick to a curved surface or wrap around the corner we need to build a system where the mesh gets cut off partway which is annoying with decals the texture gets projected onto whatever shape of geometry we want although hdrp already has native decal support urp does not and that's where shaders come in i'm basing most of the theory on nylocat's approach which is linked in the description but my shadow won't have quite as many features before we crack on i'd like to mention that all my videos are made possible by support on patreon coffee and people subscribing now let's make some decals decals are intended to be used whenever we want to project a texture over any surface whether it's flat or uneven that means it's difficult to use a mesh based approach because we'd have to wrap the geometry tightly around the object instead we're going to exploit object space and the depth texture to map the texture inside a cube let me explain we're starting with a texture which for the purposes of this tutorial i'll assume is perfectly square conceptually we want to place the texture on one end of a cube and then move it down the cube volume and paint any surface that the texture hits we'll be able to place this cube inside the scene wherever we want the decal to appear how do we do that we can only render the surface of the cube so unfortunately we can't do anything to directly paint on the section of wall that intersects the cube but what if we draw the cube after drawing all the solid geometry in the scene that allows us to indirectly paint on the cube by using the depth texture to figure out which of the cube's pixels overlap an intersection between the cube and the wall and only draw a texture on those bits that will require us to take depth values from the depth texture then reconstruct the world position from those values once we've done that we'll delete the pixels outside the intersection for example this point reconstructed from the depth texture is inside the cube but this point is not but how do we decide which positions are inside the cube as i mentioned object space is going to help a lot here object space is where each vertex or edge is defined relative to an object's pivot point for the unity default cube its edges are one unit in length and the pivot is in the center so the vertices are at positions 0.5.5.5 and minus 0.5 0.5.5 and so on therefore it's very easy to delete pixels outside the unity default cube by transforming the world position to the cube's object position and then checking it's between minus 0.5 and 0.5 in all three axes and what about mapping the texture that's easy once we've got the object position of the valid pixels we just remove the z component of the position shift by 0.5 in the x and y axis so that 0 0 is in the corner and those are the uv positions then we map the texture and output to base color let's see all of that in action we'll start with an unlit graph by going to create shader universal render pipeline unless shader graph we also need to find the project's forward renderer pipeline asset and tick the depth texture box to make sure our shader works the pipeline asset is in the asset settings folder by default this graph will only require one property the main texture you could add extra ones for a base color and for tiling an offset too but i'll stick with the bare basics we want this shader to be drawn after all other solid geometry and we might want to support transparent textures we can do both of these things by going to the graph settings and changing the surface to transparent let's crack on with the graph the process to reconstruct the world position from the depth buffer is unfortunately a bit complicated but i'll do my best to walk you through it we'll start off with the scene depth mode in raw mode which retrieves a value between 0 and 1 based on how far away from the camera each pixel that's already rendered is remember that this should contain all opaque objects in the scene these values represent the z component of clip space which represents everything relative to where it will appear on the screen so i'm going to go ahead and make a new vector3 with a scene depth in the z component slot we also need the x and y components of clip space which we can get using a screen position node this needs to be in center mode then we can use a split node to grab just the x and y components use a negate node to invert the y component because these positions are basically upside down for what we're about to do when unity is taking objects and turning them into screen pixels a series of transformation matrices get applied to all the vertices of the object the model transformation goes from object space to world space then the view transformation gets us to view space and finally the projection transform gets us to clip space which is what we've got at the moment we want to unravel everything back to world space so we need to use the inverse view projection matrix shader graph provides all of these transformations for us so use a transformation matrix node and use the drop down to select inverse view projection then multiply by the clip space vector 3. all the matters here so make sure that the matrix is in the a slot the output is a vector 4. the reason why is a little bit out of the scope of this tutorial but if you're interested in why it's a vector 4 as opposed to a vector 3 look up homogeneous coordinates all we need to know is that we should divide the y z components by the w component to be left with the correct world space x y z position we'll do that by splitting the vector 4 creating a new vector 3 and then dividing by the w component remember that the rgba is equivalent to xyzw in this context that gets us the world's base position i feel that was a little bit involved wasn't it now we would use a transform node to convert from world space to object space since the rest of our calculations require object space from here we'll deal with clipping pixels that are outside the unity cube for that we will use a step node which returns 1 if the input value is above the edge threshold and 0 otherwise if we input a vector 3 it will do the comparisons per component since the unity default cube spans between 0.5 and minus 0.5 in the x y and z directions we will use two step modes one to provide a lower bound at minus 1.5 minus 0.5 minus 1.5 and the second to provide an upper bound at 0.5.5.5 for the second one we can do a neat trick where we use 1.5 as the threshold and then use a 1 minus node to invert the selection by multiplying these two values together only pixels within the unity default cube will have a value of 1 or white and then to aggregate the results for each of the x y and z directions we can use an all node which outputs true only if all the input components equal one and pass that into a branch node which should output one if the result is true and zero otherwise this result basically tells us whether to render the pixel at all zero means no and all means yes now let's go back to the transform node and think about texturing as i mentioned before the uv coordinates are based on these object space positions all we need to do is add 1.5 which gives us correctly offset uvs unity will automatically ignore the z component here note that you might need to set the texture wrapping on your texture to repeat to make this work properly now all that's left to do is use a sample texture 2d node with the main texture and output the rgba color to base color on the output stack and then we can multiply the alpha with the zeros and ones from the previous steps this gives us a final alpha value which we can of course use for the alpha pin on the output stack i'd also recommend going back to the graph settings and ticking two-sided to make sure the decal gets stuck in the right places and alpha clip threshold which are set to a very low value like 0.01 this will completely cool pixels that have a value of zero after checking the positions were inside the unity cube for our minor performance save and that's the decal shader finished to add one to your scene add a default unity cube and add the decal material to it the texture gets projected along the z axis of the cube so make sure you orient them correctly and to avoid overdraw where the shader gets run on pixels that will get cooled anyway make the cube as thin as it can be while still covering the surface you want to paint a texture on if you love decals as much as me you'll likely end up with a scene that looks like this thanks to all my patreon supporters on screen right now you will make these videos possible if you enjoyed this video remember to subscribe and i'll see you in the next trader video until next time have fun making trailers
Info
Channel: Daniel Ilett
Views: 28,491
Rating: undefined out of 5
Keywords: unity shader tutorial, unity shader graph, shader graph, shader graph tutorial, unity shader graph tutorial, unity shaders, unity shader, unity vfx, daniel ilett shader, urp, unity 3d, shaders, decal shader, decals, decal shader graph, sticker shader, sticker shader graph, unity3d, game dev, lwrp, urp shader, lwrp shader, unity tutorial
Id: f7iO9ernEmM
Channel Id: undefined
Length: 9min 15sec (555 seconds)
Published: Sun Sep 26 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.