UnityShader入门精要-9

目录

1. Unity的渲染路径

前向渲染路径

Unity中的前向渲染

延迟渲染

Unity的光源类型

Unity的光照衰减

Unity的阴影


1. Unity的渲染路径

Unity 5.0之前,有3种渲染路径:前向渲染路径(Forward Rendering Path)、延迟渲染路径(Deferred Rendering Path)和顶点照明渲染路径(Vertex Lit Rendering Path)。

Unity5.0之后,顶点照明渲染路径已不建议使用(但Unity依然兼容);且新的延迟渲染路径代替了原来的延迟渲染路径(老版本的延迟渲染路径在Unity中依然兼容)。

前向渲染路径

每进行一次完整的前向渲染,我们需要渲染该对象的图元,并计算两个缓冲区的信息:颜色缓冲区深度缓冲区

先根据深度缓冲来决定一个片元是否可见,如果可见再更新颜色缓冲区中的颜色值。

Pass
{
    for (each primitive in this 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);
            }
        }
    }
}

每个逐像素光源都需要进行上述渲染流程。如果一个物体存在多个逐像素光源的影响,则需要执行多个Pass,每个Pass计算一个逐像素光源的光照结果,然后在帧缓冲中把这些光照结果混合,得到最终的颜色值。

Pass执行次数 = N个物体 * M个光源

限制每个物体的逐像素光照数目,以此来限制Pass执行的次数。

Unity中的前向渲染

Unity中,前向渲染有3种处理光照的方式:逐顶点处理、逐像素处理、球谐函数(Spherical Harmonics, SH)处理。

光源:取决于它的类型和渲染模式。

前向渲染中,渲染一个物体时,Unity会根据场景中各个光源的设置以及这些光源对物体的影响程度(距离远近、光照强度等)对这些光源进行重要度排序。

其中:

  • 一定数目的光源会按逐像素的方式处理;(最亮的平行光,Important

  • 最多4个光源按逐顶点的方式处理;(Not Important)

  • 剩下的光源按球谐(SH)方式处理。(Not Important)

延迟渲染

延迟渲染主要包含2个Pass。第一个Pass不进行任何光照计算,仅仅计算哪些片元可见,将可见的片元信息存储到G缓冲区。

延迟渲染适合在光源数目众多、使用前向渲染造成性能瓶颈的情况下使用,每个光源都可以按逐像素处理。

Pass 1
{
    // 第一个Pass不进行真正的光照计算
    // 仅仅把光照计算需要的信息保存到G缓冲中
    for (each primitive in this model)
    {
        for (each fragment covered by this primitive)
        {
            if (failed in depth test)
            {
                // 如果没有通过深度测试,则该片元不可见
                discard;
            }
            else
            {
                // 如果该片元可见,把需要的信息存储到G缓冲中
                writeGBuffer(materialInfo, pos, normal, lightDir, viewDir);
            }
        }
    }
}

Pass 2
{
    // 使用G缓冲中的信息进行真正的光照计算
    for (each pixel in the 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);
        }
    }
}

缺点:

  • 不支持MSAA;

  • 不能处理半透明物体;

  • 延迟渲染只能使用一个光照模型,不支持多pass

默认的G缓冲区包含了以下几个渲染纹理(Render Texture,RT):

  • RT0 :ARGB32格式,RBG通道存储漫反射颜色,A通道未被使用。

  • RT1 :ARGB32格式,RGB通道存储高光反射颜色,A通道存储高光反射的指数部分。

  • RT2 :ARGB2101010格式,RGB通道存储法线,A通道未被使用。

  • RT3 :ARGB32格式(非HDR)或ARGBHalf(HDR),用于存储自发光 + lightmap + 反射探针

  • 深度缓冲和模板缓冲

Unity的光源类型

Unity一共支持4种光源类型:平行光点光源聚光灯面光源(area light)。面光源仅在烘焙时才有用。最常使用的光源属性有:光源的位置方向(到某点的方向)、颜色强度以及衰减(到某点的衰减,与点到光源的距离有关)。

Unity的光照衰减

Unity内部使用一张名为_LightTexture0的纹理来计算光照衰减(使用cookie的光源的衰减查找纹理是_LightTextureB0)。对_LightTexture0衰减纹理的采样基于定点在光源空间中的位置,会用到_LightMatrix0矩阵用于将定点的坐标由世界空间转到光源空间:

float3 lightCoord = mul(_LightMatrix0, float4(i.worldPosition, 1)).xyz;

然后使用这个坐标模的平方对衰减纹理进行采样,得到衰减值:

fixed atten = tex2D(_LightTexture0, dot(lightCoord, lightCoord).rr).UNITY_ATTEN_CHANNEL;


float distance = length(_WorldSpaceLightPos0.xyz - i.worldPosition.xyz);
// 线性衰减
atten = 1.0 / distance;

Unity的阴影

Shadow Map技术:把相机放到与光源场合的位置,相机看不到的位置就是场景中阴影的区域。

前向渲染中,如果场景中最重要的平行光开启了阴影,Unity就会为这个光源计算它的阴影映射纹理(Shadowmap),它本质是一张深度图,记录了从光源的位置出发、能看到的场景中距离它最近的表面位置(深度信息)。为了得到映射纹理中的深度信息,需要把摄像机放到光源的位置,然后按照正常的渲染流程(调用Base Pass和Additional Pass)来更新深度信息。但是这样会造成一定的性能浪费,所以Unity使用了额外的Pass专门更新光源的阴影映射纹理,这个Pass的LightMode标签被设置为ShadowCaster

  • 传统的阴影映射纹理实现过程:

在正常渲染的Pass中把顶点位置变换到光源空间下,得到光源空间中顶点的位置,使用xy分量对阴影映射纹理进行采样得到记录的深度值,如果它小于这个顶点的z分量,则说明这个顶点在阴影中。

  • Unity 5及以后使用的屏幕空间的阴影映射技术(Screenspace Shadow Map):

调用LightMode为ShadowCaster的Pass得到光源的阴影映射纹理和相机的深度纹理,如果相机深度纹理记录的深度大于转换到光源阴影映射纹理中的深度值,则说明这个物体表面的该点处于阴影中。通过这样的方式,生成的阴影图包含了屏幕空间中所有有阴影的区域。我们只需要对这个阴影图进行采样就可以计算场景中哪些物体会呈现阴影了。

关于阴影,其实涉及投射接收两个过程。举个例子,在太阳光下物体A挡住了物体B,在物体B上产生了阴影。那么对于物体A,就是投射阴影的过程,对于物体B就是接收阴影的过程。

光源的阴影映射纹理的生成过程会计算投射物体在光源空间中的位置;

接收物体的Shader实现中会对光源的阴影映射纹理进行采样,以确认它是否会被挡住并产生阴影。

参考:《Unity Shader入门精要》笔记(十二) - 知乎

你可能感兴趣的:(unity,unity,游戏引擎)