3.72 Command Buffer及URP概述

一、Command Buffer

1.概念

CommandBuffer携带一系列的渲染命令,依赖相机,用来拓展渲染管线的渲染效果。而且可以指定在相机渲染的某个点执行本身的拓展渲染。Command buffers也可以结合屏幕后期效果使用。

简单来说就是可以在渲染流程中插入一些自定义操作,开发者得到了更多的权限。

2.渲染流程图

左图绿点处就是可以让开发者进行操作的地方,右图蓝色是蓝色线框部分。

3.怎么创建?如何启用?

1.在unity里首先创建一个C#脚本和一个Shader脚本还有一个默认材质球。

2.将材质赋给场景中的测试物体。

3.进行脚本编写

C#脚本

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;[ExecuteInEditMode]
public class CommandBufferTest01 : MonoBehaviour
{public Shader shader;public Renderer render;public CameraEvent cameraEvent;private void OnEnable(){CommandBuffer tBuffer = new CommandBuffer();//设置渲染tBuffer.DrawRenderer(render, new Material(shader));//不透明物体渲染完后执行Camera.main.AddCommandBuffer(cameraEvent,tBuffer);}private void OnDisable(){Camera.main.RemoveAllCommandBuffers();}
}

Shader脚本:

shader脚本只改下颜色即可,后面作比对用。

然后就可以看到效果:

相机在渲染完不透明物体后又渲染了一遍我们自己添加Buffer。导致Game窗口下物体为Shader中返回的颜色。

camera中也可以看到所添加的Command Buffer。

这样一个最简单的CommandBuffer应用就搞定了。

4.分析、实现Unity官方CommandBuffer Demo效果

官方Demo效果:

自己实现:

步骤:

1.先渲染不透明物体

2.抓取屏幕

3.渲染玻璃材质

4.合成

官方代码:

CommandBufferBlurRefraction.cs

using UnityEngine;
using UnityEngine.Rendering;
using System.Collections.Generic;[ExecuteInEditMode]
public class CommandBufferBlurRefraction : MonoBehaviour
{public Shader m_BlurShader;private Material m_Material;private Camera m_Cam;// We'll want to add a command buffer on any camera that renders us,// so have a dictionary of them.private Dictionary<Camera,CommandBuffer> m_Cameras = new Dictionary<Camera,CommandBuffer>();// Remove command buffers from all cameras we added intoprivate void Cleanup(){foreach (var cam in m_Cameras){if (cam.Key){cam.Key.RemoveCommandBuffer (CameraEvent.AfterSkybox, cam.Value);}}m_Cameras.Clear();Object.DestroyImmediate (m_Material);}public void OnEnable(){Cleanup();}public void OnDisable(){Cleanup();}// Whenever any camera will render us, add a command buffer to do the work on itpublic void OnWillRenderObject(){var act = gameObject.activeInHierarchy && enabled;if (!act){Cleanup();return;}var cam = Camera.current;if (!cam)return;CommandBuffer buf = null;// Did we already add the command buffer on this camera? Nothing to do then.if (m_Cameras.ContainsKey(cam))return;if (!m_Material){m_Material = new Material(m_BlurShader);m_Material.hideFlags = HideFlags.HideAndDontSave;}buf = new CommandBuffer();buf.name = "Grab screen and blur";m_Cameras[cam] = buf;// copy screen into temporary RT//获取渲染纹理IDint screenCopyID = Shader.PropertyToID("_ScreenCopyTexture");//用screenCopyID标识,获取一个模板纹理。buf.GetTemporaryRT (screenCopyID, -1, -1, 0, FilterMode.Bilinear);//BuiltinRenderTextureType.CurrentActive也就是当前屏幕的纹理标识,将其赋值进screenCopyID标识的纹理。buf.Blit (BuiltinRenderTextureType.CurrentActive, screenCopyID);// get two smaller RTs//获取2个模板纹理,后面将它们2的效果轮流混合。int blurredID = Shader.PropertyToID("_Temp1");int blurredID2 = Shader.PropertyToID("_Temp2");buf.GetTemporaryRT (blurredID, -2, -2, 0, FilterMode.Bilinear);buf.GetTemporaryRT (blurredID2, -2, -2, 0, FilterMode.Bilinear);// downsample screen copy into smaller RT, release screen RT//将抓屏纹理_ScreenCopyTexture采样填充到_Temp1后释放。buf.Blit (screenCopyID, blurredID);buf.ReleaseTemporaryRT (screenCopyID);// horizontal blur//修改SeparableBlur.shader的全局参数offsets,使其产生各个方向的模糊效果来叠加增强效果。//(如果着色器不在 Properties模块中暴露某个参数,将使用全局属性)buf.SetGlobalVector("offsets", new Vector4(2.0f/Screen.width,0,0,0));//使用m_Material渲染blurredID结果赋值给blurredID2。buf.Blit (blurredID, blurredID2, m_Material);// vertical blurbuf.SetGlobalVector("offsets", new Vector4(0,2.0f/Screen.height,0,0));buf.Blit (blurredID2, blurredID, m_Material);// horizontal blurbuf.SetGlobalVector("offsets", new Vector4(4.0f/Screen.width,0,0,0));buf.Blit (blurredID, blurredID2, m_Material);// vertical blurbuf.SetGlobalVector("offsets", new Vector4(0,4.0f/Screen.height,0,0));buf.Blit (blurredID2, blurredID, m_Material);//得到透明效果纹理后,赋值给GlassWithoutGrab.shader的_GrabBlurTexture混合得出模糊透明。buf.SetGlobalTexture("_GrabBlurTexture", blurredID);cam.AddCommandBuffer (CameraEvent.AfterSkybox, buf);}	
}

SeparableBlur.shader

// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'Shader "Hidden/SeparableGlassBlur" {Properties {_MainTex ("Base (RGB)", 2D) = "" {}}CGINCLUDE#include "UnityCG.cginc"struct v2f {float4 pos : POSITION;float2 uv : TEXCOORD0;float4 uv01 : TEXCOORD1;float4 uv23 : TEXCOORD2;float4 uv45 : TEXCOORD3;};float4 offsets;sampler2D _MainTex;//偏移定点uv,使其读取到周围颜色值进行混合。v2f vert (appdata_img v) {v2f o;o.pos = UnityObjectToClipPos(v.vertex);o.uv.xy = v.texcoord.xy;o.uv01 =  v.texcoord.xyxy + offsets.xyxy * float4(1,1, -1,-1);o.uv23 =  v.texcoord.xyxy + offsets.xyxy * float4(1,1, -1,-1) * 2.0;o.uv45 =  v.texcoord.xyxy + offsets.xyxy * float4(1,1, -1,-1) * 3.0;return o;}//混合颜色值。half4 frag (v2f i) : COLOR {half4 color = float4 (0,0,0,0);color += 0.40 * tex2D (_MainTex, i.uv);color += 0.15 * tex2D (_MainTex, i.uv01.xy);color += 0.15 * tex2D (_MainTex, i.uv01.zw);color += 0.10 * tex2D (_MainTex, i.uv23.xy);color += 0.10 * tex2D (_MainTex, i.uv23.zw);color += 0.05 * tex2D (_MainTex, i.uv45.xy);color += 0.05 * tex2D (_MainTex, i.uv45.zw);return color;}ENDCGSubshader {Pass {ZTest Always Cull Off ZWrite OffFog { Mode off }CGPROGRAM#pragma fragmentoption ARB_precision_hint_fastest#pragma vertex vert#pragma fragment fragENDCG}
}Fallback off

GlassWithoutGrab.shader

// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'// Similar to regular FX/Glass/Stained BumpDistort shader
// from standard Effects package, just without grab pass,
// and samples a texture with a different name.Shader "FX/Glass/Stained BumpDistort (no grab)" {
Properties {_BumpAmt  ("Distortion", range (0,64)) = 10_TintAmt ("Tint Amount", Range(0,1)) = 0.1_MainTex ("Tint Color (RGB)", 2D) = "white" {}_BumpMap ("Normalmap", 2D) = "bump" {}
}Category {// We must be transparent, so other objects are drawn before this one.Tags { "Queue"="Transparent" "RenderType"="Opaque" }SubShader {Pass {Name "BASE"Tags { "LightMode" = "Always" }CGPROGRAM#pragma vertex vert#pragma fragment frag#pragma multi_compile_fog#include "UnityCG.cginc"struct appdata_t {float4 vertex : POSITION;float2 texcoord: TEXCOORD0;};struct v2f {float4 vertex : POSITION;float4 uvgrab : TEXCOORD0;float2 uvbump : TEXCOORD1;float2 uvmain : TEXCOORD2;UNITY_FOG_COORDS(3)};float _BumpAmt;half _TintAmt;float4 _BumpMap_ST;float4 _MainTex_ST;v2f vert (appdata_t v){v2f o;o.vertex = UnityObjectToClipPos(v.vertex);#if UNITY_UV_STARTS_AT_TOPfloat scale = -1.0;#elsefloat scale = 1.0;#endif//将顶点裁剪空间下的坐标,计算NDC空间上的位置后,映射到模糊纹理的uv。o.uvgrab.xy = (float2(o.vertex.x, o.vertex.y*scale) + o.vertex.w) * 0.5;o.uvgrab.zw = o.vertex.zw;o.uvbump = TRANSFORM_TEX( v.texcoord, _BumpMap );o.uvmain = TRANSFORM_TEX( v.texcoord, _MainTex );UNITY_TRANSFER_FOG(o,o.vertex);return o;}//抓屏得到的模糊纹理会赋值到此。(因为Properties没有定义_GrabBlurTexture,所以默认使用全局的参数)sampler2D _GrabBlurTexture;float4 _GrabBlurTexture_TexelSize;sampler2D _BumpMap;sampler2D _MainTex;half4 frag (v2f i) : SV_Target{// calculate perturbed coordinates// we could optimize this by just reading the x & y without reconstructing the Zhalf2 bump = UnpackNormal(tex2D( _BumpMap, i.uvbump )).rg;float2 offset = bump * _BumpAmt * _GrabBlurTexture_TexelSize.xy;i.uvgrab.xy = offset * i.uvgrab.z + i.uvgrab.xy;//对模糊纹理采样half4 col = tex2Dproj (_GrabBlurTexture, UNITY_PROJ_COORD(i.uvgrab));half4 tint = tex2D(_MainTex, i.uvmain);//模糊纹理与主纹理混合得出模糊透明效果col = lerp (col, tint, _TintAmt);UNITY_APPLY_FOG(i.fogCoord, col);return col;}ENDCG}}}}

注意点:

虽然Shader.PropertyToID返回的是一个int类型,但是Blit操作用到的ID会强转成RenderTargetIdentifier,以供Blit需要。

在其源码中可以看到

5.Command Buffer相关代码结构图梳理

经过1、2、3步的操作后就应该对Command Buffer有个大致的了解,下面对Comman Buffer相关的代码进行一个梳理,只列举一些关键、常用的变量和函数。

关系图

5.1一些概念补充

先知道一些基础的概念,然后再继续深入。

  • RenderTarget
    • 在3D计算机图形领域,渲染目标是现代图形处理单元(GPU)的一个特征,它允许将3D场景渲染到中间存储缓冲区或渲染目标纹理(RTT),而不是帧缓冲区或后缓冲区。然后可以通过像素着色器操纵此RTT ,以便在显示最终图像之前将其他效果应用于最终图像。
    • 渲染目标就是一个缓冲区,(除了back frame buffer 和screen frame buffer之外的缓冲区)用来记录渲染后的输出的结果,而不直接将帧缓冲绘制到屏幕。而是将其应用到别处。
      离屏渲染,就可以通过渲染目标实现的。
  • RenderTexture
    • 渲染纹理是指可以被渲染的纹理。
    • RT(RenderTarget)可以用来实现基于图像的渲染效果,动态阴影,反射,监控相机【cs1.6里的assault仓库】。
    • 渲染纹理的经典用法是将RenderTexture设置为相机的“TargetTexture”(Camera.targetTexture),这将会使得相机渲染的结果渲染到RenderTexture上而不是直接渲染到屏幕上。
    • 需要记住的是,渲染纹理的像素内容可能会在某些事件中丢失,比如:加载新关卡,屏幕进入保护模式,全屏进入,当这种情况发生时,你的RenderTexture将会再次变成“尚未创建”,遇到这种情况需要用IsCreate函数进行检测。
    • 和其他“原生引擎对象”的类型一样,需要关注RenderTexture的生命周期,并在使用完成之后使用Release函数进行释放,因为RenderTexture不会像普通托管类型那样会被GC(垃圾回收)。
  • RenderTargetIdentifiler
    • 这个结构体主要描述,绑定的RenderTexture如何应用到RenderTarget上,使用哪个Mip,使用哪个CubeMap的Face。
    • 对于Texture3D/Texture2DArray,还能够指定使用哪个depthSlice进行写入。

5.2Command Buffer 常用函数

变量、函数名

作用

示例图

CommandBuffer.BeginSample

CommandBuffer.EndSample

将对应的Pass框选出来,方便在Profile里进行分析。但是在URP的源码里经常会出现其他的写法,使用using(newProfilingScope(CommandBuffer cmd,ProfilingSampler sampler))

CommandBuffer.GetTemporayRT

申请一张临时的RT,配合RenderIdentifier使用

CommandBuffer.SetRenderTarget

设置渲染目标,添加一个激活渲染目标的指令到CommandBuffer中。

指定一张RenderTexture作为RenderTarget可以使用好几种方法:

1.直接使用RenderTexture

2.GetTemporaryRT申请的RT的Id(int)类型

3.在渲染周期内生成的内置(Built-In)RenderTexture[BuiltinRenderTextureType]

这些全部都会被隐式转换成RenderTargetIdentifier这个结构体。

RenderBufferLoadAction

Load:当RenderBuffer被激活时,载入原本已保存的内容,这个加载读取的操作对Tile base架构的GPU比较占用带宽。

Clear:当RenderBuffer被激活时,会清理掉原本Buffer里的内容。目前不能直接使用,需要使用CommandBuffer.CleanRenderTarget作为代替。

DontCare:当RenderBuffer被激活时,GPU不会把RenderBuffer的内容加载进内存,从而减少带宽传输压力。

unity CommandBuffer.SetTarget()几个重载都是用的Load

RenderBufferStoreAction

跟RenderBufferLoadAction类似,但这个枚举类型参数主要是控制RenderBuffer存储时的策略。

Store:RenderBuffer的内容需要存储到RAM中。如果启用了MSAA,这是就会存储未Resolve(non-resolved)的着色结果。

Resolve:GPU将多重采样数据(Multisampled data)重新解析(resolve)为每像素一个采样点,并将数据存储到RenderTexture中,丢弃了多采样数据)。目前不能直接设置,只能调用API进行Resolve

StoreAndResolve: 将多重采样数据(Multisampled data)重新解析(resolve)为每像素一个采样点,并将数据存储到RenderTexture中,并且存储多重采样数据。目前不能直接使用,只能调用API进行Resolve。

DontCare:RenderBuffer的内容是不需要的,可以被丢弃。Tile-base架构的GPU将完全跳过存储阶段,有次减少带宽压力,提高性能。[RenderTextureMemoryless需要设置对象的模式]

unity CommandBuffer.SetTarget()几个重载都是用的Resolve

RenderTargetBinding

把一个或者多个ColorBuffer以及一个深度/模板缓冲[8/16/24/32bit]如何绑定到RenderTarget上,并且他们各自Load和Store的策略。

CommandBuffer.DrawMesh

mesh:绘制使用的网格

matrix:绘制时使用的模型空间矩阵(M)

material:绘制时使用的材质

submeshindex:绘制的子网格的索引值

shaderPass:Material对应的shader使用Pass的索引值,默认为-1,渲染所有Pass

properties:MaterialPropertyBlock不需要额外创建材质实例就可以覆写当前绘制时的材质属性值。

CommandBuffer.DrawMeshInstanced

使用实例化绘制多个相同Mesh,看到matrices和count这两个参数,说明绘制时需要提供模型空间矩阵(M)数组,以及绘制的数量。

需要注意的是:绘制时的Material对应的shader一定得是支持Instance绘制。

CommandBuffer.

DrawMeshInstanceProcedural

绘制Instance时的InstancePath是Instance Procedural。

跟之前的DrawMeshInstanced相比,区别就是,不支持LightMap和LightProbe需要手动在Procedural的函数实现,从Instance和CBuffer中通过InstanceID获取每个实例所对应的的LightMapST,LightMapIndex,LightProbe的SH,Occlusion数值。

CommandBuffer.

DrawMeshInstanceIndirect

绘制Instance时,除了需要提供绘制Instance的Mesh之外,还需要通过MaterialPropertyBlock设置绘制时所需要用到的Compute Shader计算的数据。

另外需要用BufferArgs控制绘制的顶点数以及实例数量,绘制时开始的Index位置,开始绘制时的Instance位置。

可以看到BufferArgs的可控性更强了,但是一般而言是会配合着Compute Shader进行控制BufferArgs的数值,然后再通过BufferArgs绘制Instance。

一般来说,DrawMeshInstanceIndirect会用来制作视锥裁剪的四叉树地形,草地等等。

CommandBuffer.DrawProcedural

选择绘制的拓扑结构进行绘制Mesh,不需要用vertex/index buffer,用Compute shader生成顶点数据,然后再调用DrawProcedural的时候用Vertex Shader对顶点数据进行描绘。

需要注意的是Compute Shader的计算不属于图元装配阶段。

Compute Shader - OpenGL Wiki

Compute Shader介绍(一)

CommandBuffer.

DrawProceduralIndirect

不需要用vertex/index buffer,同时也和

DrawMeshInstanceIndirect一样,需要提供BufferArgs控制实例化的过程参数。

在Shader里也同样能够使用InstanceID。

CommandBuffer.DrawRenderer

最为常用的用法还是写在Gameloop里用脚本获取常经理的Renderer,在管线里new一个Renderer来渲染貌似不是一个好主意。

CommandBuffer.Blit

在实际使用的时候,为了以防万一尽量使用CommandBuffer.SetGlobalTexture配合Shader.PropertyToID设置参数。

另外,最好还是得手动SetRenderTarget设置下LoadAction以及StoreAction节省带宽

5.3代码结构图

--------待补充--------


二、自定义渲染管线(Scriptobject Renderer Pipeline)

此处开始全是在URP下进行(我用的版本Unity2020.3f1c1 URP Linear空间)

1.Unity URP自带的渲染管线

1.1流程梳理

Edit->Project Setting->Quality

这个UniversalRP-HighQuality就是我们以后要替换的URPAsset。

点击这个Asset可以在Inspector面板上看到RendererList。

点击ForwardRenderer(Forward Renderer Data)。

Renderer Feature就是后面我们自定义的东西,上面的Post Process Data也是可以自定义的,先不管。

1.2关系图

1.3代码结构图

--------待补充--------

2.自定义渲染管线

Unity自带的渲染管线创建如下图。

点击Pipeine Asset(Forward Renderer),会创建两个默认的配置文件,就是上面流程梳理里面的两个文件。

不管这两个文件,下面开始创建自己的渲染管线。

2.1创建渲染管线实例

首先创建两个脚本分别叫做CustomRenderPipline和CustomRenderPiplineAsset。

分别继承RenderPipeline和RenderPipelineAsset如下。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;public class CustomRenderPipeline : RenderPipeline
{protected override void Render(ScriptableRenderContext context, Camera[] cameras){}
}
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();}
}

然后右键创建对应的RP Asset,将原本的Asset替换成我们自己创建的RP Asset,发现窗口什么都渲染不出。

因为还没有指定渲染相机。

2.2创建渲染相机脚本

创建一个新的自定义相机渲染脚本CustomCameraRenderer.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;public class CustomCameraRenderer
{private Camera _camera;private ScriptableRenderContext _context;public void Render(ScriptableRenderContext sContext, Camera rCamera){this._camera = rCamera;this._context = sContext;}
}

然后将CustomRenderPipeline设置返回值。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;public class CustomRenderPipeline : RenderPipeline
{private CustomCameraRenderer _renderer = new CustomCameraRenderer();protected override void Render(ScriptableRenderContext context, Camera[] cameras){foreach (Camera camera in cameras){_renderer.Render(context,camera); }    }
}

camera renderer 大致相当于通用RP的scriptable renderer。
这种方法能让每个相机在未来更容易支持不同的渲染方法。例如一个渲染第一人称视图,一个渲染三维地图,或前向和延迟渲染的区别。但现在我们会用同样的方式渲染所有的摄像机。

ScriptableRenderContext包含了一些渲染相关的数据,具体可以查看源码。

--------------------------------后续借用Catlike里面的截图----------------------------------

(_camera->camera 、_context->context)

2.3渲染天空盒

在CustomCameraRenderer中对可见物体进行绘制Context.DrawSkybox(camera);

将缓存中的命令提交到执行队列里。

需要提前计算VP矩阵,使最终得到的图像正常。

2.4 Command Buffer

上下文会延迟实际的渲染,直到我们提交它为止。在此之前,我们对其进行配置并向其添加命令以供后续的执行。某些任务(例如绘制天空盒)提供了专属方法,但其他命令则必须通过单独的命令缓冲区(command buffer)间接执行。我们需要用这样的缓冲区来绘制场景中的其他几何图形。

为了获得缓冲区,我们必须创建一个新的CommandBuffer对象实例。一般只需要一个缓冲区,因此默认情况下为CameraRenderer创建一个缓冲区,并将对它的引用存储在字段中。给缓冲区起一个名字,以便我们在frame debugger中识别它。就叫Render Camera好了。

CustomCameraRenderer里添加CommandBuffer,如下图所示。

我们可以使用命令缓冲区注入给Profiler样本,这些样本将同时显示在Profiler和frame debugger中。通过在适当的位置插入BeginSample和EndSample就可以完成。在本例中,在Setup和Submit的开头添加。注意两个方法必须提供相同的样本名称,为此我们直接使用缓冲区的名称。

要执行缓冲区,在上下文中调用ExecuteCommandBuffer,并把缓冲区作为一个参数。这是从缓冲区中复制命令,但并不清除它,如果我们想重新使用它,我们必须在事后明确地这样做。因为执行和清除总是一起进行的,所以添加一个方法来做这两件事是很方便的。

简单来说就是我们可以手动控制渲染顺序,并且在Profiler和Frame Debug上面进行单独框选命名(方便查看)。

2.5 清除渲染目标

我们画的东西最终会被渲染到摄像机的渲染目标上,默认情况下是帧缓冲区,但也可以是渲染纹理。之前绘制到该目标上的东西仍然存在,这可能会干扰我们现在正在渲染的图像。为了保证正常的渲染,我们必须清除渲染目标,以摆脱它的旧内容。这可以通过在命令缓冲区上调用ClearRenderTarget来实现,它属于Setup方法。

帧调试器现在显示了清除的Draw GL条目,它显示嵌套在Render Camera的一个额外层次中。这是因为ClearRenderTarget用命令缓冲区的名字将清除动作包裹在一个样本中。我们可以通过在开始我们自己的样本之前进行清除来摆脱多余的嵌套。这导致了两个相邻的Render Camera样本范围,它们被合并了。

Draw GL条目表示用Hidden/InternalClear着色器绘制全屏四边形,该着色器会写入渲染目标,这并不是最有效的清除方式。使用这种方法是因为我们是在设置摄像机属性之前进行清除。如果我们把这两个步骤的顺序对调一下,就可以得到快速的清除方式。

现在我们看到Clear(color+Z+stencil),这表明颜色和深度缓冲区都被清除了。Z(深度缓冲区)和模版数据是整个缓冲区的一部分。

2.5 Culling

剔除这部分就是要剔除相机看不到的地方,为此我们可以使用ScriptableCullingParameters结构。

我们可以在摄像机上调用TryGetCullingParameters,用它来返回一个bool值用作后续的渲染。

实际的剔除是通过在上下文中调用Cull来完成的,它会产生一个CullingResults结构。如果成功的话就在Cull中进行,并将结果存储在一个字段中。在这种情况下,我们必须把剔除参数作为一个引用参数来传递,在它前面写上ref。

2.6 Drawing Geometry

一旦我们知道什么是可见的,我们就可以继续渲染这些东西了。这可以通过在上下文中调用DrawRenderers来完成,并将剔除结果作为一个参数,告诉它要使用哪些渲染器。

除此之外,我们还必须提供绘制设置和过滤设置。两者都是结构体--绘图设置(DrawingSettings)和过滤设置(FilteringSettings),我们最初会使用它们的默认构造函数。两者都必须通过引用来传递。

在绘制天空盒之前,在DrawVisibleGeometry中这样做。

我们还没有看到任何东西,因为我们还必须指出哪种着色器通道是允许的。由于我们在本教程中只支持非亮光着色器,我们必须为SRPDefaultUnlit通道获取着色器标签ID,我们可以做一次并将其缓存在一个静态字段中。

将其作为DrawingSettings构造函数的第一个参数,同时提供一个新的SortingSettings结构值。将相机传递给排序设置的构造函数,因为它被用来确定是否适用正交或基于距离的排序。

除此之外,我们还必须指出哪些渲染队列是允许的。将RenderQueueRange.all传递给FilteringSettings构造函数,这样我们就包括了所有的东西。

可以看到现在透明与不透明物体的渲染顺序杂乱无章,下面要再对排序进行处理。

此处为语雀视频卡片,点击链接查看:bandicam 2022-01-25 14-00-57-377.mp4

这样渲染顺序就是正常的。

此处为语雀视频卡片,点击链接查看:bandicam 2022-01-25 14-10-58-873.mp4

2.7 分别单独绘制不透明物体和透明物体

目前天空盒会遮挡住透明物体,因此我们需要调整不透明物体与天空盒绘制的顺序。

我们可以通过切换到RenderQueueRange.opaque来消除初始DrawRenderers调用中的透明对象。

然后重新设置下绘制相关参数,重新进行绘制。

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

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

相关文章

R2R 的一些小tip

批次间控制器(Run-to-run Controller)&#xff0c;以应对高混合生产的挑战。将最优配方参数与各种工业特征相关联的模型是根据历史数据离线训练的。预测的最优配方参数在线用于调整工艺条件。 批次控制(R2R control)是一种先进的工艺控制技术&#xff0c;可在运行(如批次或晶圆…

招生报名缴费小程序开发笔记(上)

前期调研 1.数字化趋势&#xff1a; 随着社会的数字化转型&#xff0c;越来越多的教育机构倾向于采用数字工具来简化和优化他们的招生和报名过程。招生报名缴费小程序是应对这一趋势的一种解决方案&#xff0c;可以提供高效、方便、快速的在线招生渠道。2.用户需求&#xff1a…

进行商城的测试用例设计思路是什么?

进行商城的测试用例设计时&#xff0c;可以考虑以下思路&#xff1a; 1. 功能测试&#xff1a;测试商城的基本功能是否正常工作&#xff0c;包括用户注册、登录、浏览商品、搜索商品、添加商品到购物车、下单、支付等。 2. 数据验证测试&#xff1a;验证商城中的数据是否正确…

HarmonyOS 快速入门TypeScript

1.什么是TypeScript&#xff0c;它和JavaScript&#xff0c;ArkTs有什么区别 ArkTS是HarmonyOS优选的主力应用开发语言。它在TypeScript&#xff08;简称TS&#xff09;的基础上&#xff0c;匹配ArkUI框架&#xff0c;扩展了声明式UI、状态管理等相应的能力&#xff0c;让开发…

jenkins自动化操作步骤(gitblit)

1、登陆地址&#xff1a; http://xxxxxxxxx.org:xxxx/ admin/xxxx 2、创建任务 选择构建一个maven项目 3、配置 最多只保留一天一个任务 选择git仓库和账号密码 选择代码对应分支 build项&#xff1a; 1&#xff09;使用父项目的pom文件&#xff1a;k56-boot/pom.xml 2&…

《python语言程序设计》(2018版)第5章编程题 第41题第3次路过。总结一下。没有走完的路

这道题最大的需要就是能够进行两个数值的对比&#xff0c;同时还能让更多的数值依次进入到对比中。 这道题的解题版本 这个版本只是能统计出谁是最大数。但是无法统计最大数出现了多少次。 number "" count 0 data_number 0 while number ! 0:number eval(inpu…

Babylonjs学习笔记(五)——创建PBR材质

书接上回&#xff0c;这里讨论PBR材质&#xff01;&#xff01;&#xff01; // 创建天空盒/* */const createSkyBox (scene:Scene):void>{const envTex CubeTexture.CreateFromPrefilteredData(./env/environment.env,scene)scene.environmentTexture envTex;scene.cre…

基于元模型优化算法的主从博弈多虚拟电厂动态定价和能量管理MATLAB程序

微❤关注“电气仔推送”获得资料&#xff08;专享优惠&#xff09; 参考文献&#xff1a; 基于元模型优化算法的主从博弈多虚拟电厂动态定价和能量管理_董雷 仿真平台&#xff1a; MATLABcplex 主要内容&#xff1a; 主要为多虚拟电厂/微网的优化调度策略&#xff0c;模型…

Stable Diffusion AI绘图

提示词&#xff1a; masterpiece, best quality, 1girl, (anime), (manga), (2D), half body, perfect eyes, both eyes are the same, Global illumination, soft light, dream light, digital painting, extremely detailed CGI anime, hd, 2k, 4k background 反向提示词&…

在Go项目中二次封装Kafka客户端功能

1.摘要 在上一章节中,我利用Docker快速搭建了一个Kafka服务,并测试成功Kafka生产者和消费者功能,本章内容尝试在Go项目中对Kafka服务进行封装调用, 实现从Kafka自动接收消息并消费。 在本文中使用了Kafka的一个高性能开源库Sarama, Sarama是一个遵循MIT许可协议的Apache Kafk…

Flask路由机制分析之二

一、前言 上篇 《Flask 路由机制分析之一》主要讲了Python函数的特性以及装饰器的基本概念&#xff0c;这节我们具体分析一下路由内部机制&#xff0c;Flask路由依赖于werkzegu的routing模块来实现。 二、werkzegu的routing模块介绍 Werkzegu库的routing模块主要功能在于URL…

PDF 文档处理:使用 Java 对比 PDF 找出内容差异

不论是在团队写作还是在个人工作中&#xff0c;PDF 文档往往会经过多次修订和更新。掌握 PDF 文档内容的变化对于管理文档有极大的帮助。通过对比 PDF 文档&#xff0c;用户可以快速找出文档增加、删除和修改的内容&#xff0c;更好地了解文档的演变过程&#xff0c;轻松地管理…

大数据Flink(一百零五):SQL性能调优

文章目录 SQL性能调优 一、 ​​​​​​​MiniBatch 聚合

企业如何安全跨国传输30T文件数据

对于一些对数据敏感性比较高的企业&#xff0c;如IT企业和国企等&#xff0c;跨国数据传输是当今企业面临的一个重要挑战&#xff0c;尤其是当数据量达到30T这样的规模时&#xff0c;如何保证数据的速度、安全和合规性&#xff0c;就成为了企业必须考虑的问题。本文将从以下几个…

计算机毕业设计选题推荐-周边美食推荐微信小程序/安卓APP-项目实战

✨作者主页&#xff1a;IT毕设梦工厂✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Py…

大数据-Storm流式框架(八)---Storm案例

中国移动项目部署文档 一、项目架构 二、启动集群 1、启动Zookeeper集群 2、启动Hbase&#xff08;完全分布式需要先启动Hadoop集群&#xff09; 在conf/hbase-env.sh中设置JAVA_HOME 在conf/hbase-site.xml中&#xff0c;仅需要指定hbase和zookeeper写数据的本地路径。默…

2023 年值得关注的国外网络安全初创公司

网络安全初创公司试图解决的问题往往有点超前于主流。他们可以比大多数老牌公司更快地填补空白或新兴需求。初创公司通常可以更快地创新&#xff0c;因为它们不受安装基础的限制。 当然&#xff0c;缺点是初创公司往往缺乏资源和成熟度。公司致力于初创公司的产品或平台是有风…

Spring Web MVC入门

一&#xff1a;了解Spring Web MVC (1)关于Java开发 &#x1f31f;Java开发大多数场景是业务开发 比如说京东的业务就是电商卖货、今日头条的业务就推送新闻&#xff1b;快手的业务就是短视频推荐 (2)Spring Web MVC的简单理解 &#x1f497;Spring Web MVC&#xff1a;如何使…

缓解大模型幻觉问题的解决方案

本文记录大模型幻觉问题的相关内容。 参考&#xff1a;Mitigating LLM Hallucinations: a multifaceted approach 地址&#xff1a;https://amatriain.net/blog/hallucinations &#xff08;图&#xff1a;解决大模型幻觉的不同方式&#xff09; 什么是幻觉&#xff1f; 幻觉…

常见面试题-MySQL专栏(一)

为什么 mysql 删了行记录&#xff0c;反而磁盘空间没有减少&#xff1f; 答&#xff1a; 在 mysql 中&#xff0c;当使用 delete 删除数据时&#xff0c;mysql 会将删除的数据标记为已删除&#xff0c;但是并不去磁盘上真正进行删除&#xff0c;而是在需要使用这片存储空间时&…