天天看点

Scriptable Render Pipeline-Spotlight Shadows

https://catlikecoding.com/unity/tutorials/scriptable-render-pipeline/spotlight-shadows/

both render to and read from a texture.

render from the point of view of a light.

add a shader pass for shadow casters.

sample a shadow map.

support a mix of hard and soft shadows.

combine up to sixteen shadow maps in a single atals.

this is the fourth installment of a tutorial series covering unity’s scriptable render pipeline. in it, we will add support for up to sixteen spotlights with shadows.

1. a splotlight with shadows

shadows are very important, both to increase realism and to make the spatial relationship between objects more obvious. without shadows, it can be hard to tell whether sth. floats about a surface or sits on top of it.

the rendering 7, shadows https://catlikecoding.com/unity/tutorials/rendering/part-7/

tutorial explains how shadows work in unity’s default render pipeline, but

that exact same approach does not work for our single-pass forward renderer.

in this tutorial we will limit ourselves to shadows for spotlights.

we start with supporting exactly one shadowed light, so make a scene that contains a few objects and a

single spotlight.

a plane object is useful to receive shadows. all objects should use our Lit Opaque material.

1.1 shadow map

there are a few different ways to deal with shadows, but we will stick to the default approach of using a shadow map.

this means that we will render the scene from the light’s point of view.

we are only interested in the depth information of this render,

because that tells us how far the light reaches before it hits a surface.

anything that is further away lies in shadows.

to use a shadow map we have to create it before rendering with the normal camera.

to be able to sample the shadow map later, we have to render into a separate render texture instead of the usual fame buffer.

Add a RenderTexture field to MyPipeline

to keep a reference to the shadow map texture.

create a separate method to render the shadows, with the context as a parameter.

the first thing it has to do is get hold of a render texture.

we will do that by invoking the static RenderTexture.GetTemporary method.

that either creates a new render texture or reuses an old one that has not been cleaned up yet.

as we will most likely need the shadow map every frame, it will get resused all the time.

supply RenderTexture.GetTemporary with our map’s width and height,

the amount of bits used for the depth channel, and finally the texture format.

we will start with a fixed size of 515x512.

we will use 16 bits for the depth channel, so it is high-precision.

as we are creating a shadow map, use the RenderTextureFormat.Shadowmap format.

void RenderShadows (ScriptableRenderContext context) {
		shadowMap = RenderTexture.GetTemporary(
			512, 512, 16, RenderTextureFormat.Shadowmap
		);
	}
           

make sure that the texture’s filter mode is set the bilinear and its warp mode is set to clamp.

shadowMap = RenderTexture.GetTemporary(512, 512, 16, RenderTextureFormat.Shadowmap);
shadowMap.filterMode = FilterMode.Bilinear;
shadowMap.wrapMode = TextureWrapMode.Clamp;
           

the shadow map is to be rendered before the regular scene, so invoke RenderShadows in Render before

we setup the regular camera, but after culling.

剔除之后,渲染场景之前

void Render (ScriptableRenderContext context, Camera camera) 
{
		…
		CullResults.Cull(ref cullingParameters, context, ref cull);
		RenderShadows(context);
		context.SetupCameraProperties(camera);
		…
}
           

also, make sure to release the render texture when we are done, after we have submitted the context.

if we have a shadow map at that point, pass it to the RenderTexture.ReleaseTemporary method and clear our field.

void Render (ScriptableRenderContext context, Camera camera) {
		…
		
		context.Submit();

		if (shadowMap) {
			RenderTexture.ReleaseTemporary(shadowMap);
			shadowMap = null;
		}
	}
           

1.2 shadow command buffer

we will use a separate command buffer for all the shadow work, so we can see the shadow and regular rendering

in separate sections in the frame debugger.

CommandBuffer cameraBuffer = new CommandBuffer {
		name = "Render Camera"
	};

	CommandBuffer shadowBuffer = new CommandBuffer {
		name = "Render Shadows"
	};
           

The shadow rendering will happen in between BeginSample and EndSample commands, just like we do for regular rendering.

void RenderShadows (ScriptableRenderContext context) {
		shadowMap = RenderTexture.GetTemporary(
			512, 512, 16, RenderTextureFormat.Shadowmap
		);
		shadowMap.filterMode = FilterMode.Bilinear;
		shadowMap.wrapMode = TextureWrapMode.Clamp;

		shadowBuffer.BeginSample("Render Shadows");
		context.ExecuteCommandBuffer(shadowBuffer);
		shadowBuffer.Clear();

		shadowBuffer.EndSample("Render Shadows");
		context.ExecuteCommandBuffer(shadowBuffer);
		shadowBuffer.Clear();
	}
           

1.3 setting the render target

before we can render shadows, we first have tell the GPU to render to our shadow map.

a convenient way to do this is by invoking CoreUtils.SetRenderTarget with our command buffer and shadow map as arguments.

as we start with clearing the map, invoke it before BeginSample so the frame debugger doesn’t show and extra nested Render Shadows level.