I’ve had a longstanding issue where I have wanted to add a custom pass to shadergraph. This is a fairly common problem and there are lots of cases where people might want to do that. For example, having a separate pass to enable outlines by object ID, or to populate a buffer with object-specific data for a post process.
Previously, the only workaround I have found is to create a separate ShaderLab shader, and then exploit UsePass to include passes from a shadergraph. This works, but is not ideal. Although officially supported, UsePass has been in poor state for a while. There was a hard crash bug unfixed with the SRPBatcher for years, and other versions produce artefacts ( see here ). It also is unusable for more complicated scenarios, for example using graphs to produce moving vertices in the vertex shader requires code to be manually copied into the custom pass. There’s also the issue that most URP versions don’t name the 2d renderer pass, and so it’s impossible to do anything with the 2D renderer this way.
I’m also aware that Shader Blocks are coming, but it’s clearly going to be a long time before they are usable and I am looking for a near term solution.
I don’t really understand the decision behind this; the SRPs are great for adding flexibility, but it feels like even more flexibility would be possible if there wasn’t a bunch of stuff hidden as internal. The ability to define our own subtargets would greatly increase usability - for example, imagine having a ‘Toon shader’ subtarget that works similar to the PBR subtarget, where you just plug color etc and it handles toon lighting for you.
Back to custom passes…
Does anyone have any thoughts or suggestions on another method or way round this in the meantime?
It looks like I can add a custom subtarget to shadergraph if I take the following steps:
Embed the ShaderGraph and Universal Render Pipeline packages (required to be able to modify them).
Create a custom asmdef in my project to house the custom target. I called mine CustomAssembly.
Expose the internals of Universal.Runtime, Universal.Editor and ShaderGraph.Editor by modifying the AssemblyInfo.cs in each, appending the line: [assembly: InternalsVisibleTo("CustomAssembly")]
Copy Universal/Editor/ShaderGraph/UniversalUnlitSubTarget.cs to the custom assembly and rename. Make sure the internal kSourceCodeGuid matches your .meta.
After this it seems fairly straightforward to change the properties, block layout etc. Haven’t had much time to dig further yet.
But the level of customisation required is tedious, I’d love a way to do this without having to expose the internals.
I was able to add a custom pass to a custom shadergraph target, following the structure of the other subtargets (e.g. Lit, Unlit subtargets).
However, everything being private continues to prevent reuse of relevant parts of code. For instance, if I want to create a shader that is ‘more or less unlit with an extra pass’, I’m forward to copy all of the code in the #region Passes/PortMasks/RequiredFields/Defines/Keywords of the subtarget, because things like static class LitPasses are private.
The other alternative would be inheritance, e.g. inheriting from LitSubtarget and overriding some methods to add a pass, but this isn’t possible because the SubTarget classes are sealed. I can understand this decision, but is there a good reason for making the classes like ‘LitPasses’ private? It would be really cool if adding custom passes to ShaderGraph were possible in a way that isn’t going to require manually porting code every unity version!
(Cross linking this bit for others searching in future. You can use reflection to remove requirement of copy and pasted code by invoking the private URP functions to create a SubShader, and then modify it’s passes: How to add a pass tag to a ShaderGraph? )
Does the reflection technique still require you to embed the packages?
I would really love to use a custom sub-target but I’m wary about maintaining forked Unity packages. My use case is that I’ve created a custom lighting model and I would really love to use Shader Graph to quickly create custom shaders that use my lighting model. In your digging, have you discovered any other routes to making this possible?
It does still requiring embedding and editing the asmdef if you want to base off Unity’s passes (which is probably wise to ensure keywords, changes work across versions). Fortunately the only actual change required is asmdef, which is small enough you can automate the process
Just following up on this thread. I went and made my own custom subtarget for my lighting model and I’ve liked the result so far.
I was able to do it without embedding or reflection, using the same method from this example.
The trick for me was to create an Assembly Reference for Unity.RenderPipelines.Universal.Editor in the folder that houses my custom subtarget. This gave me access to URP internals that I needed.
It took a few days worth of work to do, but if people are interested in this, I’ve been thinking of documenting this process and making a tutorial so people can make sub-targets faster!
While I made my own, I kept a document with random notes about the process. I was starting to turn it into a guide but I got busy and now I’m working towards a deadline!
I might finish it someday, but in case these notes are enough to get you started, here they are…
It starts out sensibly, but quickly devolves into just stray thoughts