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入门精要》笔记(十二) - 知乎

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Unity Shader是一种用于渲染图形的程序,它可以控制对象的表面颜色、纹理、透明度、反射等属性,从而实现特殊的视觉效果。对于游戏开发者来说,掌握Shader编写技巧是非常重要的。 以下是关于Unity Shader入门精要: 1. ShaderLab语言 ShaderLab是Unity中用于编写Shader的语言,它是一种基于标记的语言,类似于HTML。ShaderLab可以用于定义Shader的属性、子着色器、渲染状态等信息。 2. CG语言 CG语言是Unity中用于编写Shader的主要语言,它是一种类似于C语言的语言,可以进行数学运算、向量计算、流程控制等操作。CG语言可以在ShaderLab中嵌入,用于实现Shader的具体逻辑。 3. Unity的渲染管线 Unity的渲染管线包括顶点着色器、片元着色器、几何着色器等组件,每个组件都有不同的作用。顶点着色器用于对对象的顶点进行变换,片元着色器用于计算每个像素的颜色,几何着色器用于处理几何图形的变形和细节等。 4. 模板和纹理 在Shader中,我们可以使用纹理来给对象添加图案或者贴图,也可以使用模板来控制对象的透明度、反射等属性。纹理可以通过内置函数tex2D()来获取,模板可以通过内置函数clip()来实现裁剪。 5. Shader的实现 Shader的实现需要注意以下几点: - 在ShaderLab中定义Shader的属性、子着色器、渲染状态等信息。 - 在CG语言中实现Shader的具体逻辑,包括顶点着色器、片元着色器等内容。 - 使用纹理和模板来实现特定的视觉效果。 - 在对象上应用Shader,通过调整Shader的属性来达到不同的效果。 以上是关于Unity Shader入门精要,希望对你有所帮助。如果你想更深入地了解Shader的编写技巧,可以参考官方文档或者相关教程。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值