UnityShader20.1:CommandBuffer初见(下)

本文介绍如何使用Unity通过CommandBuffer和自定义着色器实现复杂的玻璃折射效果,包括高斯模糊和环境映射。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

接上文:UnityShader20:CommandBuffer初见(上)

四、复杂一点的例子……

CommandBuffer代码参考如下:

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

[ExecuteInEditMode]
//加上ExecuteInEditMode后,下面的代码在编辑模式中就会运行,无需每次测试都要play了
public class CommandBufferBlurRefraction: MonoBehaviour
{
	public Shader m_BlurShader;
	private Material m_Material;
	private Camera m_Cam;
	private Dictionary<Camera,CommandBuffer> m_Cameras = new Dictionary<Camera, CommandBuffer>();
	private 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();
	}

	public void OnWillRenderObject()
	{
		var act = gameObject.activeInHierarchy && enabled;
		if (!act)
		{
			Cleanup();
			return;
		}
		
		var cam = Camera.current;
		if (!cam)
			return;

		CommandBuffer buf = null;
		if (m_Cameras.ContainsKey(cam))
			return;

		if (!m_Material)
		{
			m_Material = new Material(m_BlurShader);
			m_Material.hideFlags = HideFlags.HideAndDontSave;
		}

		//创建一个CommandBuffer
		buf = new CommandBuffer();
		buf.name = "Grab screen and blur";
		m_Cameras[cam] = buf;

		//新建一张纹理,得到对应的纹理ID
		int screenCopyID = Shader.PropertyToID("_ScreenCopyTexture");
		//初始化这张纹理,其中纹理的大小为摄像机视野大小
		buf.GetTemporaryRT(screenCopyID, -1, -1, 0, FilterMode.Bilinear);
		//将当前的渲染目标纹理拷贝到你的纹理"_ScreenCopyTexture"上
		buf.Blit(BuiltinRenderTextureType.CurrentActive, screenCopyID);
		
		//再新建两张纹理
		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);
		
		//开始表演
		buf.Blit(screenCopyID, blurredID);
		buf.ReleaseTemporaryRT(screenCopyID); 
		
		//进行多次的高斯模糊,一次横向,一次纵向,纹理相互拷贝
		buf.SetGlobalVector("offsets", new Vector4(2.0f/Screen.width, 0, 0, 0));
		buf.Blit(blurredID, blurredID2, m_Material);
		buf.SetGlobalVector("offsets", new Vector4(0, 2.0f/Screen.height, 0, 0));
		buf.Blit(blurredID2, blurredID, m_Material);
		buf.SetGlobalVector("offsets", new Vector4(4.0f/Screen.width, 0, 0, 0));
		buf.Blit(blurredID, blurredID2, m_Material);
		buf.SetGlobalVector("offsets", new Vector4(0, 4.0f/Screen.height, 0, 0));
		buf.Blit(blurredID2, blurredID, m_Material);

		//到这里,纹理blurredID已经就是最终我们要的:进行过高斯模糊的结果,将它赋到对应着色器中的 _RefractionTex 属性上
		buf.SetGlobalTexture("_RefractionTex", blurredID);
		cam.AddCommandBuffer(CameraEvent.AfterSkybox, buf);
	}
	//是的,这整个过程就是为了得到一张你想要的纹理,并传递给Shader
}
  1. CommandBuffer.GetTemporaryRT:获取一张临时的 RenderTexture,第1个参数为纹理主键,由 Shader.PropertyToID 创建;第2, 3个参数为纹理长宽,传负数 (-x) 表示取相机像素的  \frac{1}{x};第4个参数为深度缓冲位宽(0, 16, 24);第5个参数位纹理过滤模式。这个纹理可以由 ReleaseTemporaryRT 释放,也会由 Unity 在摄像机完成渲染后自动释放
  2. CommandBuffer.ReleaseTemporaryRT:释放指定的的临时渲染纹理

  3. Shader.PropertyToID:获得着色器属性名对应的唯一ID,这个属性名随意
  4. BuiltinRenderTextureType.CurrentActive:获取当前激活的渲染目标
  5. CommandBuffer.Blit:将一个纹理复制到另一个纹理,可使用自定义着色器,第1个参数为源纹理 src;第2个参数为目标纹理 target;第三个参数为材质 mat,其内部使用 mat 材质用 src 做 mainTex,clear 为 black 后渲染到 target 上,mat 留空可以理解为直接拷贝纹理。执行这个操作后,target 将会成为新的渲染目标
  6. CommandBuffer.SetGlobalVector:全局设置着色器向量属性
  7. CommandBuffer.SetGlobalTexture:全局设置着色器纹理属性

 

五、更好的玻璃效果(官网案例)

1):着色器代码部分:这个着色器实现了最简单的高斯模糊

Shader "Jaihk662/GaussBlurred1"
{
    Properties
    {
        _MainTex ("Base(RGB)", 2D) = "" {}
    }

    CGINCLUDE
    #include "UnityCG.cginc"

    float4 offsets;
	sampler2D _MainTex;
    /*可以直接用appdata_img代替
    struct _2vert
    {
        float4 vertex: POSITION;
        half2 texcoord: TEXCOORD0;
    };*/
    struct vert2frag 
    {
        float4 pos: SV_POSITION;
		float2 uv: TEXCOORD0;
		float4 uv01: TEXCOORD1;
		float4 uv23: TEXCOORD2;
		float4 uv45: TEXCOORD3;
    };

    vert2frag vert(appdata_img v)
    {
		vert2frag o;
		o.pos = UnityObjectToClipPos(v.vertex);
		o.uv.xy = v.texcoord.xy;
        //采样范围为7x7(周围3圈),offests的值在.cs中设置
		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(vert2frag i): SV_Target
    {
        //高斯模糊
		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;
	}
    ENDCG

    Subshader
    {
        Pass
        {
            ZTest Always Cull Off ZWrite Off
            Fog { Mode off }        //设置雾模式:关闭

            CGPROGRAM
            //使用低精度(FP16)以提升fragment着色器的运行速度,减少时间
            #pragma fragmentoption ARB_precision_hint_fastest
            #pragma vertex vert
            #pragma fragment frag
            ENDCG
        }
    }
}

CGINCLUDE 关键字:和之前常用的 CGPROGRAM 不同,在 CGINCLUDE 和 ENDCG 范围内插入的 shader 代码会被插入到所有 Pass 中

  • #pragma fragmentoption ARB_precision_hint_fastest:使用低精度(FP16)以提升fragment着色器的运行速度,减少时间
  • #pragma fragmentoption ARB_precision_hint_nicest:使用高精度(FP32)

代码中还设置了 Fog { Mode off }:这是为了关闭雾效,暂时可以不去了解

2):然后对于玻璃本身材质的着色器如下:

Shader "Jaihk662/Glass2"
{
    Properties
    {
        _MainTex ("Main Tex", 2D) = "white" {}                          //玻璃材质纹理
        _NormalMap ("Normal Map", 2D) = "white" {}                      //玻璃法线
        _Cubemap ("Environment Cubemap", Cube) = "_Skybox" {}           //模拟环境映射
        _Distortion ("Distortion", Range(0, 100)) = 10                  //模拟折射时,图像扭曲程度
        _RefractAmount ("Refract Amount", Range(0.0, 1.0)) = 1.0        //折射程度,为0只反射,为1只折射
    }
    SubShader
    {
        Tags { "Queue" = "Transparent" "RenderType" = "Opaque" }
            //Queue设置为Transparent可以保证该物体渲染时,所有的不透明物体都已经被渲染到屏幕上了
        LOD 200
        PASS 
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #include "Lighting.cginc"
 
            sampler2D _MainTex;
			sampler2D _NormalMap;
			samplerCUBE _Cubemap;
			float _Distortion;
			fixed _RefractAmount;
            sampler2D _RefractionTex;           //来自CommandBuffer
			float4 _RefractionTex_TexelSize;    //对应纹理每一像素的大小

            float4 _NormalMap_ST;
            float4 _MainTex_ST;
            struct _2vert
            {
                float4 vertex: POSITION;
                float3 normal: NORMAL;
				float4 tangent: TANGENT; 
				float2 texcoord: TEXCOORD0;
            };
            struct vert2frag 
            {
                float4 pos: SV_POSITION;
				float4 scrPos: TEXCOORD0;
				float4 uv: TEXCOORD1;
				float4 TtoW1: TEXCOORD2;  
			    float4 TtoW2: TEXCOORD3;  
			    float4 TtoW3: TEXCOORD4; 
            };
 
            vert2frag vert(_2vert v) 
            {
                vert2frag o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.scrPos = ComputeGrabScreenPos(o.pos);         //获得屏幕图像的采样坐标,考虑过平台差异,输入裁剪空间坐标,输出齐次坐标系下的屏幕坐标值(就是屏幕坐标乘上w,只计算xy,zw不变)
              	o.uv.xy = TRANSFORM_TEX(v.texcoord, _MainTex);
				o.uv.zw = TRANSFORM_TEX(v.texcoord, _NormalMap);
				
				float3 wPos = mul(unity_ObjectToWorld, v.vertex).xyz;
                float3 wNormal = UnityObjectToWorldNormal(v.normal);
                float3 wTangent = UnityObjectToWorldDir(v.tangent);
                float3 wBinormal = cross(wNormal, wTangent) * v.tangent.w;
                o.TtoW1 = float4(wTangent.x, wBinormal.x, wNormal.x, wPos.x);
                o.TtoW2 = float4(wTangent.y, wBinormal.y, wNormal.y, wPos.y);
                o.TtoW3 = float4(wTangent.z, wBinormal.z, wNormal.z, wPos.z);
                return o;
            }
            fixed4 frag(vert2frag i): SV_Target
            {
                float3 wPos = float3(i.TtoW1.w, i.TtoW2.w, i.TtoW3.w);
                fixed3 wViewDir = normalize(UnityWorldSpaceViewDir(wPos));
                fixed3 normal = UnpackNormal(tex2D(_NormalMap, i.uv.zw));	

                //计算折射,考虑玻璃材质
                float2 offest = normal.xy * _Distortion * _RefractionTex_TexelSize.xy;
                i.scrPos.xy = i.scrPos.xy + offest * i.scrPos.z;
                fixed3 refractCol = tex2D(_RefractionTex, i.scrPos.xy / i.scrPos.w).rgb;      //根据偏移后的坐标进行采样,得到折射颜色

                //不再计算反射
                //normal = normalize(half3(dot(i.TtoW1.xyz, normal), dot(i.TtoW2.xyz, normal), dot(i.TtoW3.xyz, normal)));
                fixed4 texColor = tex2D(_MainTex, i.uv.xy);
				//fixed3 reflectionDir = reflect(-wViewDir, normal);
				//fixed3 reflectionCol = texCUBE(_Cubemap, reflectionDir).rgb * texColor.rgb;
				
				fixed3 finalColor = texColor * (1 - _RefractAmount) + refractCol * _RefractAmount;
				return fixed4(finalColor, 1);
            } 
            ENDCG
        }
    }
    FallBack "Diffuse"
}

这个代码就是《UnityShader19.1:渲染纹理(下)之 GrabPass》中代码的改版,原先 GrabPass 的纹理现在由 CommandBuffer 提供,并且不再计算反射

3):最后 CommandBuffer 部分代码就是上一章中的代码

效果如下:

 

参考资料:

 

<think>我们正在讨论的是在Unity中使用BuiltinRenderTextureType.CameraTarget的最佳实践。根据引用内容,CameraTarget通常指当前摄像机的渲染目标(可能是屏幕或一个渲染纹理)。在CommandBuffer中,我们可以使用它作为源或目标纹理。最佳实践包括:1.使用CommandBuffer进行多步后处理时,应复用临时渲染目标以减少内存和切换开销(引用[2])。2.在Blit操作后,当前活动渲染目标会改变为dest(引用[3]),因此需要注意后续操作的目标设置。3.使用临时渲染目标后要记得释放(引用[2])。下面给出几种常见场景的代码示例:###场景1:简单的后处理效果(单次Blit)```csharp//创建CommandBufferCommandBuffercmd=newCommandBuffer();cmd.name="SimplePostEffect";//使用临时渲染目标inttempRT=Shader.PropertyToID("_TempRT");cmd.GetTemporaryRT(tempRT,-1,-1,0,FilterMode.Bilinear);//将当前摄像机目标(CameraTarget)复制到临时纹理,然后应用材质,再复制回摄像机目标cmd.Blit(BuiltinRenderTextureType.CameraTarget,tempRT);//源->临时目标cmd.Blit(tempRT,BuiltinRenderTextureType.CameraTarget,yourMaterial);//临时目标->目标(应用材质)//释放临时渲染目标cmd.ReleaseTemporaryRT(tempRT);//执行CommandBuffer(需要添加到摄像机的CommandBuffer事件中)camera.AddCommandBuffer(CameraEvent.AfterEverything,cmd);```###场景2:多步后处理(如模糊效果,需要多次Blit)根据引用[1]的示例,我们可以优化多步后处理:```csharpCommandBuffercmd=newCommandBuffer();cmd.name="MultiPassBlur";//创建两个临时渲染目标inttempRT1=Shader.PropertyToID("_TempRT1");inttempRT2=Shader.PropertyToID("_TempRT2");cmd.GetTemporaryRT(tempRT1,-1,-1,0,FilterMode.Bilinear);cmd.GetTemporaryRT(tempRT2,-1,-1,0,FilterMode.Bilinear);//第一步:从摄像机目标到第一个临时目标cmd.Blit(BuiltinRenderTextureType.CameraTarget,tempRT1);//多次模糊(示例为3次)for(inti=0;i<3;i++){//第一次:tempRT1->tempRT2(应用模糊材质)cmd.Blit(tempRT1,tempRT2,blurMaterial);//第二次:tempRT2->tempRT1(再次应用模糊材质,实现迭代)cmd.Blit(tempRT2,tempRT1,blurMaterial);}//最后将结果合并到摄像机目标(这里假设直接覆盖)cmd.Blit(tempRT1,BuiltinRenderTextureType.CameraTarget);//释放临时渲染目标cmd.ReleaseTemporaryRT(tempRT1);cmd.ReleaseTemporaryRT(tempRT2);```###场景3:在URP中通过RenderFeature使用CommandBuffer在URP中,我们通常通过RenderFeature来插入CommandBuffer(引用[4]):```csharppublicclassCustomRenderPass:ScriptableRenderPass{privateMaterialcustomMaterial;publicCustomRenderPass(Materialmaterial){customMaterial=material;}publicoverridevoidExecute(ScriptableRenderContextcontext,refRenderingDatarenderingData){CommandBuffercmd=CommandBufferPool.Get("CustomEffect");//获取摄像机的目标描述符vardescriptor=renderingData.cameraData.cameraTargetDescriptor;//创建临时渲染目标inttempRT=Shader.PropertyToID("_TempRT");cmd.GetTemporaryRT(tempRT,descriptor);//源目标:当前激活的渲染目标(即CameraTarget)RenderTargetIdentifiersource=renderingData.cameraData.renderer.cameraColorTarget;//第一步:复制到临时目标cmd.Blit(source,tempRT);//第二步:应用材质cmd.Blit(tempRT,source,customMaterial);//释放cmd.ReleaseTemporaryRT(tempRT);context.ExecuteCommandBuffer(cmd);CommandBufferPool.Release(cmd);}}```###注意事项:1.**性能**:尽量减少临时渲染目标的数量和大小,特别是移动端(引用[2])。2.**纹理格式**:根据需求选择合适的格式(如ARGB32、ARGBHalf等)。3.**事件时机**:根据效果需求选择正确的CameraEvent(引用[5]),例如AfterSkybox、AfterForwardOpaque等。4.**多摄像机**:如果场景中有多个摄像机,注意每个摄像机的CommandBuffer是独立的。###最佳实践总结:-使用`GetTemporaryRT`和`ReleaseTemporaryRT`成对出现,避免内存泄漏。-在复杂的多Pass效果中,复用临时渲染目标(如使用乒乓技术)[^1][^2]。-在URP中,使用`ScriptableRenderPass`和`CommandBufferPool`来管理CommandBuffer[^4]。###相关问题:1.如何在URP中实现多摄像机的后处理效果?2.CommandBuffer在哪些CameraEvent时机可以使用BuiltinRenderTextureType.CameraTarget?3.如何避免后处理效果中的纹理闪烁问题(如半分辨率下)?希望这些示例和说明能帮助你更好地使用BuiltinRenderTextureType.CameraTarget。</think># Unity中使用BuiltinRenderTextureType.CameraTarget的最佳实践 `BuiltinRenderTextureType.CameraTarget`是Unity中表示当前摄像机渲染目标的内置标识符。在使用Command Buffer进行高级渲染操作时,正确使用CameraTarget至关重要。 ## CameraTarget基本使用 ```csharp // 创建Command Buffer CommandBuffer cmd = new CommandBuffer(); cmd.name = "CameraTarget Example"; // 将当前摄像机目标复制到临时纹理 int tempRT = Shader.PropertyToID("_TempRT"); cmd.GetTemporaryRT(tempRT, -1, -1, 0, FilterMode.Bilinear); cmd.Blit(BuiltinRenderTextureType.CameraTarget, tempRT); // 应用后处理效果 cmd.Blit(tempRT, BuiltinRenderTextureType.CameraTarget, postProcessMaterial); // 释放临时纹理 cmd.ReleaseTemporaryRT(tempRT); // 添加到摄像机 camera.AddCommandBuffer(CameraEvent.AfterForwardOpaque, cmd); ``` ## 多Pass效果处理(最佳实践) ```csharp void SetupMultiPassEffect(Camera camera) { CommandBuffer cmd = new CommandBuffer(); cmd.name = "MultiPass Effect"; // 创建两个临时渲染目标(乒乓缓冲) int tempRT1 = Shader.PropertyToID("_TempRT1"); int tempRT2 = Shader.PropertyToID("_TempRT2"); cmd.GetTemporaryRT(tempRT1, -1, -1, 0, FilterMode.Bilinear); cmd.GetTemporaryRT(tempRT2, -1, -1, 0, FilterMode.Bilinear); // 第一步:从摄像机目标到临时目标1 cmd.Blit(BuiltinRenderTextureType.CameraTarget, tempRT1); // 多Pass处理(示例为模糊效果) for (int i = 0; i < 3; i++) { cmd.Blit(tempRT1, tempRT2, blurMaterial); cmd.Blit(tempRT2, tempRT1, blurMaterial); } // 最后一步:将处理结果复制回摄像机目标 cmd.Blit(tempRT1, BuiltinRenderTextureType.CameraTarget); // 释放临时纹理 cmd.ReleaseTemporaryRT(tempRT1); cmd.ReleaseTemporaryRT(tempRT2); // 添加到摄像机 camera.AddCommandBuffer(CameraEvent.AfterForwardOpaque, cmd); } ``` ## URP中的CameraTarget使用 在Universal Render Pipeline (URP)中,使用`ScriptableRenderPass`更高效: ```csharp public class CustomRenderPass : ScriptableRenderPass { private Material effectMaterial; private RenderTargetIdentifier cameraTarget; public CustomRenderPass(Material material) { effectMaterial = material; renderPassEvent = RenderPassEvent.AfterRenderingOpaques; } public void Setup(RenderTargetIdentifier cameraTarget) { this.cameraTarget = cameraTarget; } public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData) { CommandBuffer cmd = CommandBufferPool.Get("Custom Effect"); // 创建临时渲染目标 int tempRT = Shader.PropertyToID("_TempRT"); cmd.GetTemporaryRT(tempRT, renderingData.cameraData.cameraTargetDescriptor); // 应用效果 cmd.Blit(cameraTarget, tempRT, effectMaterial); cmd.Blit(tempRT, cameraTarget); // 释放资源 cmd.ReleaseTemporaryRT(tempRT); context.ExecuteCommandBuffer(cmd); CommandBufferPool.Release(cmd); } } ``` ## 性能优化技巧 1. **临时纹理复用**: ```csharp // 在类级别声明ID private static readonly int TempRT1 = Shader.PropertyToID("_GlobalTempRT1"); // 使用时 cmd.GetTemporaryRT(TempRT1, ...); // 处理... // 不要立即释放,后续其他效果可复用 ``` 2. **动态分辨率**: ```csharp int downsampledWidth = camera.pixelWidth / 2; int downsampledHeight = camera.pixelHeight / 2; cmd.GetTemporaryRT(tempRT, downsampledWidth, downsampledHeight, ...); ``` 3. **异步执行**: ```csharp // 在帧早期设置纹理 camera.AddCommandBuffer(CameraEvent.BeforeForwardOpaque, setupCmd); // 在帧后期使用 camera.AddCommandBuffer(CameraEvent.AfterForwardOpaque, renderCmd); ``` ## CameraTarget与内置纹理的结合使用 ```csharp // 保存AfterSkybox后的纹理(引用[5]) CommandBuffer afterSkyboxCmd = new CommandBuffer(); afterSkyboxCmd.name = "AfterSkybox Capture"; int afterSkyboxTex = Shader.PropertyToID("_AfterSkyboxTex"); afterSkyboxCmd.GetTemporaryRT(afterSkyboxTex, -1, -1, 0, FilterMode.Bilinear); afterSkyboxCmd.Blit(BuiltinRenderTextureType.CurrentActive, afterSkyboxTex); camera.AddCommandBuffer(CameraEvent.AfterSkybox, afterSkyboxCmd); // 在后续Pass中使用 CommandBuffer distortionCmd = new CommandBuffer(); distortionCmd.name = "Distortion Effect"; distortionCmd.SetGlobalTexture("_SkyboxTex", afterSkyboxTex); distortionCmd.Blit(BuiltinRenderTextureType.CameraTarget, BuiltinRenderTextureType.CameraTarget, distortionMaterial); camera.AddCommandBuffer(CameraEvent.BeforeImageEffects, distortionCmd); ``` ## 最佳实践总结 1. **资源管理**: - 成对使用`GetTemporaryRT`和`ReleaseTemporaryRT` - 全局复用临时纹理ID减少开销 - 使用`CommandBufferPool`管理Command Buffer生命周期 2. **性能优化**: - 减少纹理复制次数 - 使用合适的分辨率 - 避免不必要的全屏Pass 3. **时机选择**: - `AfterSkybox`:天空盒渲染后 - `BeforeForwardOpaque`:不透明物体渲染前 - `AfterForwardOpaque`:不透明物体渲染后 - `BeforeImageEffects`:后处理前 4. **URP集成**: - 使用`ScriptableRenderPass` - 通过`RenderTargetIdentifier`获取相机目标 - 在`Execute`方法中执行Command Buffer ## 常见问题解决方案 **问题:Blit后CameraTarget丢失** ```csharp // 在Blit后重新设置CameraTarget cmd.SetRenderTarget(BuiltinRenderTextureType.CameraTarget); ``` **问题:多摄像机冲突** ```csharp // 每个摄像机使用独立Command Buffer private Dictionary<Camera, CommandBuffer> cameraBuffers = new Dictionary<Camera, CommandBuffer>(); void OnEnable() { Camera.onPreRender += OnCameraPreRender; } void OnCameraPreRender(Camera cam) { if (!cameraBuffers.ContainsKey(cam)) { CommandBuffer cb = new CommandBuffer(); // 设置Command Buffer... cam.AddCommandBuffer(CameraEvent.AfterForwardOpaque, cb); cameraBuffers.Add(cam, cb); } } ``` ## 平台适配建议 ```csharp void SetupCommandBuffer(Camera camera) { CommandBuffer cmd = new CommandBuffer(); #if UNITY_STANDALONE || UNITY_EDITOR // 高质量设置 int rtFormat = (SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.ARGBFloat)) ? (int)RenderTextureFormat.ARGBFloat : (int)RenderTextureFormat.ARGB32; #elif UNITY_IOS || UNITY_ANDROID // 移动端优化 int rtFormat = (SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.ARGBHalf)) ? (int)RenderTextureFormat.ARGBHalf : (int)RenderTextureFormat.ARGB32; #endif cmd.GetTemporaryRT(tempRT, -1, -1, 0, FilterMode.Bilinear, (RenderTextureFormat)rtFormat); // 后续处理... } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值