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_EDITOR
    static 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;
	}

#else

	const 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.mfbz.cn/a/551577.html

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

相关文章

java -spring 图灵 03 各种核心组件

01.BeanDefinition 表示Bean定义&#xff0c;BeanDefinition中存在很多属性用来描述一个Bean的特点。比如&#xff1a; class&#xff0c;表示Bean类型 scope&#xff0c;表示Bean作用域&#xff0c;单例或原型等 lazyInit&#xff1a;表示Bean是否是懒加载 initMethodName&am…

postman 调试 传base64字符串 原来选xml

上个图 工具类 package org.springblade.common.utils;import com.alibaba.fastjson.JSONObject; import org.springblade.modules.tc.mas.Submit;import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStrea…

用示例说明序列化和反序列化

用示例说明序列化和反序列化 序列化和反序列化是将数据结构或对象转换为可存储或传输的格式&#xff0c;以便在需要时重新构建原始数据结构或对象的过程。常见的序列化格式包括 JSON、XML 和 Pickle。 序列化&#xff08;Serialization&#xff09;&#xff1a; 在计算机科学…

嵌入式中C++指针使用方法总结

各位开发者大家好,在分享指针之前,先来看一下int *p[3]和int (*p)[3] 的区别。 int *p[3] p是一个数组,此数组有3个元素,每个元素都是int*类型,也就是指向整型数据的指针类型。 int a=10,b=20,c=30; int*p[3]={&a,&b,&c}; 而int(*p)[3]中的p是一个指向数组的…

吐槽一下腾讯云TKE原生节点的降本增效

背景 作为一个10年腾讯云用户我本来是不想吐槽的&#xff0c;往常有问题都是第一时间在用户反馈群里面吐槽一下&#xff0c;这次我是忍不了吐槽了起因就是下面这种图&#xff1a; 恩 tke节点的新建&#xff0c;现在默认首个是原生节点&#xff0c;对没有看错&#xff0c;可以…

操作系统安全

操作系统属于软件安全的范畴。 什么是操作系统&#xff1f; 操作系统是硬件和软件应用程序之间接口的程序模块&#xff0c;是计算机资源的管理者。操作系统是保证安全的重要基础。 一、操作系统安全基础 操作系统保护的对象 操作系统的安全功能 用户认证存储器保护文件与I/O设…

初始监控工具--zabbix和安装

一、Zabbix 1. 监控系统的必要性 作为一个技术人员&#xff0c;需要会使用监控系统查看服务器状态以及网站流量指标&#xff0c;利用监控系统的数据去了解上线发布的结果和网站的健康状态。 2. 监控软件的作用 利用一个优秀的监控软件&#xff0c;我们可以: ● 通过一个友…

蚂蚁摩斯入选IDC《数据要素全景研究》报告

近日&#xff0c;全球权威研究机构IDC发布《数据要素全景研究》&#xff0c;对当前数据要素市场的主要需求、市场活动参与主体、落地形式等情况进行分析并列举了市场代表性的技术架构及应用案例为产品选型提供参考。蚂蚁数科以技术服务完整性入选代表技术厂商&#xff0c;旗下摩…

DNS服务器配置与管理(3)——综合案例

DNS服务器配置与管理 前言 在之前&#xff0c;曾详细介绍了DNS服务器原理和使用BIND部署DNS服务器&#xff0c;本文主要以一个案例为驱动&#xff0c;在网络中部署主DNS服务器、辅助DNS服务器以及子域委派的配置。 案例需求 某公司申请了域名example.com&#xff0c;公司服…

【YOLOv8改进[损失函数]】使用结合InnerIoU和Focaler的各种损失函数助力YOLOv8更优秀

目录 一 回归损失函数&#xff08;Bounding Box Regression Loss&#xff09; 1 Inner-IoU 2 Focaler-IoU&#xff1a;更聚焦的IoU损失 二 改进YOLOv8的损失函数 1 总体修改 ① ultralytics/utils/metrics.py文件 ② ultralytics/utils/loss.py文件 ③ ultralytics/uti…

服务器中毒怎么办?企业数据安全需重视

互联网企业&#xff1a; 广义的互联网企业是指以计算机网络技术为基础&#xff0c;利用网络平台提供服务并因此获得收入的企业。广义的互联网企业可以分为:基础层互联网企业、服务层互联网企业、终端层互联网企业。 狭义的互联网企业是指在互联网上注册域名&#xff0c;建立网…

11.基础乐理-音域、1=C到底是那一组的C

音域&#xff1a; 音域它指的是一个乐器&#xff08;包括人声&#xff09;&#xff0c;能发出的所有的音高总&#xff0c;比如我们拿钢琴来看&#xff0c;钢琴最低的是大字二组的A2&#xff0c; 钢琴最高音是小字五组的c5&#xff0c;钢琴的音域是A2 - c5&#xff0c;如图1所示…

数字次数排序-第12届蓝桥杯省赛Python真题精选

[导读]&#xff1a;超平老师的Scratch蓝桥杯真题解读系列在推出之后&#xff0c;受到了广大老师和家长的好评&#xff0c;非常感谢各位的认可和厚爱。作为回馈&#xff0c;超平老师计划推出《Python蓝桥杯真题解析100讲》&#xff0c;这是解读系列的第53讲。 数字次数排序&…

软硬链接与动静态库

文章目录 1.软硬链接2.动态库和静态库2.1 见一见库2.2 动静态库2.2.1 静态库2.2.2 动态库 2.3 动静态库的对比 3.真实的应用场景(ncurses库)4.库加载---可执行程序和地址空间4.1可执行程序的加载4.2 库的加载 1.软硬链接 2.动态库和静态库 2.1 见一见库 我们用过很多库。C/C的…

Pytorch官方FlashAttention速度测试

在Pytorch的2.2版本更新文档中&#xff0c;官方重点强调了通过实现FlashAtteneion-v2实现了对scaled_dot_product_attention约2X左右的加速。 今天抽空亲自试了下&#xff0c;看看加速效果是否如官方所说。测试前需要将Pytorch的版本更新到2.2及以上&#xff0c;下面是测试代码…

企业数字化转型路径有哪些?

企业数字化转型是一个复杂而全面的过程&#xff0c;涉及到企业的多个方面&#xff0c;包括管理、运营、生产、销售等。企业数字化转型的路径可以概括为以下几个方面&#xff1a; 1、开展数字化评估 企业首先需要对自身的数字化基础水平、经营管理现状以及内外部转型资源进行全…

基于汇编代码和源代码融合的漏洞检测系统

这篇文章通过结合漏洞源代码和汇编代码的特征实现了一个漏洞检测系统。实现步骤如下&#xff1a;&#xff08;1&#xff09;从源代码和汇编代码中提取代码切片&#xff1b;&#xff08;2&#xff09;通过提出的代码对齐算法对这些片断进行对齐&#xff1b;&#xff08;3&#x…

2024 收入最高的十大编程语言预测

预测2024 收入最高的十大编程语言 在过去2023年,了解哪些编程语言能为开发者提供更高的薪水至关重要,目前全球已有超过200种编程语言可供选择.为了深入了解市场趋势和受欢迎程度,DevJobsScanner在过去一年里分析了全球超过1000万个开发职位空缺.尽管这项研究主要关注美国就业市…

LLM-01 大模型 本地部署运行 ChatGLM2-6B-INT4(6GB) 简单上手 环境配置 单机单卡多卡 2070Super8GBx2 打怪升级!

写在前面 其他显卡环境也可以&#xff01;但是最少要有8GB的显存&#xff0c;不然很容易爆。 如果有多显卡的话&#xff0c;单机多卡也是很好的方案&#xff01;&#xff01;&#xff01; 背景介绍 目前借到一台算法组的服务器&#xff0c;我们可以查看一下目前显卡的情况 …

【网站项目】学生选课系统小程序

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…
最新文章