当前位置: 首页 > news >正文

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 pif (camera.TryGetCullingParameters(out p)) {return true;}return false;}

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

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

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

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

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

	bool Cull(){//ScriptableCullingParameters pif (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内的VisibleObjectscontext.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内不透明的VisibleObjectscontext.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);}

帧调试器结果:

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

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

相关文章:

Learn SRP 01

学习链接&#xff1a;Custom Render Pipeline (catlikecoding.com) 使用Unity版本&#xff1a;Unity 2022.3.5f1 1.A new Render Pipeline 1.1Project Setup 创建一个默认的3D项目&#xff0c;项目打开后可以到默认的包管理器删掉所有不需要的包&#xff0c;我们只使用Unit…...

NL2SQL进阶系列(4):ConvAI、DIN-SQL、C3-浙大、DAIL-SQL-阿里等16个业界开源应用实践详解[Text2SQL]

NL2SQL进阶系列(4)&#xff1a;ConvAI、DIN-SQL等16个业界开源应用实践详解[Text2SQL] NL2SQL基础系列(1)&#xff1a;业界顶尖排行榜、权威测评数据集及LLM大模型&#xff08;Spider vs BIRD&#xff09;全面对比优劣分析[Text2SQL、Text2DSL] NL2SQL基础系列(2)&#xff1a…...

Python统计分析库之statsmodels使用详解

概要 Python statsmodels是一个强大的统计分析库,提供了丰富的统计模型和数据处理功能,可用于数据分析、预测建模等多个领域。本文将介绍statsmodels库的安装、特性、基本功能、高级功能、实际应用场景等方面。 安装 安装statsmodels库非常简单,可以使用pip命令进行安装:…...

A Learning-Based Approach for IP Geolocation(2010年)

下载地址:Towards IP geolocation using delay and topology measurements | Proceedings of the 6th ACM SIGCOMM conference on Internet measurement 被引次数:185 Eriksson B, Barford P, Sommers J, et al. A learning-based approach for IP geolocation[C]//Passive …...

高创新 | [24年新算法]NRBO-XGBoost回归+交叉验证基于牛顿拉夫逊优化算法-XGBoost多变量回归预测

高创新 | [24年新算法]NRBO-XGBoost回归交叉验证基于牛顿拉夫逊优化算法-XGBoost多变量回归预测 目录 高创新 | [24年新算法]NRBO-XGBoost回归交叉验证基于牛顿拉夫逊优化算法-XGBoost多变量回归预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 1.Matlab实现 [24年新算…...

Web APP设计:将多个相近的机器学习模型融合到一个Web APP中

将多个相近的机器学习模型融合到一个Web APP中 FUSE-ML是一个用于预测腰椎融合术后效果的APP&#xff0c;它可以做出三个不同的结论&#xff0c;分别评价术后的腰痛、腿痛和日常功能是否提高。 这估计是部署了三个机器学习模型在这个APP中&#xff0c;因为一个机器学习模型仅…...

网络爬虫:定义、应用及法律道德考量

网络爬虫技术在当今数据驱动的世界中发挥着重要作用。本文将从网络爬虫的定义和主要功能&#xff0c;其在业界的应用实例&#xff0c;以及涉及的法律和道德问题三个方面进行深入探讨。 1. 爬虫的定义和主要功能 网络爬虫&#xff0c;也称为网页爬虫或蜘蛛&#xff0c;是一种…...

(三)ffmpeg 解码流程以及函数介绍

一、视频解码流程 二、函数介绍 1.avformat_network_init 函数作用&#xff1a; 执行网络库的全局初始化。这是可选的&#xff0c;不再推荐。 此函数仅用于解决旧GnuTLS或OpenSSL库的线程安全问题。如果libavformat链接到这些库的较新版本&#xff0c;或者不使用它们&#…...

go work模块与go mod包管理是的注意事项

如下图所示目录结构 cmd中是服务的包&#xff0c;显然auth,dbtables,pkg都是为cmd服务的。 首先需要需要将auth,dbtables,pkg定义到go.work中&#xff0c;如下&#xff1a; 在这样在各个单独的go mod管理的模块就可以互相调用了。一般情况下这些都是IDE自动进行的&#xff0c;…...

华为OD-C卷-最长子字符串的长度(一)[100分]

题目描述 给你一个字符串 s,首尾相连成一个环形,请你在环中找出 o 字符出现了偶数次最长子字符串的长度。 输入描述 输入是一个小写字母组成的字符串 输出描述 输出是一个整数 备注 1 ≤ s.length ≤ 500000s 只包含小写英文字母用例1 输入 alolobo输出 6说明 最长子字…...

实战小项目 | ESP32-S3和ESP32-C3通过ESP-Mesh-Lite组网 温湿度传感器案例

传统Wi-Fi网络所有终端设备都需要直接与路由器相连&#xff0c;这使得Wi-Fi的覆盖区域受到路由器位置的限制&#xff0c;可接入终端设备的数量也受到路由器容量的限制。而乐鑫ESP-Mesh-Lite Wi-Fi组网方案&#xff0c;所有终端设备都可以与相邻设备连接&#xff0c;摆脱了对路由…...

SiLM5350系列带米勒钳位的单通道隔离驱动器 助力汽车与工业应用实现稳定与高效的解决方案

带米勒钳位的隔离驱动SiLM5350系列 单通道 30V&#xff0c;10A 带米勒钳位的隔离驱动 具有驱动电流更大、传输延时更低、抗干扰能力更强、封装体积更小等优势, 为提高电源转换效率、安全性和可靠性提供理想之选。 SiLM5350系列产品描述&#xff1a; SiLM5350系列是单通道隔离驱…...

c#中怎么自动下载软件

以下是一个简单的 C# 示例&#xff0c;演示如何使用 WebClient 类下载软件&#xff1a; using System; using System.Net; class Program { static void Main(string[] args) { // 要下载的文件 URL string fileUrl "https://example.com/path/to/file"; // 本地保…...

Unity笔记之下拉刷新列表

这样的效果&#xff1b; 代码&#xff1a; using System; using System.Collections; using System.Collections.Generic; using Sirenix.OdinInspector; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.UI;public class ScrollRectUpdateView : Mon…...

防火墙操作!

当小编在Linux服务器上部署好程序以后&#xff0c;但是输入URL出现下述情况&#xff0c;原来是防火墙的原因&#xff01;&#xff01; 下面是一些防火墙操作&#xff01; 为保证系统安全&#xff0c;服务器的防火墙不建议关闭&#xff01;&#xff01; 但是&#xff0c;我们可…...

代码随想录算法训练营第四十一天| 343. 整数拆分,96.不同的二叉搜索树

题目与题解 343. 整数拆分 题目链接&#xff1a;343. 整数拆分 代码随想录题解&#xff1a;343. 整数拆分 视频讲解&#xff1a;动态规划&#xff0c;本题关键在于理解递推公式&#xff01;| LeetCode&#xff1a;343. 整数拆分_哔哩哔哩_bilibili 解题思路&#xff1a; 一眼懵…...

【MATLAB源码-第53期】m代码基于粒子群算法(PSO)的三维路径规划,显示最优路径和适应度曲线。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 粒子群算法&#xff08;Particle Swarm Optimization&#xff0c;简称PSO&#xff09;是一种模拟鸟群觅食行为的启发式优化方法。以下是其详细描述&#xff1a; 基本思想&#xff1a; 鸟群在寻找食物时&#xff0c;每只鸟都…...

el-table多行合并

背景 前端统计列表&#xff0c;数据乱序。按日期、产品、阶段、DD项&#xff08;所有header名称乱写&#xff09;排序&#xff0c;列表如下。 示例 日期产品阶段DDEEFFGG20240414产品1阶段1场景1A01场景2B01其他A0120240410产品1阶段1场景2B01其他A0120240402产品2阶段1场景3…...

Vue3 + Element-Plus 使用 Table 插槽时数据未及时更新

Vue3 Element-Plus 使用 Table 插槽时数据未及时更新 问题重现解决方法最终效果 问题重现 这里我已经通过二级分类 id 查询到一级分类和二级分类&#xff0c;但是使用插槽和 v-for 渲染出来还是之前的分类 id&#xff0c;但是一点击表格或者保存代码他又能正常刷新出来。 <…...

vue 2 怎么把2024-04-13T17:42:19转换成短日期格式

我们在日常开发过程中&#xff0c;通常会将日期格式在entity中设置成LocalDateTime。这样就有一个麻烦&#xff0c;我们在前端展示这个日期的时候就会变成2024-04-13T17:42:19。这显然不是我们所要的效果&#xff0c;所以我们今天来解决这个问题&#xff0c;让前端展示正确的日…...

XCTF-web-easyupload

试了试php&#xff0c;php7&#xff0c;pht&#xff0c;phtml等&#xff0c;都没有用 尝试.user.ini 抓包修改将.user.ini修改为jpg图片 在上传一个123.jpg 用蚁剑连接&#xff0c;得到flag...

地震勘探——干扰波识别、井中地震时距曲线特点

目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波&#xff1a;可以用来解决所提出的地质任务的波&#xff1b;干扰波&#xff1a;所有妨碍辨认、追踪有效波的其他波。 地震勘探中&#xff0c;有效波和干扰波是相对的。例如&#xff0c;在反射波…...

深入剖析AI大模型:大模型时代的 Prompt 工程全解析

今天聊的内容&#xff0c;我认为是AI开发里面非常重要的内容。它在AI开发里无处不在&#xff0c;当你对 AI 助手说 "用李白的风格写一首关于人工智能的诗"&#xff0c;或者让翻译模型 "将这段合同翻译成商务日语" 时&#xff0c;输入的这句话就是 Prompt。…...

基于ASP.NET+ SQL Server实现(Web)医院信息管理系统

医院信息管理系统 1. 课程设计内容 在 visual studio 2017 平台上&#xff0c;开发一个“医院信息管理系统”Web 程序。 2. 课程设计目的 综合运用 c#.net 知识&#xff0c;在 vs 2017 平台上&#xff0c;进行 ASP.NET 应用程序和简易网站的开发&#xff1b;初步熟悉开发一…...

Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例

使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件&#xff0c;常用于在两个集合之间进行数据转移&#xff0c;如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model&#xff1a;绑定右侧列表的值&…...

QMC5883L的驱动

简介 本篇文章的代码已经上传到了github上面&#xff0c;开源代码 作为一个电子罗盘模块&#xff0c;我们可以通过I2C从中获取偏航角yaw&#xff0c;相对于六轴陀螺仪的yaw&#xff0c;qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...

无法与IP建立连接,未能下载VSCode服务器

如题&#xff0c;在远程连接服务器的时候突然遇到了这个提示。 查阅了一圈&#xff0c;发现是VSCode版本自动更新惹的祸&#xff01;&#xff01;&#xff01; 在VSCode的帮助->关于这里发现前几天VSCode自动更新了&#xff0c;我的版本号变成了1.100.3 才导致了远程连接出…...

前端导出带有合并单元格的列表

// 导出async function exportExcel(fileName "共识调整.xlsx") {// 所有数据const exportData await getAllMainData();// 表头内容let fitstTitleList [];const secondTitleList [];allColumns.value.forEach(column > {if (!column.children) {fitstTitleL…...

基础测试工具使用经验

背景 vtune&#xff0c;perf, nsight system等基础测试工具&#xff0c;都是用过的&#xff0c;但是没有记录&#xff0c;都逐渐忘了。所以写这篇博客总结记录一下&#xff0c;只要以后发现新的用法&#xff0c;就记得来编辑补充一下 perf 比较基础的用法&#xff1a; 先改这…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...