Graphics unity3d:使用主摄像头';用于渲染另一个摄影机视图的深度缓冲区

Graphics unity3d:使用主摄像头';用于渲染另一个摄影机视图的深度缓冲区,graphics,unity3d,rendering,Graphics,Unity3d,Rendering,在主摄影机渲染后,我想使用(或复制)其深度缓冲区到(禁用)摄影机的深度缓冲区。 我的目标是在绘制不透明对象后使用深度缓冲区时,将粒子绘制到较小的渲染目标上(使用单独的摄影机)。 我无法在单个摄影机中执行此操作,因为出于性能原因,目标是为粒子使用较小的渲染目标 Unity中的替换着色器也不是一个选项:我希望我的粒子使用其现有着色器-我只希望在绘制粒子之前,使用主摄影机深度缓冲区的子采样版本覆盖粒子摄影机的深度缓冲区 我没有得到任何答复;因此,重新发布 这是附在我的主摄像机上的脚本。它渲染所有非粒子

在主摄影机渲染后,我想使用(或复制)其深度缓冲区到(禁用)摄影机的深度缓冲区。 我的目标是在绘制不透明对象后使用深度缓冲区时,将粒子绘制到较小的渲染目标上(使用单独的摄影机)。 我无法在单个摄影机中执行此操作,因为出于性能原因,目标是为粒子使用较小的渲染目标

Unity中的替换着色器也不是一个选项:我希望我的粒子使用其现有着色器-我只希望在绘制粒子之前,使用主摄影机深度缓冲区的子采样版本覆盖粒子摄影机的深度缓冲区

我没有得到任何答复;因此,重新发布

这是附在我的主摄像机上的脚本。它渲染所有非粒子层,我使用
OnRenderImage
调用粒子摄影机

public class MagicRenderer : MonoBehaviour {
public Shader   particleShader; // shader that uses the main camera's depth buffer to depth test particle Z
public Material blendMat;       // material that uses a simple blend shader
public int      downSampleFactor = 1;

private RenderTexture particleRT;
private static GameObject pCam;

void Awake () {
    // make the main cameras depth buffer available to the shaders via _CameraDepthTexture
    camera.depthTextureMode = DepthTextureMode.Depth;
}

// Update is called once per frame
void Update () {

}

void OnRenderImage(RenderTexture src, RenderTexture dest) {
            // create tmp RT
            particleRT = RenderTexture.GetTemporary (Screen.width / downSampleFactor, Screen.height / downSampleFactor, 0);
            particleRT.antiAliasing = 1;

            // create particle cam
            Camera pCam = GetPCam ();
            pCam.CopyFrom (camera); 
            pCam.clearFlags = CameraClearFlags.SolidColor;
            pCam.backgroundColor = new Color (0.0f, 0.0f, 0.0f, 0.0f);
            pCam.cullingMask = 1 << LayerMask.NameToLayer ("Particles");
            pCam.useOcclusionCulling = false;
            pCam.targetTexture = particleRT;
            pCam.depth = 0;

            // Draw to particleRT's colorBuffer using mainCam's depth buffer
            // ?? - how do i transfer this camera's depth buffer to pCam?
            pCam.Render ();
            // pCam.RenderWithShader (particleShader, "Transparent"); // I don't want to replace the shaders my particles use; os shader replacement isnt an option.

    // blend mainCam's colorBuffer with particleRT's colorBuffer
    // Graphics.Blit(pCam.targetTexture, src, blendMat);        

    // copy resulting buffer to destination
    Graphics.Blit (pCam.targetTexture, dest);


    // clean up
    RenderTexture.ReleaseTemporary(particleRT);
}

static public Camera GetPCam() {
    if (!pCam) {
        GameObject oldpcam = GameObject.Find("pCam");
        Debug.Log (oldpcam);
        if (oldpcam) Destroy(oldpcam);

        pCam = new GameObject("pCam");
        pCam.AddComponent<Camera>();
        pCam.camera.enabled = false;
        pCam.hideFlags = HideFlags.DontSave;
    }

    return pCam.camera;
}
并使用
pCam.Render()
渲染它们

我认为这将使用ZTest的现有着色器渲染粒子。 不幸的是,我注意到的是,在绘制粒子之前,深度模具缓冲区已被清除(请注意,我没有清除任何东西…)


为什么会发生这种情况?

我设法在用于渲染的着色器中“手动”重用摄影机Z缓冲区。更多信息,请参阅


只需更改已用于粒子渲染的粒子着色器。

已经5年了,但我为在较小的单独渲染目标中渲染粒子删除了一个几乎完整的解决方案。我写这封信是为了将来的访客。还需要很多知识

复制深度 首先,必须以较小渲染纹理的分辨率获取场景深度。 这可以通过使用颜色格式“depth”创建新的渲染纹理来完成。 要将场景深度写入低分辨率深度,请创建仅输出深度的着色器:

struct fragOut{
    float depth : DEPTH;
};

sampler2D _LastCameraDepthTexture;

fragOut frag (v2f i){
    fragOut tOut;
    tOut.depth = tex2D(_LastCameraDepthTexture, i.uv).x;
    return tOut;
}
_LastCameraDepthTexture由Unity自动填充,但有一个缺点。 仅当主摄影机使用延迟渲染进行渲染时,它才免费提供。 对于正向着色,Unity似乎仅针对深度纹理再次渲染场景。 检查帧调试器

然后,向执行着色器的主摄影机添加后期处理效果:

protected virtual void OnRenderImage(RenderTexture pFrom, RenderTexture pTo) {
    Graphics.Blit(pFrom, mSmallerSceneDepthTexture, mRenderToDepthMaterial);
    Graphics.Blit(pFrom, pTo);
}
您可能不需要第二个blit就可以做到这一点,但对我来说测试起来更容易

使用复制的深度进行渲染 要为第二个摄影机使用新的深度纹理,请调用

mSecondCamera.SetTargetBuffers(mParticleRenderTexture.colorBuffer, mSmallerSceneDepthTexture.depthBuffer);
保持targetTexture为空。 然后,您必须确保第二个摄影机不清除深度,只清除颜色。 为此,请完全禁用第二个摄像头上的清除,然后像这样手动清除

Graphics.SetRenderTarget(mParticleRenderTexture);
GL.Clear(false, true, Color.clear);
我建议也手动渲染第二个摄影机。禁用它并调用

mSecondCamera.Render();
清理后

合并 现在您必须合并主视图和单独的图层。 根据渲染的不同,最终可能会得到具有所谓预乘alpha的渲染纹理

要将其与其他内容混合使用,请在带有

fixed4 tBasis = tex2D(_MainTex, i.uv);
fixed4 tToInsert = tex2D(TransparentFX, i.uv);

//beware premultiplied alpha in insert
tBasis.rgb = tBasis.rgb * (1.0f- tToInsert.a) + tToInsert.rgb;
return tBasis;
添加材料可以开箱即用,但alpha混合材料不能。 必须使用自定义混合创建着色器才能创建可用的alpha混合材质。混合是

Blend SrcAlpha OneMinusSrcAlpha, One OneMinusSrcAlpha
这将更改每次执行混合时修改alpha通道的方式

结果 在alpha blended前面添加blended

fx图层rgb

fx层alpha

alpha混合在add blended前面

fx图层rgb

fx层alpha

我还没有测试性能是否真的提高了。
如果有人有更简单的解决方案,请告诉我。

对不起,我不知道如何复制您描述的深度缓冲区。只是想澄清一下,为什么不使用渲染纹理作为它的正常用例?来自Unity docs:-使用资源->创建->渲染纹理创建新的渲染纹理资源。-使用GameObject->Create Other->Camera创建一个新的摄像头。-将渲染纹理指定给新相机的目标纹理。-创建一个宽、高、薄的长方体-将渲染纹理拖到长方体上,以创建使用渲染纹理的材质。-进入播放模式,观察盒子的纹理根据新相机的输出实时更新。谢谢回复。阿尔法混合粒子的MSAA成本太高,我想减少它。我不能在没有深度测试的情况下将粒子渲染到一个单独的小RT,因为我需要确保不透明对象后面的粒子是Z消隐的。您是否尝试过将清除标志设置为“不清除”,像这样渲染,然后将标志设置为“清除颜色”,然后再次渲染,只是为了擦除颜色?是的。在渲染粒子之前,我需要将下采样深度缓冲区(第一个摄影机的RT)转移到第二个RT。这就是我所面临的困难。啊,我想避免在像素着色器中进行深度测试。在脚本中,您需要做什么来传递深度纹理呢?是否有
Material.SetTexture(“\u CameraDepthTexture”,Camera.main.
行?一旦添加了该行,深度纹理就会“广告化”可以通过
\u CameraDepthTexture
访问整个着色器。您可以创建一个过程,在实际渲染之前写入
RenderTexture
的Z缓冲区,然后ZTests可能会自动完成。有趣。我要检查一下。上次分析场景时,我在其中设置了
camera.depthTextureMode
在我的脚本中,我注意到Unity对整个场景进行了Z-prepass处理(如该摄影机所示)。从性能角度看,这真是糟糕透了。提交所有几何体两次而不是重复使用Z-buffer是一个巨大的成功。回来并发布所有这些信息的大道具!
Blend SrcAlpha OneMinusSrcAlpha, One OneMinusSrcAlpha