Learn SRP 02

3.Editor Rendering

3.1Drawing Legacy Shaders

因为我们的管线只支持无光照的着色过程,使用其他不同的着色过程的对象是不能被渲染的,他们被标记为不可见。尽管这是正确的,但是它还是隐藏了场景中一些使用错误着色器的对象。所以让我们来渲染它们吧,但是要和无光照分开。

为了能够兼容所有unity默认的着色器我们必须使用着色器标记ID(ShaderTagId),把Always,ForwardBase,PrepassBase,Vertex,VertexLMRGBM,和VertexLM放到一个静态数组里。

	static ShaderTagId[] legacyShaderTagIds = {new ShaderTagId("Always"),new ShaderTagId("ForwardBase"),new ShaderTagId("PrepassBase"),new ShaderTagId("Vertex"),new ShaderTagId("VertexLMRGBM"),new ShaderTagId("VertexLM")};

DrawVisibleGeometry函数之后我们使用一个单独的方法绘制所有不支持的着色器。因为它们是错误的着色过程所以无论如何渲染结果都是错误的,所以我们不用关心其他设置。我们可以使用默认的过滤设置FilteringSetting.defaultValue属性。

我们可以多此调用DrawingSettingSetShaderName方法,使用序列和标记作为参数。因为在构造函数中已经加入了第一个Tag,所有我们对数组的操作从第二个开始。

	void DrawUnsupportedShaders(){var drawingSettings = new DrawingSettings(legacyShaderTagIds[0], new SortingSettings(camera));for (int i = 1; i < legacyShaderTagIds.Length; i++){drawingSettings.SetShaderPassName(i, legacyShaderTagIds[i]);}var filteringSettings = FilteringSettings.defaultValue;context.DrawRenderers(cullingResults, ref drawingSettings, ref filteringSettings);}

3.2Error Material

为了明确的指明哪些对象使用了不支持的着色器我们用Unity错误的着色器来绘制它们。我们通过Shader.Find函数,使用Hidden/InternalErrorShader作为参数来构造一个全新的材质。我们将这个材质用静态字段缓存,这样我们就不用每帧都创建它了。然后将它赋值给DrawingSettingoverrideMaterial属性。

	static Material errorMaterial;…void DrawUnsupportedShaders () {if (errorMaterial == null) {errorMaterial =new Material(Shader.Find("Hidden/InternalErrorShader"));}var drawingSettings = new DrawingSettings(legacyShaderTagIds[0], new SortingSettings(camera)) {overrideMaterial = errorMaterial};…}

显示结果:

现在所有使用其他着色器的都变成粉色了

3.3Partial Class 

绘制无效的对象对开发模式是有用的,但是对于正式版是没有意义的。所以我们把所有的只在编辑器模式有用的代码放在一个分开的局部类文件CameraRenderer中。首先复制原始的CameraRenderer文件,并将其命名为CamreaRenderer.Editor

然后将原始的CameraRenderer转换为局部类,并从中删除ShaderTagId数组,错误的材质,和DrawUnsupportedShaders方法。

什么是局部类?

这是一种拆分类-结构体的方法,把他们放入不同的部分,存储在不同的文件中。这样做的目的是为了更好的组织代码。典型的用例是将自动生成的代码与手动编写的代码分开。就编译器而言,它们都是同一类定义的一部分。他们在Object Management, More Complex Levels教程中有介绍。

清理另一个局部类文件只包含上个文件我们之前删除的那部分。

这个编辑器部分的内容只需要在编辑器中执行,所以我们用条件宏来标记。

但是,现在发布模式运行还是会失败,因为另一个局部类中包含DrawUnsupportedShaders函数的调用,它应该只能存在于编辑器模式。为了解决这个问题,我们需要让这个方法正常执行。为此我们需要在方法前面用partial进行声明,类似于抽象方法声明。我们可以在类定义的任何部分中执行此操作,所以让我们把它放在编辑器部分。完整的方法声明也必须标记为partial。

public  partial class CameraRenderer : MonoBehaviour
{partial void DrawUnsupportedShaders();
#if UNITY_EDITORstatic Material errorMaterial;static ShaderTagId[] legacyShaderTagIds = {new ShaderTagId("Always"),new ShaderTagId("ForwardBase"),new ShaderTagId("PrepassBase"),new ShaderTagId("Vertex"),new ShaderTagId("VertexLMRGBM"),new ShaderTagId("VertexLM")};partial void DrawUnsupportedShaders(){if (errorMaterial == null){errorMaterial =new Material(Shader.Find("Hidden/InternalErrorShader"));}var drawingSettings = new DrawingSettings(legacyShaderTagIds[0], new SortingSettings(camera)){overrideMaterial = errorMaterial};for (int i = 1; i < legacyShaderTagIds.Length; i++){drawingSettings.SetShaderPassName(i, legacyShaderTagIds[i]);}var filteringSettings = FilteringSettings.defaultValue;context.DrawRenderers(cullingResults, ref drawingSettings, ref filteringSettings);}
#endif
}

3.4Drawing Gizmos

现在我们的管线不能绘制辅助图标(Gizmos),无论是场景视图还是游戏视图激活的时候都不会显示。

我们可以检查辅助图标是否应该被绘制通过执行UnityEditor.Handles.ShouldRenderGizmos函数。如果需要显示,我们必须调用context的DrawGizmos方法,并传递camera作为第一个参数,传入第二个参数来确定辅助图标的哪些子集需要被绘制。这里有两个子集,在ImageEffects(屏幕后效)之前和之后。因为我们现在不支持ImageEffects因此两个方法我们都会调用。我们在一个只有Editor模型运行的方法DrawGizmos。

using UnityEditor;
using UnityEngine;
using UnityEngine.Rendering;partial class CameraRenderer {partial void DrawGizmos ();partial void DrawUnsupportedShaders ();#if UNITY_EDITOR…partial void DrawGizmos () {if (Handles.ShouldRenderGizmos()) {context.DrawGizmos(camera, GizmoSubset.PreImageEffects);context.DrawGizmos(camera, GizmoSubset.PostImageEffects);}}partial void DrawUnsupportedShaders () { … }#endif
}

辅助图标将被绘制在所有对象绘制之后。

3.5Drawing Unity UI

另一个需要我们注意的事情是Unity的用户界面系统。例如,我们通过GameObject/UI/Button添加一个按钮来创建一个简单的UI。它将显示在game窗口,但是不会显示在scene窗口。

帧调试器显示UI是通过新增的UGUI.Rendering.RenderOverlays绘制的,并不是通过我们的渲染管线绘制的

这是因为UI的默认绘制模式是ScreenSpace-Overlay

当我们把UI的渲染模式修改成ScreenSpace-Camera模式。使用主摄像机作为RenderCamera的参数,可以使UI的渲染成为透明几何体的一部分。

在渲染scene窗口的时候我们可以通过执行ScriptableRenderContext.EmitWorldGeometryForSceneView函数和camera参数来将UI添加到世界的几何体中。在一个只有编辑器模式允许的新函数PrepareForSceneWindow中执行这些逻辑。当CameracameraType属性是CameraType.SceneView的时候我们渲染这个场景摄像机。

	partial void PrepareForSceneWindow ();#if UNITY_EDITOR…partial void PrepareForSceneWindow () {if (camera.cameraType == CameraType.SceneView) {ScriptableRenderContext.EmitWorldGeometryForSceneView(camera);}}

因为我们要把几何体添加到场景中,所有我们必须在Culling剔除之前执行这个函数。

执行完这一步后UI就会被绘制到场景视图中了。

4.Multiple Cameras

在场景中有可能同时存在多个已激活的摄像机,如果是这样我们必须保证他们同时工作。

4.1Two Cameras

每一个摄像机都有一个深度(Depth)值,默认的主摄像机的值为-1。摄像机按深度增加的顺序进行渲染。为了看到这些,复制主摄像机,改它改名为SecondaryCamera,并把深度值设置为0。同时我们给它另一个tag值,因为MainCamera的tag只能被一个摄像机使用。

现在场景被渲染了两次。渲染结果的表现仍然是一样的,因为渲染过程中执行了清楚操作。FrameDebugger向我们展示了这个过程,但是因为相邻的具有相同名字的采样被合并成我们最终得到的一个Render Camera列表。

如果每个摄像机都有自己的范围,就更清楚了。为了使之成为可能,添加一个只在编辑器模式运行的函数PrepareBuffer方法,使得缓冲区的名字和相机的名字一样。

partial void PrepareBuffer ();#if UNITY_EDITOR…partial void PrepareBuffer () {buffer.name = camera.name;}#endif

现在看就清晰很多了

4.2Dealing with Changing Buffer Names

尽管现在FrameDebugger能够分开显示每个相机的采样信息,但是当我们进入运行模式时Unity的控制台将充满警告并告诉我们BeginSampleEndSample的计数必须要匹配(但是这里我没有报错不知道为啥)。因为我们对采用和他们的缓冲区使用了不同的名字,所以匹配混淆了。除此之外,我们每次访问camera的name属性也会造成内存的分配,所以我们不想在创建的时候这样做。

为了解决这两个问题我们添加一个SampleName字符串属性。在编辑器模式我们应该在PrepareBuffer函数中设置它和缓冲区的名字,运行模式它就是RenderCamera类的一个字符串常量值。

#if UNITY_EDITOR…string SampleName { get; set; }…partial void PrepareBuffer () {buffer.name = SampleName = camera.name;}#elseconst string SampleName = bufferName;#endif

然后在开始采样和结束采样的时候都使用SampleName

我们可以看到不同通过查看 profiler-打开 Window/Analgsis/Profiler。切换到Hierarchy模式然后按GC数据进行排序。可以看到有一个GC.Alloc,是96B。这部分就是在编辑器模式下产生的GC,在打包以后这部分GC就会消失。教程里后面摄像机数组的48bit不知道为啥没有出现。

4.3Layers

摄像机可以被配置成只看到某些层级的物体。这是通过调整摄像机的Culling Mask实现的。为了看到这个效果我们把所有使用标准着色器的对象设置为IgnoreRaycast层级 

然后从主摄像机的Culling Mask中排除这个层级。

SecondaryCameraCulling Mask设置为只有IgnoreRaycast

因为SecondaryCamera最后渲染,所以我们只能看到无效的对象。

4.4Clear Flags

我们可以通过设置CameraClearFlags来让第二个摄像机在第一个摄像机的渲染结果之上进行渲染(在之前,渲染第二个摄像机时我们会清除所有缓冲)。

	void Setup () {context.SetupCameraProperties(camera);CameraClearFlags flags = camera.clearFlags;buffer.ClearRenderTarget(true, true, Color.clear);buffer.BeginSample(SampleName);ExecuteBuffer();}

CameraClearFlags枚举定义了Skybox,Color,Depth, andNothing四个值,除了 Nothing,其他情况都需要清理深度缓冲区。观看源代码还有一个SolidColor

我们只需要在标记设置为Color时清除颜色缓冲区,因为在Skybox中,我们必然都会替换之前所有的颜色数据。

如果想要清除为纯色,我们就必须使用摄像机的背景色。但是因为我们在线性空间进行渲染,我们必须把颜色转换为线性空间,所以我们使用camera.backgroundColor.linear。在其他情况下,颜色并不重要,所以我们可以用Color.clear。

SecondaryCamera的清理标记定义如何组合两个摄影机的渲染。在skybox或color的情况下,先前的结果将被完全替换。如果仅清除深度,则辅助摄影机将正常渲染,但不会绘制天空盒,因此以前的结果显示为背景。当什么都没有清除时,深度缓冲区将保留,因此未照亮的对象最终将遮挡无效对象,就像它们是由同一台摄像机绘制的一样。但是,前一个摄像机绘制的透明对象没有深度信息,因此像 skybox之前所做的那样被绘制。

通过调整相机的 Viewport Rect ,也可以将渲染区域缩小到整个渲染目标的一小部分。其余渲染目标保持不受影响。在这种情况下,将使用Hidden / InternalClear着色器进行清除。模板缓冲区用于将渲染限制在视口区域。这里将第二个相机渲染到右半屏幕。

请注意,如果每帧渲染一台以上的摄像机就必须同时进行多次剔除,设置,分类等操作这是很耗的。一种有效的方式是让每一个摄像机都有自己的渲染视角。

 

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

IP地址的主要功能及其在网络中的重要性

在当今数字化时代&#xff0c;互联网已经成为人们生活和工作中不可或缺的一部分。而IP地址&#xff08;Internet Protocol Address&#xff09;作为互联网中的关键组成部分&#xff0c;发挥着至关重要的作用。本文将探讨IP地址的主要功能以及其在网络中的重要性。 IP地址查询&…

计算机视觉数据集——扑克识别数据集

扑克数据集 数据集地址&#xff1a;https://download.csdn.net/download/matt45m/89130302 这是一个检测扑克牌种类的数据集&#xff0c;检测种类目前只有6种&#xff0c;分别是 "queen", "ten", "nine", "king", "jack"…

短视频转gif怎么做?三十秒在线转换gif

在现在这个快节奏的时代&#xff0c;gif动画相较于长时间的视频更受大众的欢迎。当我们需要将短视频、电影等视频制作成gif动画图片的时候就可以使用gif动画图片&#xff08;https://www.gif.cn/&#xff09;制作网站-GIF中文网&#xff0c;无需下载软件&#xff0c;手机、pc均…

OSCP靶场--Fail

OSCP靶场–Fail 考点(rsync未授权覆盖公钥Fail2ban提权) 1.nmap扫描 ## ┌──(root㉿kali)-[~/Desktop] └─# nmap -sV -sC 192.168.153.126 -p- -Pn --min-rate 2500 Starting Nmap 7.92 ( https://nmap.org ) at 2024-04-12 23:34 EDT Warning: 192.168.153.126 giving …

招生管理|基于SprinBoot+vue的招生管理系统系统设计与实现(源码+数据库+文档)

招生管理目录 基于SprinBootvue的招生管理系统设计与实现 一、前言 二、系统设计 三、系统功能设计 前台 后台 教师权限 学生权限&#xff1a; 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&#xff1a; 博主介绍&#xff…

大数据------JavaWeb------JDBC(完整知识点汇总)

JDBC 定义 全称为Java数据库连接&#xff08;Java DataBase Connectivity&#xff09;&#xff1a;是使用java语句来操作所有关系型数据库的一套API JDBC本质 它是官方定义的一套操作所有关系型数据库的规则&#xff08;即接口&#xff09;&#xff0c;各个数据库厂商会去实现…

VLC-Qt实现简单的视频播放器

VLC-Qt是一个结合了Qt应用程序和libVLC的免费开源库。它提供了用于媒体播放的核心类&#xff0c;以及用于快速开发媒体播放器的GUI类。由于集成了整个libVLC&#xff0c;VLC-Qt具备了libVLC的所有特性&#xff0c; 例如&#xff1a;libVLC实例和播放器、单个文件和列表播放、音…

.NET i18n 多语言支持与国际化

环境 WIN10 VS2022 .NET8 1.&#x1f44b;创建项目 2.&#x1f440;创建Resources Controllers HomeController.en.resx HomeController.fr.resx HomeController.zh.resx 3.&#x1f331;Program.cs添加国际化支持 // 添加国际化支持 builder.Services.AddLocalization(…

6.Hexo标签插件和资产文件夹

标签插件 标签插件&#xff0c;基本上是只是一些小的代码片段&#xff0c;可以将他们添加到markdown文件中 以便添加特定的代码&#xff0c;不需要编写复杂或混乱的HTML 当很多时候想要在markdown页面添加一些特殊元素&#xff0c;通常必须使用HTML 如果不想这么用HTML&#…

CSS特效---百分比加载特效

1、演示 2、一切尽在代码中 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><title>Document</title&…

公众号文章如何添加多个附件?

在公众号分享一些文档给粉丝下载&#xff0c;有那么几种方式。比如把文档转成超链接&#xff0c;放在公众号的“阅读原文”处&#xff0c;或者把文件转成二维码&#xff0c;贴在公众号文章里面。这两种方式其实都需要先把文件转成超链接&#xff08;网页链接&#xff09;&#…

【目标检测数据集】VOC2007 数据集介绍

一、介绍 VOC 数据是 PASCAL VOC Challenge 用到的数据集&#xff0c;官网&#xff1a;http://host.robots.ox.ac.uk/pascal/VOC/ 备注&#xff1a;VOC数据集常用的均值为&#xff1a;mean_RGB(122.67891434, 116.66876762, 104.00698793) Pytorch 上通用的数据集的归一化指…

OVITO-2.9版本

关注 M r . m a t e r i a l , \color{Violet} \rm Mr.material\ , Mr.material , 更 \color{red}{更} 更 多 \color{blue}{多} 多 精 \color{orange}{精} 精 彩 \color{green}{彩} 彩&#xff01; 主要专栏内容包括&#xff1a; †《LAMMPS小技巧》&#xff1a; ‾ \textbf…

系统架构最佳实践 -- 一般优惠券平台系统架构设计

优惠券是商城的一种基础的营销工具&#xff0c;在目前c端用户对于电子优惠券已经非常熟悉的情况下&#xff0c;一般自营商城的营销活动系统&#xff0c;都是从优惠券开始搭建。 一、名词定义 基于个人理解&#xff0c;为方便表述&#xff0c;首先对可能产生歧义的名词进行如下…

ubuntu 设置 root 用户密码,创建新用户并赋权限

ubuntu 设置 root 用户密码&#xff0c;创建新用户并赋权限 在适用于 Linux 的 Windows 子系统上运行 Linux GUI 应用&#xff0c; 安装 Ubuntu-20.04 系统&#xff0c;新安装好的系统&#xff0c;设置用户名密码时&#xff0c; root 用户密码默认为空&#xff0c;这时需要设置…

信息学奥赛一本通T1442-小木棍【dfs】

信息学奥赛一本通T1442-小木棍 - C语言网 (dotcpp.com) #include <iostream> #include <algorithm> #include <cmath> #include <cstring> using namespace std; const int N1e5100; int n; int res1e9; int a[N],p0,sd0; bool vis[N]; bool dfs(int i…

【R语言】绘制标准地图(指北针,比例尺,图例)

在绘制地图时&#xff0c;我们一般都是利用“ArcGIS”"MapGIS"等专业软件进行手动操作。这样制作的地图自定义的效果强&#xff0c;我们可以随意调换地图的各种元素&#xff0c;但是今天本文要将的是如何使用R语言绘制具备地图三要素的精美地图&#xff0c;当然代码绘…

Android 14.0 SystemUI修改状态栏电池图标样式为横屏显示

1.概述 在14.0的系统rom产品定制化开发中,对于原生系统中SystemUId 状态栏的电池图标是竖着显示的,一般手机的电池图标都是横屏显示的 可以觉得样式挺不错的,所以由于产品开发要求电池图标横着显示和手机的样式一样,所以就得重新更换SystemUI状态栏的电池样式了 如图: 2.S…

volatile是如何禁止指令进行重排序的

Further Reading &#xff1a; 内存屏障类型介绍&#xff08;StoreStore&#xff0c;StoreLoad&#xff0c;LoadLoad&#xff0c;LoadStore&#xff09; Further Reading &#xff1a; 什么是指令重排 重排序分为编译器重排序和处理器重排序。 为了实现volatile内存语义&#x…

【计算机毕业设计】人事管理系统——后附源码

&#x1f389;**欢迎来到我的技术世界&#xff01;**&#x1f389; &#x1f4d8; 博主小档案&#xff1a; 一名来自世界500强的资深程序媛&#xff0c;毕业于国内知名985高校。 &#x1f527; 技术专长&#xff1a; 在深度学习任务中展现出卓越的能力&#xff0c;包括但不限于…