[Unity/URP学习]反射探针(Reflection Probe)

这篇具有很好参考价值的文章主要介绍了[Unity/URP学习]反射探针(Reflection Probe)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

反射探针

传统上,游戏使用一种称为_反射贴图_的技术来模拟来自对象的反射,同时将处理开销保持在可接受的水平。此技术假定场景中的所有反射对象都可以“看到”(因此会反射)完全相同的周围环境。如果游戏的主角(比如闪亮的汽车)处于开放空间中,此技术将非常有效,但是当角色进入不同的周围环境时,便看起来不真实;如果一辆汽车驶入隧道但天空仍然在窗户上产生明显反射,看起来就很奇怪。

Unity 通过使用__反射探针__改进了基本反射贴图,这种探针可在场景中的关键点对视觉环境进行采样。通常情况下,应将这些探针放置在反射对象外观发生明显变化的每个点上(例如,隧道、建筑物附近区域和地面颜色变化的地方)。当反射对象靠近探针时,探针采样的反射可用于对象的反射贴图。此外,当几个探针位于彼此附近时,Unity 可在它们之间进行插值,从而实现反射的逐渐变化。因此,使用反射探针可以产生非常逼真的反射,同时将处理开销控制在可接受的水平。

1.反射探针的工作原理

场景中某个点的视觉环境可由立方体贴图表示。立方体贴图在概念上很像一个在内部表面绘有六个方向(上、下、左、右、前、后)平面图像的盒子。

[Unity/URP学习]反射探针(Reflection Probe)

天空盒立方体贴图的内部表面(去除了前面)

为了让对象显示反射,其着色器必须能够访问表示立方体贴图的图像。对象表面的每个点都可在表面朝向的方向(即表面法向矢量的方向)上“看到”立方体贴图的一小块区域。着色器在此处使用立方体贴图的颜色来计算对象表面应该是什么颜色;镜面材质可能会准确反射颜色,而闪亮的汽车可能会略微褪色和着色。

如上所述,传统的反射贴图仅使用单个立方体贴图来表示整个场景的周围环境。立方体贴图可由美术师绘制,也可通过从场景中的一个点进行六次“快照”(每个立方体面对应一次快照)获得。反射探针在这方面进行了改进,允许在场景中设置许多预定义的点在这些位置点可创建立方体贴图快照。因此,可以在场景中存在明显不同反射的任何位置点记录周围视图。

除了视点之外,探针还有一个由场景中的不可见盒体形状定义的效果区域。在探针区域内通过的反射对象的反射立方体贴图由该探针临时提供。当对象从一个区域移动到另一个区域时,立方体贴图会相应改变。

2. 反射探针的类型

反射探针有三种基本类型,通过检视面板中的 Type 属性可进行相应选择:

烘焙 (Baked) 探针可存储 Editor 中生成(烘焙)的反射立方体贴图。

**可以通过单击 Reflection Probe Inspector 底部的 Bake 按钮或 Lighting 窗口中的 Build 按钮来触发烘焙。**如果在 Lighting 窗口中启用了 Auto_,则在 Scene 视图中放置对象时,烘焙探针将自动更新。**烘焙探针的反射只能显示 Inspector 中标记为 Reflection Probe Static_ 的对象。(烘焙的时候注意如果想把某物体烘焙进去,该物体应该勾选ReflectionProbe static)**此设置向 Unity 指示这些对象不会在运行时移动。

实时 (Realtime) 探针在运行时在播放器中而不是在 Editor 中创建立方体贴图。

这意味着反射不仅限于静态对象,而且**可以实时更新以显示场景中的变化。**但是,刷新探针的视图需要相当长的处理时间,因此谨慎管理更新就显得十分重要。**Unity 允许从脚本中触发更新,以便能够准确控制更新的发生时间。**此外,还有一个选项是应用_时间切片_来探测更新,使更新可在几帧内逐渐发生。

烘焙反射探针自定义 (Custom) 探针。

这些探针允许在 Editor 中烘焙视图,就像烘焙探针一样,还能为反射提供自定义的立方体贴图。自定义探针无法在运行时更新。
下面将详细说明这三种类型。

2.1烘焙反射探针

烘焙反射探针是在 Unity Editor 中捕获反射立方体贴图并存储以供后续在播放器中使用的探针捕获过程完成后,反射将被“冻结”,因此**烘焙探针无法响应移动对象在场景中引起的运行时变化。**但是,与实时探针(能响应变化)相比,烘焙探针的处理开销要低得多,并且在许多用途中是可接受的。例如,如果只有一个移动的反射对象,那么它只需要反射其静态环境。

使用烘焙探针

应将探针的 Type 属性设置为 Baked 或 Custom 以将其用作烘焙探针

烘焙探针捕获的反射只能包含标记为 Reflection Probe Static 的场景对象(使用检视面板左上角的 Static 菜单标记所有对象)。可以使用 Culling Mask 和 Clipping Planes 属性进一步优化包含在反射立方体贴图中的对象;具体的工作方式与摄像机的工作方式相同**(探针本质上类似于通过旋转观察六个立方体贴图面每一面的摄像机)**。

当 Auto 选项启用时(从 Lighting 窗口中设置),烘焙反射将在对象放置到场景中时自动更新。如果不使用自动烘焙,则需要单击 Reflection Probe Inspector 中的 Bake 按钮以更新探针。(Lighting 窗口中的 Build 按钮也将触发探针更新。)

无论使用自动烘焙还是手动烘焙,烘焙过程都将继续在 Editor 中工作时异步进行。但是,如果移动任何静态对象、更改其材质或以其他方式改变其视觉外观,则会重新启动烘焙过程。

2.2自定义探针

默认情况下,自定义探针的工作方式与烘焙探针的工作方式相同,但自定义探针还有其他选项可以更改此行为。

自定义探针检视面板上的 Dynamic Objects 属性允许未标记为 Reflection Probe Static 的对象包含在反射立方体贴图中。但是请注意,这些对象的位置在烘焙时仍然会在反射中“冻结”。

Cubemap 属性允许将自定义的立方体贴图分配给探针,从而使探针完全独立于可从其视点“看到”的对象。可以使用此属性来设置从 3D 建模应用程序生成的天空盒或立方体贴图作为反射源。

2.3实时探针

烘焙探针可用于许多用途,并具有良好的运行时性能,但它们的缺点是不能在播放器中实时更新。这意味着对象可在场景中移动但不能让它们的反射随之移动。如果这种问题带来了太多限制,可以使用实时探针,它们会在运行时更新反射立方体贴图。这种效果会带来更高的处理开销,但可提供更强的真实感。

使用实时探针
要使探针在运行时更新,应在反射探针检视面板中将其 Type 属性设置为 Realtime。不需要将对象标记为 Reflection Probe Static 来捕获它们的反射(就像使用烘焙探针一样)。但是,可以使用 Culling Mask 和 Clipping Planes 属性在反射立方体贴图中选择性地排除对象;具体的工作方式与摄像机的工作方式相同(探针本质上类似于通过旋转观察六个立方体贴图面每一面的摄像机)。

在 Editor 中,实时探针与烘焙探针具有大致相同的工作流程,但它们的渲染速度往往更快。当 Auto 选项启用时(从 Lighting 窗口中设置),反射将在对象放置到场景中时自动更新。如果不使用自动烘焙,则需要单击 Reflection Probe Inspector 中的 Bake 按钮以更新探针。(Lighting 窗口中的 Build 按钮也将触发探针更新。)

无论使用自动烘焙还是手动烘焙,烘焙过程都将继续在 Editor 中工作时异步进行。但是,如果移动任何静态对象、更改其材质或以其他方式改变其视觉外观,则会重新启动烘焙过程。

注意:目前,实时探针仅在 Reflection Probe Static 对象移动或改变其外观时才会更新在 Scene 视图中的反射。这意味着,即使移动的动态对象出现在反射中,这些对象也不会触发更新。应该从 Lighting 窗口的 Build 按钮弹出窗口中选择 Bake Reflection Probes 选项,从而在动态对象改变时更新反射。

3.使用反射探针

这里主要讲解一下烘焙反射探针的实现

3.1创建反射探针

从GameObject->light->Reflection Probe创建,就可以得到一个反射探针:
[Unity/URP学习]反射探针(Reflection Probe)
立方体空间是反射空间,只有在这个空间里的物体才会被反射出来,最好把探针放在一个物体列表下,可以将探针中心与物体重合,之后将探针采集的立方体贴图应用于此物体(如一个球),那么就可以直接对此立方体贴图进行采样,这样比较精确

3.2反射探针属性

[Unity/URP学习]反射探针(Reflection Probe)

3.3反射物体设置

注意被烘焙的物体为静态物体,反射探针只反射静态物体,如果不勾选静态物体,不会反射。
[Unity/URP学习]反射探针(Reflection Probe)

3.4进行烘焙得到反射贴图

[Unity/URP学习]反射探针(Reflection Probe)
点击Bake,就可以获取到一张烘焙的cubemap纹理贴图

3.5创建shader和材质

创建shader并读取我们获得的烘焙贴图:
shader代码:

Shader "Unlit/RP_Cubemap_reflection_shader"
{
    Properties
    {
        _Diffuse("Diffuse", Color) = (1, 1, 1, 1)
        _ReflectColor("_ReflectColor", Color) = (1, 1, 1, 1)
        _ReflectAmount("_ReflectAmount", Range(0,1)) = 1
        _Reflection_CubeMap("_Reflection_CubeMap", Cube) = "_Skybox"{}
        
        _Specular("Specular", Color) = (1, 1, 1, 1)
        _Gloss("Gloss", Range(8,255)) = 8.0  
    }
    SubShader
    {
        Tags {"Queue" = "Transparent" "RenderType" = "Transparent" "RenderPipeline" = "UniversalPipeline"}
        Pass
        {
            Tags {"LightMode" = "UniversalForward" }
            
            
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile _ _MAIN_LIGHT_SHADOWS //接收阴影
            #pragma multi_compile _ _MAIN_LIGHT_SHADOWS_CASCADE //得到正确的阴影坐标
            #pragma multi_compile _ _SHADOWS_SOFT //软阴影

            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Shadows.hlsl"
           
          
            
            CBUFFER_START(UnityPerMaterial)
            half4 _Diffuse;
            float4 _ReflectColor;//用于控制反射的天空盒子的颜色量
            float _ReflectAmount;//用于控制反射的天空盒子的颜色和漫反射diffuse在总体反射中的占比
            samplerCUBE _Reflection_CubeMap;
            
            
            half4 _Specular;
            half _Gloss;

            
            CBUFFER_END

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
                float3 vertex_normal : NORMAL;
            };

            struct v2f
            {
                float4 position_CS : SV_POSITION;
                float4 position_WS : TEXCOORD0;
                float3 normal_WS : TEXCOORD1;
                float3 view_dir_WS : TEXCOORD2;
                float3 reflect_dir_WS : TEXCOORD3;
                
            };
            

            v2f vert (appdata v)
            {
                v2f o;
                o.position_CS = TransformObjectToHClip(v.vertex);//获取裁剪空间的顶点坐标
                o.position_WS = mul(unity_ObjectToWorld, v.vertex);//获取世界空间的顶点坐标
                o.normal_WS = TransformObjectToWorldNormal(v.vertex_normal);//获取世界空间的法线
                
                
                o.view_dir_WS = GetWorldSpaceViewDir(o.position_WS);
                o.reflect_dir_WS = reflect(-normalize(o.view_dir_WS), normalize(o.normal_WS));//获取反射光线
                
                return o;
            }

            half4 frag (v2f i) : SV_Target
            {
                float4 SHADOW_COORDS = TransformWorldToShadowCoord(i.position_WS);//获取将世界空间的顶点坐标转换到光源空间获取的阴影坐标,这里在片元着色器里面进行,利用了插值之后的结果
                Light main_light = GetMainLight(SHADOW_COORDS);
                
                float3 light_dir_WS = normalize(TransformObjectToWorld(main_light.direction));//获取世界空间的光照单位矢量
                float3 view_dir_WS = normalize(i.view_dir_WS);
                float3 reflect_dir_WS = normalize(i.reflect_dir_WS);
                float3 normal_WS = normalize(i.normal_WS);
                
                
                half3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;//获取环境光强度
                
                half3 diffuse = main_light.color.rgb * _Diffuse.rgb * saturate(dot(normal_WS, light_dir_WS));//获取漫反射强度,light.color.rgb是光照强度
                half3 reflection = texCUBE(_Reflection_CubeMap,reflect_dir_WS).rgb * _ReflectColor.rgb;//获取反射的天空盒子的颜色
                
                half3 half_dir = normalize(light_dir_WS + view_dir_WS);//获取半程向量
                half3 specular = main_light.color.rgb * _Specular.rgb * pow(saturate(dot(half_dir,normal_WS)), _Gloss);//获取高光

                
                reflection = lerp(diffuse, reflection,  _ReflectAmount);//通过平滑函数获得最佳的反射
                
                
                half3 return_color = ambient + reflection  + specular;
                return half4(return_color, 1.0);
            }
            ENDHLSL
        }
        UsePass "Universal Render Pipeline/Lit/ShadowCaster"
    }
    Fallback "Diffuse"
}

创建材质,在cubemap中选择我们得到的烘焙贴图,将材质拖到物体上,就可以获得反射效果:
[Unity/URP学习]反射探针(Reflection Probe)
如果加入了其他物体并设为静态物体,想要更新反射需要重新点击Bake实现烘焙贴图的更新。文章来源地址https://www.toymoban.com/news/detail-406016.html

到了这里,关于[Unity/URP学习]反射探针(Reflection Probe)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • Unity URP 平面反射笔记

    平面反射的具体内容可以参考以下几篇文章,都总结得很不错: https://zhuanlan.zhihu.com/p/493766119?utm_id=0 https://blog.csdn.net/Phantom1516/article/details/128762625 https://blog.csdn.net/puppet_master/article/details/80808486 下面是平面反射实现的具体思路: 首先在原相机的基础上拷贝一个反射摄像机,其位

    2024年02月09日
    浏览(37)
  • K8S哲学 - probe 探针

    liveness probe readiness probe startup probe Liveness Probe :用于检查容器是否还在运行。如果 Liveness Probe 失败,Kubernetes 会杀死容器,然后根据你的重启策略来决定是否重新启动容器。常见的做法是使用与 Readiness Probe 相同的低成本 HTTP 端点,但是设置更高的  failureThreshold ,这样可以确

    2024年04月28日
    浏览(25)
  • Unity中URP下的SimpleLit的 Lambert漫反射计算

    在之前的文章中,我们已经知道了 SimpleLit 下的主光数据怎么获取。 Unity中URP下获取主灯信息 Unity中ShaderGraph下获取主灯 有了这些数据,我们就可以计算 Lambert漫反射 和 BlinnPhone高光反射 了。 我们在获取了主光信息后 Light mainLight = GetMainLight1(inputData, shadowMask, aoFactor); 就来到了

    2024年01月21日
    浏览(83)
  • 【HDRP】自动生成的光照探针——Probe Volume

    HDRP中,增加了Probe Volume,可代替旧版的光照探针Light Probe Group。 使用此功能的物体,不再需要光照贴图。 详细说明可查看官方说明。 1.Probe Volume按像素而不是按对象发光,这意味着 HDRP 可以更准确地照亮对象。 2.如果使用体积雾,则每像素照明可为雾体的变化提供更准确的

    2024年02月10日
    浏览(32)
  • k8s之Pod及Probe 探针机制(健康检查机制)

    1.1、定义 Pod 是一组(一个或多个) 容器(docker容器)的集合 (就像在豌豆荚中);这些容器共享存储、网络、以及怎样运行这些容器的声明 - 我们一般不直接创建Pod,而是创建一些工作负载由他们来创建Pod 1.2、Pod的形式 Pod对容器有自恢复能力(Pod自动重启失败的容器)

    2024年02月12日
    浏览(36)
  • Java中的Reflection(反射)、暴力反射

    1.1 反射的出现背景 Java程序中,所有的对象都有两种类型: 编译时类型 和 运行时类型 ,而很多时候对象的编译时类型和运行时类型 不一致 。 例如: 如上 :某些变量或形参的声明类型是 Object 类型,但是程序却需要调用该对象运行时类型的方法,该方法不是Object中的方法

    2024年02月04日
    浏览(32)
  • c#反射(Reflection)

    当我们在C#中使用反射时,可以动态地获取和操作程序集、类型和成员。下面是一个简单的C#反射示例,展示了如何使用反射来调用一个类的方法: 在这个示例中,我们首先获取了MyClass的类型,并使用Activator.CreateInstance创建了一个MyClass的实例。然后,我们使用GetMethod方法获取

    2024年02月10日
    浏览(28)
  • .net反射(Reflection)

    .NET 反射(Reflection)是指在运行时动态地检查、访问和修改程序集中的类型、成员和对象的能力。通过反射,你可以在运行时获取类型的信息、调用方法、访问字段和属性,以及创建对象实例,而无需在编译时知道这些类型的具体信息。 换句话说,反射可以在类的内部成员不

    2024年04月25日
    浏览(28)
  • Reflection java反射源码分析

    Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。这种 动态获取程序信息 以及 动态调用对象的功能 称为Java语言的反射机制。反

    2024年02月02日
    浏览(27)
  • 【.NET Core】反射(Reflection)详解(三)

    反射提供了对已封装的程序集、模型和类型的对象一种动态访问方法。反射包含动态加载程序集的Assembly类、程序集中模块访问的Module类、对类信息Type类、构造函数信息ConstructorInfo类、方法信息MethodInfo类、字段信息FieldInfo类、事件信息EventInfo类、属性信息PropertyInfo类、参数信

    2024年02月03日
    浏览(22)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包