IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> (CatlikeCoding)SRP自定义脚本渲染管线学习(一) -> 正文阅读

[游戏开发](CatlikeCoding)SRP自定义脚本渲染管线学习(一)

这是个人学习笔记,有错欢迎指出
参考学习Custom Render Pipeline by catlikecoding

新的渲染管线

1.要渲染任何东西,Unity必须知道绘制的形状、什么时候渲染、在哪里渲染还有相关的设置,还有一些灯光、阴影、透明度、图片的效果、volumetric effects(体积效果)
2.Unity2018出了实验性的the Lightweight RP and the High Definition RP.
3.Unity 2019 出了通用渲染管线 the Universal RP in Unity 2019.3

The Universal RP

1.设计URP的目的: to replace the current legacy RP as the default.
2.主要思想:it is a one-size-fits-most RP that will also be fairly easy to customize. 就是可以适合大多数RP但是可以方便自定义
3.这个系列要制作一个完整的RP

创建步骤

1.基础搭建

1.1 将颜色色彩空间改为线性空间

gamma和linear的区别目前还没有弄清楚
目前的理解是:数值上的颜色变换,将数值做一个映射

1.2.在场景中添加一些物体

材质有:standard, unlit opaque and transparent materials.
Unlit/Transparent shader的UV使用材质贴图

在这里插入图片描述

红色的为standard,蓝色的为standard,并且把Rendering Mode设置为Transparent,绿色的shader为Unlit/Color,白色的为Unlit/Transparent

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1.3 创建脚本CustomRenderPipelineAsset

创建脚本

在脚本中添加如下代码:
using UnityEngine;
using UnityEngine.Rendering;
[CreateAssetMenu(menuName ="Rendering/Custom Render Pipeline")]
public class CustomRenderPipelineAsset : RenderPipelineAsset
{
    protected override RenderPipeline CreatePipeline()
    {
        return null;
    }
}

1.Rendering pipeline asset的作用是:获取负责渲染的管线对象实例
2.asset本身是一个handle(句柄)和储存settings(设置)的地方
3.RenderPipeline函数返回的是 RenderPipeline的实例,目前先返回null
4.我们需要向项目中添加这种类型的资产,做法是在头部添加CreateAssetMenu

在这里插入图片描述

更改pipeline settings

在edit/project settings/Graphics中添加新设置的RP

在这里插入图片描述

目前为止:
  • Graphics中的设置选项变少
  • Game/Scene窗口是黑色的
1.4 Render Pipeline Instance
同样创建一个CustomRenderPipeline脚本

添加代码

using UnityEngine;
using UnityEngine.Rendering;
public class CustomRenderPipeline : RenderPipeline
{
	protected override void Render(ScriptableRenderContext context, Camera[] cameras)  {
	}
}

什么是ScriptableRenderContext?
官网解释:ScriptableRenderContext
主要:用于设置要提交给GPU的状态和绘制命令

之后把CustomRenderPipelineAsset的CreatePipeline()的return项改为改为
return new CustomRenderPipeline();

2.渲染

每一帧Unity都在RP实例上调用Render。它传递了一个上下文结构(context struct),该结构提供了到本机引擎的连接,我们可以用它来渲染。它还传递一个摄像机数组,因为场景中可能有多个活动摄像机。RP的负责按照提供的顺序渲染这些摄像机

2.1 Camera Renderer
创建一个CameraRenderer脚本

每个摄像机渲染是独立的,所以新建一个脚本,再自定义一个Render的方法

using UnityEngine;
using UnityEngine.Rendering;

public class CameraRenderer {
	ScriptableRenderContext context;
	Camera camera;
	public void Render (ScriptableRenderContext context, Camera camera) {
		this.context = context;
		this.camera = camera;
	}
}
在CustomRenderPipeline中创建

在CustomRenderPipeline渲染管线中,遍历ScriptableRenderContext与camera

CameraRenderer renderer = new CameraRenderer();
	protected override void Render(ScriptableRenderContext context, Camera[] cameras)  {
		foreach(Camera camera in cameras)
        {
			renderer.Render(context, camera);
        }
	}
2.2 绘制天空盒

1.CameraRenderer.Render的工作是负责渲染所有摄像机视角范围内的物体,因此我们使用DrawVisibleGeometry完成这个特定的任务
2.调用context.DrawSkybox(camera);可以完成这个任务
3.还需要在最后调用context.submit来完成,因为context.DrawSkybox(camera)是缓存的。
注:在Window/Analysis/Frame Debug下查看以下窗口

在这里插入图片描述
CameraRenderer代码如下:

public void Render(ScriptableRenderContext context, Camera camera)
	{
		this.context = context;
		this.camera = camera;
		DrawVisibleGeometry();
		Submit();
	}
	void Submit()
    {
		context.Submit();
    }
	void DrawVisibleGeometry()
    {
		context.DrawSkybox(camera);
    }

效果:终于出来了!
在这里插入图片描述

补一个setup
为了正确渲染天空盒:我们必须设置视图投影矩阵。这个变换矩阵结合了相机的位置和方向-视图矩阵- 和相机的透视或正投影---- 投影矩阵。它在着色器中被称为 unity _ matrixvp

	    Setup();
		DrawVisibleGeometry();
		Submit();
	}
	void Setup()
	{
		context.SetupCameraProperties(camera);
	}
2.2 Command Buffers

1.有些任务(比如绘制 skybox)可以通过专用方法发出,但其他命令必须通过单独的命令缓冲区间接发出。我们需要这样一个缓冲区来绘制场景中的其他几何图形
2.在创建新对象时,可以将代码块附加到构造函数的调用。然后就可以在块中设置对象的字段和属性,而不必显式地引用对象实例。(还未理解,这里讲的是下面代码的合理性)

//CameraRenderer
const string bufferName = "Render Camera"; 
	CommandBuffer buffer = new CommandBuffer //创建缓冲区
	{
		name = bufferName
	};

我们可以使用命令缓冲区来注入分析器示例(inject profiler samples),这将同时显示在分析器和帧调试器中。这是通过在适当的点调用BeginSample和EndSample来完成的

void Setup()
	{
		buffer.BeginSample(bufferName); //新添加
		ExecuteBuffer(); //新添加 
		context.SetupCameraProperties(camera);
	}
	void Submit()
    {
		buffer.EndSample(bufferName);//新添加
		ExecuteBuffer();//新添加
		context.Submit();
    }
	void ExecuteBuffer()
    {
		context.ExecuteCommandBuffer(buffer);//新添加
		buffer.Clear();//新添加
    }

1.To guarantee proper rendering we have to clear the render target to get rid of its old contents.清屏操作
2.要注意这几个语句的顺序,比如:
(1)清屏之前需要设置摄像机,
(2)buffer.ClearRenderTarget要在buffer.BeginSample之前,不然会造成嵌套的重复

	void Setup()
	{
		context.SetupCameraProperties(camera);
		buffer.ClearRenderTarget(true, true, Color.clear);
		buffer.BeginSample(bufferName);		
		ExecuteBuffer();	
	}

在这里插入图片描述

2.3 Culling

1.我们要剔除摄像机视角外的物体
2.要想确定哪些内容可以被剔除,我们需要跟踪多个摄像机设置和矩阵,为此我们可以使用ScriptableCullingParameters结构体
3.It returns whether the parameters could be successfully retrieved返回信息是否被成功检索
4.out是引用对象,指向参数所在的内存堆栈上的位置。当方法更改参数时,它影响的是该值,而不是副本。

    CullingResults cullingResults;	
	public void Render(ScriptableRenderContext context, Camera camera)
	{
		this.context = context;
		this.camera = camera;
		if(!cull())
        {
			return;
        }
        ...
     }
	bool cull()
    {
		if(camera.TryGetCullingParameters(out ScriptableCullingParameters p))
        {
			cullingResults = context.Cull(ref p);
			return true;
        }
		return false;
    }
2.4 几何体绘制

FilteringSettings:描述如何过滤要渲染的给定可见对象集
DrawingSettings :用哪个着色器(shaderPassName)和对可见对象排序(sortingSettings)
criteria的作用:看渲染顺序,会发现透明物体的显示有问题,这是因为目前场景中物体的渲染顺序是随机的,如果想正确显示透明物体,我们需要在所有不透明物体之后渲染。我们可以在sortingSettings的criteria属性中进行设置。

static ShaderTagId unlitShaderTagId = new ShaderTagId("SRPDefaultUnlit");//着色器标记 ID
void DrawVisibleGeometry()
    {
		var sortingSettings = new SortingSettings(camera)
		{
			criteria = SortingCriteria.CommonOpaque
		};
		var drawingSettings = new DrawingSettings(unlitShaderTagId,sortingSettings);
		var filteringSettings = new FilteringSettings(RenderQueueRange.all);
		...
 	}
2.5 Drawing Opaque and Transparent Geometry Separately(分开渲染透明和不透明的)

问题:在绘制天空盒后,透明物体看不到
原因:这是因为透明着色器不会写入深度缓冲区。它们不会隐藏身后的东西,因为我们可以看穿它们。
解决方案是先画不透明的物体,然后是天空盒,然后才是透明的物体。
调整顺序如下:

		//顺序:不透明->天空盒->透明
 		var sortingSettings = new SortingSettings(camera) { criteria = SortingCriteria.CommonOpaque };
		var drawingSettings = new DrawingSettings(unlitShaderTagId, sortingSettings);				//定义绘制选择
		var filteringSettings = new FilteringSettings(RenderQueueRange.opaque);						

		context.DrawRenderers(cullingResults, ref drawingSettings, ref filteringSettings);			
		context.DrawSkybox(camera); 
		//透明
		sortingSettings.criteria = SortingCriteria.CommonTransparent;							
		drawingSettings.sortingSettings = sortingSettings;											
		filteringSettings.renderQueueRange = RenderQueueRange.transparent;				

		context.DrawRenderers(cullingResults, ref drawingSettings, ref filteringSettings);			

在这里插入图片描述

3.Editor Rendering

3.1 Drawing Legacy Shaders

1.为了覆盖所有Unity的默认着色器,我们必须为Always, ForwardBase, PrepassBase, Vertex, VertexLMRGBM和VertexLM通道使用着色器标签id。在静态数组中加入
2.绘制不支持的着色器:我们可以通过 FilteringSettings.defaultValue 属性获得默认的过滤设置

	static ShaderTagId[] legacyShaderTagIds = {
		new ShaderTagId("Always"),
		new ShaderTagId("ForwardBase"),
		new ShaderTagId("PrepassBase"),
		new ShaderTagId("Vertex"),
		new ShaderTagId("VertexLMRGBM"),
		new ShaderTagId("VertexLM")
	};
	public void Render(ScriptableRenderContext context, Camera camera)
	{
		...
		Setup();
		DrawVisibleGeometry();
		DrawUnsupportedShaders();  //新加
		Submit();
		...
	}
	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 //可以通过 FilteringSettings.defaultValue 属性获得默认的过滤设置
		);
	}

用标准着色器渲染的物体出现了,但它们现在是纯黑色的,因为RP还没有为它们设置所需的着色器属性
在这里插入图片描述

3.2 Error Material

为了清楚地指出哪些对象使用了不支持的着色器,我们将用Unity的错误着色器(Hidden/InternalErrorShader)绘制它们。用shader作为参数构造一个新的材质,我们可以通过Find.shader来找到它。使用Hidden/InternalErrorShader字符串作为参数进行查找。

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
		};
		...
	}

在这里插入图片描述

添加partial

1.这是一种将类(或结构)定义拆分为多个部分、存储在不同文件中的方法。
2.唯一的目的是组织代码。典型的用例是将自动生成的代码与手动编写的代码分开。就编译器而言,它们都是同一个类定义的一部分

public partial class CameraRenderer 
添加脚本CameraRenderer.Editor

把static ShaderTagId[] legacyShaderTagIds、static Material errorMaterial、DrawUnsupportedShaders() 移到新的脚本中

//注意3个partial的位置
partial class CameraRenderer
{
	partial void DrawUnsupportedShaders();
#if UNITY_EDITOR
	static ShaderTagId[] legacyShaderTagIds = {...};
	static Material errorMaterial;
	partial void DrawUnsupportedShaders() {...}  //绘制所有不支持的着色器
#endif
3.3 Drawing Gizmos
using UnityEngine;
using UnityEngine.Rendering;
using UnityEditor;
partial class CameraRenderer
{
	partial void DrawGizmos();
	...
#if UNITY_EDITOR
	...
	partial void DrawGizmos () {
		if (Handles.ShouldRenderGizmos()) {
			context.DrawGizmos(camera, GizmoSubset.PreImageEffects);
			context.DrawGizmos(camera, GizmoSubset.PostImageEffects);
		}
	}
	...
#endif
CameraRenderer中添加
public void Render(ScriptableRenderContext context, Camera camera)
	{
		...
		Setup();
		DrawVisibleGeometry();
		DrawUnsupportedShaders();
		DrawGizmos();  //新加
		Submit();
		...
	}

在这里插入图片描述

3.4 Drawing Unity UI

添加UI后发现,在scene中看不到,而在game视图中看得到,UI是单独渲染的

在这里插入图片描述

解决方法:We have to explicitly add the UI to the world geometry when rendering for the scene window

	partial void PrepareForSceneWindow();
#if UNITY_EDITOR
	...
	partial void PrepareForSceneWindow()
	{
		if (camera.cameraType == CameraType.SceneView)
		{
			ScriptableRenderContext.EmitWorldGeometryForSceneView(camera);
		}
	}
public void Render(ScriptableRenderContext context, Camera camera)
	{
		this.context = context;
		this.camera = camera;
		PrepareForSceneWindow();
		if (!cull())

4.Multiple Cameras

4.1 Two Cameras

在场景中可以有多个活动的摄像机。如果是这样的话,我们必须确保它们一起工作。

在这里插入图片描述

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

在这里插入图片描述

4.2 Dealing with Changing Buffer Names

尽管帧调试器现在在每个摄像头上显示一个单独的样本层次结构,但当我们进入游戏模式时,Unity的控制台将会被警告我们BeginSample和EndSample计数必须被匹配的消息所填满。

这一部分目前没有看懂,先复制上来等以后再分析

using UnityEngine.Profiling;
#if UNITY_EDITORstring SampleName { get; set; }partial void PrepareBuffer () {
		Profiler.BeginSample("Editor Only");
		buffer.name = SampleName = camera.name;
		Profiler.EndSample();
	}
#else
	const string SampleName = bufferName;

#endif
void Setup () {
		context.SetupCameraProperties(camera);
		buffer.ClearRenderTarget(true, true, Color.clear);
		buffer.BeginSample(SampleName);
		ExecuteBuffer();
	}

	void Submit () {
		buffer.EndSample(SampleName);
		ExecuteBuffer();
		context.Submit();
	}

在这里插入图片描述

4.3 Layers

摄像头也可以设置成只能看到特定层面上的东西。这是通过调整他们的culling mask来完成的。
步骤:
1.设置物体的Layer为Ignore Raycaster
2.设置Main camera的Culling Mask不包含Ignore Raycaster
3.设置Secondary camera的Culling Mask只有Ignore Raycaster
效果:game视图只有一个正方体

在这里插入图片描述

4.4 Clear Flags

1.我们可以通过调整第二个渲染的clear flags来结合两个相机的结果。
2.它们是由 CameraClearFlags enum 定义的,我们可以通过相机的 clearFlags 属性获取它们。
3.在清除之前在安装程序中执行此操作。

void Setup()
	{
		context.SetupCameraProperties(camera);
		CameraClearFlags flags = camera.clearFlags;
		buffer.ClearRenderTarget(flags <= CameraClearFlags.Depth, flags == CameraClearFlags.Color, flags == CameraClearFlags.Color ?
				camera.backgroundColor.linear : Color.clear);
		buffer.BeginSample(SampleName);		
		ExecuteBuffer();	
	}

在这里插入图片描述

感动~~第一小节完成,撒花★,°:.☆( ̄▽ ̄)/$:.°★
2021-07-23

  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2021-07-25 11:59:17  更:2021-07-25 11:59:38 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年4日历 -2024/4/27 9:10:54-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码