I’m trying to figure out how Unity renders stuff with URP. I have a character going from room to room in a house. The effect I want to achieve is the following. When the player switches room, I want the first room to change its opacity (not all object, one by one, changing their opacity, but the room as a whole) to disappear, while the second room appears the same way. The character should remain visible during the animation.
I tried using GameObjects layers to differentiate rooms but :
I’m not sure they should be used in this situation;
That would imply making several cameras to render each room with a separate camera. That seems way too complicated for the effect I’m trying to create, and there are some issues with lighting since the character should remain visible and should produce shadow on the ground of the rooms.
Additionally, I came across the Rendering Layer Mask of a mesh renderer, but I can’t find a way to use that with URP (the renderer features only show the layers first mentioned, and not the layers of the mesh renderer).
Is there a way to achieve this effect directly with URP ? Should I modify URP by creating my own scriptable render pipeline ? (Does it have anything to do with URP or am I asking a stupid question ?)
I doubt it has anything to do with the SRP. Supposedly each room is an object with its room elements as children, then you can just make a script that changes the opacity of all children that have a renderer component
something like this:
void SetOpacity(float value)
{
// note that this will override the opacity of all children
Renderer[] renderers = GetComponentsInChildren<Renderer>(true);
foreach (var renderer in renderers)
{
if (renderer.sharedMaterial == null)
continue;
// best cached in a static class
int baseColorID = Shader.PropertyToID("_BaseColor");
int srcBlendID = Shader.PropertyToID("_SrcBlend");
int dstBlend = Shader.PropertyToID("_DstBlend");
Color baseColor = renderer.sharedMaterial.GetColor(baseColorID);
baseColor.a = value;
// in order for this to work the material needs to have Transparent blending
// let's force alpha blending
renderer.sharedMaterial.SetInt(srcBlendID, (int)UnityEngine.Rendering.BlendMode.SrcAlpha);
renderer.sharedMaterial.SetInt(dstBlend, (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
renderer.sharedMaterial.SetColor(baseColorID, baseColor);
}
}
I tried your solution, but every objects actually change their opacity, resulting in not so pleasing results (the objects behind other objects become visible). I want the whole room to be rendered, and then to change its opacity.
Got it. So you want it to fade as if it’s an image. In this case, you’ll have to make sure the material writes into depth and that the scene depth is always rendered before the color. One way of doing this is by using a CustomForwardRendererData (see attached image)
//edit
DepthOnly uses a custom shader that only writes into depth, basically ColorMask 0 and ZWrite On
Hey ! Awesome ! Exactly what I wanted. Thanks a lot.
Can you just explain quickly what happens with the custom renderer ? I’m trying to understand what the second part is for.
And also, this method means that I can’t share materials between rooms. Is this a good practice to duplicate materials that are the same, just for this purpose ?
It’s me again, I was trying on a black background, and it worked great. But I tried with a skybox, and the shape of the objects still appear black when their opacity is set to 0. Have I done a mistake ?
No problem You need to plug the CustomForwardRendererData to the SRP asset (see image)
It’s not good practice to clone a material as that would increase the draw calls. The traditional way of doing it would be by enabling GPU Instancing for the shared material and setting the above uniforms per renderer using MaterialPropertyBlock instead of per material. But I haven’t managed to sort that out for SRP yet. I’m trying to figure it out in this thread MaterialPropertyBlock and SRP batcher
You haven’t done anything wrong, that’s how it should be. Once you render the depth buffer first, everything that has a depth higher than the current pixel will not render anymore (the skybox is always behind everything else in this case)
To fix this you have at least these 2 options:
render the skybox before everything else (even before the depth only path) by adding another render layer
change the Event for both Depth and Color layers to After Rendering Skybox
This may cause some other issues in the future when you want other objects to render behind the rooms so you have to be careful with this level of granularity