渲染15——延迟灯光

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wodownload2/article/details/82740874

使用定制的光照shader
解码LDR颜色
增加一个独立的通道给灯光
支持平行光、聚光灯、点光源
手动采样光照贴图
本节是渲染教程的第15节,之前的一节,我们增加了雾的效果,本节我们将创建延迟灯效果。
从现在开始,渲染教程使用unity版本为5.6.0. 这个版本的unity在编辑器和shader上有些变化。
这里写图片描述

1光照shader
在渲染课程的第13节,我们增加了支持延迟渲染得路径。我们唯一做的就是填充g缓冲。灯光将被延后处理。那个小节简要介绍这些灯是如何被unity加进去的。这次,我们自己渲染这些灯了。

为了测试这些灯,我们将把环境强度设置为0,并且使用一个延迟HDE相机进行渲染。

场景中所有的物体都被我们自己的shader渲染到了G缓冲中去。但是所有的灯都是使用unity默认的shader渲染。这个shader的名字为:Hidden/Internal-DeferredShader. 你可以验证下:
Edit->Project Settings->Graphics
把Built-in Shader Settings的Defferred设置为Custom shader
这里写图片描述

1.1 使用自定义的shader
所有的延迟灯都是使用单独的通道,它改变了图片的颜色。它能有效的控制图片的效果,比如之前的延迟雾shader。我们将开始重写一个shader,从零开始。

Shader "Custom/DeferredShading" {
	
	Properties {
	}

	SubShader {

		Pass {
			Cull Off
			ZTest Always
			ZWrite Off
			
			CGPROGRAM

			#pragma target 3.0
			#pragma vertex VertexProgram
			#pragma fragment FragmentProgram
			
			#pragma exclude_renderers nomrt
			
			#include "UnityCG.cginc"

			struct VertexData {
				float4 vertex : POSITION;
			};

			struct Interpolators {
				float4 pos : SV_POSITION;
			};

			Interpolators VertexProgram (VertexData v) {
				Interpolators i;
				i.pos = UnityObjectToClipPos(v.vertex);
				return i;
			}

			float4 FragmentProgram (Interpolators i) : SV_Target {
				return 0;
			}

			ENDCG
		}
	}
}

然后让unity使用这个自定义的shader。
这里写图片描述

1.2 第二个通道
当使用我们自己定义的shader之后,unity会报出一个错误,说没有足够的通道。很显然,我们要增加一个额外的通道:

Pass {
			…
		}

		Pass {
			…
		}

unity现在使用了我们的shader,并且用它来渲染了平行光。得到的结果是,所有的物体变黑了。除了天空。模板缓冲被用于遮罩,用来避免渲染。因为平行光不会受到背景的影响。

第二个通道用来干嘛呢? 当HDR被关闭的时候,光照数据是指数形式编码的。最后一个通道需要解码。这就是第二个通道应该做的事情。所以一旦你关闭了相机的HDR,那么第二个通道将会用来解码光照数据。

1.3 avoiding sky
当使用LDR模式的时候,你将会看到天空也变为了黑色。当天空变为了黑色,这个转换通道将不能正确使用模板缓冲作为遮罩。为了修复这个问题,把模板缓冲的设置为参数配置。只有当一个像素不是背景的一部分才会被渲染。合适的模板值是通过_StencilNonBackground来提供的。

Pass
{
	Cull Off
	ZTest Always
	ZWrite Off
	Stencil
	{
		Ref [_StencilNonBackground]
		ReadMask [_StencilNonBackground]
		CompBack Equal
		CompFront Equal
	}
	……
}

很遗憾的是在frame debug中看不到关于模板换从的信息。

1.4 转换颜色
改写第二个通道代码,使其可用。我们必须在光照缓冲中转换数据。和我们的雾shader类似,一个和屏幕大小一样的方块是使用uv坐标来绘制的,我们可以从缓冲中采样:

struct VertexData 
{
		float4 vertex : POSITION;
		float2 uv : TEXCOORD0;
};

struct Interpolators 
{
		float4 pos : SV_POSITION;
		float2 uv : TEXCOORD0;
};

Interpolators VertexProgram (VertexData v) 
{
		Interpolators i;
		i.pos = UnityObjectToClipPos(v.vertex);
		i.uv = v.uv;
		return i;
}

灯缓冲数据可以从变量_LightBuffer来获取:

sampler2D _LightBuffer;
…

float4 FragmentProgram (Interpolators i) : SV_Target
{
		return tex2D(_LightBuffer, i.uv);
}

LDR颜色是指数编码,如2-C,为了解码我们使用如下方式:

return -log2(tex2D(_LightBuffer, i.uv));

这样就可以有作用了,然后打开HDR。

猜你喜欢

转载自blog.csdn.net/wodownload2/article/details/82740874