[Unity/URP学习]平面反射(PlanarReflection)

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

平面反射

1.平面反射的原理

平面可以用来模拟光滑度很高的镜面效果,但是只能用在高度一致的平面,很多水体渲染方案中的反射部分,就是用平面反射来解决的,下面先讲一下实现平面反射的方法思路。

1.1基本思路

最简单的光线反射向量推导:
由反射现象可知,入射角等于反射角,对于反射材质,我们只能利用现有的法线和入射光线的方向来计算反射光线的方向,所以进行下面的计算:
[Unity/URP学习]平面反射(PlanarReflection)
求反射光线的代码:

float3 reflect(float3 I, float3 N)
{
    return I - 2 * dotProduct(I, N) * N;
}

Unity直接提供reflect函数,传入入射光线和法线方向的单位矢量,即可以得出出射方向的单位矢量。

平面反射实现思路:
[Unity/URP学习]平面反射(PlanarReflection)

当前使用相机E,看向P、Q两点时,根据光线的反射原理,在P、Q两点应该看到的是A、B两点,所以我们的思路是:在这个平面采样一张贴图,贴图上的P点对应A点的颜色,Q点对应B点的颜色,并且保证光照正确,那这一张贴图该如何获取?如何采样?
想象出一个物体关于平面对称后产生一个倒立的一模一样的物体,原物体的 A、B 对应想象物体的A’、B’,根据光线的反射原理推出,相机 E要在P点通过反射看到A点,等同于相机的视线穿过平面看到A’点,我们只要采样到相机E此时看到的A’点时的颜色就可以了。

按上面的方法,我们有了反射平面的贴图了,该怎么采样它才能正确的显示呢?通过上面的推导和图可以知道, 当我们在模型变换后乘以反射矩阵,那么物体对应的点(仍然从原来摄像机的视角看过去,物体是先变换到对称位置再进行VP变换,此时我们看到的物体即是反射了的点)

1.2实现步骤

1.根据已知平面,计算出该平面的镜像矩阵R

2.对当前摄像机进行拷贝,得到一个新的相机,在相机的MVP矩阵的V之前乘以反射矩阵R,即得到MRVP(因为下面我们求得的反射矩阵是在世界空间下,将物体的点变换到关于世界空间中的平面的镜像点,所以在模型变换之后,得到点的世界坐标,需要先进行R变换后再进行VP的变换,R变换之后,其实我们是从和原来一样的视角看到物体在镜像的一个位置)

3.根据平面向量计算镜像相机的斜裁剪矩阵,以裁剪平面负方向的不可视区域

4.渲染镜像相机得到RenderTexture并投影到平面的材质shader,在shader中计算投影坐标最终得到镜面反射效果

2.反射矩阵

2.1反射矩阵的作用:

用来将物体的世界空间位置转移到对应的反射像的世界空间位置的矩阵。

2.2反射矩阵的推导过程:

由平面点法式可知:平面可以由平面上任意一点和垂直于平面的任意一个向量确定,这个垂直于平面的向量就是平面的法向量:
假设平面上有一个确定的点P0(x0,y0,z0),法向量为N(a,b,c),那么平面上任意一个点P(x,y,z)满足:P0P * N = 0,
即(x - x0, y - y0, z- z0) * (a, b, c) = 0,点法式展开,则为一般式:ax + by + cz + (-a * x0 - b * y0 - c * z0) = 0,其中(-a * x0 - b * y0 - c * z0)为常数,表示为d,则一般式可以表示为:
ax + by + cz + d = 0
所以点法式可以表示为:
P * N + d = 0,其中P为平面上任意一点,N为平面法线向量,而d则表示:d = -P * N = -dot(P, N),要求d,则知道N和平面上任意一点就可以,这个点可以为P0,所以d = -dot(P0, N);

下图是关于平面对称点的图:
[Unity/URP学习]平面反射(PlanarReflection)
由上图可知,我们只要求解出|AB|就可以通过已知的条件求解出平面的对称点,所以我们的问题可以转变为:
求世界空间中A点到平面P * N + d = 0的距离|AB|,再来一张图,推导一下AB的距离:
[Unity/URP学习]平面反射(PlanarReflection)
注意:图中P为平面上的任一点,A为空间中的任意一点,B为点A在平面上的投影点,N为平面法线
由图,我们已经得到|AB| = dot(A, N) - dot(P, N),而平面上任意一点可以表示为dot(P, N) + d = 0,所以:
dot(P, N) = -d,
所以:|AB| = dot(A, N) + d
而由上面推导的A’ = A - 2* N * |AB|可得:
A‘ = A - 2 * N * [dot(A, N) + d]

进行简化拆分:
[Unity/URP学习]平面反射(PlanarReflection)到此,我们就得到了世界空间中某一个点变换到某一个平面的反射成像点的反射矩阵,在矩阵中,d是 -dot(P, N),其中N为该平面的法向量P为平面上某一个点在世界空间中的坐标

3.平面反射代码

3.1摄像机脚本

挂在用于获取反射贴图的摄像机

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
using UnityEngine.SceneManagement;
using UnityEngine.UIElements;
using UnityEditor;

public class Planar_Reflection_camera_Script1 : MonoBehaviour
{
    [ExecuteAlways] 
    public Camera Main_camera;
    public Camera Reflection_camera;
    public Camera Scene_Reflection_camera;
    public GameObject Plane;
    
    private readonly RenderTexture _Reflection_camera_RT;//定义反射RT
    private int _Reflection_camera_RT_ID;//定义主纹理_MainTex属性名称的ID
    public Material Reflection_material;//传入材质

    private readonly RenderTexture _Scene_Reflection_camera_RT;
    private int _Scene_Reflection_camera_RT_ID;

    public Shader shader;

    void Start()//Start函数在脚本运行开始的时候执行
    {
        Debug.Log("Planar Reflection succes!");
        if (this.Reflection_camera == null)
        {
            var R_gameobject = new GameObject("Reflection camera");//申请新组件
            this.Reflection_camera = R_gameobject.AddComponent<Camera>();//获取Camera类型的组件
        }
        RenderPipelineManager.beginCameraRendering += OnBeginCameraRendering;//订阅事件
    }

    void OnBeginCameraRendering(ScriptableRenderContext context, Camera camera)
    {
        if (camera == this.Reflection_camera)
        {
            Update_camera(this.Reflection_camera);
            camera.clearFlags = CameraClearFlags.SolidColor;//清除刚初始化信息的Reflection_camera中的DepthBuffer和ColorBuffer,用Background属性颜色替代
            camera.backgroundColor = Color.clear;//清除掉背景颜色
            camera.cullingMask = LayerMask.GetMask("Reflection");//确定摄像机的渲染层
            
            var Reflection_camera_M = CalculateReflectionCameraMatrix(this.Plane.transform.up, this.Plane.transform.position);//构建反射矩阵
            Reflection_camera.worldToCameraMatrix = Reflection_camera.worldToCameraMatrix * Reflection_camera_M;//在进VP变换之前
            GL.invertCulling = true;//进行裁剪顺序的翻转
            
            UniversalRenderPipeline.RenderSingleCamera(context, camera);//摄像机开始渲染
            
            RenderTexture Reflection_camera_temporary_RT = RenderTexture.GetTemporary(Screen.width, Screen.height, 0);//创建临时的RT(截图保存)
            print(Screen.width);
            print(Screen.height);
            _Reflection_camera_RT_ID = Shader.PropertyToID("_Reflection_camera_RT");//先获取着色器属性名称_Reflection_camera_RT的唯一标识符_Reflection_camera_RT_ID 
            Shader.SetGlobalTexture(_Reflection_camera_RT_ID, Reflection_camera_temporary_RT);//再利用_Reflection_camera_RT_ID和Reflection_camera_temporary_RT为所有着色器设置一个全局纹理

            camera.targetTexture = Reflection_camera_temporary_RT;//设置自定义的渲染纹理为摄像机的目标纹理,定义完成后,反射相机的输出就会输出到纹理上
            
            Reflection_material.SetTexture(_Reflection_camera_RT_ID, Reflection_camera_temporary_RT);//将贴图传进材质

            RenderTexture.ReleaseTemporary(Reflection_camera_temporary_RT);//释放掉临时纹理
            
        }
        
        else
        {
            GL.invertCulling = false;
        }
    }

    private void OnDisable()
    {
        RenderPipelineManager.beginCameraRendering -= OnBeginCameraRendering;//取消事件订阅
    }

    private void Update_camera(Camera Reflection_camera)//同步两个摄像机的数据,相当于反射相机初始化
    {
        if (Reflection_camera == null||this.Main_camera == null)
                return;
        //先同步两个摄像机的数据,初始化完Reflection_camera后立刻背景颜色和深度的清除,然后设置在相机开始渲染前的各种设置
        int target_display = Reflection_camera.targetDisplay;
        Reflection_camera.CopyFrom(this.Main_camera);
        Reflection_camera.targetDisplay = target_display;
    }
    
    private Matrix4x4 CalculateReflectionCameraMatrix(Vector3 N, Vector3 plane_position)//计算返回反射矩阵
    {
        //下面计算反射矩阵是在世界空间计算的
        Matrix4x4 Reflection_camera_M = Matrix4x4.identity;//初始化反射矩阵
        float d = -Vector3.Dot(plane_position, N);//d = -dot(P, N),P是平面上的任意一点,N是平面的法向量
        
        Reflection_camera_M.m00 = 1 - 2 * N.x * N.x;
        Reflection_camera_M.m01 = - 2 * N.x * N.y;
        Reflection_camera_M.m02 = - 2 * N.x * N.z;
        Reflection_camera_M.m03 = - 2 * N.x * d;
        
        Reflection_camera_M.m10 = - 2 * N.x * N.y;
        Reflection_camera_M.m11 = 1 - 2 * N.y * N.y;
        Reflection_camera_M.m12 = - 2 * N.y * N.z;
        Reflection_camera_M.m13 = - 2 * N.y * d;
        
        Reflection_camera_M.m20 = - 2 * N.x * N.z;
        Reflection_camera_M.m21 = - 2 * N.y * N.z;
        Reflection_camera_M.m22 = 1 - 2 * N.z * N.z;
        Reflection_camera_M.m23 = - 2 * N.z * d;
        
        Reflection_camera_M.m30 = 0;
        Reflection_camera_M.m31 = 0;
        Reflection_camera_M.m32 = 0;
        Reflection_camera_M.m33 = 1;

        return Reflection_camera_M;
    } 
}

3.2进行RT贴图采样(shader)

这里同时进行了了天空盒的反射

Shader "Unlit/PlanerReflection_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"{}
        
        _Fresnel("Fresnel", Float) = 1.0
        
        _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;
            

            float _Fresnel;
            
            half4 _Specular;
            half _Gloss;

            TEXTURE2D(_Reflection_camera_RT);
            SamplerState sampler_Reflection_camera_RT;//采样采样设置

            TEXTURE2D(_Reflection_Scene_camera_RT);
            SamplerState sample_Reflection_Scene_camera_RT;
            
            CBUFFER_END
            
            struct appdata
            {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
                //float2 uv: TEXCOORD0;
            };

            struct v2f
            {
                float4 position_CS : SV_POSITION;//裁剪空间的坐标
                float4 screenPos : TEXCOORD0;//屏幕坐标
                float4 position_WS : TEXCOORD1;//顶点位置坐标
                float3 view_direct_WS : TEXCOORD2;//人眼光路的入射方向
                float3 reflec_direct_WS : TEXCOORD3;//人眼光路反射后的方向,用于采样天空盒
                float3 normal_WS : TEXCOORD4;//顶点在世界空间的法线方向
                
            };

            
            v2f vert (appdata v)
            {
                v2f o;
                o.position_CS = TransformObjectToHClip(v.vertex);//获得顶点在裁剪空间的坐标
                o.position_WS = mul(unity_ObjectToWorld, v.vertex);
                o.screenPos = ComputeScreenPos(o.position_CS);//计算用于采样反射贴图的顶点
                o.normal_WS = TransformObjectToWorldNormal(v.normal);//计算世界空间下的法线方向
                o.view_direct_WS = GetWorldSpaceViewDir(o.position_WS);//获取世界空间下的人眼光路的入射方向,这里获取的方向是从顶点指向摄像机
                o.reflec_direct_WS = reflect(-normalize(o.view_direct_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_direction_WS = normalize(TransformObjectToWorld(main_light.direction));//获取世界空间的光照单位矢量
                float3 view_direct_WS = normalize(i.view_direct_WS);
                float3 reflec_direct_WS = normalize(i.reflec_direct_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_direction_WS));//获取漫反射强度,light.color.rgb是光照强度
                half3 reflection = texCUBE(_Reflection_CubeMap,reflec_direct_WS).rgb * _ReflectColor.rgb;//获取反射的天空盒子的颜色
                
                half3 half_dir = normalize(light_direction_WS + view_direct_WS);//获取半程向量
                half3 specular = main_light.color.rgb * _Specular.rgb * pow(saturate(dot(half_dir,normal_WS)), _Gloss);//获取高光

                
                reflection = lerp(diffuse, reflection,  _ReflectAmount);//通过平滑函数获得最佳的反射
                
                float fresnel = pow(1 - dot(normal_WS, view_direct_WS), _Fresnel);
                
                
                
                half4 reflection_map_color = _Reflection_camera_RT.Sample(sampler_Reflection_camera_RT, i.screenPos.xy / i.position_CS.w);//采样反射纹理贴图
                half4 color = half4(ambient + fresnel * reflection  + specular, 1.0);
                
                color = reflection_map_color + color;
                return color;
            }
            ENDHLSL
        }
    }
    Fallback off
}

[Unity/URP学习]平面反射(PlanarReflection)
[Unity/URP学习]平面反射(PlanarReflection)

3.3进行视锥体的裁剪

[Unity/URP学习]平面反射(PlanarReflection)
[Unity/URP学习]平面反射(PlanarReflection)
不对视锥体进行裁剪的话就会出现上面奇怪的现象,当实物移动到平面下面的时候,在平面上还是会出现其反射的像
具体方法过程如下:
思路:
视锥体要以反射平面为近平面,如下图,视锥体A部分要剔除,只保留B部分
[Unity/URP学习]平面反射(PlanarReflection)
分析:
要进行视锥体裁剪,需要用到斜截视锥体的技术:
通过改变MVP矩阵的中的P矩阵,实现用指定平面来当作近平面,同时影响远平面,而其他四个平面不受到影响。
推导参考以下文章:
https://vn.sru.baidu.com/r/TLGAzdGavS?f=qf&u=9c5e09c2692df66a
所以在订阅的事件内,在相机渲染之前,对反射相机(已获取的反射的像)进行视锥体裁剪,代码如下:

//下面进行视锥体裁剪
            Vector4 viewPlane = new Vector4(this.Plane.transform.up.x, 
                this.Plane.transform.up.y, 
                this.Plane.transform.up.z, 
                -Vector3.Dot(this.Plane.transform.position, this.Plane.transform.up));//用四维向量表示平面
            viewPlane = Reflection_camera.worldToCameraMatrix.inverse.transpose * viewPlane;//将世界空间中的平面表示转换成相机空间中的平面表示

            var ClipMatrix = Reflection_camera.CalculateObliqueMatrix(viewPlane);//获取以反射平面为近平面的投影矩阵
            Reflection_camera.projectionMatrix = ClipMatrix;//获取新的投影矩阵

最终的反射相机的脚本代码:

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
using UnityEngine.SceneManagement;
using UnityEngine.UIElements;
using UnityEditor;

public class Planar_Reflection_camera_Script1 : MonoBehaviour
{
    [ExecuteAlways] 
    public Camera Main_camera;
    public Camera Reflection_camera;
    public Camera Scene_Reflection_camera;
    public GameObject Plane;
    
    private readonly RenderTexture _Reflection_camera_RT;//定义反射RT
    private int _Reflection_camera_RT_ID;//定义主纹理_MainTex属性名称的ID
    public Material Reflection_material;//传入材质

    private readonly RenderTexture _Scene_Reflection_camera_RT;
    private int _Scene_Reflection_camera_RT_ID;

    public Shader shader;

    void Start()//Start函数在脚本运行开始的时候执行
    {
        Debug.Log("Planar Reflection succes!");
        if (this.Reflection_camera == null)
        {
            var R_gameobject = new GameObject("Reflection camera");//申请新组件
            this.Reflection_camera = R_gameobject.AddComponent<Camera>();//获取Camera类型的组件
        }

        RenderPipelineManager.beginCameraRendering += OnBeginCameraRendering;//订阅事件
        
    }

    void OnBeginCameraRendering(ScriptableRenderContext context, Camera camera)
    {
        if (camera == this.Reflection_camera)
        {
            Update_camera(this.Reflection_camera);
            camera.clearFlags = CameraClearFlags.SolidColor;//清除刚初始化信息的Reflection_camera中的DepthBuffer和ColorBuffer,用Background属性颜色替代
            camera.backgroundColor = Color.clear;//清除掉背景颜色
            camera.cullingMask = LayerMask.GetMask("Reflection");//确定摄像机的渲染层
            
            var Reflection_camera_M = CalculateReflectionCameraMatrix(this.Plane.transform.up, this.Plane.transform.position);//构建反射矩阵
            Reflection_camera.worldToCameraMatrix = Reflection_camera.worldToCameraMatrix * Reflection_camera_M;//在进VP变换之前
            GL.invertCulling = true;//将裁剪顺序翻转回去,因为反射矩阵的变化会引起裁剪顺序的变化
            
            //下面进行视锥体裁剪
            Vector4 viewPlane = new Vector4(this.Plane.transform.up.x, 
                this.Plane.transform.up.y, 
                this.Plane.transform.up.z, 
                -Vector3.Dot(this.Plane.transform.position, this.Plane.transform.up));//用四维向量表示平面
            viewPlane = Reflection_camera.worldToCameraMatrix.inverse.transpose * viewPlane;//将世界空间中的平面表示转换成相机空间中的平面表示

            var ClipMatrix = Reflection_camera.CalculateObliqueMatrix(viewPlane);//获取以反射平面为近平面的投影矩阵
            Reflection_camera.projectionMatrix = ClipMatrix;//获取新的投影矩阵
            
            UniversalRenderPipeline.RenderSingleCamera(context, camera);//摄像机开始渲染
            
            RenderTexture Reflection_camera_temporary_RT = RenderTexture.GetTemporary(Screen.width, Screen.height, 0);//创建临时的RT(截图保存)
            
            _Reflection_camera_RT_ID = Shader.PropertyToID("_Reflection_camera_RT");//先获取着色器属性名称_Reflection_camera_RT的唯一标识符_Reflection_camera_RT_ID 
            Shader.SetGlobalTexture(_Reflection_camera_RT_ID, Reflection_camera_temporary_RT);//再利用_Reflection_camera_RT_ID和Reflection_camera_temporary_RT为所有着色器设置一个全局纹理

            camera.targetTexture = Reflection_camera_temporary_RT;//设置自定义的渲染纹理为摄像机的目标纹理,定义完成后,反射相机的输出就会输出到纹理上
            
            Reflection_material.SetTexture(_Reflection_camera_RT_ID, Reflection_camera_temporary_RT);//将贴图传进材质

            RenderTexture.ReleaseTemporary(Reflection_camera_temporary_RT);//释放掉临时纹理

        }
        
        else
        {
            GL.invertCulling = false;
        }
    }

    private void OnDisable()
    {
        RenderPipelineManager.beginCameraRendering -= OnBeginCameraRendering;//取消事件订阅
    }

    private void Update_camera(Camera Reflection_camera)//同步两个摄像机的数据,相当于反射相机初始化
    {
        if (Reflection_camera == null||this.Main_camera == null)
                return;
        //先同步两个摄像机的数据,初始化完Reflection_camera后立刻背景颜色和深度的清除,然后设置在相机开始渲染前的各种设置
        int target_display = Reflection_camera.targetDisplay;
        Reflection_camera.CopyFrom(this.Main_camera);
        Reflection_camera.targetDisplay = target_display;
        
    }

    
    private Matrix4x4 CalculateReflectionCameraMatrix(Vector3 N, Vector3 plane_position)//计算返回反射矩阵
    {
        //下面计算反射矩阵是在世界空间计算的
        Matrix4x4 Reflection_camera_M = Matrix4x4.identity;//初始化反射矩阵
        float d = -Vector3.Dot(plane_position, N);//d = -dot(P, N),P是平面上的任意一点,N是平面的法向量
        
        Reflection_camera_M.m00 = 1 - 2 * N.x * N.x;
        Reflection_camera_M.m01 = - 2 * N.x * N.y;
        Reflection_camera_M.m02 = - 2 * N.x * N.z;
        Reflection_camera_M.m03 = - 2 * N.x * d;
        
        Reflection_camera_M.m10 = - 2 * N.x * N.y;
        Reflection_camera_M.m11 = 1 - 2 * N.y * N.y;
        Reflection_camera_M.m12 = - 2 * N.y * N.z;
        Reflection_camera_M.m13 = - 2 * N.y * d;
        
        Reflection_camera_M.m20 = - 2 * N.x * N.z;
        Reflection_camera_M.m21 = - 2 * N.y * N.z;
        Reflection_camera_M.m22 = 1 - 2 * N.z * N.z;
        Reflection_camera_M.m23 = - 2 * N.z * d;
        
        Reflection_camera_M.m30 = 0;
        Reflection_camera_M.m31 = 0;
        Reflection_camera_M.m32 = 0;
        Reflection_camera_M.m33 = 1;

        return Reflection_camera_M;
    }
}

[Unity/URP学习]平面反射(PlanarReflection)
[Unity/URP学习]平面反射(PlanarReflection)

[Unity/URP学习]平面反射(PlanarReflection)
[Unity/URP学习]平面反射(PlanarReflection)

补充资料:

Camera.clearFlags:

[Unity/URP学习]平面反射(PlanarReflection)
[Unity/URP学习]平面反射(PlanarReflection)
[Unity/URP学习]平面反射(PlanarReflection)

CommandBuffer.GetTemporaryRT

[Unity/URP学习]平面反射(PlanarReflection)

该函数不仅申请了一个临时rt,同时还将该rt与第一个参数"nameID"所代表的全局的ShaderProperty进行了绑定,也就是说之后的shader都可以用与nameID对应的名称使用该rt。

官方的urp渲染管线中,CopyColorPass就是用该函数将"_CameraColorTexture"这个纹理拷贝到临时rt中供之后的shader使用。

坑点:在调用CommandBuffer.ReleaseTemporaryRT进行释放并不是将该纹理清空了,该纹理依旧在内存中,如果在之后重新调用CommandBuffer.GetTemporaryRT申请一个大小格式相同的临时纹理,会拿到该纹理,也就是说CommandBuffer.GetTemporaryRT得到的不一定是一张干净的纹理,很有可能是已经被写过的,所以必要的时侯要进行clear。其次,如果发生了上述这种回收重用的情况,rt的名称可能会错乱,也就是说尽管shaderproperty的绑定逻辑是没有问题,但是rt的name没有相应的更新,在framedebugger中会让人十分困惑,例如两个相机,分别在afterRenderingTransparent之后进行了一次全屏copyblit,前者给了_Transparent的rt,后者给了_UITranspaernt的rt,但是之后发现在前一个相机渲染中,物体shader中_Transparent获取的纹理名称叫_UITranspaernt,在使用renderdoc会发现两次全屏copyblit的目标纹理使用的是同一个rt,自始至终只有一个叫_UITranspaernt的rt,该rt仅仅是在过程中更换一下和shaderproperty的绑定,因此shader中的_Transparent和_UITranspaernt都会得到该rt。虽然在逻辑上没有错,最终结果是正确的,但是会给debug过程中带来困扰,如果想要规避这种情况可以显示地使用new RenderTexture创建rt。

ComputeScreenPos

[Unity/URP学习]平面反射(PlanarReflection)
[Unity/URP学习]平面反射(PlanarReflection)
注意:unity之所以不在顶点着色器中除以w分量,是因为要留到片段着色器进行线性插值后再除以w,这样得到的结果是正确的。如果提前除以w再经过片段着色器线性差值后,得到的结果就不准确,因为投影空间不是线性空间

Unity Shader ProjectionParams

[Unity/URP学习]平面反射(PlanarReflection)

Camera.CalculateObliqueMatrix

[Unity/URP学习]平面反射(PlanarReflection)文章来源地址https://www.toymoban.com/news/detail-411979.html

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

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

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

相关文章

  • Unity——URP & HDRP 渲染模式学习笔记

    目录 HDRP和UPR是两种完全不同的渲染模式,不能混用,只能选择其中一种,最好在项目刚开始的时候就确定好。否则后期所有模型的材质都要改变非常麻烦。 修改渲染模式方法: HDRP, URP, BRP区别 备注 project settins: 1. graphics-Scriptable render pipeline settings 要修改,假如从hdrp改为

    2024年02月04日
    浏览(47)
  • [Unity/URP学习]风格化水体渲染(一)

    着色:水体颜色、水体反射、水体折射、岸边泡沫、水面于天空沿边线消除、水体焦散 动画处理:水体流动、顶点动画、水体交互、水体浮力 (实现顺序没有严格按照着色和动画处理的分类来实现) 要制作水体颜色,要考虑的内容如下: 风格化的水体渐变颜色、水体深浅区

    2024年02月13日
    浏览(52)
  • Unity-3DRPG游戏 学习笔记(1)--使用URP渲染管线

    教程地址: Unity2020 3DRPG游戏开发教程|Core核心功能01:Create Project 创建项目导入素材|Unity中文课堂_哔哩哔哩_bilibili 创建URP通用渲染管线(2021版本) 1. 打开:Windows--Package Manager--左上角下拉选择Unity Registry--搜索Universal RP--Install 2. Project窗口--Assets--右键Create--Rendering--URP Asstes

    2024年02月11日
    浏览(43)
  • Unity3D学习记录01:URP渲染管线以及3D游戏场景设置

    以下内容所使用的版本均为Unity2022.3 先在 Window-Package Manager-Unity Registry 里面搜索添加Universal RP   Unity中,创建渲染管线的方式为Asset文件夹下右键 Create-Readering-URP Asset(with Universal Asset) 会创建以下两个Pipeline:  接着在图中的设置里添加这两个渲染管线(Project Setting在Edit窗口下

    2024年02月08日
    浏览(58)
  • Unity Shader 学习笔记(4)URP渲染管线带阴影PBR-Shader模板 -- 新增可自定义阴影颜色

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

    2024年02月12日
    浏览(48)
  • 【Unity URP】URP Camera使用及动态设置

    官方文档:   摄像机 | Universal RP | 12.1.1 https://docs.unity3d.com/cn/Packages/com.unity.render-pipelines.universal@12.1/manual/cameras.html 相机类型: Render Type(渲染类型) Base(基础相机):渲染到特定渲染目标的通用相机,场景中必须至少有一个。 Overlay(叠加相机):将其视图渲染在另一个摄

    2024年02月12日
    浏览(43)
  • Unity 基础之 URP 项目创建\项目转URP Pipline

    目录 Unity 基础之 URP 项目创建项目转URP Pipline 一、简单介绍 二、创建 URP 项目 三、工程项目转 URP Unity中的一些基础知识点,方便日后查阅。 Unity游戏开发中,这里简单介绍如何创建 URP 工程项目,和把已有项目转为 URP 项目的过程,这里做简单记录,如果有不对,欢迎指出。

    2024年02月16日
    浏览(47)
  • Unity——URP相机详解

    2021版本URP项目下的相机,一般新建一个相机有如下组件 1:Render Type( 渲染类型 ) 有Base和Overlay两种选项,默认是Base选项         Base:主相机使用该种渲染方式,负责渲染场景中的主要图形元素         Overlay(叠加):使用了Oveylay的相机会把它渲染出的图形叠加到其他相机之上

    2024年02月05日
    浏览(47)
  • 【Unity URP】手写PBR:从build-in转到URP

    写在前面 后续要在URP下实现PBR+NPR的风格化渲染,所以这里要赶紧把之前手写的PBR挪到URP管线下。由于URP各个版本更新换代太快了,贴一下项目环境,给后面看到这篇文章的小伙伴提个醒,我的项目环境: URP12.1.7 Unity2021.3.8f1 整体框架几乎一样吧,目前先实现主光源的部分,

    2024年02月14日
    浏览(78)
  • unity urp 实现丝绸渲染

    首先看一下实际上真实的效果 再来一张 这是专门去找的。 可以看到丝绸渲染使用了各向异性的GGX去实现,有点仿头发的感觉,接下来看一下怎么实现的。 首先,准备实现双向反射率分布函数(BRDF)的DVF项。 D项使用UE里面的各项异性GGX: V项使用配合D项的Vis_SmithJointAniso F项

    2024年02月10日
    浏览(39)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包