目录 |
1 受光的着色器 1.1 Lit 着色器 1.2 法线向量 1.3 漫反射光 2 可见光 2.1 光缓冲 2.2 填充缓冲区 2.3 配置灯光 2.4 可变的灯光数量 3 点光源 3.1 灯光位置 3.2 距离衰减 3.3 灯光范围 4 聚光灯 4.1 聚光方向 4.2 角度衰减 5 逐物体光照 5.1 灯光索引 5.2 多可见光 5.3 顶点光 5.4 过多的可见光 5.5 0可见光 |
本文重点:
1、漫反射着色
2、支持方向光、点光源和聚光灯
3、每帧允许16个可见光源
4、每个对象最多计算四个像素光和四个顶点光
这是涵盖Unity可编写脚本的渲染管线的教程系列的第三部分。这次,我们将通过一个Drawcall为每个对象最多着色8个灯光来增加对漫反射光照的支持。
本教程使用Unity 2018.3.0f2制作。
本教程是CatLikeCoding系列的一部分,原文地址见文章底部。“原创”标识意为原创翻译而非原创教程。
(256个球 8个灯光 214个 draw calls)
1 受光的着色器
为了支持灯光,我们必须在管线中添加一个照明着色器。照明的复杂性可以从非常简单的(仅包括漫射光)到非常复杂的成熟的基于物理的阴影。或者像卡通渲染一样,它也可以是不真实的。我们将从最小的照明着色器开始,该着色器可用于计算没有阴影的漫反射的定向光。
1.1 Lit 着色器
复制Unlit.hlsl并将其重命名为Lit.hlsl。将新文件中所有unlit的地方替换为Lit,特别是包含定义以及顶点和片段函数名称。
复制Unlit.shader并将其重命名为Lit.shader,在新文件中再次将Unlit替换为lit。
不应该要使用一个lit的通道(pass)吗?
因为我们的管线仍然非常的基础,所以先不管。
现在,我们可以使用新的着色器创建一个受光的不透明材质,尽管它仍然与不受光的变体完全一样。
(不受光的着色器资产)
1.2 法线向量
为了计算定向光的贡献,我们需要知道表面法线。因此,我们必须向顶点输入和输出结构都添加法线向量。有关如何计算照明的详细说明,请参见渲染4,第一个照明。
在LitPassVertex中将法线从对象空间转换为世界空间。因为我们假设仅使用统一的比例尺,所以我们可以简单地使用模型矩阵的3×3部分,然后在LitPassFragment中对每个片段进行归一化。对非均匀比例尺的支持会需要我们使用转置的世界到对象矩阵。
为了验证我们最终得到正确的法向矢量,请将它们用作最终颜色。但是仍然要跟踪该材质,因为我们稍后将其用于反照率。
(显示原始世界空间法线向量)
1.3 漫反射光
漫射光的贡献取决于光入射到表面的角度,该角度是通过计算表面法线的点积与光的出射方向得出的,从而消除了负的结果。在定向光的情况下,光矢量是恒定的。现在让我们使用一个硬编码的方向,笔直向上。将漫射光与反照率相乘以获得最终颜色。
(从入射光的0°到90°角度的漫反射衰减)
(顶面的漫反射)
2 可见光
为了能够使用场景中定义的灯光,我们的管线必须将灯光数据发送到GPU。一个场景中可能有多个灯光,因此我们也应该支持多个灯光。有多种方法可以做到这一点。Unity的默认管线针对每个对象在单独的通道中渲染每个灯光。轻量级管线针对每个对象在一次通道中渲染所有灯光。HD管线使用延迟渲染,该渲染将渲染所有对象的表面数据,然后每光源渲染一遍。
我们将使用与“轻量级”管线相同的方法,因此每个对象都要渲染一次,并考虑所有光照。通过发送GPU当前可见的所有灯光的数据来实现。场景中不影响渲染的任何灯光都会被忽略。
2.1 光缓冲
一次渲染所有灯光意味着所有灯光数据必须同时可用。目前仅限于定向光,这意味着我们需要知道每种光的颜色和方向。为了支持任意数量的灯光,我们将使用数组存储此数据,并将其放入一个单独的缓冲区中,该缓冲区名为_LightBuffer。像在C#中一样,在着色器中定义数组,但方括号在变量名称之后而不是类型后面。
但是,我们不能任意定义数组的大小。数组定义必须立即声明其大小。让我们使用4的数组长度。这意味着我们可以一次支持多达四个可见光。使用宏定义此限制,以方便参考。
在灯光缓冲区下方,添加一个DiffuseLight函数,该函数使用灯光数据来进行光照计算。它需要一个光索引和法线向量作为参数,从数组中提取相关数据,然后执行漫射照明计算并将其返回,并由光的颜色进行调制。
在LitPassFragment中,使用for循环对每个光调用一次新函数,从而累积影响片段的总漫射光。