【Unity Shader入门精要学习】复杂的光照(一)

Unity的渲染路径

在Unity里,渲染路径(Rendering Path)决定了光照是如何应用到Unity Shader中的,只有正确的设置每个Pass的渲染路径,Unity才会给Shader提供正确的光源信息(位置,方向,颜色,强度,衰减),我们在计算光照时才会有正确的结果。
在Unity中提供了两种渲染路径

一、前向渲染(Forward Rendering Path)

前向渲染也有两种路径:

Base Pass
Tags{"LightModel"="ForwardBase"}
Addtional Pass
Tags{"LightModel"="ForwardAdd"}

前向渲染路径是传统的渲染方式,也是常用的一种渲染路径

伪代码
Pass
{
    for(each primitive in model)
    {
        //所有被这个三角形包围的片元
        for(each fragment covered by this primitive)
        {
            if(failed in depth test)
            {
                //如果深度测试失败,说明该片元不可见,不应该被着色
                discard;
            }
            else{
                float4 color = Shading(materialInfo,pos,normal,lightDir,viewDir);
                writeFrameBuffer(fragment,color);
            }  
        }
    }
}

1、原理

每进行一次完整的前向渲染,都需要渲染该对象的渲染图元,并计算两个缓冲区的信息:颜色缓冲区,深度缓冲区。利用深度缓冲区来决定一个片元是否可见,如果可见就更新颜色缓冲区的颜色值。
每个逐像素光源,都需要进行一次完整的渲染流程。如果一个物体在多个逐像素光源的影响区域内,则这个物体需要执行多个Pass,每个Pass计算一个逐像素的的光照结果,然后在帧缓冲中把这些光照结果混合起来得到最终的颜色值。也就是如果有N个物体受M个光源影响,则需要执行N*M次的Pass。所以逐像素的光源如果多了就会有更多的Pass。因此引擎一般都会限制逐像素光照的数目。

2、Unity中的前向渲染

而实际上一个Pass不仅仅是可以执行逐像素的光照,还可以进行逐顶点等光照,这取决于计算光照的流水线以及使用的数学模型。当渲染一个物体时,Unity会计算哪些光源会照亮这个物体,以及这个光源照亮物体的方式(就是用哪种方式来计算)。
在Unity中,前向渲染路径有3种处理光照(即照亮物体的方式)的方式:逐顶点处理,逐像素处理,球谐函数(Spherical Harmonics Function)处理。而决定一个光源使用哪种处理模式取决于光源的类型和渲染的模式光源类型指的是该光源是平行光还是其他类型光,而光源的渲染模式指的是该光源是否是重要的(Important)。如果设置成Important,就意味着使用逐像素的方式。
在前向渲染中,Unity会根据场景中各个光源的设置以及这些光源对物体的影响程度(距离物体的远近,光强度等)对场景中的光源进行一个重要度的排序。其中一定数目的光源按照逐像素的方式处理,最多有4个光源会逐顶点的方式处理,剩下的就会按照SH处理,具体规则如下:
(1)场景中最亮的平行光总是按照逐像素处理。
(2)渲染模式被设置成Not Important的会按照逐顶点或SH处理

image.png

(3)渲染模式被设置成Important的会按照逐像素处理
image.png

(4)如果场景中逐像素的光源数目小于Quality Setting中的逐像素光源数量,那么剩余的逐像素光源也会按照逐像素处理
image.png

两种Pass的对光照的计算
(1)Base Pass
支持访问光照纹理(lightmap)、平行光默认支持阴影(平行光源开启阴影功能)、可以在Base Pass中计算环境光,自发光(因为这两种光只需要计算一次就可以)。既可以逐像素光照,也可以逐顶点。一般只执行一次。
(2)Addtional Pass
Addtional Pass中渲染的光源默认情况下没有阴影,即便光源本身设置了Shadow Type,但是可以使用编译指令:
#pragma multi_compile_fwadd_fullshadows
为点光源,聚光灯开启阴影效果。还需要开启混合模式,因为我们需要把Addtional Pass中的得到光照颜色和上一次的光照叠加起来,从而得到多个光照渲染的效果,如果没有混合,之前的颜色会覆盖掉,看起来像只受最后一个光源的影响,一般都是Blend One One,也是可以是其他。
Shadow Type

通常对于前向渲染UnityShader只需要定义一个Base Pass(也可以定义多个,如双面渲染,需要先画后面,再画前面),一个Addtional Pass。一般Base Pass只会执行一次(定义多个就执行多次),而Addtional Pass会根据影响该物体的其他逐像素光源的数目被多次调用(逐顶点也可以)。
渲染路径的设置用于告诉Unity该Pass在前向渲染路径中的位置,引擎就会在相关计算中填充一些内置变量。至于如何使用这些变量,完全取决于开发者,如在BasePass和Addtional Pass中逐顶点光照而不是逐像素光照。


两种Pass的特性

二、延迟渲染(Deferred Rendering Path)

前向渲染的问题是:当场景中存在大量实时光源时,渲染性能会急剧下降。当有很多光源重叠在一个区域内,为了得到最终的光照颜色,需要为每个光源执行一次Pass,如果这个区域内很多物体那么执行Pass的次数会成倍增加,而每执行一次Pass就会重新渲染一次物体,这样性能会下降。
延迟渲染(Deferred Rendering)目的是为了解决前向渲染产生的瓶颈问题(延迟渲染已经是一种古老的渲染方法,只是近几年又流行起来)。除了前向渲染使用的颜色缓冲区和深度缓冲区延迟渲染还会使用额外的缓冲区,被统称为G缓冲区(Geometry,G-Buffer)G缓冲区存储了我们所关心的表面信息(通常都是离摄像机最近的表面),如表面的法线,位置,材质属性。

1、原理

伪代码
Pass1
{
    //第一个Pass不进行光照,只根据深度把能写入G-Buffer的片元挑选出来
    for(each primitive in model)
    {
        //所有被这个三角形包围的片元
        for(each fragment covered by this primitive)
        {
            if(failed in depth test)
            {
                //如果深度测试失败,说明该片元不可见,不应该被写入G-Buffer
                discard;
            }
            else{
                 //如果可见就就要把相关信息存储到G-Buffer中
                WriteGBuffer(materialInfo,pos,normal,lightDir,viewDir);
            }  
        }
    }
}

Pass2
{
    //第二个Pass会根据G-Buffer中的片元信息进行光照
    for(each pixel in screen)
    {
       if(the pixel is valid)
       {
           //如果该像素是有效的
           //读取它对应的G缓冲中的信息
           readGBuffer(pixel,materialInfo,pos,normal,lightDir,viewDir);

           //根据读取的信息进行光照
           float4 color = Shading(materialInfo,pos,normal,lightDir,viewDir);
           WriteFrameBuffer(pixel,color);
       }
    }
}

延迟渲染主要包含两个Pass,第一个Pass中不进行任何光照,只根据深度信息把符合条件的片元信息存储到G-Buffer中。第二个Pass,会根据G-Buffer中的各个片元的信息,如法线,视角方向,漫反射系数等进行光照计算。
可以看出延迟渲染和场景中的光源数量是没有关系的,因为经过第一个Pass之后,G-Buffer里面存储的就已经是可以直接进行光照计算的片元信息了,只需要画一次就可以了(对与前向渲染可能得需要画N*M次)。所以对于延迟渲染G-Buffer的大小才是性能的瓶颈所在,而G-Buffer又和屏幕空间大小有关,所以延迟渲染的效率在于我们使用的屏幕空间大小。所以说延迟渲染所有光源都可以使用逐像素处理。

2、延迟渲染的缺点

(1)不支持真正的抗锯齿(anti-aliasing)
(2)无法处理半透明物体(半透明关闭了深度写入)
(3)需要显卡支持

3、Unity中的延迟渲染

在Unity中使用延迟渲染。需要提供两个Pass。
第一个Pass用于渲染G缓冲,在这个Pass中,需要把物体的漫反射颜色,高光反射颜色,平滑度,法线,和深度信息渲染到屏幕空间的G-Buffer中,对于每个物体,这个Pass只会执行一次。
第二个Pass,用于真正的关照计算,会使用上一个Pass中的渲染数据来计算最终的颜色,在存储到帧缓冲区中。第二个Pass可以默认使用Unity内置的Standard光照模型。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,366评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,521评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,689评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,925评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,942评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,727评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,447评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,349评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,820评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,990评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,127评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,812评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,471评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,017评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,142评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,388评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,066评论 2 355

推荐阅读更多精彩内容