Unity CRP学习笔记(一)

Unity CRP学习笔记(一)

主要参考:
https://catlikecoding.com/unity/tutorials/custom-srp/
https://docs.unity.cn/cn/2022.3/ScriptReference/index.html
中文教程部分参考(可选):
https://tuncle.blog/custom_render_pipeline/index.html
https://edu.uwa4d.com/lesson-detail/282/1308/0(依照Unity 2019版的内容翻译,不太适用于Unity 2022)
本文主要是,在参考以上内容学习的过程中,对一些基于已有内容但还是不太能理解的部分进行了一些额外的补充。

Custom Render Pipeline

涉及的C#代码如下,对我自己不太理解的部分做了尽量详细的注释。对于无法在代码中简单说明的部分,在代码部分之后尝试进行了较为详细的解释。

/*CustomRenderPipelineAsset.cs*/
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;

[CreateAssetMenu(menuName = "Rendering/CustomRenderPipeline")]
public class CustomRenderPipelineAsset : RenderPipelineAsset
{
    protected override RenderPipeline CreatePipeline()
    {
        return new CustomRenderPipeline();
    }
}
/*CustomRenderPipeline*/
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;

public class CustomRenderPipeline : RenderPipeline {
    CameraRenderer renderer = new CameraRenderer();

    protected override void Render(ScriptableRenderContext context, Camera[] cameras){ }

    protected override void Render(ScriptableRenderContext context, List<Camera> cameras)
    {
        for (int i = 0; i < cameras.Count; i++)
        {
            renderer.Render(context, cameras[i]);
        }
    }
}
/*CameraRender.cs*/
using System;
using UnityEngine;
using UnityEngine.Rendering;

public partial class CameraRenderer
{
    ScriptableRenderContext context;

    Camera camera;

    CullingResults cullingRes;
    static ShaderTagId unlitShaderTagId = new ShaderTagId("SRPDefaultUnlit");
    CommandBuffer buffer = new CommandBuffer();

    public void Render(ScriptableRenderContext context, Camera camera)
    {
        this.context = context;
        this.camera = camera;
        buffer.name = camera.name;

        //setup
        //设置物体渲染的参数
        SortingSettings sortingOpaqueSettings = new SortingSettings(camera)
        {
            criteria = SortingCriteria.CommonOpaque
        };
        SortingSettings sortingTransparentSettings = new SortingSettings(camera)
        {
            criteria = SortingCriteria.CommonTransparent
        };
        DrawingSettings drawingOpaqueSettings = new DrawingSettings(unlitShaderTagId, sortingOpaqueSettings);
        DrawingSettings drawingTransparentSettings = new DrawingSettings(unlitShaderTagId, sortingTransparentSettings);
        //根据RenderQueueRange设定的范围筛选物体,RenderQueueRange可以在shader中设置(通过 Tags { "RenderType"="Opaque" })也可以在C#脚本中设置
        FilteringSettings filteringOpaqueSettings = new FilteringSettings(RenderQueueRange.opaque);
        FilteringSettings filteringTransparentSettings = new FilteringSettings(RenderQueueRange.transparent);
        //向命令缓存区中增加开启采样的命令,其实就是启用对缓冲区中指令执行的性能分析,其中name参数只标识该采样,在Profiler中采样域名称为buffer.name
        buffer.BeginSample(camera.name);
        //传入当前相机的view和projection矩阵,否则unity_MatrixVP为固定的默认值
        context.SetupCameraProperties(camera);
        //压入清空缓冲区的指令
        CameraClearFlags flags = camera.clearFlags;
        buffer.ClearRenderTarget(flags <= CameraClearFlags.Depth, flags == CameraClearFlags.Color,
                    flags == CameraClearFlags.Color ? camera.backgroundColor.linear : Color.clear);
        //需要在渲染前执行缓冲区指令,否则渲染结果就被直接清空了
        context.ExecuteCommandBuffer(buffer);
        buffer.Clear();
        //在scene相机中渲染UI
        DrawSceneViewUnityUI();
        //剔除视野外的物体
        if (!Cull()) return;


        //render
        //先渲染DrawRender
        context.DrawRenderers(cullingRes, ref drawingOpaqueSettings, ref filteringOpaqueSettings);
        DrawUnsupportShader();
        context.DrawSkybox(camera);
        context.DrawRenderers(cullingRes, ref drawingTransparentSettings, ref filteringTransparentSettings);
        DrawGizmos();
        //关闭采样
        buffer.EndSample(camera.name);
        context.ExecuteCommandBuffer(buffer);
        buffer.Clear();

        //submit(end)
        //context发送的渲染命令都是缓冲的,需要提交执行
        context.Submit();
    }

    bool Cull()
    {
        ScriptableCullingParameters p;

        if(camera.TryGetCullingParameters(out p))
        {
            cullingRes = context.Cull(ref p);
            return true;
        }
        return false;
    }
}
/*CameraRender.editor.cs*/
using System;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEditor;

public partial class CameraRenderer
{
    partial void DrawUnsupportShader();
    partial void DrawGizmos();
    partial void DrawSceneViewUnityUI();

#if UNITY_EDITOR
    static Material errorMaterial = new Material(Shader.Find("Hidden/InternalErrorShader"));

    //内置渲染管线材质,ShaderTagId的参数是在Shader Pass中的LightMode设定的
    static ShaderTagId[] legacyShaderTagIds = {
        new ShaderTagId("Always"),
        new ShaderTagId("ForwardBase"),
        new ShaderTagId("PrepassBase"),
        new ShaderTagId("Vertex"),
        new ShaderTagId("VertexLMRGBM"),
        new ShaderTagId("VertexLM")
    };

    partial void DrawUnsupportShader()
    {
        //设置所有需要渲染的对象的材质为errorMaterial
        var drawingSettings = new DrawingSettings(legacyShaderTagIds[0], new SortingSettings(camera)) { overrideMaterial = errorMaterial };

        for(int i = 1; i < legacyShaderTagIds.Length; ++i)
        {
            //添加其他legacy的Shader Pass Name
            drawingSettings.SetShaderPassName(i, legacyShaderTagIds[i]);
        }

        FilteringSettings filteringSettings = FilteringSettings.defaultValue;

        context.DrawRenderers(cullingRes, ref drawingSettings, ref filteringSettings);
    }

    partial void DrawGizmos()
    {
        if (Handles.ShouldRenderGizmos())
        {
            //GizmoSubset.PreImageEffects表示受后处理影响的Gizmos,GizmoSubset.PostImageEffects表示不受后处理影响的部分
            context.DrawGizmos(camera, GizmoSubset.PreImageEffects);
            context.DrawGizmos(camera, GizmoSubset.PostImageEffects);
        }
    }

    partial void DrawSceneViewUnityUI()
    {
        if(camera.cameraType == CameraType.SceneView)
        {
            ScriptableRenderContext.EmitWorldGeometryForSceneView(camera);
        }
    }
#endif
}

Render Mode

Unity RenderMode有三种。

根据unity doc:
ScreenSpace-Overlay 使用 2D Canvas 在场景末端渲染。
ScreenSpace-Camera 使用在 Canvas上配置的 Camera 渲染。
WorldSpace 使用场景中可渲染该层的任意 Camera 渲染。

ScreenSpace-Overlay效果是在屏幕空间中最后渲染UI,UI会覆盖所有物体。
ScreenSpace-Camera则Canvas会有一个深度,根据深度值渲染,所以物体可能覆盖UI。
在这里插入图片描述
在游戏运行时的摄像机中(CameraType为Game),ScreenSpace-Overlay和ScreenSpace-Camera都是由Unity UI package实现渲染的,而WorldSpace则会经过SRP渲染。
而在场景窗口中(CameraType为SceneView),三种RenderMode都是在WorldSpace下渲染的,所以在Scene中会看到Canvas和其他物体一样,固定在世界空间中不随相机运动改变位置。
![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/6301e911ab7344a4930f53a0ef865ef6.png
注:根据custom-render-pipeline实现的PrepareForSceneWindow函数依然不能在Game窗口中在WorldSpace下渲染出UI,如需要可以自行实现。

多相机

custom-render-pipeline中实现的多相机,只是更改了性能分析时的作用域名,且代码不涉及缓冲区。在没有明确选择缓冲区的情况下,渲染将默认到屏幕,所以两个相机会相继渲染到同一个缓冲区,但多相机通常应该是每个相机都渲染到自己的缓冲区。此时,如果去改变RenderMode为ScreenSpace-Camera则会出现一些问题。

RenderMode为ScreenSpace-Overlay时

在这里插入图片描述
在所有渲染完成后绘制GUI,所以GUI会在Game窗口中正常显示。

RenderMode为ScreenSpace-Camera时

在这里插入图片描述
GUI的渲染被放到了第一次渲染半透明物体的部分中且先与半透明物体进行渲染,而当再次渲染SecondCamera时GUI就不会再次进行渲染了,导致GUI不会显示。猜测ScreenSpace-Camera的GUI渲染机制是当渲染RenderQueueRange为transparent时(或者渲染队列值超过某个特定值),会回调某个函数开始渲染GUI。

Clear Flags

Clear Flags是Camera内置的参数,共有四个参数,分别为Skybox,Solid Color,Depth only,Don’t Clear。如果使用SRP渲染则该参数基本失效(这里用基本是因为context.DrawSkybox会判断camera.clearFlags是否为Skybox,判断通过才会绘制天空盒,否则即使用了context.DrawSkybox也不会绘制天空盒),为了让其依然能达到在URP中原有的效果,所以需要修改ClearRenderTarget的实现方式。

C#相关内容(按出现顺序)

C# {} 对象初始化器

在CRP中,经常能看到使用 {} 而不是 () 来进行对象的初始化,使用 {} 的原因是如果想要使用 (),需要有对应支持的构造函数,而 {} 可以直接对对象的公开属性或字段进行初始化,非常灵活。

SortingSettings sortingSettings = new SortingSettings(camera)
{
    criteria = SortingCriteria.CommonOpaque
};

例如在上述代码块中,可以只创建一个构造函数,通过 {} 根据需要来对camera以外的其他属性或字段进行初始化。

C#函数参数传递方式

1.按值传递(不必多言)

2.out 关键字

out 关键字的主要作用是允许方法在返回时修改该参数的值,并将结果传递回调用者,而不需要通过返回值来传递结果。

例如camera.TryGetCullingParameters(out p),返回值是bool值,同时还可以传递赋值后的变量p

3.ref 关键字

同样是允许修改参数并传递回调用者,区别必须是已经初始化的变量(传递 out 参数时,调用方不需要预先初始化参数)。

C# partial关键字

1.partial类

partial 类允许将一个类的定义拆分为多个部分(文件或代码块)进行编写。

2.partial函数

partial 函数允许开发者将函数的定义和具体实现分开。使用 partial 关键字的方法可以没有定义全部实现,如果某个 partial 方法没有在其他地方定义实现,编译器不会报错,并且该方法调用将被忽略,没有任何运行时开销。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/899221.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

算力的定义、单位、影响因素、提升方法、分类、应用等。附超算排名

文章目录 算力的定义算力的单位FLOPS&#xff08;Floating Point Operations Per Second&#xff0c;浮点运算次数/秒&#xff09;IPS&#xff08;Instructions Per Second&#xff0c;指令/秒&#xff09;TOPS&#xff08;Trillion Operations Per Second&#xff0c;万亿次/秒…

Win10系统安装docker操作步骤

Docker下载 docker下载地址&#xff1a;Docker: Accelerated Container Application Development 打开网页后&#xff0c;点击图下所示&#xff0c;下载windows版本的docker 启用Hyper-V 和容器特性 右键左下角windows图标&#xff0c;选择应用和功能 然后在下面的界面中&am…

【Nuvoton干货分享】开发应用篇 4 -- 8bit MCU Flash 操作

我们在进行实际开发设计中&#xff0c;难免需要进行数据存储&#xff0c;早期很多都是外接EEPROM来进行设计&#xff0c;但是需要增加成本。其实芯片内部的Flash也是可以当成数据存储空间的。本章节主要介绍新唐的8位机如何进行常量数据的存储操作。 一、存储空间划分 我这边…

w~自动驾驶合集6

我自己的原文哦~ https://blog.51cto.com/whaosoft/12286744 #自动驾驶的技术发展路线 端到端自动驾驶 Recent Advancements in End-to-End Autonomous Driving using Deep Learning: A SurveyEnd-to-end Autonomous Driving: Challenges and Frontiers 在线高精地图 HDMa…

小程序无法获取头像昵称以及手机号码

用户在使用小程序的时候&#xff0c;登录弹出获取昵称头像或者个人中心点击默认头像弹窗获取头像昵称的时候&#xff0c;点击弹窗中的头像昵称均无反应&#xff0c; 这个是因为你的小程序隐私政策没有更新&#xff0c;或者老版本没有弹窗让用户同意导致的 解决办法&#xff1…

利用彩色相机给激光点云染色

文章目录 概述核心代码效果概述 在激光SLAM(Simultaneous Localization and Mapping)中,使用彩色相机为激光点云染色是一个常见的做法。这种技术结合了激光雷达的高精度距离测量和相机的丰富色彩信息,使得生成的点云不仅包含空间位置信息,还包含颜色信息,从而更直观和细…

【OpenAI】第六节(语音生成与语音识别技术)从 ChatGPT 到 Whisper 的全方位指南

前言 在人工智能的浪潮中&#xff0c;语音识别技术正逐渐成为我们日常生活中不可或缺的一部分。随着 OpenAI 的 Whisper 模型的推出&#xff0c;语音转文本的过程变得前所未有的简单和高效。无论是从 YouTube 视频中提取信息&#xff0c;还是将播客内容转化为文本&#xff0c;…

[实时计算flink]数据摄入YAML作业快速入门

实时计算Flink版基于Flink CDC&#xff0c;通过开发YAML作业的方式有效地实现了将数据从源端同步到目标端的数据摄入工作。本文介绍如何快速构建一个YAML作业将MySQL库中的所有数据同步到StarRocks中。 前提条件 已创建Flink工作空间&#xff0c;详情请参见开通实时计算Flink版…

Jenkins配置CI/CD开发环境(理论到实践的完整流程)

目录 一、对于CI/CD的理解1.1、什么是CI&#xff08;持续集成&#xff09;1.2、CI 的主要特点&#xff1a;1.3、CI 的优势&#xff1a;**实际开发中的场景举例:** 1.4、什么是 CD&#xff08;持续交付和持续部署&#xff09;1.5、持续交付&#xff08;Continuous Delivery&…

鸿蒙HarmonyOS NEXT 5.0开发(2)—— ArkUI布局组件

文章目录 布局Column&#xff1a;从上往下的布局Row&#xff1a;从左往右的布局Stack&#xff1a;堆叠布局Flex&#xff1a;自动换行或列 组件Swiper各种选择组件 华为官方教程B站视频教程 布局 主轴和交叉轴的概念&#xff1a; 对于Column布局而言&#xff0c;主轴是垂直方…

如何区分真假Facebook三不限海外户?

对于需要推广到海外的企业来说&#xff0c;Facebook是一个重要的渠道。由于Facebook对国内普通企业户的风控极为严格&#xff0c;让不少出海广告主都很头疼&#xff0c;一到要出量的时刻就限额、挂户各种问题&#xff0c;根本没有办法跑起来量&#xff0c;白白错过好时机。对于…

R语言统计分析——置换检验3

参考资料&#xff1a;R语言实战【第2版】 列联表的独立性 通过chisq_test()或cmh_test()函数&#xff0c;我们可以用置换检验判断两类别型变量的独立性。当数据可根据第三个类别型变量进行分层时&#xff0c;需要使用后一个函数。若变量都是有序型&#xff0c;可使用lbl_test(…

047_python基于Hadoop的租房数据分析系统的设计与实现

目录 系统展示 开发背景 代码实现 项目案例 获取源码 博主介绍&#xff1a;CodeMentor毕业设计领航者、全网关注者30W群落&#xff0c;InfoQ特邀专栏作家、技术博客领航者、InfoQ新星培育计划导师、Web开发领域杰出贡献者&#xff0c;博客领航之星、开发者头条/腾讯云/AW…

基于 Konva 实现Web PPT 编辑器(三)

完善公式 上一节我们简单讲述了公式的使用&#xff0c;并没有给出完整的样例&#xff0c;下面还是完善下相关步骤&#xff0c;我们是默认支持公式的编辑功能的哈&#xff0c;因此&#xff0c;我们只需要提供必要的符号即可&#xff1a; 符号所表达的含义是 mathlive 的command命…

socket套接字

1.IP地址 IP地址是在IP协议中, 用来标识网络中不同主机的地址。由点分十进制组成&#xff0c;在数据传输中IP地址是一直不变的。 在IP数据包头部中, 有两个IP地址&#xff0c; 分别叫做源IP地址和目的IP地址。 2.端口号 由2字节16位整数组成&#xff0c;标识当前主机的唯一…

YOLO V3 网络构架解析

YOLO V3&#xff08;You Only Look Once version 3&#xff09;是由Joseph Redmon等人于2018年提出的一种基于深度学习的目标检测算法。它在速度和精度上相较于之前的版本有了显著提升&#xff0c;成为计算机视觉领域的一个重要里程碑。本文将详细解析YOLO V3的网络架构&#x…

关于WPF项目降低.Net版本

本来有项目是.NET Framework 4.8的&#xff0c;为了兼容升级到.NET 8.0&#xff0c;后期又为了兼容放弃.NET 8.0&#xff0c;升级的步骤&#xff1a;利用vs2022 的 .NET Upgrade Assistant 扩展&#xff0c;磕磕绊绊也升级完成了&#xff1b; 扩展链接&#xff1a; Upgrading…

前端拥抱AI:LangChain.js 入门遇山开路之PromptTemplate

PromptTemplate是什么 PromptTemplate是一个可重复使用的模板&#xff0c;用于生成引导模型生成特定输出的文本。与Prompt的区别: PromptTemplate相对于普通Prompt的优势&#xff0c;即其灵活性和可定制性。 简单了解PromptTemplate后&#xff0c;咱们就来聊聊LangChain里的P…

Hadoop 安装教程——单节点模式和分布式模式配置

文章目录 一、预备知识1.1 Hadoop 发行版本1.2 部署方式 二、预备条件2.1 环境准备2.2 创建新用户(可选)2.3 配置 SSH 无密码登录2.4 下载 Hadoop2.5 编辑 hadoop-env.sh 脚本2.6 编辑 dfs 和 yarn 脚本 三、单节点模式部署3.1 官方使用案例3.2 查看运行结果 四、伪分布模式部署…

golang 手动解析 epub 电子书格式

如题&#xff0c;本篇简单分析如何使用go语言解析epub格式的电子书&#xff0c;获取其内部资源内容。 EPUB格式 首先我们需要了解epub格式具有哪些特点。 已知的是&#xff0c;epub是一种类似doc或者pdf&#xff0c;可以提供图文并茂电子书的格式。 那么我们首先使用二进制…