使用定制的光照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。