Learn SRP 01

学习链接:Custom Render Pipeline (catlikecoding.com)

使用Unity版本:Unity 2022.3.5f1

1.A new Render Pipeline

1.1Project Setup

创建一个默认的3D项目,项目打开后可以到默认的包管理器删掉所有不需要的包,我们只使用Unity UI这个包来绘制UI,因此可以保留这个包

另外,在项目设置里将颜色空间设置为线性空间

接着在场景中放置一些物体,分别使用 standard, unlit opaque and transparent 材质. The Unlit/Transparent shader only works with a texture, so here is a UV sphere map for that.

红色的立方体使用 Standard shader, 绿色和黄色的立方体使用Unlit/Color shader. 蓝色的球体使用 Standard shader with Rendering Mode set to Transparent, 白色的球体使用 Unlit/Transparent shader.

1.2Pipeline Asset

到目前位置,Unity还是使用的默认的渲染管线。我们首先需要创建一个可编程渲染管线资产并使用它来替换默认渲染管线。我们将使用和Unity通用渲染管线(URP)相似的文件结构。创建名为Custom RP资源文件夹和一个名为Runtime的子文件夹。在这里放入一个新的C#脚本名字为CustomRenderPipelineAsset。

这个资产类型必须继承自RenderPiplineAsset,它在UnityEngine.Rendering命名空间下。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;

[CreateAssetMenu(menuName = "Rendering/Custom Render Pipeline")]
public class CustomRenderPipelineAsset : RenderPipelineAsset
{
    protected override RenderPipeline CreatePipeline()
    {
        return null;
    }


}

渲染管线资产的主要目的是为Unity提供一种获取负责渲染的管道对象实例的方法。资产本身只是一个句柄和一个存储设置的地方。我们暂时还没有任何设置,所以我们要做的只是给予Unity一个获取我们渲染管线实例的方式。我们通过重写抽象方法CreatePipeline返回一个RenderPipeline的实例来实现。但是我们现在还没有定义一个自定义渲染管线,所以我们先返回空(null)。

CreatePipeline方法是由protected限制符定义的,这意味着类本身和继承自RenderPipelineAsset的类才可以使用此方法。

我们需要为我们的项目添加一个这个类的资产。为了实现这个功能我们为CustomRenderPipelineAsset添加一个CreateAssetMenu的特性。

这将在Asset/Create按钮下添加一个入口。为了保持整洁让我们把它放到Rendering子按钮下。我们通过menuName属性设置为Rendering/Custom Render Pipeline来实现。此属性可以直接设置在属性类型后面的圆括号内。

使用这个新按钮来添加资产到项目中,然后到Graphics 项目设置中,在Scriptable Render Pipeline Settings面板选择设置这个资产。

替换默认的渲染管线会造成一些事情发生变化。首先在graphics settings信息面板中的一些选项将消失。第二,我们禁用了默认的渲染管线,但没有提供有效的替换,因此不会再呈现任何内容。Game视图,场景视图,材质预览将不再起作用。如果你打开frame debugger -Window / Analysis / Frame Debugger并开启它,你会看到没有任何东西绘制到game窗口。

1.3Render Pipeline Instance

创建一个CustomRenderPipeline类,把它放入和CustomRenderPipelineAsset相同的文件夹。这个类必须继承自RenderPipeline,它将被使用在CustomRenderPipelineAsset中创建并返回的渲染管线的实例。

RenderPipeline定义了一个受保护的抽象方法Render,我们必须重写它来实现一个具体的管线。它有两个参数:一个ScriptableRenderContext和一个Camera数组。暂时将方法置空。

using UnityEngine;
using UnityEngine.Rendering;

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

使CustomRenderPipelineAsset.CreatePipeline函数返回一个CustomeRenderPipeline的实例。这将为我们提供一个有效且功能强大的管线,尽管它还没有呈现任何内容。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;

[CreateAssetMenu(menuName = "Rendering/Custom Render Pipeline")]
public class CustomRenderPipelineAsset : RenderPipelineAsset
{
    protected override RenderPipeline CreatePipeline()
    {
        return new CustomRenderPipeline();
    }


}

2.Rendering

在渲染管线实例中Unity每帧都会调用Render函数。这个函数传递了一个上下文结构来提供一个和原生引擎的连接,我们可以使用它来进行渲染。因为场景中可能有多个激活的摄像机,所以这个函数同时也传递一组摄像机的信息。根据渲染的顺序对摄像机进行渲染是渲染管线的责任。

2.1Camera Renderer

每一个摄像机的渲染都是独立的。因此与其让CustomRenderPipeline渲染所有摄像机,不如创建一个全新的类来专门负责单独摄像机的渲染。这个类的名字叫做CameraRenderer,给它创建一个公开Render函数包括contextcamera两个参数。为了方便起见让我们保存这些参数到字段中。

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创建的时候创建一个CameraRenderer的实例,然后在一个循环中使用它来渲染所以摄像机。

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]);
		}
	}
}

我们的摄像机渲染逻辑大致相当于可编程渲染管线中的通用渲染管线。这种方式使得将来每个摄像机支持不同的渲染方式变得更加简单,例如第一人称视角,3D地图覆盖,或者是正向渲染和延迟渲染。但是现在我们将使用同样的方式渲染所有摄像机。

2.2Drawing the Skybox

CameraRenderer.Render的工作是绘制所有相机可以看到的几何体。为了使代码清晰,我们在单独的一个函数DrawVisibleGeometry内执行此逻辑。我们先通过使用contextDrawSkybox函数,并传递camera的作为参数来绘制默认的天空盒。

但这还不能使天空盒显示。因为这些我们发布到context的命令只是一些缓冲。我们必须使用contextSubmit方法来提交这些排队等待执行指令。让我们在执行完DrawVisibleGemometry后,在一个独立的Submit函数执行提交。

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

		DrawVisibleGeometry();
		Submit();
	}

	void Submit()
	{
		context.Submit();
	}

	void DrawVisibleGeometry()
	{
		context.DrawSkybox(camera);
	}

天空盒最终在gamescene视图中都显示了。当你激活frame debugger的时候你也可以看到一个天空盒的入口。它以列表的形式的呈现在Camera.RenderSkybox中,在它下面有一个唯一的选项DrawMesh,这代表了实际发生的draw call。这个列表对应游戏窗口的渲染情况。FrameDebgger不会提供其他窗口的渲染情况。

请注意,当前摄像机的方向并不会影响天空盒的绘制结果。我们传递cameraDrawSkybox函数,只用于通过控制摄像机的清除标志来确认天空盒是否应该被绘制。

为了正确的渲染天空盒-整个场景-我们需要设置视图-投影矩阵。这个转换矩阵包含了摄像机的位置和方向-视图矩阵-摄像机的正交和透视投影-投影矩阵。它被熟知是作为着色器中的绘制几何体的时候使用的着色器属性之一untiy_MatrixVP。你可以在frame debugger中选中一个drawcall,在ShaderProperties部分观察这个矩阵。

目前unity_MatrixVP矩阵总是相同的。我们需要通过SetupCameraProperties方法将摄像机的属性应用于context上下文。它设置了矩阵和其他一些摄像机的属性。我们在DrawVisibleGeometry函数之前的单独函数Setup中执行这些操作。 

执行完这一步以后天空盒就正确对齐了,并且会随着摄像机的朝向而改变

2.3Command Buffers

上下文将延迟渲染直到我们提交它。在这之前,我们将对其进行配置并添加命令以供后面执行。一些任务-像绘制天空盒-可以通过专用方法发布,但是其他的指令必须通过单独的命令缓冲区间接发布。我们需要这样的缓冲区来绘制场景中的其他几何体。

为了获得一个缓冲区,我们必须创建一个新的CommandBuffer实例对象。我们只需要一个缓冲区,所以我们在默认情况下为CameraRender创建一个缓冲区,并将它的引用存储在字段中。同时我们给缓冲区一个名字,这样我们就可以在frame debugger中识别它。那就叫做 Render Camera吧。

	const string bufferName = "Render Camera";

	CommandBuffer buffer = new CommandBuffer {
		name = bufferName
	};

对象的实例化语法如何工作?

就像如果我们代码为buffer.name = bufferName;在构造方法执行完毕后作为一段分离的部分。但是当创建一个新对象的时候,也可以添加一个代码块到构造函数中执行。然后你可以设置对象的字段和属性而不需要显示的调用对象的引用。这明确的规定了实例只有在设置完这些属性和字段之后才可以被使用。除此之外它使只使用一个语句进行初始化变为可能-例如,字段的初始化,我们在这里使用它,从而不在需要有许多参数变量的构造函数。

请注意我们省略了空参数列表的构造函数的执行,这种语法也是被允许的。

我们可以使用命令缓冲区注入到采样分析器,他将同时显示在profiler中和framedebugger中。这是通过在适当的点调用BeginSampleEndSample来完成的,在我们的案例中它们在Setup函数和Submit函数中。两个方法都必须提供相同的采样名称,我们将使用缓冲区的名字。

	void Setup () {
		buffer.BeginSample(bufferName);
		context.SetupCameraProperties(camera);
	}

	void Submit () {
		buffer.EndSample(bufferName);
		context.Submit();
	}

为了执行缓冲区,我们将缓冲区作为参数执行contextExecuteCommandBuffer方法。它从缓冲区复制命令,但并没有清除缓冲区,如果我们想重用它,我们必须显示的清除它。因为执行和清除总是一起完成的,所以添加一个同时执行和清除的方法会很方便。

Camera.RenderSkyBox采样嵌套在RenderCamera内部。

2.4Clearing the Render Target

无论我们绘制什么最终都会渲染到摄像机的渲染目标中,默认情况下是帧缓冲区,但也可以是渲染纹理(RT)。之前被绘制到目标上的东西依然存在,这可能会干扰我们现在渲染的图像。为了保证正确的渲染,我们必须清除渲染目标以去除其旧内容。这是通过调用命令缓冲区上的ClearRenderTarget来完成的,它在Setup方法中执行。

CommandBuffer.ClearRenderTarget函数至少需要三个参数。前两个参数表示是否清除深度缓冲和颜色缓冲数据,我们设置为true。第三个参数为清除后的颜色(可以理解为替换的颜色),我们选择Color.clear。

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

FrameDebugger现在显示了一个DrawGL入口用于清理行为,嵌套在RenderCamera的内部。之所以发生这种情况是因为ClearRenderTarget在示例中放置在开始采样之后,会被程序认为是开始采样之后的操作。我们可以在开始采样前,清除多余的嵌套内容。这样做的结果是两个相邻的"Render Camera"采样将可以合并。 

	void Setup()
	{
		buffer.ClearRenderTarget(true, true, Color.clear);

		buffer.BeginSample(bufferName);
		ExecuteBuffer();
		context.SetupCameraProperties(camera);
	}

Draw GL入口表示使用Hidden/InternalClear着色器来绘制全屏四边形,并写入渲染目标,但这不是清除目标的最有效方法。之所以使用这种方法,是因为我们在设置相机属性之前先进行了清理。如果我们交换这两个步骤的顺序,便可以更快速清除。

这里DrawGL是通过着色器渲染之后写入之后渲染目标,而clear方法是直接修改三个缓冲的值,很明显后者效率要更好一些,现在我们看到Clear(color+Z+stencil),这表示颜色,深度和模板缓冲区都被清除了。

2.5Culling

现在可以看见天空盒了,但是仍然看不见物体。我们只需要渲染在摄像机可视范围内的可视对象,而不是所有的对象。我们首先从场景中所有具有渲染组件的对象开始,然后剔除掉那些不在摄像机视椎体之内的对象。

要想弄清楚哪些是可以被剔除的,我们需要通过使用ScriptableCullingParameters结构体来跟踪多个摄像机的设置和矩阵。我们可以调用cameraTryGetCullingParameters函数来代替自己填充结构体的数据。这个函数将返回这些参数是否可以成功获取并返回,对于错误的摄像机参数它有可能返回失败。为了获取这个参数数据我们必须将它作为一个输出参数,通过在变量前面添加out关键字。我们在一个独立的Cull函数内执行这些操作,它将返回成功或者失败。

	bool Cull () {
		ScriptableCullingParameters p
		if (camera.TryGetCullingParameters(out p)) {
			return true;
		}
		return false;
	}

为什么我们需要写out关键字?

当一个结构体参数被定义为一个输出参数的时候它的行为像是一个对象引用,指向了该参数所在的内存堆栈上的位置。当方法改变这个参数的时候它将影响这个值对象,而不是创建一个拷贝。

out关键字告诉我们该方法负责正确设置参数,替换先前的值。

Try-get方法是一个普遍的方式来判断执行的成功或失败,并返回一个结果。

当用作输出参数时,可以在参数列表中内联变量声明,所以让我们这样做吧。

	bool Cull()
	{
		//ScriptableCullingParameters p
		if (camera.TryGetCullingParameters(out ScriptableCullingParameters p))
		{
			return true;
		}
		return false;
	}

Render函数中Setup函数之前执行Cull函数,如果执行函数失败将返回。

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

		if (!Cull())
		{
			return;
		}

		Setup();
		DrawVisibleGeometry();
		Submit();
	}

实际的剔除操作是通过执行contextCull函数来完成的,它会生成一个CullingResults的结构体。它将在Cull函数中执行如果成功会将结构保存在字段中。在这种情况下,我们必须将剔除参数作为引用参数传递,方法是在它前面写ref关键字。

	CullingResults cullingResults;

	…
	
	bool Cull () {
		if (camera.TryGetCullingParameters(out ScriptableCullingParameters p)) {
			cullingResults = context.Cull(ref p);
			return true;
		}
		return false;
	}

为什么我们必须使用ref

ref关键字的执行和out很像,不同的是方法中不需要为它初始化。谁调用该方法就需要提取对参数进行初始化。所有它一点可比被作为输入,同时也可以作为输出。

设个例子中ref被使用作为一个优化,避免了传参过程中ScriptableCullingParameters结构体的复制,这个结构体确实很大。它是一个结构体来代替一个对象时另一个优化,为了减少内存的分配。

2.6Drawing Geometry

一旦我们知道哪些物体可见,我们就可以继续渲染这些物体了。渲染这些物体需要使用剔除的结果作为参数,执行contextDrawRenderers函数来告诉管线哪些物体可以被渲染。除此之外,我们还必须提供绘制设置参数和过滤设置参数。它们都是结构体-DrawingSettingsFilteringSettings-我们将首先使用它们的默认构造函数,它们都需要通过引用进行传递。在DrawVisibleGeometry函数中绘制天空盒之前执行这些逻辑。

	void DrawVisibleGeometry()
	{
		var drawingSettings = new DrawingSettings();
		var filteringSettings = new FilteringSettings();

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

		context.DrawSkybox(camera);
	}

我们还是看不到任何东西,因为我们需要表明哪些类型的着色器过程(shader passes)是被允许渲染的。由于我们本节教程只支持无光照的着色器,因此我们需要获取SRPDefaultUnlit的着色器标记ID。我们可以只获取一次并将它缓存到静态字段中。

​
static ShaderTagId unlitShaderTagId = new ShaderTagId("SRPDefaultUnlit");

​

将它作为DrawingSettings构造函数的第一个参数,并创建一个SortingSettings作为第二个参数。把camera传给sortingsetting的构造函数,因为用camera来确定是使用正交排序还是使用基于距离的排序。

除此之外,我们还需要指定哪些渲染队列被允许渲染。传递RenderQueueRange.allFilteringSettings的构造函数,以便我们可以允许所有内容进行渲染。

最终代码:

	void DrawVisibleGeometry()
	{
		//决定物体绘制顺序是正交排序还是基于深度排序的配置
		var sortingSettings = new SortingSettings(camera)
		{
			criteria = SortingCriteria.CommonOpaque
		};
		//决定摄像机支持的Shader Pass和绘制顺序等的配置
		var drawingSettings = new DrawingSettings(unlitShaderTagId, sortingSettings);
		//决定过滤哪些Visible Objects的配置,包括支持的RenderQueue等
		var filteringSettings = new FilteringSettings(RenderQueueRange.all);
		//渲染CullingResults内的VisibleObjects
		context.DrawRenderers(cullingResults, ref drawingSettings, ref filteringSettings);
		//添加“绘制天空盒”指令,DrawSkybox为ScriptableRenderContext下已有函数,这里就体现了为什么说Unity已经帮我们封装好了很多我们要用到的函数,SPR的画笔~
		context.DrawSkybox(camera);
	}

绘制结果:

可以看到这个透明的球的效果还有一些奇怪

分析Frame Debugger又增加了一个RenderLoop.Draw,可以知道每调用一次context.DrawRenderers就是一次renderloop

这里如果调用两次绘制的话,会自动合并到RenderLoop.Draw里

如果在中间加入一次绘制天空盒的调用,就会产生两个renderLoop.draw

接下俩就是要正确绘制透明物体。

2.7Drawing Opaque and Transparent Geometry Separately

通过Frame Debugger 可以知道目前的渲染顺序是先绘制所有unlit物体然后绘制天空盒,但是unlit物体中的透明物体渲染的时候是不会写入深度的,因此天空盒会将透明物体覆盖。所以,正确的绘制顺序应该是先绘制不透明物体,然后绘制天空盒,最后绘制透明物体。

我们可以通过切换到RenderQueueRange.opaque来排除透明物体。

然后在绘制天空盒之后,我们再次调用DrawRenderers。但是在这之前先改变渲染范围为RenderQueueRange.transparent。也要修改排序标准为SortingCriteria.CommonTransparent然后设置给DrawingSettings。这将反转透明对象的绘制顺序。

这里修改了渲染顺序为从后往前,因为这样才能体现出前后关系

绘制结果:

最终代码:

	void DrawVisibleGeometry()
	{
		//决定物体绘制顺序是正交排序还是基于深度排序的配置
		var sortingSettings = new SortingSettings(camera)
		{
			criteria = SortingCriteria.CommonOpaque
		};
		//决定摄像机支持的Shader Pass和绘制顺序等的配置
		var drawingSettings = new DrawingSettings(unlitShaderTagId, sortingSettings);
		//决定过滤哪些Visible Objects的配置,包括支持的RenderQueue等
		var filteringSettings = new FilteringSettings(RenderQueueRange.opaque);
		//渲染CullingResults内不透明的VisibleObjects
		context.DrawRenderers(cullingResults, ref drawingSettings, ref filteringSettings);
		//添加“绘制天空盒”指令,DrawSkybox为ScriptableRenderContext下已有函数,这里就体现了为什么说Unity已经帮我们封装好了很多我们要用到的函数,SPR的画笔~
		context.DrawSkybox(camera);
		//渲染透明物体
		//设置绘制顺序为从后往前
		sortingSettings.criteria = SortingCriteria.CommonTransparent;
		//注意值类型
		drawingSettings.sortingSettings = sortingSettings;
		//过滤出RenderQueue属于Transparent的物体
		filteringSettings.renderQueueRange = RenderQueueRange.transparent;
		//绘制透明物体
		context.DrawRenderers(cullingResults, ref drawingSettings, ref filteringSettings);

	}

帧调试器结果:

可以看到是先绘制两个不透明物体,然后是天空盒,最后是三个不透明物体

目前我们已经可以渲染出所有无光照着色器的物体,但是使用其他着色器的物体并没有显示出来,下一节的目的就是将使用其他着色器的物体也显示出来。

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

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

相关文章

陆面、生态、水文模拟与多源遥感数据同化

原文链接&#xff1a;陆面、生态、水文模拟与多源遥感数据同化https://mp.weixin.qq.com/s?__bizMzUzNTczMDMxMg&mid2247601198&idx6&sn51b9b26b75c9df1f11dcb9a187878261&chksmfa820dc9cdf584df9ac3b997c767d63fef263d79d30238a6523db94f68aec621e1f91df85f6…

算法——字符串

T04BF &#x1f44b;热门专栏: 算法|JAVA|MySQL|C语言 &#x1faf5; 小比特 大梦想 此篇文章与大家分享字符串相关算法 如果有不足的或者错误的请您指出! 目录 1.最长公共前缀1.1解析1.2题解 2.最长回文子串2.1解析2.2题解 3.二级制求和3.1解析3.2题解 4.字符串相乘4.1解析4.2…

【环境变量】常见的环境变量 | 相关指令 | 环境变量系统程序的结合理解 | 环境变量表 | 本地变量环境变量 | 外部命令内建命令

目录 常见的环境变量 HOME PWD SHELL HISTSIZE 环境变量相关的指令 echo&env export unset 本地变量 环境变量整体理解 程序现象_代码查看环境变量 ​整体理解 环境变量表 环境变量表的传递 环境变量表的查看 内建命令 少说废话&#x1f197; 每个用…

大型网站系统架构演化

大型网站质量属性优先级&#xff1a;高性能 高可用 可维护 应变 安全 一、单体架构 应用程序&#xff0c;数据库&#xff0c;文件等所有资源都在一台服务器上。 二、垂直架构 应用和数据分离&#xff0c;使用三台服务器&#xff1a;应用服务器、文件服务器、数据服务器 应用服…

JavaEE 初阶篇-深入了解 CAS 机制与12种锁的特征(如乐观锁和悲观锁、轻量级锁与重量级锁、自旋锁与挂起等待锁、可重入锁与不可重入锁等等)

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 乐观锁与悲观锁概述 1.1 悲观锁&#xff08;Pessimistic Locking&#xff09; 1.2 乐观锁&#xff08;Optimistic Locking&#xff09; 1.3 区别与适用场景 2.0 轻…

我企业的业务需要制作企业网站吗?11个支持的理由以及5个反对的理由!

如果你的企业经营得还不错&#xff0c;你可能会找出很多理由&#xff0c;说明为什么一个高效的网站对你来说并不那么重要。确实&#xff0c;你明白企业需要在互联网上有一定的存在感&#xff0c;但你可能并不认为一个高效的网站会对你的特定业务产生太大的影响——尤其是当你已…

实战纪实 | 编辑器漏洞之Ueditor-任意文件上传漏洞 (老洞新谈)

UEditor 任意文件上传漏洞 前言 前段时间在做某政府单位的项目的时候发现存在该漏洞&#xff0c;虽然是一个老洞&#xff0c;但这也是容易被忽视&#xff0c;且能快速拿到shell的漏洞&#xff0c;在利用方式上有一些不一样的心得&#xff0c;希望能帮助到一些还不太了解的小伙…

PCIe总线-存储器域和PCIe总线域访问流程(二)

1.概述 PCIe总线的最大特点是像CPU访问DDR一样&#xff0c;可以直接使用地址访问PCIe设备&#xff08;桥&#xff09;&#xff0c;但不同的是DDR和CPU同属于存储器域&#xff0c;而CPU和PCIe设备属于两个不同的域&#xff0c;PCIe设备&#xff08;桥&#xff09;的地址空间属于…

[RK3399 Linux] 使用busybox 1.36.1制作rootfs

一、 编译、安装、配置 busybox 1.1 下载源码 根文件系统是根据busybox来制作的。 下载地址:https://busybox.net/downloads/。 这里就以1.36.1版本为例进行编译安装介绍: 注意:编译linux内核与文件系统中的所有程序要使用相同的交叉编译器。 下载完成后解压: mkdir …

03 SQL基础 -- 查询与运算符

一、SELECT 语句基础 1.1 从表中选取数据 SELECT 语句 从表中选取数据时需要使用SELECT语句,也就是只从表中选出(SELECT)必要数据的意思。通过SELECT语句查询并选取出必要数据的过程称为匹配查询或查询(query) 基本SELECT语句包含了SELECT和FROM两个子句(clause)。示…

NAT实验

要求&#xff1a; 1、AR2为ISP路由器&#xff0c;其上只能配置IP地址&#xff0c;不得再进行其他的任何配置 2、PC1-PC2可以ping通客户平板和DNS服务器&#xff1b; 3、客户端可以通过域名访问http1&#xff0c;通过地址访问http2 4、R1为边界路由器&#xff0c;其上只有一…

计算机视觉工程师

为进一步贯彻落实中共中央印发《关于深化人才发展体制机制改革的意见》和国务院印发《关于“十四五”数字经济发展规划》等有关工作的部署要求&#xff0c;深入实施人才强国战略和创新驱动发展战略&#xff0c;加强全国数字化人才队伍建设&#xff0c;持续推进人工智能从业人员…

【深度学习】深度学习md笔记总结第4篇:TensorFlow介绍,学习目标【附代码文档】

深度学习笔记完整教程&#xff08;附代码资料&#xff09;主要内容讲述&#xff1a;深度学习课程&#xff0c;深度学习介绍要求,目标,学习目标,1.1.1 区别,学习目标,学习目标。TensorFlow介绍&#xff0c;2.4 张量学习目标,2.4.1 张量(Tensor),2.4.2 创建张量的指令,2.4.3 张量…

C语言世界上最详细自定义类型:联合和枚举

前言&#xff1a; hello! 大家好&#xff0c;我是小陈&#xff0c;今天给大家带来一篇联合和枚举的博客&#xff01;&#xff01;&#xff01; 1.联合体类型的声明 像结构体⼀样&#xff0c;联合体也是由⼀个或者多个成员构成&#xff0c;这些成员可以不同的类型。 但是编译…

安装达梦(DM8)数据库(图形化安装)

一、配置DM8数据库系统环境 在CentOS7系统环境安装DM8&#xff08;达梦&#xff09;数据库前的准备。&#xff08;注意&#xff1a;安装前必须创建 dmdba 用户&#xff0c;禁止使用 root 用户安装数据库。&#xff09; 1、新建用户 运行SecureCRT工具&#xff0c;root登录16…

域控软件安全隔离关键技术剖析:MCU域 VS SOC域

安全隔离的需求 功能安全开发中&#xff0c;软件阶段由软件V模型左边的软件安全需求SSR开始。SSR是从技术安全需求TSR中提取出软件的功能安全需求&#xff0c;大多数情况下具有不同的ASIL等级。 图1 功能安全软件开发V模型 随后&#xff0c;软件安全需求会被分配到软件架构中的…

利用SARscape对日本填海造陆和天然气开采进行地表形变监测

日本千叶市&#xff0c;是日本南部重要的工业港市。位于西部的浦安市是一个典型的"填海造田"城市&#xff0c;东南部的东金区有一片天然气开采区域&#xff0c;本文利用SARscape&#xff0c;用干涉叠加的方法&#xff0c;即PS和SBAS&#xff0c;对这两个区域进行地表…

36-代码测试(上):如何编写Go语言单元测试和性能测试用例?

每种语言通常都有自己的测试包/模块&#xff0c;Go语言也不例外。在Go中&#xff0c;我们可以通过testing包对代码进行单元测试和性能测试。 如何测试 Go 代码&#xff1f; Go语言有自带的测试框架testing&#xff0c;可以用来实现单元测试&#xff08;T类型&#xff09;和性…

Point-Nerf复现及解析

Point-Nerf复现及解析 鸣谢&#xff1a;同组的李xx师兄博士(交流思路)、辰昶仪器的狗哥等人&#xff08;帮忙down资源&#xff09; 0.0我自己的复现工程0.1相关库介绍0.1.1 pytorch0.1.2 h5py0.1.3 Scikit-Image0.1.4 imageio0.1.5 scipy0.1.6 Matplotlib0.1.7 fonttools 0.2…

JAVA的学习日记DAY6

文章目录 数组例子数组的使用数组的注意事项和细节练习数组赋值机制数组拷贝数组反转数组添加 排序冒泡排序 查找多维数组 - 二维数组二维数组的使用二维数组的遍历杨辉三角二维数组的使用细节和注意事项练习 开始每日一更&#xff01;得加快速度了&#xff01; 数组 数组可以…