{"version":"https:\/\/jsonfeed.org\/version\/1","title":"Joe Fazzino","home_page_url":"https:\/\/fazz.dev","feed_url":"https:\/\/fazz.dev\/rss\/feed.json","description":"Your blog description","icon":"https:\/\/fazz.dev\/favicon.ico","author":{"name":"Joe Fazzino"},"items":[{"id":"https:\/\/fazz.dev\/articles\/technical-writing-sucks","content_html":"<figure><img alt=\"\" srcSet=\"\/_next\/image?url=%2F_next%2Fstatic%2Fmedia%2FTFT.fc674dc1.jpg&amp;w=828&amp;q=75 1x, \/_next\/image?url=%2F_next%2Fstatic%2Fmedia%2FTFT.fc674dc1.jpg&amp;w=1920&amp;q=75 2x\" src=\"\/_next\/image?url=%2F_next%2Fstatic%2Fmedia%2FTFT.fc674dc1.jpg&amp;w=1920&amp;q=75\" width=\"776\" height=\"577\" decoding=\"async\" data-nimg=\"1\" loading=\"lazy\" style=\"color:transparent\"\/><figcaption>The Fighting Temeraire<\/figcaption><\/figure>\n<p>I&#x27;m not gonna pretend I made this website to &quot;help out other developers&quot;. I made it because it looks good to employers to have some sort of site out there and I generally like teaching.<\/p>\n<p>So this is my attempt to put them off (if my salary expectations weren&#x27;t doing a good enough job of that already).<\/p>\n<p>What I have discovered is that technical writing is not teaching, it&#x27;s not really even writing either. For me technical writing is a process of feeling incredibly anxious that anything I&#x27;ve written is wrong because all of this <strong>[gestures around]<\/strong> is pretty hard.<\/p>\n<p>On a broader level though, does technical writing actually <strong>do<\/strong> anything to help make better games?<\/p>\n<p>I&#x27;ve now worked on two long term games projects (6 months+), three if you include the one I&#x27;ve been working on outside of work. Luckily they&#x27;ve all been pretty well recieved despite the fact that the code and architecture are... lacking.<\/p>\n<p>This has given me the thought.<\/p>\n<blockquote>\n<p>What is the point of programming beyond just being the tool that enables you to make things?<\/p>\n<\/blockquote>\n<p>Is a good programmer one who gets shit done or is it one who writes good code? Ideally both but this is games, it&#x27;s never both.<\/p>\n<p>I don&#x27;t want people to think I&#x27;m ignorant, I am aware that good code\/architecture have benefits. If you&#x27;re making a GaaS then you can even put a dollar value on how maintainable and extensible your code is. I&#x27;m not talking about this though, <strong>I&#x27;m talking about making <em>good<\/em> games<\/strong>.<\/p>\n<p>Even if you understand ECS to the level of Richard Fabian or Mike Acton, will it make the game you&#x27;re working on a <em>better<\/em> game? Understanding technology and it&#x27;s constraints only helps you appreciate the limitations that you have to work within and the types of experience you can make. It doesn&#x27;t mean that the actual product is going to be good, I would even go as far as to say there is <strong>no correlation<\/strong> between the quality of the code and the quality of the final product.<\/p>\n<blockquote>\n<p>How many 4\/10 games and failed start up companies rest on the foundations of a SOLID codebase?<\/p>\n<\/blockquote>\n<p>I was recently describing my thoughts on programming to a friend (also a programmer) and I offhandedly said what I have felt for maybe a couple of years now.<\/p>\n<blockquote>\n<p>I don&#x27;t really like programming<\/p>\n<\/blockquote>\n<p>As an activity, programming, does very little for me. I hate getting stuck on bugs and I don&#x27;t really find it satisfying when I fix them except for the 10ish seconds after it happens. Breaking down tasks into smaller subtasks isn&#x27;t a special magic thing exclusive to programming, most jobs have this parrallel in one way or another.<\/p>\n<p>I often describe myself as a &quot;product focused programmer&quot;. I want to make things and programming is just the only way I know how to. If I could do it another way (that I enjoyed more) then I would.<\/p>\n<p>This is now going to be pretentious.<\/p>\n<p>My thoughts on programming are similar to the way (I have to imagine) a painter thinks about paint. Studying the nuance and the relationships of the subject, figuring out how to use what&#x27;s in front of you to solve a complex\/abstract problem is absolutely an interesting and compelling part of the job. But I&#x27;m not here to paint I&#x27;m here to make art.<\/p>","url":"https:\/\/fazz.dev\/articles\/technical-writing-sucks","title":"I don't like technical writing","summary":"and I barely like programming too","date_modified":"2023-09-26T00:00:00.000Z","author":{"name":"Joe Fazzino"}},{"id":"https:\/\/fazz.dev\/articles\/outline-shader-urp-part-two","content_html":"<p>Hi! Hopefully you&#x27;ve done part one before you get to this point otherwise it&#x27;s going to be chaos. As this tutorial is based entirely off of the Roystan Outline Shader tutorial and this part will be more about recreating the aspects of what&#x27;s written there. I&#x27;m not going to be going over the technical detail of <em>how<\/em> the outline shader works but I will be using the code and give a brief summary of what it achieves and only go into depth when we need to do something that really diverges from the original tutorial. Capeesh? Okay lets start.<\/p>\n<h2>Adding Shader Properties<\/h2>\n<p>I&#x27;ll cover this one at the top as the tutorial asks that this be done quite frequently. It is pretty simple to add properties to our Outline shader that we can configure in the ScriptableRenderFeature. I&#x27;ll explain the process briefly here because I expect if you can follow these tutorials and understand what you&#x27;re writing you could quite easily figure it out youself. Feel free to skip if that&#x27;s you!<\/p>\n<pre class=\"language-cs\"><code class=\"language-cs\"><span class=\"token punctuation\">[<\/span><span class=\"token attribute\"><span class=\"token class-name\">Serializable<\/span><\/span><span class=\"token punctuation\">]<\/span>\n<span class=\"token keyword\">public<\/span> <span class=\"token keyword\">class<\/span> <span class=\"token class-name\">OutlineSettings<\/span>\n<span class=\"token punctuation\">{<\/span>\n    <span class=\"token keyword\">public<\/span> <span class=\"token class-name\">Shader<\/span> OutlineShader<span class=\"token punctuation\">;<\/span>\n    <span class=\"token keyword\">public<\/span> <span class=\"token class-name\">Color<\/span> OutlineColor<span class=\"token punctuation\">;<\/span>\n    <span class=\"token keyword\">public<\/span> <span class=\"token class-name\"><span class=\"token keyword\">int<\/span><\/span> Scale <span class=\"token operator\">=<\/span> <span class=\"token number\">1<\/span><span class=\"token punctuation\">;<\/span>\n    <span class=\"token range operator\">..<\/span><span class=\"token punctuation\">.<\/span>\n\n<span class=\"token keyword\">public<\/span> <span class=\"token keyword\">class<\/span> <span class=\"token class-name\">OutlineRenderFeature<\/span> <span class=\"token punctuation\">:<\/span> <span class=\"token type-list\"><span class=\"token class-name\">ScriptableRendererFeature<\/span><\/span>\n<span class=\"token punctuation\">{<\/span>\n    <span class=\"token class-name\">OutlinePass<\/span> m_OutlinePass<span class=\"token punctuation\">;<\/span>\n\n    <span class=\"token keyword\">public<\/span> <span class=\"token class-name\">OutlineSettings<\/span> Settings<span class=\"token punctuation\">;<\/span>\n\n    <span class=\"token class-name\">Material<\/span> m_Material<span class=\"token punctuation\">;<\/span>\n\n    <span class=\"token keyword\">public<\/span> <span class=\"token keyword\">override<\/span> <span class=\"token return-type class-name\"><span class=\"token keyword\">void<\/span><\/span> <span class=\"token function\">Create<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span>\n    <span class=\"token punctuation\">{<\/span>\n        <span class=\"token keyword\">if<\/span> <span class=\"token punctuation\">(<\/span>Settings<span class=\"token punctuation\">.<\/span>OutlineShader <span class=\"token operator\">!=<\/span> <span class=\"token keyword\">null<\/span><span class=\"token punctuation\">)<\/span>\n            m_Material <span class=\"token operator\">=<\/span> <span class=\"token keyword\">new<\/span> <span class=\"token constructor-invocation class-name\">Material<\/span><span class=\"token punctuation\">(<\/span>Settings<span class=\"token punctuation\">.<\/span>OutlineShader<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span>\n\n        m_OutlinePass <span class=\"token operator\">=<\/span> <span class=\"token keyword\">new<\/span> <span class=\"token constructor-invocation class-name\">OutlinePass<\/span><span class=\"token punctuation\">(<\/span>m_Material<span class=\"token punctuation\">,<\/span>\n            Settings<span class=\"token punctuation\">.<\/span>OutlineColor<span class=\"token punctuation\">,<\/span>\n            Settings<span class=\"token punctuation\">.<\/span>Scale<span class=\"token punctuation\">,<\/span>\n            <span class=\"token range operator\">..<\/span><span class=\"token punctuation\">.<\/span>\n<\/code><\/pre>\n<p>OutlineRenderFeature.cs<\/p>\n<pre class=\"language-cs\"><code class=\"language-cs\"><span class=\"token keyword\">class<\/span> <span class=\"token class-name\">OutlinePass<\/span> <span class=\"token punctuation\">:<\/span> <span class=\"token type-list\"><span class=\"token class-name\">ScriptableRenderPass<\/span><\/span>\n<span class=\"token punctuation\">{<\/span>\n    <span class=\"token keyword\">readonly<\/span> <span class=\"token class-name\"><span class=\"token keyword\">float<\/span><\/span> m_Scale<span class=\"token punctuation\">,<\/span> m_DepthThreshold<span class=\"token punctuation\">,<\/span> m_NormalThreshold<span class=\"token punctuation\">,<\/span> m_DepthNormalThreshold<span class=\"token punctuation\">,<\/span> m_DepthNormalThresholdScale<span class=\"token punctuation\">;<\/span>\n    <span class=\"token keyword\">readonly<\/span> <span class=\"token class-name\">Color<\/span> m_Color<span class=\"token punctuation\">;<\/span>\n    <span class=\"token class-name\">RenderTargetIdentifier<\/span> m_CameraColorTarget<span class=\"token punctuation\">;<\/span>\n    <span class=\"token class-name\">Material<\/span> m_Material<span class=\"token punctuation\">;<\/span>\n    <span class=\"token keyword\">static<\/span> <span class=\"token class-name\"><span class=\"token keyword\">int<\/span><\/span> colorID <span class=\"token operator\">=<\/span> Shader<span class=\"token punctuation\">.<\/span><span class=\"token function\">PropertyToID<\/span><span class=\"token punctuation\">(<\/span><span class=\"token string\">&quot;_OutlineColor&quot;<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span>\n    <span class=\"token keyword\">static<\/span> <span class=\"token class-name\"><span class=\"token keyword\">int<\/span><\/span> scaleID <span class=\"token operator\">=<\/span> Shader<span class=\"token punctuation\">.<\/span><span class=\"token function\">PropertyToID<\/span><span class=\"token punctuation\">(<\/span><span class=\"token string\">&quot;_Scale&quot;<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span>\n    <span class=\"token range operator\">..<\/span><span class=\"token punctuation\">.<\/span>\n\n    <span class=\"token keyword\">public<\/span> <span class=\"token function\">OutlinePass<\/span><span class=\"token punctuation\">(<\/span><span class=\"token class-name\">Material<\/span> material<span class=\"token punctuation\">,<\/span> <span class=\"token class-name\">Color<\/span> color<span class=\"token punctuation\">,<\/span> <span class=\"token class-name\"><span class=\"token keyword\">float<\/span><\/span> scale<span class=\"token punctuation\">,<\/span> <span class=\"token range operator\">..<\/span><span class=\"token punctuation\">.<\/span><span class=\"token punctuation\">)<\/span>\n    <span class=\"token punctuation\">{<\/span>\n        m_Material <span class=\"token operator\">=<\/span> material<span class=\"token punctuation\">;<\/span>\n        m_Color <span class=\"token operator\">=<\/span> color<span class=\"token punctuation\">;<\/span>\n        m_Scale <span class=\"token operator\">=<\/span> scale<span class=\"token punctuation\">;<\/span>\n        <span class=\"token range operator\">..<\/span><span class=\"token punctuation\">.<\/span>\n\n    <span class=\"token keyword\">public<\/span> <span class=\"token keyword\">override<\/span> <span class=\"token return-type class-name\"><span class=\"token keyword\">void<\/span><\/span> <span class=\"token function\">Execute<\/span><span class=\"token punctuation\">(<\/span><span class=\"token class-name\">ScriptableRenderContext<\/span> context<span class=\"token punctuation\">,<\/span> <span class=\"token keyword\">ref<\/span> <span class=\"token class-name\">RenderingData<\/span> renderingData<span class=\"token punctuation\">)<\/span>\n    <span class=\"token punctuation\">{<\/span>\n        <span class=\"token class-name\"><span class=\"token keyword\">var<\/span><\/span> camera <span class=\"token operator\">=<\/span> renderingData<span class=\"token punctuation\">.<\/span>cameraData<span class=\"token punctuation\">.<\/span>camera<span class=\"token punctuation\">;<\/span>\n        <span class=\"token keyword\">if<\/span> <span class=\"token punctuation\">(<\/span>camera<span class=\"token punctuation\">.<\/span>cameraType <span class=\"token operator\">!=<\/span> CameraType<span class=\"token punctuation\">.<\/span>Game<span class=\"token punctuation\">)<\/span>\n            <span class=\"token keyword\">return<\/span><span class=\"token punctuation\">;<\/span>\n\n        <span class=\"token keyword\">if<\/span> <span class=\"token punctuation\">(<\/span>m_Material <span class=\"token operator\">==<\/span> <span class=\"token keyword\">null<\/span><span class=\"token punctuation\">)<\/span>\n            <span class=\"token keyword\">return<\/span><span class=\"token punctuation\">;<\/span>\n\n        <span class=\"token class-name\">CommandBuffer<\/span> cb <span class=\"token operator\">=<\/span> CommandBufferPool<span class=\"token punctuation\">.<\/span><span class=\"token function\">Get<\/span><span class=\"token punctuation\">(<\/span><span class=\"token named-parameter punctuation\">name<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token string\">&quot;OutlinePass&quot;<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span>\n        cb<span class=\"token punctuation\">.<\/span><span class=\"token function\">BeginSample<\/span><span class=\"token punctuation\">(<\/span><span class=\"token string\">&quot;Outline Pass&quot;<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span>\n\n        m_Material<span class=\"token punctuation\">.<\/span><span class=\"token function\">SetColor<\/span><span class=\"token punctuation\">(<\/span>colorID<span class=\"token punctuation\">,<\/span> m_Color<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span>\n        m_Material<span class=\"token punctuation\">.<\/span><span class=\"token function\">SetFloat<\/span><span class=\"token punctuation\">(<\/span>scaleID<span class=\"token punctuation\">,<\/span> m_Scale<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span>\n        <span class=\"token range operator\">..<\/span><span class=\"token punctuation\">.<\/span>  \n<\/code><\/pre>\n<h2>Sampling Camera Opaque\/Depth<\/h2>\n<p>We can get the Opaque and Depth textures from the camera by accessing the properties that are built in to the shader. These can be accessed because the shader is called late in the rendering pipeline when these textures already exist.<\/p>\n<pre class=\"language-hlsl\"><code class=\"language-hlsl\"><span class=\"token function\">TEXTURE2D<\/span><span class=\"token punctuation\">(<\/span>_CameraOpaqueTexture<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span>\n<span class=\"token function\">TEXTURE2D<\/span><span class=\"token punctuation\">(<\/span>_CameraDepthTexture<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span>\n\n<span class=\"token function\">SAMPLER<\/span><span class=\"token punctuation\">(<\/span>sampler_CameraOpaqueTexture<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span>\n<span class=\"token function\">SAMPLER<\/span><span class=\"token punctuation\">(<\/span>sampler_CameraDepthTexture<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span>\n\n<span class=\"token keyword\">float4<\/span> _CameraOpaqueTexture_TexelSize<span class=\"token punctuation\">;<\/span>\n<\/code><\/pre>\n<p>The texel size is important for following the guide on generating the UVs for the edge detection to work.<\/p>\n<h2>View Normals<\/h2>\n<p>This was probably the trickiest part of making this tutorial work with URP. I didn&#x27;t want to do what the original author did in the tutorial which is just give a magic file that solves it for you.<\/p>\n<p>In the SRP there is a nice easy way to grab the DepthNormals texture for a camera.<\/p>\n<h2>Alpha Blend<\/h2>\n<p>Alpha blend isn&#x27;t a thing in our shader but we can achieve the same effect that the author has in their tutorial by calling the HLSL <code>clip<\/code> command to tell shader to not render the fragments that don&#x27;t have any outline. This means that we will &#x27;see though&#x27; our shader to the cameras opaque texture achiving the same sort of effect that is in the original tutorial.<\/p>\n<p>Clip either does or doesn&#x27;t draw a pixel, if you wanted to do an actual alpha blend it wouldn&#x27;t be too hard just instead of clip you make sure that you render the fragments that have an outline with an alpha value of 1 and then every other fragment with a lower value. Not sure if this would actually look any good but have a go if you like!<\/p>\n<h2>Plan<\/h2>\n<ul>\n<li>\n<p>Copy pasta copy pasta<\/p>\n<\/li>\n<li>\n<p>Normals texture explanation<\/p>\n<\/li>\n<li>\n<p>View direction explanation<\/p>\n<\/li>\n<\/ul>","url":"https:\/\/fazz.dev\/articles\/outline-shader-urp-part-two","title":"URP Fullscreen Outline Effect - Part Two","summary":"There is outlining in this part!","date_modified":"2022-12-07T00:00:00.000Z","author":{"name":"Joe Fazzino"}},{"id":"https:\/\/fazz.dev\/articles\/outline-shader-urp","content_html":"<blockquote>\n<p>Edit: This was originally to be part one of a two parter but I actually decided I don&#x27;t like technical writing so I&#x27;ve edited it to be a \u00cc useful resource.<\/p>\n<\/blockquote>\n<p>Continuing the emerging theme of &#x27;do a tutorial and then write about it&#x27; except this time I did actually have to figure some things out myself, very exciting!<\/p>\n<p>I knew very little about doing an outline shader. I knew the theory behind how to create a &#x27;toon&#x27; shader style effect because I got to see that at Gravity Sketch but a full screen effect was new to me and I was curious about the implementation. Turns out it&#x27;s actually pretty straightforward with maybe one or two caveats.<\/p>\n<p>The tutorial I was following <a href=\"https:\/\/roystan.net\/articles\/outline-shader\/\">this tutorial from Roystan.net<\/a>, this was done in Unity 2018 and the BiRP so I knew I would have a bit of extra work to do but after doing the <a href=\"https:\/\/fazz.dev\/articles\/building-srp-part-one\">Custom SRP<\/a> tutorials I was feeling more confident on my shader\/SRP abilities. If you want to follow along to my slightly modified version of this tutorial you can find the <a href=\"https:\/\/github.com\/joefazz\/URPOutline-complete\">complete project here<\/a> or you can download the <a href=\"https:\/\/github.com\/joefazz\/URPOutline-skeleton\">skeleton project here<\/a>. I specifically used Unity version 2021.3.13 but you should be able safely use any 2021 LTS version.<\/p>\n<p>In this part one I&#x27;ll be setting up all the scripts and the shader we&#x27;ll use so that we can actually perform a full screen effect as, at the time of writing, URP does not have a simple way of adding a post processing layer like what exists in the Post Processing v2 package.<\/p>\n<p>The skeleton project from the original tutorial is quite fleshed out and so mine is quite a bit more barebones but that&#x27;s personal preference on my part. All that is set up is the <strong>Universal RP<\/strong> package is installed and the graphics and quality settings have been updated to use the renderer assets. By default URP generates different quality levels for the assets but we don&#x27;t need it so I deleted those and just use the one asset.<\/p>\n<hr\/>\n<p>Immediately the tutorial starts with a premade project that has a custom Post Processing layer with a Post Processing volume and a Post Processing profile. None of which we are using in URP because URP uses the <strong>Volume<\/strong> component to apply post processing effects.<\/p>\n<p>As mentioned, in URP there isn&#x27;t currently a way to create custom Volume components but we can still create a full screen effect by injecting <code>ScriptableRenderFeature<\/code>s into the URP pipeline before\/after any specific pass. This is a great substitute for what we&#x27;re trying to achieve with a full screen effect but you lose the flexibility of making post processing volumes a fixed size in world space which you could use to create trippy scenarios.<\/p>\n<blockquote>\n<p>To be fair even when you can create custom volume components you might still prefer to use this solution as it gives you the flexibility of deciding when it should run in the pipeline rather than being stuck in the PostProcessing pass.<\/p>\n<\/blockquote>\n<p>A high level overview of what we want to achieve can be broken nicely into the following steps:<\/p>\n<ol>\n<li><strong>Read in the different buffers we need to do edge detection<\/strong><\/li>\n<li><strong>Perform edge detection<\/strong><\/li>\n<li><strong>Render output to a full screen sized quad<\/strong><\/li>\n<li><strong>Profit?<\/strong><\/li>\n<\/ol>\n<p>If you look at the <strong>Settings\/URP Forward Renderer<\/strong> asset in the inspector then you can see at the bottom there is a button that says &quot;Add Renderer Feature&quot;. If you click this then you can see a list of Renderer Features you can add that are built in with the URP package. We want to create our own one of these!<\/p>\n<p>Open up your favourite code editor and create a file named <code>OutlineRendererFeature.cs<\/code> inside the <code>Scripts<\/code> folder. Make a class inside this folder with the same name as the file and make the class inherit from <code>ScriptableRendererFeature<\/code>. If your code editor is nice it should automagically import the required <code>using<\/code>s and it will yell at you that you haven&#x27;t overriden the methods marked. If your editor isn&#x27;t nice then this is the file you should have and you shouldn&#x27;t feel bad about copy and pasting it!<\/p>\n<pre class=\"language-cs\"><code class=\"language-cs\"><span class=\"token keyword\">using<\/span> <span class=\"token namespace\">UnityEngine<\/span><span class=\"token punctuation\">;<\/span>\n<span class=\"token keyword\">using<\/span> <span class=\"token namespace\">UnityEngine<span class=\"token punctuation\">.<\/span>Rendering<span class=\"token punctuation\">.<\/span>Universal<\/span><span class=\"token punctuation\">;<\/span>\n\n<span class=\"token keyword\">public<\/span> <span class=\"token keyword\">class<\/span> <span class=\"token class-name\">OutlineRendererFeature<\/span> <span class=\"token punctuation\">:<\/span> <span class=\"token type-list\"><span class=\"token class-name\">ScriptableRendererFeature<\/span><\/span>\n<span class=\"token punctuation\">{<\/span>\n    <span class=\"token keyword\">public<\/span> <span class=\"token keyword\">override<\/span> <span class=\"token return-type class-name\"><span class=\"token keyword\">void<\/span><\/span> <span class=\"token function\">Create<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span>\n    <span class=\"token punctuation\">{<\/span>\n\n    <span class=\"token punctuation\">}<\/span>\n\n    <span class=\"token keyword\">public<\/span> <span class=\"token keyword\">override<\/span> <span class=\"token return-type class-name\"><span class=\"token keyword\">void<\/span><\/span> <span class=\"token function\">AddRenderPasses<\/span><span class=\"token punctuation\">(<\/span><span class=\"token class-name\">ScriptableRenderer<\/span> renderer<span class=\"token punctuation\">,<\/span> <span class=\"token keyword\">ref<\/span> <span class=\"token class-name\">RenderingData<\/span> renderingData<span class=\"token punctuation\">)<\/span>\n    <span class=\"token punctuation\">{<\/span>\n        \n    <span class=\"token punctuation\">}<\/span>\n<span class=\"token punctuation\">}<\/span>\n<\/code><\/pre>\n<p>Now we can go back to out <strong>Settings\/URP Forward Renderer<\/strong> asset and we can see that inside the <em>Add Renderer Feature<\/em> list the new class we made appears! Click on it and you&#x27;ll see nothing changes but that&#x27;s because we&#x27;re not doing anything inside this feature. To affect anything we need to impelment a <em>Render Pass<\/em>.<\/p>\n<p>Create another script named <code>OutlinePass.cs<\/code>. This class should inherit from <code>ScriptableRenderPass<\/code>. Once you satisfy all the errors from unimplemented member functions it should look like this...<\/p>\n<pre class=\"language-cs\"><code class=\"language-cs\"><span class=\"token keyword\">using<\/span> <span class=\"token namespace\">UnityEngine<span class=\"token punctuation\">.<\/span>Rendering<\/span><span class=\"token punctuation\">;<\/span>\n<span class=\"token keyword\">using<\/span> <span class=\"token namespace\">UnityEngine<span class=\"token punctuation\">.<\/span>Rendering<span class=\"token punctuation\">.<\/span>Universal<\/span><span class=\"token punctuation\">;<\/span>\n\n<span class=\"token keyword\">public<\/span> <span class=\"token keyword\">class<\/span> <span class=\"token class-name\">OutlinePass<\/span> <span class=\"token punctuation\">:<\/span> <span class=\"token type-list\"><span class=\"token class-name\">ScriptableRenderPass<\/span><\/span>\n<span class=\"token punctuation\">{<\/span>\n    <span class=\"token keyword\">public<\/span> <span class=\"token keyword\">override<\/span> <span class=\"token return-type class-name\"><span class=\"token keyword\">void<\/span><\/span> <span class=\"token function\">Execute<\/span><span class=\"token punctuation\">(<\/span><span class=\"token class-name\">ScriptableRenderContext<\/span> context<span class=\"token punctuation\">,<\/span> <span class=\"token keyword\">ref<\/span> <span class=\"token class-name\">RenderingData<\/span> renderingData<span class=\"token punctuation\">)<\/span>\n    <span class=\"token punctuation\">{<\/span>\n        \n    <span class=\"token punctuation\">}<\/span>\n<span class=\"token punctuation\">}<\/span>\n<\/code><\/pre>\n<p>In order to enqueue the pass it needs to be created by the <code>ScriptableRendererFeature<\/code> inside of the <code>Create()<\/code> method. Create a member variable of the class and assign it as a <code>new<\/code> instance of the <code>OutlinePass<\/code>. Then to enqueue it, call <code>renderer.Enqueue(m_OutlinePass)<\/code> inside of the <code>AddRenderPasses<\/code> method.<\/p>\n<p>Your code should look like this now.<\/p>\n<pre class=\"language-cs\"><code class=\"language-cs\"><span class=\"token keyword\">using<\/span> <span class=\"token namespace\">UnityEngine<\/span><span class=\"token punctuation\">;<\/span>\n<span class=\"token keyword\">using<\/span> <span class=\"token namespace\">UnityEngine<span class=\"token punctuation\">.<\/span>Rendering<span class=\"token punctuation\">.<\/span>Universal<\/span><span class=\"token punctuation\">;<\/span>\n\n<span class=\"token keyword\">public<\/span> <span class=\"token keyword\">class<\/span> <span class=\"token class-name\">OutlineRendererFeature<\/span> <span class=\"token punctuation\">:<\/span> <span class=\"token type-list\"><span class=\"token class-name\">ScriptableRendererFeature<\/span><\/span>\n<span class=\"token punctuation\">{<\/span>\n    <span class=\"token class-name\">OutlinePass<\/span> m_OutlinePass<span class=\"token punctuation\">;<\/span>\n\n    <span class=\"token keyword\">public<\/span> <span class=\"token keyword\">override<\/span> <span class=\"token return-type class-name\"><span class=\"token keyword\">void<\/span><\/span> <span class=\"token function\">Create<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span>\n    <span class=\"token punctuation\">{<\/span>\n        m_OutlinePass <span class=\"token operator\">=<\/span> <span class=\"token keyword\">new<\/span> <span class=\"token constructor-invocation class-name\">OutlinePass<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span>\n    <span class=\"token punctuation\">}<\/span>\n\n    <span class=\"token keyword\">public<\/span> <span class=\"token keyword\">override<\/span> <span class=\"token return-type class-name\"><span class=\"token keyword\">void<\/span><\/span> <span class=\"token function\">AddRenderPasses<\/span><span class=\"token punctuation\">(<\/span><span class=\"token class-name\">ScriptableRenderer<\/span> renderer<span class=\"token punctuation\">,<\/span> <span class=\"token keyword\">ref<\/span> <span class=\"token class-name\">RenderingData<\/span> renderingData<span class=\"token punctuation\">)<\/span>\n    <span class=\"token punctuation\">{<\/span>\n        renderer<span class=\"token punctuation\">.<\/span><span class=\"token function\">EnqueuePass<\/span><span class=\"token punctuation\">(<\/span>m_OutlinePass<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span>\n    <span class=\"token punctuation\">}<\/span>\n<span class=\"token punctuation\">}<\/span>\n<\/code><\/pre>\n<blockquote>\n<p>Fun fact: This code styling is the official Unity code styling, I don&#x27;t love it \ud83d\ude05<\/p>\n<\/blockquote>\n<p>While we&#x27;re inside the <code>OutlineRenderFeature<\/code> class lets finish writing it as there&#x27;s not a lot more to it.<\/p>\n<p>Now that the OutlinePass is being executed every frame all we need to do is pass all of the user configurable values to the pass so that eventually the full screen shader can read them in and use them to modify the effect.<\/p>\n<p>Any <code>public<\/code> field or <code>[SerializeField]<\/code> inside this class will be exposed in the Forward Renderer asset. In order to expose the settings that we need eventually need for the shader.<\/p>\n<pre class=\"language-cs\"><code class=\"language-cs\"><span class=\"token keyword\">using<\/span> <span class=\"token namespace\">System<\/span><span class=\"token punctuation\">;<\/span>\n<span class=\"token keyword\">using<\/span> <span class=\"token namespace\">UnityEngine<\/span><span class=\"token punctuation\">;<\/span>\n<span class=\"token keyword\">using<\/span> <span class=\"token namespace\">UnityEngine<span class=\"token punctuation\">.<\/span>Rendering<\/span><span class=\"token punctuation\">;<\/span>\n<span class=\"token keyword\">using<\/span> <span class=\"token namespace\">UnityEngine<span class=\"token punctuation\">.<\/span>Rendering<span class=\"token punctuation\">.<\/span>Universal<\/span><span class=\"token punctuation\">;<\/span>\n\n<span class=\"token punctuation\">[<\/span><span class=\"token attribute\"><span class=\"token class-name\">Serializable<\/span><\/span><span class=\"token punctuation\">]<\/span>\n<span class=\"token keyword\">public<\/span> <span class=\"token keyword\">class<\/span> <span class=\"token class-name\">OutlineSettings<\/span>\n<span class=\"token punctuation\">{<\/span>\n    <span class=\"token keyword\">public<\/span> <span class=\"token class-name\">Shader<\/span> OutlineShader<span class=\"token punctuation\">;<\/span>\n    <span class=\"token keyword\">public<\/span> <span class=\"token class-name\">Color<\/span> OutlineColor<span class=\"token punctuation\">;<\/span>\n<span class=\"token punctuation\">}<\/span>\n\n<span class=\"token keyword\">public<\/span> <span class=\"token keyword\">class<\/span> <span class=\"token class-name\">OutlineRenderFeature<\/span> <span class=\"token punctuation\">:<\/span> <span class=\"token type-list\"><span class=\"token class-name\">ScriptableRendererFeature<\/span><\/span>\n<span class=\"token punctuation\">{<\/span>\n    <span class=\"token keyword\">public<\/span> <span class=\"token class-name\">OutlineSettings<\/span> Settings<span class=\"token punctuation\">;<\/span>\n\n    <span class=\"token class-name\">OutlinePass<\/span> m_OutlinePass<span class=\"token punctuation\">;<\/span>\n    <span class=\"token class-name\">Material<\/span> m_Material<span class=\"token punctuation\">;<\/span>\n\n    <span class=\"token keyword\">public<\/span> <span class=\"token keyword\">override<\/span> <span class=\"token return-type class-name\"><span class=\"token keyword\">void<\/span><\/span> <span class=\"token function\">Create<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span>\n    <span class=\"token punctuation\">{<\/span>\n        <span class=\"token keyword\">if<\/span> <span class=\"token punctuation\">(<\/span>Settings<span class=\"token punctuation\">.<\/span>OutlineShader <span class=\"token operator\">!=<\/span> <span class=\"token keyword\">null<\/span><span class=\"token punctuation\">)<\/span>\n            m_Material <span class=\"token operator\">=<\/span> <span class=\"token keyword\">new<\/span> <span class=\"token constructor-invocation class-name\">Material<\/span><span class=\"token punctuation\">(<\/span>Settings<span class=\"token punctuation\">.<\/span>OutlineShader<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span>\n\n        m_OutlinePass <span class=\"token operator\">=<\/span> <span class=\"token keyword\">new<\/span> <span class=\"token constructor-invocation class-name\">OutlinePass<\/span><span class=\"token punctuation\">(<\/span>m_Material<span class=\"token punctuation\">,<\/span>\n            Settings<span class=\"token punctuation\">.<\/span>OutlineColor<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span>\n    <span class=\"token punctuation\">}<\/span>\n\n    <span class=\"token keyword\">public<\/span> <span class=\"token keyword\">override<\/span> <span class=\"token return-type class-name\"><span class=\"token keyword\">void<\/span><\/span> <span class=\"token function\">AddRenderPasses<\/span><span class=\"token punctuation\">(<\/span><span class=\"token class-name\">ScriptableRenderer<\/span> renderer<span class=\"token punctuation\">,<\/span> <span class=\"token keyword\">ref<\/span> <span class=\"token class-name\">RenderingData<\/span> renderingData<span class=\"token punctuation\">)<\/span>\n    <span class=\"token punctuation\">{<\/span>\n        <span class=\"token keyword\">if<\/span> <span class=\"token punctuation\">(<\/span>renderingData<span class=\"token punctuation\">.<\/span>cameraData<span class=\"token punctuation\">.<\/span>cameraType <span class=\"token operator\">!=<\/span> CameraType<span class=\"token punctuation\">.<\/span>Game<span class=\"token punctuation\">)<\/span> <span class=\"token keyword\">return<\/span><span class=\"token punctuation\">;<\/span>\n\n        m_OutlinePass<span class=\"token punctuation\">.<\/span><span class=\"token function\">ConfigureInput<\/span><span class=\"token punctuation\">(<\/span>ScriptableRenderPassInput<span class=\"token punctuation\">.<\/span>Color<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span>\n        m_OutlinePass<span class=\"token punctuation\">.<\/span><span class=\"token function\">SetTarget<\/span><span class=\"token punctuation\">(<\/span>renderer<span class=\"token punctuation\">.<\/span>cameraColorTarget<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span>\n        renderer<span class=\"token punctuation\">.<\/span><span class=\"token function\">EnqueuePass<\/span><span class=\"token punctuation\">(<\/span>m_OutlinePass<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span>\n    <span class=\"token punctuation\">}<\/span>\n\n    <span class=\"token keyword\">protected<\/span> <span class=\"token keyword\">override<\/span> <span class=\"token return-type class-name\"><span class=\"token keyword\">void<\/span><\/span> <span class=\"token function\">Dispose<\/span><span class=\"token punctuation\">(<\/span><span class=\"token class-name\"><span class=\"token keyword\">bool<\/span><\/span> disposing<span class=\"token punctuation\">)<\/span>\n    <span class=\"token punctuation\">{<\/span>\n        CoreUtils<span class=\"token punctuation\">.<\/span><span class=\"token function\">Destroy<\/span><span class=\"token punctuation\">(<\/span>m_Material<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span>\n    <span class=\"token punctuation\">}<\/span>\n<span class=\"token punctuation\">}<\/span>\n<\/code><\/pre>\n<p>There are quite a few new things here but it&#x27;s fairly straightforward. In order to apply our shader to a full screen quad, we need a material to assign it to which is created at runtime by this feature. This material and the settings for the shader are then passed into the constructor of the OutlinePass which will save both of these to private member variables.<\/p>\n<p>Inside the <code>AddRenderPasses<\/code> method we make it so that this effect will only apply to the Game camera and configure the input for the pass to be the color buffer and, by creating a method insider the OutlinePass, set the output target to be the cameras color buffer.<\/p>\n<p>The final new part of this is that we want to safely destroy the material when we&#x27;re finished with the renderer which we can do with this built in utility <code>CoreUtils.Destory<\/code>.<\/p>\n<p>Inside <code>OutlinePass<\/code>, in order to plug all of this in we need a constructor that stores the passed variables as member variables and a method that will store the current color buffer for the frame that can be reused in the <code>Execute<\/code> method.<\/p>\n<pre class=\"language-cs\"><code class=\"language-cs\"><span class=\"token keyword\">class<\/span> <span class=\"token class-name\">OutlinePass<\/span> <span class=\"token punctuation\">:<\/span> <span class=\"token type-list\"><span class=\"token class-name\">ScriptableRenderPass<\/span><\/span>\n<span class=\"token punctuation\">{<\/span>\n    <span class=\"token keyword\">readonly<\/span> <span class=\"token class-name\">Color<\/span> m_Color<span class=\"token punctuation\">;<\/span>\n    <span class=\"token class-name\">RenderTargetIdentifier<\/span> m_CameraColorTarget<span class=\"token punctuation\">;<\/span>\n    <span class=\"token class-name\">Material<\/span> m_Material<span class=\"token punctuation\">;<\/span>\n    <span class=\"token keyword\">static<\/span> <span class=\"token class-name\"><span class=\"token keyword\">int<\/span><\/span> colorID <span class=\"token operator\">=<\/span> Shader<span class=\"token punctuation\">.<\/span><span class=\"token function\">PropertyToID<\/span><span class=\"token punctuation\">(<\/span><span class=\"token string\">&quot;_OutlineColor&quot;<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span>\n\n    <span class=\"token keyword\">public<\/span> <span class=\"token function\">OutlinePass<\/span><span class=\"token punctuation\">(<\/span><span class=\"token class-name\">Material<\/span> material<span class=\"token punctuation\">,<\/span> <span class=\"token class-name\">Color<\/span> color<span class=\"token punctuation\">)<\/span>\n    <span class=\"token punctuation\">{<\/span>\n        m_Material <span class=\"token operator\">=<\/span> material<span class=\"token punctuation\">;<\/span>\n        m_Color <span class=\"token operator\">=<\/span> color<span class=\"token punctuation\">;<\/span>\n        \n        renderPassEvent <span class=\"token operator\">=<\/span> RenderPassEvent<span class=\"token punctuation\">.<\/span>BeforeRenderingPostProcessing<span class=\"token punctuation\">;<\/span>\n    <span class=\"token punctuation\">}<\/span>\n\n    <span class=\"token keyword\">public<\/span> <span class=\"token return-type class-name\"><span class=\"token keyword\">void<\/span><\/span> <span class=\"token function\">SetTarget<\/span><span class=\"token punctuation\">(<\/span><span class=\"token class-name\">RenderTargetIdentifier<\/span> colorHandle<span class=\"token punctuation\">)<\/span>\n    <span class=\"token punctuation\">{<\/span>\n        m_CameraColorTarget <span class=\"token operator\">=<\/span> colorHandle<span class=\"token punctuation\">;<\/span>\n    <span class=\"token punctuation\">}<\/span>\n\n    <span class=\"token keyword\">public<\/span> <span class=\"token keyword\">override<\/span> <span class=\"token return-type class-name\"><span class=\"token keyword\">void<\/span><\/span> <span class=\"token function\">OnCameraSetup<\/span><span class=\"token punctuation\">(<\/span><span class=\"token class-name\">CommandBuffer<\/span> cmd<span class=\"token punctuation\">,<\/span> <span class=\"token keyword\">ref<\/span> <span class=\"token class-name\">RenderingData<\/span> renderingData<span class=\"token punctuation\">)<\/span>\n    <span class=\"token punctuation\">{<\/span>\n        <span class=\"token keyword\">base<\/span><span class=\"token punctuation\">.<\/span><span class=\"token function\">OnCameraSetup<\/span><span class=\"token punctuation\">(<\/span>cmd<span class=\"token punctuation\">,<\/span> <span class=\"token keyword\">ref<\/span> renderingData<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span>\n        <span class=\"token function\">ConfigureTarget<\/span><span class=\"token punctuation\">(<\/span><span class=\"token keyword\">new<\/span> <span class=\"token constructor-invocation class-name\">RenderTargetIdentifier<\/span><span class=\"token punctuation\">(<\/span>m_CameraColorTarget<span class=\"token punctuation\">,<\/span> <span class=\"token number\">0<\/span><span class=\"token punctuation\">,<\/span> CubemapFace<span class=\"token punctuation\">.<\/span>Unknown<span class=\"token punctuation\">,<\/span> <span class=\"token operator\">-<\/span><span class=\"token number\">1<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span>\n    <span class=\"token punctuation\">}<\/span>\n\n    <span class=\"token comment\">\/\/ Execute<\/span>\n<span class=\"token punctuation\">}<\/span>\n<\/code><\/pre>\n<p>Nothing crazy is going on here. We use the <code>OnCameraSetup<\/code> event to make sure that the pass is going to be rendered to the camera color buffer.<\/p>\n<p>A line worth mentioning is the line <code>renderPassEvent = RenderPassEvent.BeforeRenderingPostProcessing<\/code>. This tells the renderer where to inject our outline pass in the pipeline. In this case I&#x27;ve chosen before post processing so any additional effects that we might apply would also apply to the outlines but you might not want that so you can change it to <code>AfterRenderingPostProcessing<\/code>. You can see a full list of the <a href=\"\">rendering events here<\/a>.<\/p>\n<p>Now in the execute method we need to take the camera color buffer that was assigned earlier, create a command buffer that has the color buffer as the render target and then draw a full screen quad with our material applied to it.<\/p>\n<pre class=\"language-cs\"><code class=\"language-cs\"><span class=\"token keyword\">public<\/span> <span class=\"token keyword\">override<\/span> <span class=\"token return-type class-name\"><span class=\"token keyword\">void<\/span><\/span> <span class=\"token function\">Execute<\/span><span class=\"token punctuation\">(<\/span><span class=\"token class-name\">ScriptableRenderContext<\/span> context<span class=\"token punctuation\">,<\/span> <span class=\"token keyword\">ref<\/span> <span class=\"token class-name\">RenderingData<\/span> renderingData<span class=\"token punctuation\">)<\/span>\n<span class=\"token punctuation\">{<\/span>\n    <span class=\"token class-name\"><span class=\"token keyword\">var<\/span><\/span> camera <span class=\"token operator\">=<\/span> renderingData<span class=\"token punctuation\">.<\/span>cameraData<span class=\"token punctuation\">.<\/span>camera<span class=\"token punctuation\">;<\/span>\n    <span class=\"token keyword\">if<\/span> <span class=\"token punctuation\">(<\/span>camera<span class=\"token punctuation\">.<\/span>cameraType <span class=\"token operator\">!=<\/span> CameraType<span class=\"token punctuation\">.<\/span>Game<span class=\"token punctuation\">)<\/span>\n        <span class=\"token keyword\">return<\/span><span class=\"token punctuation\">;<\/span>\n\n    <span class=\"token keyword\">if<\/span> <span class=\"token punctuation\">(<\/span>m_Material <span class=\"token operator\">==<\/span> <span class=\"token keyword\">null<\/span><span class=\"token punctuation\">)<\/span>\n        <span class=\"token keyword\">return<\/span><span class=\"token punctuation\">;<\/span>\n\n    <span class=\"token class-name\">CommandBuffer<\/span> cb <span class=\"token operator\">=<\/span> CommandBufferPool<span class=\"token punctuation\">.<\/span><span class=\"token function\">Get<\/span><span class=\"token punctuation\">(<\/span><span class=\"token named-parameter punctuation\">name<\/span><span class=\"token punctuation\">:<\/span> <span class=\"token string\">&quot;OutlinePass&quot;<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span>\n    cb<span class=\"token punctuation\">.<\/span><span class=\"token function\">BeginSample<\/span><span class=\"token punctuation\">(<\/span><span class=\"token string\">&quot;Outline Pass&quot;<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span>\n\n    m_Material<span class=\"token punctuation\">.<\/span><span class=\"token function\">SetColor<\/span><span class=\"token punctuation\">(<\/span>colorID<span class=\"token punctuation\">,<\/span> m_Color<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span>\n\n    cb<span class=\"token punctuation\">.<\/span><span class=\"token function\">SetRenderTarget<\/span><span class=\"token punctuation\">(<\/span><span class=\"token keyword\">new<\/span> <span class=\"token constructor-invocation class-name\">RenderTargetIdentifier<\/span><span class=\"token punctuation\">(<\/span>m_CameraColorTarget<span class=\"token punctuation\">,<\/span> <span class=\"token number\">0<\/span><span class=\"token punctuation\">,<\/span> CubemapFace<span class=\"token punctuation\">.<\/span>Unknown<span class=\"token punctuation\">,<\/span> <span class=\"token operator\">-<\/span><span class=\"token number\">1<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span>\n    cb<span class=\"token punctuation\">.<\/span><span class=\"token function\">DrawMesh<\/span><span class=\"token punctuation\">(<\/span>RenderingUtils<span class=\"token punctuation\">.<\/span>fullscreenMesh<span class=\"token punctuation\">,<\/span> Matrix4x4<span class=\"token punctuation\">.<\/span>identity<span class=\"token punctuation\">,<\/span> m_Material<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span>\n\n    cb<span class=\"token punctuation\">.<\/span><span class=\"token function\">EndSample<\/span><span class=\"token punctuation\">(<\/span><span class=\"token string\">&quot;Outline Pass&quot;<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span>\n    context<span class=\"token punctuation\">.<\/span><span class=\"token function\">ExecuteCommandBuffer<\/span><span class=\"token punctuation\">(<\/span>cb<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span>\n    cb<span class=\"token punctuation\">.<\/span><span class=\"token function\">Clear<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span>\n    CommandBufferPool<span class=\"token punctuation\">.<\/span><span class=\"token function\">Release<\/span><span class=\"token punctuation\">(<\/span>cb<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span>\n<span class=\"token punctuation\">}<\/span>\n<\/code><\/pre>\n<p>If you have no idea what a command buffer is <a href=\"https:\/\/fazz.dev\/articles\/building-srp-part-two\">I wrote some words about that recently<\/a>! This method starts pretty simple, make sure we&#x27;re in the Game camera and a material does exist. Then grab a command buffer from the CommandBufferPool.<\/p>\n<blockquote>\n<p>The <code>BeginSample<\/code> call is so we can see our pass clearly in the Frame Debugger and doesn&#x27;t affect functionality.<\/p>\n<\/blockquote>\n<p>We assign the material shader properies using the static IDs defined at the top of the file. Then set the command buffers render target to the cameras color buffer (the other parameters in this don&#x27;t really matter but feel free to look them up). <code>cb.DrawMesh<\/code> is rendering the full screen quad and assigning our material to it.<\/p>\n<p>Then that&#x27;s it we execute the command buffer against the rendering context, tidy it up, and release the buffer back to the pool.<\/p>\n<blockquote>\n<p>So you don&#x27;t need to do it exactly like this. There is a <code>cb.Blit()<\/code> method that you could use but it doesn&#x27;t work with XR devices and why not do it like this anyway it&#x27;s not much harder.<\/p>\n<\/blockquote>\n<p>To make sure all of this is working lets make a file called <code>Outline.shader<\/code>, I don&#x27;t recommend using any of the shader options in the create menu unless you know what you&#x27;re doing with converting it to an SRP shader. You can copy here for the rough <em>outline<\/em> of our shader.<\/p>\n<pre class=\"language-hlsl\"><code class=\"language-hlsl\">Shader <span class=\"token string\">&quot;RenderObjects\/Outline&quot;<\/span>\n<span class=\"token punctuation\">{<\/span>\n    Properties\n    <span class=\"token punctuation\">{<\/span>\n        <span class=\"token function\">_OutlineColor<\/span> <span class=\"token punctuation\">(<\/span><span class=\"token string\">&quot;OutlineColor&quot;<\/span><span class=\"token punctuation\">,<\/span> Color<span class=\"token punctuation\">)<\/span> <span class=\"token operator\">=<\/span> <span class=\"token punctuation\">(<\/span><span class=\"token number\">0.0<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token number\">0.0<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token number\">0.0<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token number\">1.0<\/span><span class=\"token punctuation\">)<\/span>\n    <span class=\"token punctuation\">}<\/span>\n    SubShader\n    <span class=\"token punctuation\">{<\/span>\n        Tags\n        <span class=\"token punctuation\">{<\/span>\n            <span class=\"token string\">&quot;RenderType&quot;<\/span><span class=\"token operator\">=<\/span><span class=\"token string\">&quot;Opaque&quot;<\/span> <span class=\"token string\">&quot;RenderingPipeline&quot;<\/span><span class=\"token operator\">=<\/span><span class=\"token string\">&quot;UniversalPipeline&quot;<\/span>\n        <span class=\"token punctuation\">}<\/span>\n        LOD <span class=\"token number\">100<\/span>\n        ZWrite Off Cull Off\n\n        Pass\n        <span class=\"token punctuation\">{<\/span>\n            Name <span class=\"token string\">&quot;OutlinePass&quot;<\/span>\n\n            HLSLPROGRAM\n            <span class=\"token macro property\"><span class=\"token directive-hash\">#<\/span><span class=\"token directive keyword\">pragma<\/span> <span class=\"token expression\">vertex vert<\/span><\/span>\n            <span class=\"token macro property\"><span class=\"token directive-hash\">#<\/span><span class=\"token directive keyword\">pragma<\/span> <span class=\"token expression\">fragment frag<\/span><\/span>\n            <span class=\"token macro property\"><span class=\"token directive-hash\">#<\/span><span class=\"token directive keyword\">include<\/span> <span class=\"token string\">&quot;Packages\/com.unity.render-pipelines.universal\/ShaderLibrary\/Core.hlsl&quot;<\/span><\/span>\n\n            <span class=\"token keyword\">struct<\/span> <span class=\"token class-name\">Attributes<\/span>\n            <span class=\"token punctuation\">{<\/span>\n                <span class=\"token keyword\">float4<\/span> positionHCS <span class=\"token operator\">:<\/span> POSITION<span class=\"token punctuation\">;<\/span>\n                <span class=\"token keyword\">float2<\/span> uv <span class=\"token operator\">:<\/span> TEXCOORD0<span class=\"token punctuation\">;<\/span>\n                UNITY_VERTEX_INPUT_INSTANCE_ID\n            <span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">;<\/span>\n\n            <span class=\"token keyword\">struct<\/span> <span class=\"token class-name\">Varyings<\/span>\n            <span class=\"token punctuation\">{<\/span>\n                <span class=\"token keyword\">float4<\/span> positionCS <span class=\"token operator\">:<\/span> SV_POSITION<span class=\"token punctuation\">;<\/span>\n                <span class=\"token keyword\">float2<\/span> uv <span class=\"token operator\">:<\/span> TEXCOORD0<span class=\"token punctuation\">;<\/span>\n            <span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">;<\/span>\n\n            Varyings <span class=\"token function\">vert<\/span><span class=\"token punctuation\">(<\/span>Attributes input<span class=\"token punctuation\">)<\/span>\n            <span class=\"token punctuation\">{<\/span>\n                Varyings o<span class=\"token punctuation\">;<\/span>\n                <span class=\"token function\">UNITY_SETUP_INSTANCE_ID<\/span><span class=\"token punctuation\">(<\/span>input<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span>\n\n                o<span class=\"token punctuation\">.<\/span>positionCS <span class=\"token operator\">=<\/span> <span class=\"token keyword\">float4<\/span><span class=\"token punctuation\">(<\/span>input<span class=\"token punctuation\">.<\/span>positionHCS<span class=\"token punctuation\">.<\/span>xy<span class=\"token punctuation\">,<\/span> <span class=\"token number\">0.0<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token number\">1.0<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span>\n                o<span class=\"token punctuation\">.<\/span>uv <span class=\"token operator\">=<\/span> input<span class=\"token punctuation\">.<\/span>uv<span class=\"token punctuation\">;<\/span>\n\n                <span class=\"token comment\">\/\/ If we&#x27;re on a Direct3D like platform<\/span>\n                <span class=\"token macro property\"><span class=\"token directive-hash\">#<\/span><span class=\"token directive keyword\">if<\/span> <span class=\"token expression\">UNITY_UV_STARTS_AT_TOP<\/span><\/span>\n                    <span class=\"token comment\">\/\/ Flip UVs<\/span>\n                    o<span class=\"token punctuation\">.<\/span>uv <span class=\"token operator\">=<\/span> o<span class=\"token punctuation\">.<\/span>uv <span class=\"token operator\">*<\/span> <span class=\"token keyword\">float2<\/span><span class=\"token punctuation\">(<\/span><span class=\"token number\">1.0<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token operator\">-<\/span><span class=\"token number\">1.0<\/span><span class=\"token punctuation\">)<\/span> <span class=\"token operator\">+<\/span> <span class=\"token keyword\">float2<\/span><span class=\"token punctuation\">(<\/span><span class=\"token number\">0.0<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token number\">1.0<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span>\n                <span class=\"token macro property\"><span class=\"token directive-hash\">#<\/span><span class=\"token directive keyword\">endif<\/span><\/span>\n                \n                <span class=\"token keyword\">return<\/span> o<span class=\"token punctuation\">;<\/span>\n            <span class=\"token punctuation\">}<\/span>\n\n            <span class=\"token keyword\">float4<\/span> _OutlineColor<span class=\"token punctuation\">;<\/span>\n\n            <span class=\"token keyword\">half4<\/span> <span class=\"token function\">frag<\/span><span class=\"token punctuation\">(<\/span>Varyings input<span class=\"token punctuation\">)<\/span> <span class=\"token operator\">:<\/span> SV_Target\n            <span class=\"token punctuation\">{<\/span>\n                <span class=\"token keyword\">return<\/span> _OutlineColor<span class=\"token punctuation\">;<\/span>\n            <span class=\"token punctuation\">}<\/span>\n            ENDHLSL\n        <span class=\"token punctuation\">}<\/span>\n    <span class=\"token punctuation\">}<\/span>\n<span class=\"token punctuation\">}<\/span>\n<\/code><\/pre>\n<p>The only couple of things I&#x27;ll mention here is that because of <del>magic<\/del> matrices our vertex position is already in homogenous clip space rather than object space so no need to transform it. I&#x27;m also using a Mac which means I&#x27;m rendering with Metal so my UVs look very different to the UVs in the original tutorial so I wrote this code inside of the <code>UNITY_UV_STARTS_AT_TOP<\/code> pre processor directive to make sure my UVs are the same as in the tutorial which I assume used OpenGL or something.<\/p>\n<p>Return <code>_OutlineColor<\/code> in the fragment shader and then assign this shader file to the slot in OutlineRendererFeature to hopefully see the whole screen turn whatever color is assigned in the Color property!<\/p>\n<p>Now you can actually do the other tutorial...<\/p>\n<p>This whole thing might feel like a bit of an indictment against URP. I can hear you saying &quot;Really? A whole tutorial just to get to the start of the other one when it was basically for free before.&quot;. But I actually think this is worth learning anyway and also, URP has a much nicer way of gathering the normals for the edge detection then is done in the tutorial. So take that BiRP!<\/p>\n<p>We&#x27;ve achieved steps 1 and 3 of the 4 step plan to success laid out earlier. In part two, edge detection and profit(?).<\/p>\n<blockquote>\n<p>Edit: There was to be no part two enjoy figuring things out<\/p>\n<\/blockquote>\n<p>If you like working things out for yourself then you&#x27;re welcome to only read this part then you might have fun trying and figure out for yourself how to make everything work from the older tutorial.<\/p>\n<h2>Resources that were really helpful when doing this<\/h2>\n<ul>\n<li><a href=\"https:\/\/roystan.net\/articles\/outline-shader\/\">Roystan - Outline Shader<\/a> (obviously)<\/li>\n<li><a href=\"https:\/\/danielilett.com\/2021-04-02-basics-3-shaders-in-urp\/\">Daniel Ilett - Shaders in URP<\/a><\/li>\n<li><a href=\"https:\/\/docs.unity3d.com\/Packages\/com.unity.render-pipelines.universal@12.1\/manual\/renderer-features\/how-to-fullscreen-blit-in-xr-spi.html\">Unity Docs - How to perform a full screen blit in Single Pass Instanced rendering in XR<\/a><\/li>\n<li><a href=\"https:\/\/docs.unity3d.com\/Packages\/com.unity.render-pipelines.universal@12.1\/manual\/renderer-features\/how-to-custom-effect-render-objects.html\">Unity Docs - Example: How to create a custom rendering effect using the Render Objects Renderer Feature<\/a><\/li>\n<\/ul>","url":"https:\/\/fazz.dev\/articles\/outline-shader-urp","title":"Setting up for a URP Fullscreen Outline Effect","summary":"There is no outlining here, instead some fundamental setup so that you can continue with the very good Roystan tutorial. This focuses mostly on Scriptable Renderer Features and Scriptable Render Passes.","date_modified":"2022-12-02T00:00:00.000Z","author":{"name":"Joe Fazzino"}},{"id":"https:\/\/fazz.dev\/articles\/building-srp-part-five","content_html":"<p>In hindsight this post probably should&#x27;ve come earlier in the series rather than being <em>the last one<\/em>. Better late than never though and you don&#x27;t need this kind of knowledge to understand what&#x27;s in the other posts.<\/p>\n<p>In this final post I&#x27;m going to discuss the difference between rendering an opaque material and rendering a transparent one. Hopefully by the end you understand why transparent materials are more expensive than opaques to render and maybe some cheat ways that you can simulate the effect of transparency without the cost.<\/p>\n<h2>The render queue<\/h2>\n<p>In the BiRP there were five default render queue tags: Background, Geometry, Alpha Test, Transparent and Overlay. You could chose what <em>Rendering Mode<\/em> in the material drop down. In the SRPs it is a similar story but there are also rendering events that you can hook into and add your own custom <em>Render Objects<\/em>.<\/p>\n<p>The rendering events also expose information depending upon if you&#x27;re using deferred rendering or forward rendering. For example, if you&#x27;re using deferred rendering it is possible to create a <em>Render Object<\/em> that happens before\/after the GBuffer is created.<\/p>\n<h2>Opaques<\/h2>\n<p>Opaque objects, by default, will write to the camera&#x27;s depth buffer as they&#x27;re drawn which is done to prevent overdraw in future passes, although it isn&#x27;t fool proof. You can cause overdraw in the geometry queue if you have a complex mesh that has intersecting geometry (which is bad and whoever doing your 3D models should avoid). Even though skyboxes are opaque objects they are rendered towards the end of the pipeline because otherwise they would also incur overdraw.<\/p>\n<p>It is possible to perform a <strong>depth pre-pass<\/strong> before opaques are rendered that will mean overdraw is mostly eliminated during the opaque pass. Any passes that require fragment shading thereafter will be able to compare the values in the depth buffer to determine which objects should be shaded and which should be skipped as they fail the z test. You can perform a depth pre-pass in both URP and HDRP but it is worth profiling and ensuring that your scene benefits from it.<\/p>\n<h2>Transparents<\/h2>\n<p>Transparent objects do <em>not<\/em> write to the depth buffer (shadows wouldn&#x27;t work properly if they did) so they need to be rendered back to front.<\/p>\n<p>To understand why, it&#x27;s easiest to think about how you would paint this scene with a brush and paint. If you want to paint a scene where you have two pieces of transparent glass in front of each other but the one furthest away from the camera has a dark tint. Starting by painting the closest window to you first  will mean that when it comes to paint the tinted window it will appear as though it&#x27;s on top of the one that is was supposed to be closest.<\/p>\n<blockquote>\n<p>Fun fact: <a href=\"https:\/\/en.wikipedia.org\/wiki\/Painter%27s_algorithm\">The Painter&#x27;s Algorithm<\/a> is a real thing, it is a classical rendering technique that was an alternative to Rasterisation back in the day but it&#x27;s too inefficent for real time. Rather than drawing pixel by pixel (like Rasterisation), it draws polygon by polygon after sorting the whole scene back to front. The downside of this is you can end up overdrawing all over the place as even though you&#x27;re sorting by depth you&#x27;re drawing the entire polygon each time so will invariably end up overdrawing the pixels.<\/p>\n<\/blockquote>\n<p>This back to front sorting is known as &#x27;z-sorting&#x27; and is a fairly expensive operation that requires sorting all of the render nodes that are part of the transparent pass. Not only is this expensive but it means that batching is also less efficent!<\/p>\n<h2>Cheap transparency - Alpha Clipping<\/h2>\n<p>It is possible to render opaque objects that have holes in them. In a fragment shader the <code>clip(value)<\/code> function will discard the current fragment from being drawn if the value in the parentheses is less than 0. This means you would be able to see whatever is behind it because you wouldn&#x27;t be overwritting the color buffer at that pixel. It also wouldn&#x27;t write those clipped fragments to the depth buffer either so the skybox can still render in the gaps.<\/p>\n<p>This is where the Alpha Test queue comes in. If you&#x27;re going to be rendering some opaques with holes in them then it is important to render them after the rest of the opaques have been drawn as opaques are not by default z-sorted and you might not be using a depth pre-pass. In building the custom renderer the code for alpha clipping looks like this.<\/p>\n<pre class=\"language-hlsl\"><code class=\"language-hlsl\"><span class=\"token macro property\"><span class=\"token directive-hash\">#<\/span><span class=\"token directive keyword\">if<\/span> <span class=\"token expression\"><span class=\"token function\">defined<\/span><span class=\"token punctuation\">(<\/span>_CLIPPING<span class=\"token punctuation\">)<\/span><\/span><\/span>\n    <span class=\"token function\">clip<\/span><span class=\"token punctuation\">(<\/span>base<span class=\"token punctuation\">.<\/span>a <span class=\"token operator\">-<\/span> _Cutoff<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span>\n    <span class=\"token comment\">\/\/ Discards fragment if param &lt;= 0<\/span>\n<span class=\"token macro property\"><span class=\"token directive-hash\">#<\/span><span class=\"token directive keyword\">endif<\/span><\/span>\n<\/code><\/pre>\n<p>Here, if clipping is enabled then we clip the pixel if the alpha value of the fragment is less than the cutoff which is defined in the shaders properties. As clipping is either discarding a fragment or not discarding it you can only render either the full color or nothing. If you need a translucent or fade effect then you must use render as part of the transparent pass.<\/p>\n<h2>Series Conclusion<\/h2>\n<p>Thanks for reading these posts. I&#x27;ve found it harder than I expected to write them but it feels like a good investment of time and it&#x27;s really solidfied the things I&#x27;ve learnt as I&#x27;m absolutely terrified of saying something here that ends up being wrong! Even though this is (I think) the shortest post, it took ages because I spent half a day trying to figure out how the render queue in SRP worked which involved talking to the people who make the render pipeline \ud83d\ude05. If you&#x27;ve read any of this and found it particularly helpful then please let me know and it will motivate me to do more, or don&#x27;t and I might end up doing it anyway!<\/p>","url":"https:\/\/fazz.dev\/articles\/building-srp-part-five","title":"Building a custom renderer for Unity - Rendering opaques and transparents","summary":"In which I go off on a tangent about the Painter's Algorithm when discussing transparent rendering.","date_modified":"2022-11-25T00:00:00.000Z","author":{"name":"Joe Fazzino"}},{"id":"https:\/\/fazz.dev\/articles\/building-srp-part-four","content_html":"<p>When rendering (real-time) shadows in a scene, they&#x27;re are grouped into two categories. <strong>Directional Shadows<\/strong> are suitible for lights representing the sun and will cast shadows for objects marked as a shadow caster and render shadows on objects in the scene marked as a shadow reciever within a predefined &#x27;max shadow distance&#x27;.<\/p>\n<p><strong>Punctual Shadows<\/strong> are shadows that are cast from punctual lights like Point Lights and Spotlights. Both need to be tackled differently and frankly I don&#x27;t really know how punctual shadows exactly work yet but I did implement directional shadows in my custom renderer so here I&#x27;ll be discussing the things I learnt about realtime directional shadows.<\/p>\n<blockquote>\n<p>I&#x27;ll be calling the directional light in the scene the sun from now on even though you can have multiple directional lights in the scene<\/p>\n<\/blockquote>\n<p>It personally helped me to break it down by thinking <em>&#x27;what actually is a shadow&#x27;<\/em>. A shadow is what you get when the a light source can no longer directly see a surface because something is blocking it.<\/p>\n<p>We already have the capacity to know what objects block other objects in rendering by using a <strong>depth buffer<\/strong> (also known as a z-buffer). When you render an object as an &#x27;Opaque&#x27; then it is also being drawn to the cameras depth buffer, this is done so when the final render happens we don&#x27;t waste time drawing to the same frame buffer position multiple times where we might have overlapping geometry. Overwriting the frame buffer data multiple times is known as &#x27;overdraw&#x27;.<\/p>\n<figure><img alt=\"divided in two vertically, showing a scene with three objects, the top half has materials coloured and the bottom is a black and white representation where as objects are further away they are lighter and objects closer are darker\" srcSet=\"\/_next\/image?url=%2F_next%2Fstatic%2Fmedia%2FZ_buffer.5c5629eb.png&amp;w=384&amp;q=75 1x, \/_next\/image?url=%2F_next%2Fstatic%2Fmedia%2FZ_buffer.5c5629eb.png&amp;w=640&amp;q=75 2x\" src=\"\/_next\/image?url=%2F_next%2Fstatic%2Fmedia%2FZ_buffer.5c5629eb.png&amp;w=640&amp;q=75\" width=\"310\" height=\"400\" decoding=\"async\" data-nimg=\"1\" loading=\"lazy\" style=\"color:transparent;margin:auto\"\/><figcaption align=\"center\"><a href=\"https:\/\/en.wikipedia.org\/wiki\/Z-buffering\">Wikipedia - Z-Buffering<\/a><\/figcaption><\/figure>\n<p>If a shadow is what you get when the sun can&#x27;t see an object because another is blocking it, what we need is a depth buffer but from the perspective of the sun, rather than the camera. There&#x27;s a lot of math that goes into actually figuring out the position that the depth buffer should be rendered from so I recommend actually just looking at <a href=\"https:\/\/catlikecoding.com\/unity\/tutorials\/custom-srp\/directional-shadows\/\">the full tutorial<\/a> if you&#x27;re interested.<\/p>\n<h2>Shadow Atlas<\/h2>\n<p>A shadow atlas is just the label that we assign to the depth buffer storing our shadow data. In order to write data to the atlas a specific ShadowCaster shader pass needs to be performed.\nThe point of this pass is two-fold, it means that we have seperated the idea of something being drawn in the final output and it&#x27;s ability to cast shadows, and it means we can populate the atlas with the correct data.<\/p>\n<blockquote>\n<p>A &#x27;pass&#x27; is when you go over objects in the scene, for some reason I imagine it a bit like passing my hand over the whole screen, if I have multiple passes then I do it multiple times (in my head definetely not in real life).<\/p>\n<\/blockquote>\n<p>To demonstrate what this looks like I&#x27;m going to bring back the three amigos<\/p>\n<figure><img alt=\"scene showing a red capsule, a yellow cube and a grey sphere with cut outs in it, all are casting shadows, the cube and capsule cast to the floor while the sphere casts to the capsule\" srcSet=\"\/_next\/image?url=%2F_next%2Fstatic%2Fmedia%2Fsimple_scene_noshadow.07363511.png&amp;w=750&amp;q=75 1x, \/_next\/image?url=%2F_next%2Fstatic%2Fmedia%2Fsimple_scene_noshadow.07363511.png&amp;w=1920&amp;q=75 2x\" src=\"\/_next\/image?url=%2F_next%2Fstatic%2Fmedia%2Fsimple_scene_noshadow.07363511.png&amp;w=1920&amp;q=75\" width=\"711\" height=\"400\" decoding=\"async\" data-nimg=\"1\" loading=\"lazy\" style=\"color:transparent;margin:auto\"\/><figcaption align=\"center\">The scene without any shadows being cast<\/figcaption><\/figure>\n<figure><img alt=\"depth texture being rendered from the perspective of the sun, the objects are small in the texture except for the plane the objects are on which is comparitively large\" srcSet=\"\/_next\/image?url=%2F_next%2Fstatic%2Fmedia%2Fshadowpass.93ff6bfc.png&amp;w=640&amp;q=75 1x, \/_next\/image?url=%2F_next%2Fstatic%2Fmedia%2Fshadowpass.93ff6bfc.png&amp;w=828&amp;q=75 2x\" src=\"\/_next\/image?url=%2F_next%2Fstatic%2Fmedia%2Fshadowpass.93ff6bfc.png&amp;w=828&amp;q=75\" width=\"402\" height=\"401\" decoding=\"async\" data-nimg=\"1\" loading=\"lazy\" style=\"color:transparent;margin:auto\"\/><figcaption align=\"center\">What the Shadow Atlas looks like for this scene. Notably you can see the sphere with it&#x27;s cutouts in front of the capsule looking lighter.<\/figcaption><\/figure>\n<p>A shadow atlas is just a render texture, so we can configure the resolution and raise it which will make the shadows sharper but will also increase the memory used. In order to actually draw shadows in the scene each shadow reciever in the scene will need to sample the shadow atlas at the position that it maps to. In the tutorial this is referred to as a view space to shadow texture space transformation. This is done to see if the fragment should be the correct surface color or if it should be shaded and some <em>attenuation<\/em> needs to be added.<\/p>\n<p>I&#x27;m not going to explain the math that goes into how the shadows are actually rendered because I&#x27;ve also not described how the lit shader in this scene even works which is a pretty key component. The shadow map is sampled by each object and where it knows there is an object in front of itself it can attenuate the amount of light that should be shown on the surface where attunuation of 1 is no shadow (surface color) and 0 is full shadow (black).<\/p>\n<h2>Cascades<\/h2>\n<p>When above I mentioned that you create a depth buffer from the perspective of the sun? It doesn&#x27;t really make sense because directional lights in Unity don&#x27;t have a source position, they&#x27;re infinite, so we pick a distance to render from. An issue of this approach though is that the further away you are, the less definition you will have on your shadows because they will occupy less pixels in the atlas. You can see the effect this has in the scene below.<\/p>\n<figure><img alt=\"the same scene with the three objects but this time casting shadows, the shadows being cast from the sphere onto the capsule are quite aliased and blurry\" srcSet=\"\/_next\/image?url=%2F_next%2Fstatic%2Fmedia%2Fsimple_scene.f6d89057.png&amp;w=750&amp;q=75 1x, \/_next\/image?url=%2F_next%2Fstatic%2Fmedia%2Fsimple_scene.f6d89057.png&amp;w=1920&amp;q=75 2x\" src=\"\/_next\/image?url=%2F_next%2Fstatic%2Fmedia%2Fsimple_scene.f6d89057.png&amp;w=1920&amp;q=75\" width=\"711\" height=\"400\" decoding=\"async\" data-nimg=\"1\" loading=\"lazy\" style=\"color:transparent;margin:auto\"\/><figcaption align=\"center\">Notice how the shadow from the sphere is visually soft and aliased but the shadow cast from the capsule is sharp<\/figcaption><\/figure>\n<p>If we change the distance that we render the shadows from to be closer to the objects in the scene then this is the result we get.<\/p>\n<figure><img alt=\"the same as above but the shadows from the sphere are sharp and the shadows from the capsule and the cube fade into nothing\" srcSet=\"\/_next\/image?url=%2F_next%2Fstatic%2Fmedia%2Fsimple_scene_max_distancelow.e7a9a67b.png&amp;w=750&amp;q=75 1x, \/_next\/image?url=%2F_next%2Fstatic%2Fmedia%2Fsimple_scene_max_distancelow.e7a9a67b.png&amp;w=1920&amp;q=75 2x\" src=\"\/_next\/image?url=%2F_next%2Fstatic%2Fmedia%2Fsimple_scene_max_distancelow.e7a9a67b.png&amp;w=1920&amp;q=75\" width=\"711\" height=\"400\" decoding=\"async\" data-nimg=\"1\" loading=\"lazy\" style=\"color:transparent;margin:auto\"\/><figcaption align=\"center\">Now notice that the shadow cast from the sphere is nice and sharp but we aren&#x27;t rendering the full shadow from the capsule anymore because the max shadow distance is too low.<\/figcaption><\/figure>\n<p>The way to solve this is by using <strong>Shadow Cascades<\/strong> which is a technique where we perform our shadow pass multiple times with different max distance values and then blend the shadows in the scene based on their proximity to the camera. The closest shadows can be sampled from the lowest max distance cascade but further objects are sampled from cascades with higher max distances.<\/p>\n<p>This technique works well when combined with some sort of blending for shadows that are on the border of a cascade so you don&#x27;t get a really harsh line where the shadow clearly improves\/deteriorates in resolution. Most games I&#x27;ve seen have used four cascades but because you&#x27;re rendering the shadow map four times it can have a significant impact on render time so reducing the cascade count can be necessary.<\/p>\n<p>Here is what the shadow atlas looks like for this scene when we&#x27;re rendering all four cascades.<\/p>\n<figure><img alt=\"\" srcSet=\"\/_next\/image?url=%2F_next%2Fstatic%2Fmedia%2Ffour_cascades.7476e56a.png&amp;w=640&amp;q=75 1x, \/_next\/image?url=%2F_next%2Fstatic%2Fmedia%2Ffour_cascades.7476e56a.png&amp;w=828&amp;q=75 2x\" src=\"\/_next\/image?url=%2F_next%2Fstatic%2Fmedia%2Ffour_cascades.7476e56a.png&amp;w=828&amp;q=75\" width=\"400\" height=\"398\" decoding=\"async\" data-nimg=\"1\" loading=\"lazy\" style=\"color:transparent;margin:auto\"\/><figcaption align=\"center\">Now the shadow recievers can pick which cascade to use for any given fragment from this atlas<\/figcaption><\/figure>\n<p>So long as you can afford the performance and tweak the values to be suitable to the level being made then this resolves the shadow distance resolution issues.<\/p>\n<h2>Shadow Bias<\/h2>\n<p>This is something I had no idea about before I implemented this in the custom SRP, it was very easy to implement but quite hard for me to get my head around.<\/p>\n<p>Everytime I&#x27;ve shown a screenshot of my fully rendered scene I&#x27;ve been showing it with a &#x27;depth bias&#x27; being applied. This is what it looks like if I remove this bias.<\/p>\n<figure><img alt=\"same scene with three objects but all surfaces have thin black vertical lines at frequent intervals\" srcSet=\"\/_next\/image?url=%2F_next%2Fstatic%2Fmedia%2Fself_shadowing.2d397766.png&amp;w=750&amp;q=75 1x, \/_next\/image?url=%2F_next%2Fstatic%2Fmedia%2Fself_shadowing.2d397766.png&amp;w=1920&amp;q=75 2x\" src=\"\/_next\/image?url=%2F_next%2Fstatic%2Fmedia%2Fself_shadowing.2d397766.png&amp;w=1920&amp;q=75\" width=\"711\" height=\"400\" decoding=\"async\" data-nimg=\"1\" loading=\"lazy\" style=\"color:transparent;margin:auto\"\/><figcaption align=\"center\">Everything has these awful dark lines and looks blocky<\/figcaption><\/figure>\n<p>This is known as <strong>Shadow Acne<\/strong> and it happens when a surface isn&#x27;t perfectly aligned with the light i.e. not perfectly parallel. Some fragments will fail the depth test to see if they should be shaded or not due to floating point errors when comparing the texel depth from the perspective of the camera and the texel depth from the perspective of the light.<\/p>\n<p>The fix for this is to add a bias that gets applied at the point where the shadow atlas is being rendered that effectively offsets all of the values so that these comparisions are less likely to fail.<\/p>\n<p>When adding a bias it&#x27;s important to not be too over zealous with the parameters as you&#x27;ll cause what&#x27;s known as <strong>Peter Panning<\/strong> where the shadow looks as though it&#x27;s become detached from the object that should be casting it.<\/p>\n<figure><img alt=\"the same scene with the shadows looking detached from all of their casters by a considerable margin\" srcSet=\"\/_next\/image?url=%2F_next%2Fstatic%2Fmedia%2Fpeter_panning.8608cce8.png&amp;w=750&amp;q=75 1x, \/_next\/image?url=%2F_next%2Fstatic%2Fmedia%2Fpeter_panning.8608cce8.png&amp;w=1920&amp;q=75 2x\" src=\"\/_next\/image?url=%2F_next%2Fstatic%2Fmedia%2Fpeter_panning.8608cce8.png&amp;w=1920&amp;q=75\" width=\"665\" height=\"374\" decoding=\"async\" data-nimg=\"1\" loading=\"lazy\" style=\"color:transparent;margin:auto\"\/><figcaption align=\"center\">This is a very exaggerated example<\/figcaption><\/figure>\n<p>Biases are configured per light as different scenes will have different requirements, it is worth playing with these values when you feel as though there are shadows being cast when there shouldn&#x27;t be or shadows aren&#x27;t correctly attached to their casters.<\/p>\n<p>Once again there&#x27;s lots more details that I could go into about this but you should just do the tutorial if you want to know more.<\/p>\n<h2>Conclusion<\/h2>\n<p>That&#x27;s not actually everything, I didn&#x27;t mention how to handle multiple directional shadows, shadow filtering, cascade blending techniques, different shadow bias techniques etc. This post does have to end eventually though and I&#x27;m satisfied that if you read this and are intrigued then you would benefit more from just doing the tutorials rather than reading my CliffsNotes version of them.<\/p>\n<h3>Related Posts\/Links<\/h3>\n<ul>\n<li><a href=\"https:\/\/fazz.dev\/articles\/building-srp-part-three\">Building a custom renderer for Unity - Draw Calls &amp; Batching<\/a><\/li>\n<li><a href=\"https:\/\/fazz.dev\/articles\/building-srp-part-two\">Building a custom renderer for Unity - Command Buffers<\/a><\/li>\n<li><a href=\"https:\/\/fazz.dev\/articles\/building-srp-part-one\">Building a custom renderer for Unity - Intro<\/a><\/li>\n<li><a href=\"https:\/\/docs.unity3d.com\/Manual\/shadow-cascades.html\">Shadow Cascades - Unity Docs<\/a><\/li>\n<\/ul>","url":"https:\/\/fazz.dev\/articles\/building-srp-part-four","title":"Building a custom renderer for Unity - Directional Shadow Rendering","summary":"Rendering shadows is a computationally intensive task, but vital for most real-time rendering applications. Here I discuss some technical details about why they are heavy, techniques to prevent shadow artifacts and a \"light\" explanation of how they are drawn\/sampled.","date_modified":"2022-11-24T00:00:00.000Z","author":{"name":"Joe Fazzino"}},{"id":"https:\/\/fazz.dev\/articles\/building-srp-part-three","content_html":"<p>This is probably the most important part of rendering with the new SRPs so it&#x27;s important that we get some ideas straight from the off.<\/p>\n<h2>Draw Call<\/h2>\n<p>A &#x27;draw call&#x27; is a bit of a vague term that describes the CPU telling the GPU to draw... something.\nIt&#x27;s vague on purpose, you might read around online loads of people confidently saying\n&quot;the number of draw calls is how many things are being drawn on your screen&quot; or &quot;a draw call is when you have a mesh and a material being drawn&quot;\nthese things are sort of true but also too specific. The Unity documentation has a great definition that is suitably vague for my liking.<\/p>\n<blockquote>\n<p>A draw call tells the graphics API what to draw and how to draw it. Each draw call contains all the information the graphics API needs to draw on the screen, such as information about textures, shaders, and buffers. Draw calls can be resource intensive, but often the preparation for a draw call is more resource intensive than the draw call itself.<\/p>\n<\/blockquote>\n<p>So it makes sense why reducing draw calls is often common advice you&#x27;ll find about optimising how long your GPU takes to render your frame. It stands to reason that if you have less draw calls you&#x27;ll also have less total preperation time and you&#x27;ll make performance gains.<\/p>\n<h2>Batching<\/h2>\n<p>Batching is a term used to describe optimising a collection of draw calls. This can be done by reducing the number of draw calls or reducing the render-state changes between them (which is the heavy pre-draw call work discussed earlier).<\/p>\n<p>Common batching techniques that you might be familiar with in Unity are:<\/p>\n<ul>\n<li><strong>GPU Instancing<\/strong>: This isn&#x27;t actually &#x27;batching&#x27; but it is draw call optimisation. Only works when you&#x27;re using the exact same mesh with the exact same material across the scene, very effective at moving work from CPU to GPU but worth bearing in mind it&#x27;s not a silver bullet (if you&#x27;re already GPU bound it&#x27;s not going to help).<\/li>\n<li><strong>Static Batching<\/strong>: When you check that box in the inspector that says <em>Static<\/em> then Unity will attempt to combine meshes that don&#x27;t move to reduce render-state changes per frame (doesn&#x27;t reduce draw calls). This does have <a href=\"https:\/\/docs.unity3d.com\/Manual\/static-batching.html#performance-implications\">performance caveats<\/a>.<\/li>\n<li><strong>Dynamic Batching<\/strong>: Not really worth using anymore unless you&#x27;re targetting low end devices (it&#x27;s actually not compatible with HDRP for this reason) as the overhead from the draw call is generally lower than the overhead from the batching. Theoretically good for very small meshes like quads for particle effects.<\/li>\n<\/ul>\n<blockquote>\n<p>Listing the above is a classic interview question<\/p>\n<\/blockquote>\n<h2>BiRP vs SRP Frame<\/h2>\n<p>Here is a capture in the <a href=\"https:\/\/docs.unity3d.com\/Manual\/FrameDebugger.html\">Frame Debugger<\/a> of the three amigos of basic meshes being rendered in the Built-in Render Pipeline.<\/p>\n<figure><img alt=\"frame from built in render pipeline showing 3 distinct draw calls for 3 different meshes\" srcSet=\"\/_next\/image?url=%2F_next%2Fstatic%2Fmedia%2Fbirp-frame.b7f87498.png&amp;w=1080&amp;q=75 1x, \/_next\/image?url=%2F_next%2Fstatic%2Fmedia%2Fbirp-frame.b7f87498.png&amp;w=1920&amp;q=75 2x\" src=\"\/_next\/image?url=%2F_next%2Fstatic%2Fmedia%2Fbirp-frame.b7f87498.png&amp;w=1920&amp;q=75\" width=\"917\" height=\"443\" decoding=\"async\" data-nimg=\"1\" loading=\"lazy\" style=\"color:transparent\"\/><figcaption align=\"center\">If you&#x27;ve never used the Frame Debugger I highly recommend you check it out! You can step through how your scene is renderered to understand which part is responsible for what you see.<\/figcaption><\/figure>\n<p>And here is the same scene but being rendered with the Universal Render Pipeline.<\/p>\n<figure><img alt=\"frame from urp showing 1 SRP batch\" srcSet=\"\/_next\/image?url=%2F_next%2Fstatic%2Fmedia%2Fsrp-frame.41cd6862.png&amp;w=1080&amp;q=75 1x, \/_next\/image?url=%2F_next%2Fstatic%2Fmedia%2Fsrp-frame.41cd6862.png&amp;w=1920&amp;q=75 2x\" src=\"\/_next\/image?url=%2F_next%2Fstatic%2Fmedia%2Fsrp-frame.41cd6862.png&amp;w=1920&amp;q=75\" width=\"917\" height=\"443\" decoding=\"async\" data-nimg=\"1\" loading=\"lazy\" style=\"color:transparent\"\/><figcaption align=\"center\">In SRPs you don&#x27;t get to see each draw call the same way but if you want to know how many are made by each batch that information is available when you click on the batch in the right side (offscreen)<\/figcaption><\/figure>\n<p>You can see that in the BiRP frame there are three distinct draw calls for each mesh, there is no batching being performed because GPU instancing doesn&#x27;t work across different mesh and the meshes aren&#x27;t marked as static.<\/p>\n<p>In the URP frame however, you can see that there is only one call within the Opaque pass that says <em>SRP Batch<\/em>. In essence this is the magic of the SRP batcher, with no additional work (at the editor level) we now have batched draw calls!<\/p>\n<h2>The SRP Batcher<\/h2>\n<p>The <a href=\"https:\/\/docs.unity3d.com\/Manual\/SRPBatcher.html\">Unity documentation for this<\/a> is actually really good so I&#x27;ll briefly explain what is happening and then you should look there for nerdy details!<\/p>\n<p>The SRP batcher is able to batch draw calls together that use the same <em>shader variants<\/em>. What this means is you can have any amount of materials that all have different property values but all use the same shader and no matter what meshes you\napply them to, the draw calls will be batched. The kind of gains that you can expect from this depend on how many shader variants you have in your project but generally it is <strong>very<\/strong> good.<\/p>\n<p>The way this works is by using fixed size buffers for material property data size and storing object data buffers in their own &#x27;Per Object Large Buffer&#x27;. By using fixed buffers it means that render-state changes are minimised between draw calls as the material is not changing and properties that belong to the mesh\ncan be processed seperately by special dedicated fast code for object level properties like vertex data.<\/p>\n<p>In implementing shaders that we want to run on our scriptable render pipeline, it&#x27;s very straightforward to add support for all kinds of batching.<\/p>\n<p>Any properties that your shader uses need to be wrapped in UnityPerMaterial calls.<\/p>\n<pre class=\"language-hlsl\"><code class=\"language-hlsl\">Properties\n    <span class=\"token punctuation\">{<\/span>\n        <span class=\"token function\">_BaseMap<\/span><span class=\"token punctuation\">(<\/span><span class=\"token string\">&quot;Texture&quot;<\/span><span class=\"token punctuation\">,<\/span> 2D<span class=\"token punctuation\">)<\/span> <span class=\"token operator\">=<\/span> <span class=\"token string\">&quot;white&quot;<\/span> <span class=\"token punctuation\">{<\/span><span class=\"token punctuation\">}<\/span>\n        <span class=\"token function\">_BaseColor<\/span><span class=\"token punctuation\">(<\/span><span class=\"token string\">&quot;Color&quot;<\/span><span class=\"token punctuation\">,<\/span> Color<span class=\"token punctuation\">)<\/span> <span class=\"token operator\">=<\/span> <span class=\"token punctuation\">(<\/span><span class=\"token number\">0.5<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token number\">0.5<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token number\">0.5<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token number\">1.0<\/span><span class=\"token punctuation\">)<\/span>\n        <span class=\"token function\">_Cutoff<\/span><span class=\"token punctuation\">(<\/span><span class=\"token string\">&quot;Alpha Cutoff&quot;<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token function\">Range<\/span><span class=\"token punctuation\">(<\/span><span class=\"token number\">0.0<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token number\">1.0<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">)<\/span> <span class=\"token operator\">=<\/span> <span class=\"token number\">0.5<\/span>\n        <span class=\"token function\">_Metallic<\/span> <span class=\"token punctuation\">(<\/span><span class=\"token string\">&quot;Metallic&quot;<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token function\">Range<\/span><span class=\"token punctuation\">(<\/span><span class=\"token number\">0<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token number\">1<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">)<\/span> <span class=\"token operator\">=<\/span> <span class=\"token number\">0<\/span>\n        <span class=\"token function\">_Smoothness<\/span> <span class=\"token punctuation\">(<\/span><span class=\"token string\">&quot;Smoothness&quot;<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token function\">Range<\/span><span class=\"token punctuation\">(<\/span><span class=\"token number\">0<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token number\">1<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">)<\/span> <span class=\"token operator\">=<\/span> <span class=\"token number\">0.5<\/span>\n    <span class=\"token punctuation\">}<\/span>\n\nSubshader <span class=\"token punctuation\">{<\/span>\n    Pass <span class=\"token punctuation\">{<\/span>\n        <span class=\"token punctuation\">.<\/span><span class=\"token punctuation\">.<\/span><span class=\"token punctuation\">.<\/span>\n        <span class=\"token function\">UNITY_INSTANCING_BUFFER_START<\/span><span class=\"token punctuation\">(<\/span>UnityPerMaterial<span class=\"token punctuation\">)<\/span>\n            <span class=\"token function\">UNITY_DEFINE_INSTANCED_PROP<\/span><span class=\"token punctuation\">(<\/span><span class=\"token keyword\">float4<\/span><span class=\"token punctuation\">,<\/span> _BaseMap_ST<span class=\"token punctuation\">)<\/span>\n            <span class=\"token function\">UNITY_DEFINE_INSTANCED_PROP<\/span><span class=\"token punctuation\">(<\/span><span class=\"token keyword\">float4<\/span><span class=\"token punctuation\">,<\/span> _BaseColor<span class=\"token punctuation\">)<\/span>\n            <span class=\"token function\">UNITY_DEFINE_INSTANCED_PROP<\/span><span class=\"token punctuation\">(<\/span><span class=\"token keyword\">float<\/span><span class=\"token punctuation\">,<\/span> _Cutoff<span class=\"token punctuation\">)<\/span>\n            <span class=\"token function\">UNITY_DEFINE_INSTANCED_PROP<\/span><span class=\"token punctuation\">(<\/span><span class=\"token keyword\">float<\/span><span class=\"token punctuation\">,<\/span> _Metallic<span class=\"token punctuation\">)<\/span>\n            <span class=\"token function\">UNITY_DEFINE_INSTANCED_PROP<\/span><span class=\"token punctuation\">(<\/span><span class=\"token keyword\">float<\/span><span class=\"token punctuation\">,<\/span> _Smoothness<span class=\"token punctuation\">)<\/span>\n        <span class=\"token function\">UNITY_INSTANCING_BUFFER_END<\/span><span class=\"token punctuation\">(<\/span>UnityPerMaterial<span class=\"token punctuation\">)<\/span>\n        <span class=\"token punctuation\">.<\/span><span class=\"token punctuation\">.<\/span><span class=\"token punctuation\">.<\/span>\n<\/code><\/pre>\n<blockquote>\n<p>Note: If you read materials online you&#x27;ll find that CBUFFER_START(UnityPerMaterial) is used. That also works but if you also want to be able to support GPU instancing in your shader you can use UNITY_INSTANCING_BUFFER_START. CBUFFER means Constant Buffer by the way!<\/p>\n<\/blockquote>\n<p>Every shader will also need the inputs from Unity for the object data such as it&#x27;s transformation matrices.<\/p>\n<pre class=\"language-hlsl\"><code class=\"language-hlsl\"><span class=\"token function\">CBUFFER_START<\/span><span class=\"token punctuation\">(<\/span>UnityPerDraw<span class=\"token punctuation\">)<\/span>\n    <span class=\"token keyword\">float4x4<\/span> unity_ObjectToWorld<span class=\"token punctuation\">;<\/span>\n    <span class=\"token keyword\">float4x4<\/span> unity_WorldToObject<span class=\"token punctuation\">;<\/span>\n    <span class=\"token keyword\">float4<\/span> unity_LODFade<span class=\"token punctuation\">;<\/span>\n    real4 unity_WorldTransformParams<span class=\"token punctuation\">;<\/span>\nCBUFFER_END\n<\/code><\/pre>\n<p>Now the shader (and our custom renderer) can support SRP batching and GPU instancing with.<\/p>\n<pre class=\"language-cs\"><code class=\"language-cs\">GraphicsSettings<span class=\"token punctuation\">.<\/span>useScriptableRenderPipelineBatching <span class=\"token operator\">=<\/span> <span class=\"token boolean\">true<\/span><span class=\"token punctuation\">;<\/span> \n<span class=\"token keyword\">this<\/span><span class=\"token punctuation\">.<\/span>useGPUInstancing <span class=\"token operator\">=<\/span> <span class=\"token boolean\">true<\/span><span class=\"token punctuation\">;<\/span>\n<\/code><\/pre>\n<p>Where <code>this<\/code> is referring to the class inheriting from <code>RenderPipeline<\/code>.<\/p>\n<h2>How to break SRP batching<\/h2>\n<p>You might want to do this on purpose so that you can use GPU instancing or you might accidentally do this. An unfortunate side effect of this very good new batching is that in the past, where you would use <code>MaterialPropertyBlock<\/code> to set a property of a material\nlike changing the base color you will break SRP batching (for that mesh). It breaks because, even though you aren&#x27;t modifying the material buffer size, you are changing it which means that the data needs to be rebound to the CBUFFERs.<\/p>\n<p>Here is a photo I borrowed that shows why this rebinding is an issue from the excellent offical <a href=\"https:\/\/blog.unity.com\/technology\/srp-batcher-speed-up-your-rendering\">Unity blog post by Arnaud Carr\u00e9<\/a> where he talks in far more detail about the SRP batcher.<\/p>\n<figure><img alt=\"the flow of how the SRP batcher works with details about material data being changed\" srcSet=\"\/_next\/image?url=%2F_next%2Fstatic%2Fmedia%2Fsrpbatcher.4db10a30.png&amp;w=1080&amp;q=75 1x, \/_next\/image?url=%2F_next%2Fstatic%2Fmedia%2Fsrpbatcher.4db10a30.png&amp;w=2048&amp;q=75 2x\" src=\"\/_next\/image?url=%2F_next%2Fstatic%2Fmedia%2Fsrpbatcher.4db10a30.png&amp;w=2048&amp;q=75\" width=\"1022\" height=\"497\" decoding=\"async\" data-nimg=\"1\" loading=\"lazy\" style=\"color:transparent\"\/><figcaption align=\"center\">When the data changes for the material it will need to repeat the steps in the flow for material data<\/figcaption><\/figure>\n<p>How to get around this issue is to create the material before you&#x27;re in playmode. If you need to do something like animate a material from one color to another you can lerp between materials using <code>Material.Lerp<\/code> and this shouldn&#x27;t break batching.<\/p>\n<h2>Conclusion<\/h2>\n<p>This post was so long I felt like I had to write a conclusion! In my opinion though this is the most important part of the whole experience, learning how the batcher works is going to help me make better games and I hope after\nreading this it can help you too!<\/p>\n<h3>Related Posts\/Links<\/h3>\n<ul>\n<li><a href=\"https:\/\/fazz.dev\/articles\/building-srp-part-four\">Building a custom renderer for Unity - Directional Shadow Rendering<\/a><\/li>\n<li><a href=\"https:\/\/fazz.dev\/articles\/building-srp-part-two\">Building a custom renderer for Unity - Command Buffers<\/a><\/li>\n<li><a href=\"https:\/\/fazz.dev\/articles\/building-srp-part-one\">Building a custom renderer for Unity - Intro<\/a><\/li>\n<li><a href=\"https:\/\/blog.unity.com\/technology\/srp-batcher-speed-up-your-rendering\">SRP Batcher: Speed up your rendering!<\/a><\/li>\n<li><a href=\"https:\/\/docs.unity3d.com\/Manual\/optimizing-draw-calls.html\">Optimizing Draw Calls - Unity Docs<\/a><\/li>\n<li><a href=\"https:\/\/docs.unity3d.com\/Manual\/SRPBatcher.html\">SRP Batcher - Unity Docs<\/a><\/li>\n<li><a href=\"https:\/\/docs.unity3d.com\/Manual\/DrawCallBatching.html\">Draw Call Batching - Unity Docs<\/a><\/li>\n<li><a href=\"https:\/\/docs.unity3d.com\/Manual\/GPUInstancing.html\">GPU Instancing - Unity Docs<\/a><\/li>\n<\/ul>","url":"https:\/\/fazz.dev\/articles\/building-srp-part-three","title":"Building a custom renderer for Unity - Draw Calls & Batching","summary":"I once went down to the river to skip stones, there sat a wise old man who said to me \"Don't use MaterialPropertyBlock in the new SRPs (unless you want to break batching on purpose in which case you do you)\"","date_modified":"2022-11-23T00:00:00.000Z","author":{"name":"Joe Fazzino"}},{"id":"https:\/\/fazz.dev\/articles\/building-srp-part-two","content_html":"<p><strong>Command Buffers<\/strong> are a concept that once you understand what they are and what they can do, you have more or less figured out what the SRP is all about. You create a Command Buffer that lets you queue up instructions for the GPU . An example of this is in the following code which is creating a RenderTexture and getting it ready to be used for the directional Shadow Atlas.<\/p>\n<pre class=\"language-cs\"><code class=\"language-cs\"><span class=\"token class-name\">CommandBuffer<\/span> commandBuffer <span class=\"token operator\">=<\/span> <span class=\"token keyword\">new<\/span> <span class=\"token constructor-invocation class-name\">CommandBuffer<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span>\n\ncommandBuffer<span class=\"token punctuation\">.<\/span><span class=\"token function\">GetTemporaryRT<\/span><span class=\"token punctuation\">(<\/span>dirShadowAtlasId<span class=\"token punctuation\">,<\/span> atlasSize<span class=\"token punctuation\">,<\/span> atlasSize<span class=\"token punctuation\">,<\/span> <span class=\"token number\">32<\/span><span class=\"token punctuation\">,<\/span>\n    FilterMode<span class=\"token punctuation\">.<\/span>Bilinear<span class=\"token punctuation\">,<\/span> RenderTextureFormat<span class=\"token punctuation\">.<\/span>Shadowmap<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span>\n\ncommandBuffer<span class=\"token punctuation\">.<\/span><span class=\"token function\">SetRenderTarget<\/span><span class=\"token punctuation\">(<\/span>dirShadowAtlasId<span class=\"token punctuation\">,<\/span>\n    RenderBufferLoadAction<span class=\"token punctuation\">.<\/span>DontCare<span class=\"token punctuation\">,<\/span> RenderBufferStoreAction<span class=\"token punctuation\">.<\/span>Store<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span>\n\ncommandBuffer<span class=\"token punctuation\">.<\/span><span class=\"token function\">ClearRenderTarget<\/span><span class=\"token punctuation\">(<\/span><span class=\"token boolean\">true<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token boolean\">false<\/span><span class=\"token punctuation\">,<\/span> Color<span class=\"token punctuation\">.<\/span>clear<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span>\ncontext<span class=\"token punctuation\">.<\/span><span class=\"token function\">ExecuteCommandBuffer<\/span><span class=\"token punctuation\">(<\/span>commandBuffer<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span>\ncommandBuffer<span class=\"token punctuation\">.<\/span><span class=\"token function\">Clear<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span>\n<\/code><\/pre>\n<p>The actual behind the scenes contents of the code is a series of GPU instructions that are then copied and scheduled in the <strong>ScriptableRenderContext<\/strong> by the line <code>context.ExecuteCommandBuffer(commandBuffer);<\/code>. The buffer then needs to be manually cleared so it can be reused, typically you would create the buffer at a higher level than demonstrated here I just wanted to add it here for clarity.<\/p>\n<p>The ScriptableRenderContext will execute all instructions when <code>context.Submit()<\/code> is called however it is possible to immediately execute a Command Buffer by calling <code>Graphics.ExecuteCommandBuffer<\/code> but this will take place outside of the rendering pipeline.<\/p>\n<p>You can also use Command Buffers to pass variables from your C# scripts to your Shaders. The code block from earlier was creating a render texture but in order to render shadows there are lots of configuration options we might want to pass down to the shader level such as, the amount of cascades that should be used, the size of the atlas, how far should shadows be rendered, etc. Command Buffers allow you to pass this information down with a call like <code>buffer.SetGlobalInt(Shader.PropertyToID(&quot;_CascadeCount&quot;), 2)<\/code>!<\/p>\n<p>A cool feature of Command Buffers is that you can add in handy debugging markers so that in the Frame Debugger and Profiler you can more easily understand what is going on when the frame is in progress of being drawn. I had an issue at one point where I was drawing my directional shadow atlas and then it was being immediately cleared by a different buffer which meant there were no shadows rendered. Because of these markers it was easier to see and I could follow the logic of the how the frame was executing to see where the issue was.<\/p>\n<img alt=\"custom shadow buffer marker reading &#x27;My custom shadow buffer&#x27;\" srcSet=\"\/_next\/image?url=%2F_next%2Fstatic%2Fmedia%2FCustomShadowCommandBuffer.83124034.png&amp;w=384&amp;q=75 1x, \/_next\/image?url=%2F_next%2Fstatic%2Fmedia%2FCustomShadowCommandBuffer.83124034.png&amp;w=640&amp;q=75 2x\" src=\"\/_next\/image?url=%2F_next%2Fstatic%2Fmedia%2FCustomShadowCommandBuffer.83124034.png&amp;w=640&amp;q=75\" width=\"282\" height=\"354\" decoding=\"async\" data-nimg=\"1\" loading=\"lazy\" style=\"color:transparent\"\/>\n<p>If you want to learn more about Command Buffers and how they fit into the SRPs then I recommend the <a href=\"https:\/\/docs.unity3d.com\/Manual\/ScriptableRenderPipeline.html\">Scriptable Render Pipeline Fundamentals<\/a> chapter of the Unity Docs.<\/p>\n<h2>Next time<\/h2>\n<p>Batching in the SRP!<\/p>\n<h3>Related Posts\/Links<\/h3>\n<ul>\n<li><a href=\"https:\/\/fazz.dev\/articles\/building-srp-part-one\">Building a custom renderer for Unity - Intro<\/a><\/li>\n<li><a href=\"https:\/\/fazz.dev\/articles\/building-srp-part-three\">Building a custom renderer for Unity - Draw Calls &amp; Batching<\/a><\/li>\n<li><a href=\"https:\/\/fazz.dev\/articles\/building-srp-part-four\">Building a custom renderer for Unity - Directional Shadow Rendering<\/a><\/li>\n<\/ul>","url":"https:\/\/fazz.dev\/articles\/building-srp-part-two","title":"Building a custom renderer for Unity - Command Buffers","summary":"Not sure if it's just me but the word 'Buffer' feels unneccessarily scary.","date_modified":"2022-11-22T00:00:00.000Z","author":{"name":"Joe Fazzino"}},{"id":"https:\/\/fazz.dev\/articles\/building-srp-part-one","content_html":"<p>You can skip this if you don&#x27;t really care about the context of why I did this!<\/p>\n<p>The Scriptable Render Pipelines (SRPs) are a few years old now and the Universal Render Pipeline (URP) is on track to replace the Built In Render Pipeline (BIRP) when it has complete feature parity.<\/p>\n<p>I recently completed a project that was using the High Definition Render Pipeline (HDRP) to create a beautiful art style and we faced lots of challenges around balancing out the performance and the visual fidelity. During this project I had to quickly get acquianted with obscure GPU API calls and how to interpret information out of native GPU profilers like <a href=\"https:\/\/devblogs.microsoft.com\/pix\/download\/\">PIX<\/a>. I had very surface level knowledge of the difference between URP and HDRP when I started this work. I knew that HDRP could produce &#x27;prettier&#x27; results than URP and I knew that it couldn&#x27;t run on low end devices. I had also learnt another strength of the SRPs is that the SRP Batcher is &#x27;good&#x27;. I didn&#x27;t know how it was just something I read.<\/p>\n<p>By the end of the project I had a bit of a firmer grasp on what actually goes on when a frame is rendered because I had to fix issues around shadow render times and culling etc but it still felt quite surface level. I didn&#x27;t know actually how shadow rendering works, I just knew how to make the atlas draw faster and what cascades were. Still surface level but a slightly deeper surface.<\/p>\n<p>With the project complete I had time as part of my role to investigate in more detail to actually understand a bit more how (real-time) rendering works. I found <a href=\"https:\/\/www.youtube.com\/playlist?list=PLplnkTzzqsZS3R5DjmCQsqupu43oS9CFN\">a very good video series<\/a> made by a lecturer at the University of Utah which I could go through to understand better how Interactive Graphics work which is very cool but doesn&#x27;t help me understand much about the SRPs.<\/p>\n<p>In the Unity slack everywhere there is a dicussion about low level graphics and the SRPs it is inevitable that someone links the <a href=\"https:\/\/catlikecoding.com\/unity\/tutorials\/custom-srp\/\">series of tutorials from Catlike Coding about building a custom SRP<\/a>. So for roughly 2 weeks I tried to follow these tutorials as much as possible to hopefully get a grasp on how the SRPs (and real-time rendering in general) work.<\/p>\n<p>That&#x27;s all as an intro goes but the next post (coming out tomorrow) is going to be discussing \u2728<b>Command Buffers<\/b>\u2728.<\/p>\n<p>Bye!<\/p>\n<h3>Related Posts<\/h3>\n<ul>\n<li><a href=\"https:\/\/fazz.dev\/articles\/building-srp-part-two\">Building a custom renderer for Unity - Command Buffers<\/a><\/li>\n<li><a href=\"https:\/\/fazz.dev\/articles\/building-srp-part-three\">Building a custom renderer for Unity - Draw Calls &amp; Batching<\/a><\/li>\n<li><a href=\"https:\/\/fazz.dev\/articles\/building-srp-part-four\">Building a custom renderer for Unity - Directional Shadow Rendering<\/a><\/li>\n<\/ul>","url":"https:\/\/fazz.dev\/articles\/building-srp-part-one","title":"Building a custom renderer for Unity - Intro","summary":"The 'new' rendering pipelines have been around for a few years now but a lot of developers aren't familiar with how these actually work. Understanding how Unity is rendering your game will help you make better games and trying to make an SRP is a great way to understand why SRPs are the future.","date_modified":"2022-11-21T00:00:00.000Z","author":{"name":"Joe Fazzino"}},{"id":"https:\/\/fazz.dev\/articles\/sprite-shadows","content_html":"<h2>The Problem<\/h2>\n<p>You have a SpriteSheet with a bunch of sprites in different states of animation and you want to be able to use these in the normal way while working in 3D, using the SpriteRenderer on it&#x27;s own however doesn&#x27;t cast shadows which isn&#x27;t very &#x27;3D&#x27; of it and if you decide to use a Quad then you&#x27;ll have to break up your SpriteSheet into individual textures (or use a Texture Atlas) have to write some supporting code.<\/p>\n<h2>The Solution<\/h2>\n<p>Just to get this out of the way early on for people Googling to find this out as I was today for over an hour, if you want to be able to cast and receive shadows using a SpriteRenderer. Here is what you do.<\/p>\n<h3>1. Create a new ShaderGraph<\/h3>\n<p>I&#x27;m going to assume you have either got the ShaderGraph package installed or can Google and figure that part out yourself \ud83d\ude09<\/p>\n<p>The purpose of this shader is going to be to do nothing, it&#x27;s going to pass the colour from the vertex multiplied by the texture to the albedo. In the output targets however the Active Target should be Universal and then within the Universal dropdown the Material type select <strong>Lit<\/strong> or <strong>Unlit<\/strong> depending on whether or not you want your sprite to receive shadows (this will not impact if it will cast shadows). For workflow you&#x27;ll either know what this does or you won&#x27;t in which case I recommend going with Metallic <a href=\"https:\/\/docs.unity3d.com\/Manual\/StandardShaderMetallicVsSpecular.html\">(info here for the curious)<\/a>. If you want to do transparency properly it will require slightly more set up but is optional. Optionally also you can turn on Two Sided which will mean both the front and the back of the sprite will be visible.<\/p>\n<img alt=\"shader graph \" srcSet=\"\/_next\/image?url=%2F_next%2Fstatic%2Fmedia%2FCompleteShaderGraph.0a6207f8.png&amp;w=3840&amp;q=75 1x\" src=\"\/_next\/image?url=%2F_next%2Fstatic%2Fmedia%2FCompleteShaderGraph.0a6207f8.png&amp;w=3840&amp;q=75\" width=\"2880\" height=\"1616\" decoding=\"async\" data-nimg=\"1\" loading=\"lazy\" style=\"color:transparent\"\/>\n<p>The ShaderGraph should look like this. Important to note that an external property is defined _MainTex, as well as defining this in the property tab it is also important to change the Reference ID to _MainTex as this is what the SpriteRenderer looks for to put the sprite loaded onto.<\/p>\n<h3>2. Using Our Shader<\/h3>\n<p>Now that we have the shader that is going to let our sprite cast shadows all we have to do now is create a new material using our shader...<\/p>\n<img alt=\"shader graph \" srcSet=\"\/_next\/image?url=%2F_next%2Fstatic%2Fmedia%2FDemoMaterial.bd414a48.png&amp;w=750&amp;q=75 1x, \/_next\/image?url=%2F_next%2Fstatic%2Fmedia%2FDemoMaterial.bd414a48.png&amp;w=1920&amp;q=75 2x\" src=\"\/_next\/image?url=%2F_next%2Fstatic%2Fmedia%2FDemoMaterial.bd414a48.png&amp;w=1920&amp;q=75\" width=\"678\" height=\"722\" decoding=\"async\" data-nimg=\"1\" loading=\"lazy\" style=\"color:transparent;margin:auto\"\/>\n<p>...and apply it to the GameObject with the SpriteRenderer.<\/p>\n<p>If you get a warning about _MainTex having to be a property on the shader it&#x27;s because you forgot to change the Reference of the property in the shader.<\/p>\n<h3>3. Debug Mode<\/h3>\n<p>At this point you should be seeing your sprite in all of it&#x27;s glory in 3D space but still no shadows?! At this point you have to open the Debug mode for the Inspector so that you can change some magic toggles, steps shown below.<\/p>\n<img alt=\"shader graph \" srcSet=\"\/_next\/image?url=%2F_next%2Fstatic%2Fmedia%2FDebugMenu.bdb5d8fc.png&amp;w=750&amp;q=75 1x, \/_next\/image?url=%2F_next%2Fstatic%2Fmedia%2FDebugMenu.bdb5d8fc.png&amp;w=1920&amp;q=75 2x\" src=\"\/_next\/image?url=%2F_next%2Fstatic%2Fmedia%2FDebugMenu.bdb5d8fc.png&amp;w=1920&amp;q=75\" width=\"700\" height=\"210\" decoding=\"async\" data-nimg=\"1\" loading=\"lazy\" style=\"color:transparent;margin:auto\"\/>\n<img alt=\"shader graph \" srcSet=\"\/_next\/image?url=%2F_next%2Fstatic%2Fmedia%2FSpriteRendererDebugSettings.f779b25d.png&amp;w=750&amp;q=75 1x, \/_next\/image?url=%2F_next%2Fstatic%2Fmedia%2FSpriteRendererDebugSettings.f779b25d.png&amp;w=1920&amp;q=75 2x\" src=\"\/_next\/image?url=%2F_next%2Fstatic%2Fmedia%2FSpriteRendererDebugSettings.f779b25d.png&amp;w=1920&amp;q=75\" width=\"668\" height=\"258\" decoding=\"async\" data-nimg=\"1\" loading=\"lazy\" style=\"color:transparent;margin:auto\"\/>\n<h3>4. Profit<\/h3>\n<img alt=\"shader graph \" srcSet=\"\/_next\/image?url=%2F_next%2Fstatic%2Fmedia%2FDemoSpriteShadows.6036235e.png&amp;w=1080&amp;q=75 1x, \/_next\/image?url=%2F_next%2Fstatic%2Fmedia%2FDemoSpriteShadows.6036235e.png&amp;w=2048&amp;q=75 2x\" src=\"\/_next\/image?url=%2F_next%2Fstatic%2Fmedia%2FDemoSpriteShadows.6036235e.png&amp;w=2048&amp;q=75\" width=\"964\" height=\"850\" decoding=\"async\" data-nimg=\"1\" loading=\"lazy\" style=\"color:transparent;margin:auto\"\/>\n<p>At this point you should have some beautiful shadows rendering in your scene. If you want to go further it&#x27;s definitely a good idea to add support for Opacity so you don&#x27;t treat any non 0 alpha value as 1. You can do this fairly simply in the shader, if you use a Split node to take the output of the multiply and then multiply the alpha output by an _Opacity property feeding that into the Alpha value on the Fragment node.<\/p>\n<p><a href=\"https:\/\/twitter.com\/joefazzinoin3d\">Let me know if you found this useful!<\/a><\/p>","url":"https:\/\/fazz.dev\/articles\/sprite-shadows","title":"2D in 3D: Using a SpriteRenderer with Shadows","summary":"Sprite renderers do not cast shadows. In this post I make them do so.","date_modified":"2021-03-01T00:00:00.000Z","author":{"name":"Joe Fazzino"}},{"id":"https:\/\/fazz.dev\/articles\/web-vs-games","content_html":"<h2>Who is this post for?<\/h2>\n<p>This is for people with a basic understanding of programming, in particular web development or game development.<\/p>\n<h2>Introduction<\/h2>\n<p>In the roughly 3 years of being a professional developer I&#x27;ve done 3 different jobs targetting 3 different platforms using 3 different technologies. On the one hand maybe this is a bit short sighted as I could be sitting pretty with three solid years experience in a singular technology with a strong knowledge of developing software for a certain platform in a certain environment. But I find that boring as heck and it&#x27;s way more fun to try new things, even more so when I can be paid <em>cash money<\/em> to learn new things.<\/p>\n<p>This blog post is going to talk about my most recent tech transition which is from the sole frontend web developer at <a href=\"https:\/\/gravitysketch.com\">Gravity Sketch<\/a> to being one of 8 members of the dev team working with <a href=\"https:\/\/unity.com\">Unity<\/a> every day making pull requests to the same repo.<\/p>\n<h2>Web Development<\/h2>\n<p>Web development is equal parts fun and frustrating. The speed at which you can develop something and ship it to production in the same day as having the idea\/receiving the feature request is so great and as a sole developer, is very empowering and a point of pride to me that I could work that quickly when I needed to.<\/p>\n<p>I&#x27;m not going to dwell too much on web dev as it&#x27;s probably the more familiar area of development for most people, even non web devs probably have a pretty solid understanding of how it works.<\/p>\n<p>A small list of reasons I like web development:<\/p>\n<ul>\n<li>R A P I D deployment<\/li>\n<li><em>whispers<\/em> I like CSS or more specifically I like <a href=\"https:\/\/tailwind.com\">Tailwind<\/a><\/li>\n<li>There is so much information online about how to do almost everything there&#x27;s almost never such a thing as an original problem so help is almost always a Google search away<\/li>\n<li>I know how to do it and I like making websites<\/li>\n<\/ul>\n<blockquote>\n<p>Editors note (still me): While writing this post on my blog (that I over engineered) everything was being incredibly slow and I was getting progressively more irritated so web dev still knows how to toy with me despite how much I like it.<\/p>\n<\/blockquote>\n<h2>Game Development<\/h2>\n<p>I suppose I should start this by prefacing with my job title not actually having anything to do with Game Development as my title is actually <strong>UI Engineer<\/strong> however I don&#x27;t feel like a <em>total<\/em> fraud titling this post with the words and talking about it as it is more than just an adjacent role (I also have some side game projects I&#x27;m working on). I&#x27;ve been doing Unity professionally since September 2020 and started casually learning it around May\/June time. When I started in September I was a bit intimidated by the change but at this point I feel very comfortable with C# (which Unity uses for scripting) in Rider (IDE), using the Unity Editor itself and it&#x27;s associated <em>quirks<\/em> of which there are plenty. At this point I&#x27;m spending roughly 20-30 hours a week inside Unity or Rider so doing the math I have maybe around 300ish hours experience which I know is incredibly arbitrary but I&#x27;m trying to communicate that I sort of maybe know what I&#x27;m talking about \ud83d\ude42<\/p>\n<h2>Some terminology...<\/h2>\n<p>There&#x27;s going to be a few of words and definitions that need to be described in order to explore the similarities and differences between creating UI for Unity projects vs React projects.<\/p>\n<ul>\n<li><strong>GameObjects<\/strong> is the base class for all Unity entities that are in the scene. GameObjects are just containers for components and will always have a transform component (2D or 3D depending on usage) that tell the rendering engine where the entity is in the world. GameObjects are hierarchical like DOM elements in the way they have a parent GameObject and can have many siblings\/children.<\/li>\n<li>(Unity) <strong>Components<\/strong> are scripts that attach to GameObjects to give them special functionality, for example to display a 2D image you need to attach the <code>Image<\/code> component to your GameObject which will in turn have lots of additional property fields that let you select the colour of the image, the sprite to use etc.<\/li>\n<li><strong>Prefabs<\/strong> are reusable collections of GameObjects that are saved to the disk with the .prefab extension and can be either drag and dropped into a scene or Instantiated through the code. You can have prefabs within prefabs but unless you edit them at their root you won&#x27;t change all the other instances of the prefab.<\/li>\n<\/ul>\n<p>There are obviously more low level concepts but these are the core ones that are important to understand in order to do 2D UI in a way that is comparable to the web.<\/p>\n<h2>Scripting vs Coding<\/h2>\n<p>I&#x27;m not going to deep dive into the differences between the languages and the tools better devs than me have done that kind of thing but I will make some maybe more <em>unique<\/em> comparisons that probably do exist but I never found them.<\/p>\n<p>One thing I will mention before I go a bit deeper is that online writing code for the Unity Engine is referred to as writing <em>Scripts<\/em>. This isn&#x27;t to say that the code is not like real coding (which is a dumb idea anyway) but it&#x27;s just the terminology that Unity itself uses to refer to custom components that are attached to GameObjects. I also find it to be a really good descriptor for the way that coding in Unity works as you can write relatively small scripts that have significant impact on a GameObject.<\/p>\n<h2>Prefabs vs (React) Components<\/h2>\n<p>As mentioned when explaining the terminology Prefabs are collections of GameObjects including all of their components and properties set at the time of saving. In the UI projects I&#x27;ve worked on I&#x27;ve found myself creating a library of UI Prefabs similar to what I might have in React but because the concept of props doesn&#x27;t exist what I end up doing is making a singular general Button prefab with the correct styling, size, etcetera and then when I need a slightly modified version say for a secondary button, I tend to duplicate the prefab instance and modify the components properties directly, this doesn&#x27;t override the original and if I wanted to I could actually save the modified version as a variant of the original prefab which is super helpful!<\/p>\n<p>React creates all your elements when the page loads which is quite different to Unity where you can assemble your Prefabs and GameObjects before you press the play button in the <em>Scene view<\/em> and the final built output will resemble what you see in the exactly (provided you don&#x27;t edit the UI at runtime with a script). This is such a nice workflow in comparison to web development where you have to wait for the page to reload and switch to a browser, instant accurate feedback is pretty hard to compete with though.<\/p>\n<p>It is also possible to instantiate a prefab from the code at runtime and the actual code to do it is quite straightforward.<\/p>\n<p><code>Instantiate(prefabName, parentTransform)<\/code><\/p>\n<p>The cases you might want to do this range but commonly what is done in the projects I&#x27;ve been a part of is all the prefabs are assembled ahead of time and then in each <em>Scene<\/em> there will be a script that will instantiate all the relevant GameObjects and components that are needed for the game to work when it&#x27;s in <em>play mode<\/em>.<\/p>\n<p>This tells the engine to create a new clone of the prefab that uses the 2nd argument to determine the transform of the GameObject that will be the parent in the hierarchy. In Unity UI it&#x27;s quite common that the prefab will depend on it&#x27;s parent in some way to determine the size of it so it&#x27;s important when editing these types of prefabs that they are edited in the context of the parent which luckily is nice and easy to do.<\/p>\n<h2>Developer Experience<\/h2>\n<p>In Web Dev land the experience with webpack and CRA makes this pretty nice overall and rarely did I have much to complain when it came to how productive I felt. You didn&#x27;t need great hardware to have an experience where it feels almost seamless between editing UI code and seeing the results on your browser du jour.<\/p>\n<p>Unity is far more... turbulent, it can feel as rapid as possible like in the above mentioned example when you&#x27;re editing a purely UI prefab in the scene view and you get to see the changes you&#x27;re making and be confident that it will look the same way in play mode.<\/p>\n<p>Times where you can really drag though do occur thick and fast especially when you&#x27;re trying to plug in business logic via Scripts. To undersell Unity a bit, it&#x27;s a glorified compiler, this means that when you&#x27;re editing code in your editor of choice (VSCode works way better than it has any right to imo) it doesn&#x27;t compile until Unity realises you have changed one of the files in the solution and sometimes it can be quite tedious waiting for it to compile and hearing your laptop fan kick up, especially when you&#x27;re just changing a single value to see the effect it has on physics (there are ways to mitigate this). Resolving conflicts in prefabs is also basically a no go and you need to fix it by visually comparing the two which is not that straightforward.<\/p>\n<p>Another fact of the matter is that Unity is a Game Engine and the rule of thumb (that I was told) is that if you want to make a game your hardware should be twice as powerful as the lowest end device you want to target. The new M1 MacBook Air&#x27;s might be the new web dev laptop of choice but unless you have a discrete GPU in your laptop you&#x27;re going to struggle with complex shaders and real time lighting.<\/p>\n<h2>The Future of Unity UI<\/h2>\n<p>Every year Unity hosts a conference for developers and in 2019 they announced a new feature that is particularly interesting to me called <strong>UI Builder<\/strong> which, to cut a long story short is Unity&#x27;s attempt at implementing a HTML and CSS style workflow into GameObjects. You can build your UI graphically as you can now, but instead of using Unity UI components you&#x27;ll be creating custom components built off of a small number of primitives (React Native style) that you can extend with a near exact CSS like syntax (USS). This new way of doing UI in Unity is planned to be released this year and I&#x27;m quite excited to get into it but at the same time this is classic Unity where they release a new system for something essential and then some years later go &quot;oh actually we don&#x27;t like that try this out&quot; <em>cough<\/em> input system <em>cough<\/em>. Either way though I think this is a good direction and there is the possibility to blend systems together so you can use traditional Unity UI components in some places and the UI Builder in others.<\/p>\n<h2>Conclusion<\/h2>\n<p>Unity and the Web are two distantly related cousins that are looking to get in touch with each in different extents. in the web, web assembly is letting developers write code in any language to compile down to a fast binary that can run on the web. In Unity they&#x27;re borrowing ideas from the web that web developers have come to be very familiar with and utilising them to help everyone make better interfaces. I personally have a pretty vested interest in both these platforms becoming more like the other and borrowing each others best ideas and regardless of which platform you choose to work with, now is the best time to be working there.<\/p>","url":"https:\/\/fazz.dev\/articles\/web-vs-games","title":"Web Development vs Game Development","summary":"I used to be a web developer. These were my thoughts at the time that I made the transition to games about the differences. It was written before I was a Game Developer when I was working with Unity to make mobile\/desktop\/vr products at Gravity Sketch!","date_modified":"2021-01-03T00:00:00.000Z","author":{"name":"Joe Fazzino"}},{"id":"https:\/\/fazz.dev\/articles\/learn-your-way","content_html":"<p>This is my first post on dev.to but I really like the platform so I thought it&#x27;d make a good place to make this slightly charged post.<\/p>\n<p>Developing software is an ongoing battle against the enemy of time. If you were a web developer 5 years ago, and decided to return to the industry in 2018, you&#x27;d find that it is no longer the world you once knew.<\/p>\n<p>Everyday new tools, languages, and standards are being established, because of this it&#x27;s easy to feel like you&#x27;ve slipped behind or the skills you once learnt are no longer relevant.<\/p>\n<p>With all these new tools, there is always a learning experience for beginners that can vary wildly between &#x27;easy to pickup&#x27; (Python) and &#x27;terrifying&#x27; (C++). This leads to the inevitable question that is posted to sites...<\/p>\n<blockquote>\n<p>&quot;What is the best way to learn [INSERT TECH HERE]?&quot;<\/p>\n<\/blockquote>\n<h2>The Problem<\/h2>\n<p>I recently read a thread on a forum on the best way to learn React Native, a technology I am very familiar with. There were a couple of answers which instructed the wannabe cross platform developer to &quot;Learn modern JavaScript before you start React Native&quot;. This sounds like a very common sense answer, React Native is written with JavaScript (for the most part) so good knowledge of the foundations will lead to you being a better React Native developer. However, I was learning React Native about 15 months ago and went straight in with no prior JavaScript knowledge at all. I had done some Android development in Java but that was pretty much as far as it went.<\/p>\n<p>I didn&#x27;t know it then but this was absolutely 100% the correct way for me to teach myself React Native. I had always found self teaching a really difficult thing to do. I have read books, watched courses but nothing ever seemed to stick in my brain. I now realise that for me personally there is only one way to teach myself something from scratch... <em>make things!<\/em><\/p>\n<p>For me, the best way to learn something new is to just jump into it and then I figure out things as I go. Most recently, I&#x27;m teaching myself Go for my big final project at University and I wanted to split my code into multiple files\/folders but Go is not like JavaScript, where you just stick <code>export<\/code> in front of the function you want to access from somewhere else. It turns out you have to capitalise the first letter of the function. That may seem like something you&#x27;d learn in the first few pages of a book but now I know for certain that I won&#x27;t forget how to export a function in Go.<\/p>\n<h2>The Point<\/h2>\n<p>Only one person knows the best way for you to pick up a new skill... <strong>You!<\/strong><\/p>\n<p>So take the StackOverflow answers with a grain of salt, experiment with different learning techniques and don&#x27;t forget to have fun doing it!<\/p>","url":"https:\/\/fazz.dev\/articles\/learn-your-way","title":"Stop letting people tell you how to learn","summary":"This is an article I wrote on Dev.To that stemmed from a lot of frustration I would see around gate keeping on how to best learn xyz. At the time it was written about React Native but this applies to everything including Unity.","date_modified":"2018-10-03T00:00:00.000Z","author":{"name":"Joe Fazzino"}}]}