Unity ARFoundation中如何在C#脚本中识别屏幕阴影像素?
解决方案:ARFoundation URP中获取AR平面阴影纹理的方法
一、为什么unity_ShadowMasks为空?
unity_ShadowMasks是URP的全局内置纹理,不属于单个材质的属性,而是由渲染管线在运行时全局绑定的。直接通过material.GetTexture()访问自然拿不到——这个属性不会出现在材质的暴露参数里,也不会被实例化到材质对象上。
另外,你用的Shadow Receiver URP透明阴影接收器着色器,本质是通过采样**阴影贴图(Shadow Atlas)**渲染阴影,而非依赖Shadow Mask技术(后者是用于静态物体的预烘焙阴影),所以你找错了属性方向。
二、用渲染纹理捕获AR平面阴影的具体实现步骤
这里提供两种适配ARFoundation+URP场景的可行方案:
方案1:修改Shadow Receiver着色器,输出阴影到RenderTexture
编辑Shadow Receiver的Shader Graph
在项目中找到你使用的“透明阴影接收器”Shader Graph,添加一个Render Target节点,指定一个自定义Render Texture作为输出目标(命名为_ShadowCaptureRT)。- 调整着色器逻辑:将原本用于显示阴影的颜色输出到这个Render Target,同时保留原有的透明平面渲染逻辑。
- 在Shader Graph的Blackboard中添加
Texture2D _ShadowCaptureRT属性并设置为“暴露(Exposed)”,方便脚本访问。
编写C#脚本绑定并读取RenderTexture
替换你原有的脚本,改用以下代码:using UnityEngine; public class ShadowCaptureScript : MonoBehaviour { private Renderer _planeRenderer; private RenderTexture _shadowRT; void Awake() { _planeRenderer = GetComponent<Renderer>(); // 创建适配屏幕分辨率的RenderTexture _shadowRT = new RenderTexture(Screen.width, Screen.height, 0, RenderTextureFormat.ARGB32); _shadowRT.name = "ARPlaneShadowRT"; // 绑定到材质的自定义属性 _planeRenderer.material.SetTexture("_ShadowCaptureRT", _shadowRT); } void Update() { // 按需读取(示例:按空格键触发,避免每帧读取影响性能) if (_shadowRT != null && Input.GetKeyDown(KeyCode.Space)) { Texture2D shadowTex = new Texture2D(_shadowRT.width, _shadowRT.height, TextureFormat.ARGB32, false); RenderTexture.active = _shadowRT; shadowTex.ReadPixels(new Rect(0, 0, _shadowRT.width, _shadowRT.height), 0, 0); shadowTex.Apply(); // 此处可处理shadowTex的像素数据(如CV后处理) Debug.Log($"捕获到阴影纹理:{shadowTex.width}x{shadowTex.height}"); // 销毁临时Texture2D释放内存 Destroy(shadowTex); } } void OnDestroy() { if (_shadowRT != null) Destroy(_shadowRT); } }性能优化提示
- 不要每帧读取RenderTexture像素,仅在CV后处理需要时触发。
- 可降低RenderTexture分辨率(如取屏幕尺寸的1/2),减少内存占用和读取耗时。
方案2:用CommandBuffer捕获阴影渲染结果
若不想修改Shader Graph,可借助URP的CommandBuffer在渲染AR平面时捕获阴影:
- 编写脚本注册CommandBuffer
using UnityEngine; using UnityEngine.Rendering; using UnityEngine.Rendering.Universal; public class CommandBufferShadowCapture : MonoBehaviour { private RenderTexture _shadowRT; private CommandBuffer _cmdBuffer; private Camera _mainCamera; void Awake() { _mainCamera = Camera.main; _shadowRT = new RenderTexture(Screen.width, Screen.height, 0, RenderTextureFormat.ARGB32); _cmdBuffer = new CommandBuffer(); _cmdBuffer.name = "CaptureARPlaneShadow"; } void OnEnable() { // 在透明物体渲染后执行捕获(适配AR平面的透明属性) var cameraData = _mainCamera.GetComponent<UniversalAdditionalCameraData>(); cameraData.scriptableRenderer.EnqueuePass(new CustomShadowCapturePass(_cmdBuffer, _shadowRT, GetComponent<Renderer>())); } void OnDisable() { var cameraData = _mainCamera.GetComponent<UniversalAdditionalCameraData>(); cameraData.scriptableRenderer.RemovePass(typeof(CustomShadowCapturePass)); _cmdBuffer.Release(); } // 自定义渲染Pass,负责捕获AR平面阴影 private class CustomShadowCapturePass : ScriptableRenderPass { private CommandBuffer _cmd; private RenderTexture _targetRT; private Renderer _planeRenderer; public CustomShadowCapturePass(CommandBuffer cmd, RenderTexture targetRT, Renderer planeRenderer) { _cmd = cmd; _targetRT = targetRT; _planeRenderer = planeRenderer; renderPassEvent = RenderPassEvent.AfterRenderingTransparents; } public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData) { _cmd.BeginSample("CaptureShadow"); // 设置渲染目标为自定义RT _cmd.SetRenderTarget(_targetRT); // 清除RT内容 _cmd.ClearRenderTarget(true, true, Color.clear); // 仅渲染AR平面的阴影Pass(需替换为Shader实际的阴影Pass索引) _cmd.DrawRenderer(_planeRenderer, _planeRenderer.material, 0, 1); _cmd.EndSample("CaptureShadow"); context.ExecuteCommandBuffer(_cmd); _cmd.Clear(); } } // 读取阴影纹理的方法 public void ReadShadowTexture() { if (_shadowRT == null) return; Texture2D shadowTex = new Texture2D(_shadowRT.width, _shadowRT.height); RenderTexture.active = _shadowRT; shadowTex.ReadPixels(new Rect(0, 0, _shadowRT.width, _shadowRT.height), 0, 0); shadowTex.Apply(); // 处理像素数据 Destroy(shadowTex); } } - 注意事项
- 需要确认Shadow Receiver着色器的阴影Pass索引:打开Shader Graph生成的Shader代码,找到对应阴影渲染的Pass,修改
DrawRenderer的最后一个参数为对应索引。 - 该方案无需修改Shader,但需要对URP渲染流程有基础了解。
- 需要确认Shadow Receiver着色器的阴影Pass索引:打开Shader Graph生成的Shader代码,找到对应阴影渲染的Pass,修改
三、与ARFoundation原始帧结合的建议
你已通过CpuImageSample.cs获取原始AR帧,可将捕获到的阴影纹理与原始帧做像素级运算或叠加,用于CV后处理。注意两者的分辨率和坐标系对齐——AR原始帧分辨率可能与屏幕不同,需做缩放或坐标转换。
内容的提问来源于stack exchange,提问作者salam
相关产品推荐
相关产品推荐

