【Unity Shader】Unity中阴影映射标准制作流程

这篇具有很好参考价值的文章主要介绍了【Unity Shader】Unity中阴影映射标准制作流程。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前半部分的基于图片的实时阴影技术是百人计划的前半部分总结,后面的Unity中的实现过程是《入门精要》中的实现。


1 基于图片的实时阴影技术

这里的“基于图片”指阴影生成一张图片。

1.1 平面投影阴影

他并不是一个基于图片的解决方案,但思路值得借鉴。

缺点:只能投影到平面;投影物体必须在光线和平面之间——意味着这种方法曲面/折面就不可行了,那么需要解决这个问题——在光源地方放一个Camera,生成一个深度图,记录距离最近表面的深度信息:

【Unity Shader】Unity中阴影映射标准制作流程

这个就是阴影映射的核心思想。 

1.2 阴影映射 Shadow Map

上面的图中,右侧有一个光照到整个场景,右图就是一个深度图,记录深度值(0-1的颜色记录下来每个像素的位置远近和深度关系)。

阴影映射的大概思路:

1.首先在光源位置生成光源空间下的深度图(一张Shadowmap);

2.然后从真正的Camera视角去渲染整个场景的物体,每次渲染都需要先把当前片元的深度zp(屏幕空间)与Shadowmap(光源空间下)记录的深度值zs作比较(深度测试),如果zp > zs,意味着该片元在阴影里。

2 Unity中的屏幕空间阴影映射

前向渲染想要获得shadowmap,会在光源处同样进行一次Base Pass和Additional Pass来得到深度值,但这样消耗太大了!所以Unity提供了一种新的思路——屏幕空间阴影映射,以此实现Shadow Map。

Unity中实现Shadow Map主要有两个特点,

  • 额外提供了一个LightMode为ShadowCaster的Pass;
  • 是采样的方式不同——屏幕空间阴影映射 

过程大概是,

  1. 渲染屏幕空间深度图;
  2. 光源方向渲染一个shadowmap——用的是调用ShadowCaster的Pass的方式;
  3. 在屏幕空间做一次阴影收集,把光源下的shadowmap和摄像机下的深度图做判断之后,得到一个新的深度图——屏幕空间阴影纹理
  4. 最后uv直接采样3中得到的屏幕空间阴影纹理。

2.1 物体投射阴影

如果想要一个物体投射阴影,意味着它将要参与shadowmap的计算中去。我们知道,一个Pass是在Shader中定义的,那么Shader与材质紧紧关联,如果一个物体的Shader中开启了ShadowCaster这个Pass,那么这个物体就可以参与到光源方向渲染shadowmap记录的信息中去,意味着这个物体的信息已经可以参与其他物体计算阴影的过程中去了。

在Unity中的实现

让我们在Unity中实际看看这个过程是如何实现的。

【Unity Shader】Unity中阴影映射标准制作流程

 Unity中每个物体的lighting组件都会有Cast ShadowsReceive Shadows两个可选项。

当物体Cast Shadows为On,意味着当前物体处于可投射阴影的状态。此时Unity就会为当前物体执行LightMode为ShadowCaster的Pass

查看ShadowCaster Pass

默认的前向渲染路径都是只有Base Pass和Additional Pass,但Shaderlab的Fallback语义我们通常会定义:

Fallback "Specular"

查找Unity内置的Vertex.Lit:

    // Pass to render object as a shadow caster
    Pass {
        Name "ShadowCaster"
        Tags { "LightMode" = "ShadowCaster" }

CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma target 2.0
#pragma multi_compile_shadowcaster
#pragma multi_compile_instancing // allow instanced shadow pass for most of the shaders
#include "UnityCG.cginc"

struct v2f {
    V2F_SHADOW_CASTER;
    UNITY_VERTEX_OUTPUT_STEREO
};

v2f vert( appdata_base v )
{
    v2f o;
    UNITY_SETUP_INSTANCE_ID(v);
    UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
    TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)
    return o;
}

float4 frag( v2f i ) : SV_Target
{
    SHADOW_CASTER_FRAGMENT(i)
}
ENDCG

    }

}

}

上述代码的目的就是,把当前的深度信息写入渲染目标中。

2.2 物体接收阴影

想要一个物体接受阴影,就是物体在进行光照计算后绘制的颜色除了光照计算的结果值,还需要考虑阴影的颜色,也就是还需要加上“采样阴影映射纹理”这一步,再把采样的结果和光照计算结果相乘,才算是考虑了阴影效果。我们举个例子:

如下图所示,左图是最终的效果图,右边是屏幕空间阴影纹理。这里的平面相当于“开启接受阴影”的物体,那么在渲染得到平面上每个片元的颜色时,就还需要多家一步用每个片元对应位置去采样右边的阴影纹理的步骤,光照结果*采样结果就能得到最终的值(值总是非0即1的)。

【Unity Shader】Unity中阴影映射标准制作流程【Unity Shader】Unity中阴影映射标准制作流程

Base Pass中处理阴影投射

让一个物体接受阴影就没有实现阴影投射那么简单了,如下情况:

【Unity Shader】Unity中阴影映射标准制作流程

右边的高一点Cube的阴影并没有投射到左边的小Cube上,上述效果对于一个不透明物体来说,肯定是错误的。

如何纠正呢?

上述场景中的两个Cube我挂的Shader都是没考虑阴影的前向渲染Shader,这里我们要加上Unity内置的宏,SHADOW_COORDSTRANSFER_SHADOWSHADOW_ATTENUATION,计算出采样的阴影值后与diffuse和specular值相乘即可,三个宏也都已经体现在下面的代码中了:

Shader "Unity Shaders Book/Chapter 9/Shadow"
{
    Properties
    {
        _Diffuse ("Diffuse", Color) = (1, 1, 1, 1)
        _Specular ("Specular", Color) = (1, 1, 1, 1)
        _Gloss ("Gloss", Range(8, 256)) = 20.0
    }

    SubShader {


        // Pass for ambient light & directional light
        Pass {
            Tags {"LightMode"="ForwardBase"}

            CGPROGRAM
            
            #pragma vertex vert
            #pragma fragment frag
            #include "Lighting.cginc"
            #include "AutoLight.cginc"  //for shadow--
            //need to add this declaration
            #pragma multi_compile_fwdbase

            //properties
            fixed4 _Diffuse;
            fixed4 _Specular;
            float _Gloss;

            struct a2v {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
            };
            struct v2f {
                float4 pos : SV_POSITION;
                float3 worldNormal : TEXCOORD0;
                float3 worldPos : TEXCOORD1;
                SHADOW_COORDS(2)  //内置宏->声明一个用于对阴影纹理采样的坐标
            };

            v2f vert(a2v v) {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.worldNormal = UnityObjectToWorldNormal(v.normal);
                o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
                TRANSFER_SHADOW(o);  //计算上一步中声明的阴影纹理坐标
                return o;
            }

            fixed4 frag(v2f i) : SV_Target {
                fixed3 worldNormal = normalize(i.worldNormal);
                fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
                fixed worldViewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
                fixed3 halfDir = normalize(worldViewDir + worldLightDir);

                fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLightDir));
                fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(halfDir, worldNormal)), _Gloss);
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;

                //attenuation of directional light
                fixed atten = 1.0;
                //根据纹理坐标采样
                fixed shadow = SHADOW_ATTENUATION(i);
                return fixed4 (ambient + (diffuse + specular) * atten * shadow, 1.0);
            }
            ENDCG
        }
        Pass {
            Tags {"LightMode"="ForwardAdd"}
            Blend One One

            CGPROGRAM

            #pragma vertex vert
            #pragma fragment frag
            #include "Lighting.cginc"
            #include "Autolight.cginc"
            #pragma multi_compile_fwdadd

            //properties
            fixed4 _Diffuse;
            fixed4 _Specular;
            float _Gloss;

            struct a2v {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
            };
            struct v2f {
                float4 pos : SV_POSITION;
                float3 worldNormal : TEXCOORD0;
                float3 worldPos : TEXCOORD1;
            };

            v2f vert(a2v v) {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.worldNormal = UnityObjectToWorldNormal(v.normal);
                o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
                return o;
            }

            fixed4 frag(v2f i) : SV_Target {
                fixed3 worldNormal = normalize(i.worldNormal);
                #ifdef USING_DIRECTIONAL_LIGHT
                    fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
                #else  //is pointlight
                    fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz - i.worldPos.xyz);
                #endif
                fixed worldViewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
                fixed3 halfDir = normalize(worldViewDir + worldLightDir);

                fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLightDir));
                fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(halfDir, worldNormal)), _Gloss);
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;

                //attenuation of directional light
                #ifdef USING_DIRECTIONAL_LIGHT
                    fixed atten = 1.0;
                #else
                    //1.Change point from world to lightspace, add-> "Autolight.cginc"
                    float3 lightCoord = mul(unity_WorldToLight, float4(i.worldPos, 1)).xyz;
                    //2.sample
                    fixed atten = tex2D(_LightTexture0, dot(lightCoord, lightCoord).rr).UNITY_ATTEN_CHANNEL;
                #endif
                return fixed4 (ambient + (diffuse + specular) * atten, 1.0);
            }
            ENDCG
        }
    }
    FallBack  "Specular"
}

最终的效果为,现在就正确啦: 

【Unity Shader】Unity中阴影映射标准制作流程

Additonal Pass中处理阴影投射

在前向渲染学习过程中我们了解到,Base Pass是处理平行光的,Additional Pass是处理点光源等等的,当我们把场景中的平行光换成点光源时,没有对Additional Pass加上阴影处理,效果将会是这样:

【Unity Shader】Unity中阴影映射标准制作流程

右边的Cube阴影又一次”穿过了“左边的Cube,这又是一种错误的效果。

如何解决呢?

这里有用到了一个新的宏——UNITY_LIGHT_ATTENUATION

UNITY_LIGHT_ATTENUATION(atten, i, worldPos);

如果要用它,两个Pass要一起用上,也就是Base Pass就不需要再单独处理阴影了,也不需要单独在Additional Pass中单独处理光照衰减量atten。

这个在AutoLight.cginc中定义的宏,给出了一些SPOT、DIRECTIONAL、POINT_COOKIE、DIRECTIONAL_COOKIE,也就是针对不同光源类型、是否启用cookie等情况声明了对应版本的UNITY_LIGHT_ATTENUATION。

最后整个代码:

Shader "Unity Shaders Book/Chapter 9/Shadow"
{
    Properties
    {
        _Diffuse ("Diffuse", Color) = (1, 1, 1, 1)
        _Specular ("Specular", Color) = (1, 1, 1, 1)
        _Gloss ("Gloss", Range(8, 256)) = 20.0
    }

    SubShader {


        // Pass for ambient light & directional light
        Pass {
            Tags {"LightMode"="ForwardBase"}

            CGPROGRAM
            
            #pragma vertex vert
            #pragma fragment frag
            #include "Lighting.cginc"
            #include "AutoLight.cginc"  //for shadow--
            //need to add this declaration
            #pragma multi_compile_fwdbase

            //properties
            fixed4 _Diffuse;
            fixed4 _Specular;
            float _Gloss;

            struct a2v {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
            };
            struct v2f {
                float4 pos : SV_POSITION;
                float3 worldNormal : TEXCOORD0;
                float3 worldPos : TEXCOORD1;
                SHADOW_COORDS(2)  //内置宏->声明一个用于对阴影纹理采样的坐标
            };

            v2f vert(a2v v) {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.worldNormal = UnityObjectToWorldNormal(v.normal);
                o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
                TRANSFER_SHADOW(o);  //计算上一步中声明的阴影纹理坐标
                return o;
            }

            fixed4 frag(v2f i) : SV_Target {
                fixed3 worldNormal = normalize(i.worldNormal);
                fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
                fixed worldViewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
                fixed3 halfDir = normalize(worldViewDir + worldLightDir);

                fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLightDir));
                fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(halfDir, worldNormal)), _Gloss);
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;

                // //attenuation of directional light
                // fixed atten = 1.0;
                //根据纹理坐标采样
                UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);
                return fixed4 (ambient + (diffuse + specular) * atten, 1.0);
            }
            ENDCG
        }
        Pass {
            Tags {"LightMode"="ForwardAdd"}
            Blend One One

            CGPROGRAM

            #pragma vertex vert
            #pragma fragment frag
            #include "Lighting.cginc"
            #include "AutoLight.cginc"
            #pragma multi_compile_fwdadd_fullshadows

            //properties
            fixed4 _Diffuse;
            fixed4 _Specular;
            float _Gloss;

            struct a2v {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
            };
            struct v2f {
                float4 pos : SV_POSITION;
                float3 worldNormal : TEXCOORD0;
                float3 worldPos : TEXCOORD1;
                SHADOW_COORDS(2)
            };

            v2f vert(a2v v) {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.worldNormal = UnityObjectToWorldNormal(v.normal);
                o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
                TRANSFER_SHADOW(o);
                return o;
            }

            fixed4 frag(v2f i) : SV_Target {
                fixed3 worldNormal = normalize(i.worldNormal);
                #ifdef USING_DIRECTIONAL_LIGHT
                    fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
                #else  //is pointlight
                    fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz - i.worldPos.xyz);
                #endif
                fixed worldViewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
                fixed3 halfDir = normalize(worldViewDir + worldLightDir);

                fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLightDir));
                fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(halfDir, worldNormal)), _Gloss);
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;

                // //attenuation of directional light
                // #ifdef USING_DIRECTIONAL_LIGHT
                //     fixed atten = 1.0;
                // #else
                //     //1.Change point from world to lightspace, add-> "Autolight.cginc"
                //     float3 lightCoord = mul(unity_WorldToLight, float4(i.worldPos, 1)).xyz;
                //     //2.sample
                //     fixed atten = tex2D(_LightTexture0, dot(lightCoord, lightCoord).rr).UNITY_ATTEN_CHANNEL;
                // #endif
                UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);
                return fixed4 (ambient + (diffuse + specular) * atten, 1.0);
            }
            ENDCG
        }
    }
    FallBack  "Specular"
}

最后的效果:

【Unity Shader】Unity中阴影映射标准制作流程文章来源地址https://www.toymoban.com/news/detail-416651.html

到了这里,关于【Unity Shader】Unity中阴影映射标准制作流程的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Unity Shader 学习笔记(4)URP渲染管线带阴影PBR-Shader模板 -- 新增可自定义阴影颜色

    材质面板截图 功能实现(URP渲染管线下): 1、进一步优化Shader结构和算法; 2、包含PBR材质; 3、投射和接收阴影,并升级 支持自定义阴影颜色 ; 4、支持点光源照射(但不支持点光源阴影)。 通用渲染截图 自定义阴影颜色截图 完整代码: 写在最后: 1、在我的上一篇文

    2024年02月12日
    浏览(48)
  • 【Unity ShaderGraph】| 快速制作一个 卡通阴影色块效果

    前言 本文将使用ShaderGraph制作一个卡通阴影色块的效果,可以直接拿到项目中使用。 对ShaderGraph还不了解的小伙伴可以参考这篇文章:【Unity ShaderGraph】| Shader Graph入门介绍 | 简介 | 配置环境 | 窗口介绍 | 简单案例 下面就开始看一下具体的制作流程,然后自己动手制作一个吧

    2024年02月06日
    浏览(48)
  • unity URP 利用particle system制作简单的shader交互

    首先这里制作了一个简单交互,使用shader grapgh,根据计算距离和变化数值的差实现交互后的扩散,同时计算消散遮罩让它逐渐谈去。 将他赋予材质物体,根据脚本传入位置和逐渐变化的大小后,呈现这样的效果。 但是,shader graph这样的工具,在做这种效果非常快的同时,也

    2024年02月07日
    浏览(43)
  • Unity标准化的场景渲染流程

    笔者用的unity版本为2020.3.30f1c1,开一个HDRP模板场景,OK开始我们的操作。 第一步:删除场景中的所有东西只留下竹林玻璃房作为展示场景,将所有物体勾选Static,新增一个摄像机,如下图: 第二步:GameObject Volume Sky and Fog Volume来创建一个自带环境光设置的Sky and Fog Volume,此

    2024年02月11日
    浏览(41)
  • 【unity】快速了解游戏制作流程-制作九宫格简单游戏demo

            hi~大家好呀!欢迎来到我的unity学习笔记系列~,本篇我会简单的记录一下游戏流程并且简单上手一个通过九宫格移动到指定位置的小游戏,话不多说,我们直接开始吧~                  本篇源自我看B站一位up主的视频所做的笔记,感兴趣的可以去看原视频哦

    2023年04月08日
    浏览(54)
  • Unity学习笔记(7) Unity2D骨骼动画制作流程

    用骨骼动画最大的好处就是方便之后做换装系统。 无论对3Dor2D游戏都是这样的。 这篇博文记录一下2D骨骼动画的制作流程,参考b站麦扣的系列教程: https://space.bilibili.com/370283072/channel/seriesdetail?sid=211996 用PS做一个将人物各个部件(骨骼)都分解开来的人物模型。 导出时注意

    2023年04月09日
    浏览(56)
  • unity到小游戏instantGame(流程演示,非内容制作 亲测可用)

    instantGame 后妈级教学,主打一个填自己的坑,有问题下方评论交流 根据此教程使用后的一点心得记录和可能出现的问题补充,instant-game-joker使用教程; (建议教程非游戏部分反复琢磨) 冲冲冲 unity版本使用2021.2.5f1c302,安装instantGame ,官方教程; 下载插件,并导入到unity中

    2024年04月25日
    浏览(37)
  • 利用MATLAB制作DEM山体阴影

    在地理绘图中,我们使用的DEM数据添加山体阴影使得绘制的图件显得更加的美观。 GIS中使用ArcGIS软件就可以达到这一目的,或者使用GMT,同样可以得到山体阴影的效果。 本文提供了一个MATLAB的函数,可以得到山体阴影。 其中调用的函数 hillshade_esri.m如下: 其中有三个参数可

    2024年02月15日
    浏览(44)
  • python matlplotlib/seaborn 绘制曲线的平均值标准差阴影图

    旧版本(0.8.1)中使用tsplot,新版本中使用lineplot 直线代表均值,阴影代表mean±std(带有置信区间,参数ci) 不进行平滑处理 平滑处理 画mean+/- standard deviation(std)的曲线图。 导入需要的库:matplotlib 用matplotlib.pyplot画均值曲线(图里的实线) 根据方差,用“fill_between”命令设

    2024年01月23日
    浏览(82)
  • Unity Lighting -- 配置阴影

            本笔记使用URP管线进行说明         在项目窗口中,主菜单中选择Edit - ProjectSettings,然后选择Quality,查看当前项目所使用的URP资源,目前例子所使用的资源为UniversalRP_HighQuality资源          在工程窗口中,会有对应的渲染管线配置资源文件,选择这个文件,查看其

    2024年02月14日
    浏览(39)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包