分享cocos 3.5.2延迟渲染透明物体穿模解决办法

  • Creator 版本: 3.5.2

  • 目标平台: 浏览器 chrome

  • 重现方式:
    当一个opaque 材质的模型放置在近处同时在 距离摄像机更深的位置上放置transparent材质的模型,会出现可以看穿opaque 材质的模型,看到后面透明物体,而理论上不透明材质在前面的话,应该不能看到其挡住的物体,透明的也不行。这应该是一种基本物理规则吧。
    我这项目场景必现,不知道咋复现。。。也可能是我没用对(ps:用了一个摄像机做镜像,但是我想该摄像机是输出到RT中,且优先级比主摄像机低,也就是其在主摄像机前渲染)。

不管啥原因,出了问题就要找原因。
通过阅读延迟渲染部分的源码,我发现在引擎中透明物体是在light-stage时渲染的,而且其渲染时,深度模板数据应该被重置了?望知道的大佬解释下。从而渲染透明问题出现穿模现象。分析了原因,解决办法也好办,把Gbuffer阶段的深度模板数据传给透明物体的shader中,然后根据物体顶点在NDC的深度进行discard就行了。

 不多说上代码吧!
          :grinning:

引用
import { gfx, pipeline } from “cc”;

const { DescriptorSetLayoutBinding, UniformSamplerTexture, DescriptorType, ShaderStageFlagBit, Type } = gfx;

const { SetIndex, PipelineGlobalBindings, globalDescriptorSetLayout } = pipeline;

let GlobalBindingStart = PipelineGlobalBindings.COUNT;

let GlobalBindingIndex = 0;

const UNIFORM_DEPTH_STENCIL_NAME = ‘cc_depth_stencil’;

export const UNIFORM_DEPTH_STENCIL_BINDING = GlobalBindingStart + GlobalBindingIndex++;

const UNIFORM_DEPTH_STENCIL_DESCRIPTOR = new DescriptorSetLayoutBinding(UNIFORM_DEPTH_STENCIL_BINDING, DescriptorType.SAMPLER_TEXTURE, 1, ShaderStageFlagBit.FRAGMENT);

const UNIFORM_DEPTH_STENCIL_LAYOUT = new UniformSamplerTexture(SetIndex.GLOBAL, UNIFORM_DEPTH_STENCIL_BINDING, UNIFORM_DEPTH_STENCIL_NAME, Type.SAMPLER2D, 1);

globalDescriptorSetLayout.layouts[UNIFORM_DEPTH_STENCIL_NAME] = UNIFORM_DEPTH_STENCIL_LAYOUT;

globalDescriptorSetLayout.bindings[UNIFORM_DEPTH_STENCIL_BINDING] = UNIFORM_DEPTH_STENCIL_DESCRIPTOR;

预格式化文本将缩进 4 格

import { _decorator, Component, Node, RenderPipeline, DeferredPipeline, gfx } from “cc”;

import { UNIFORM_DEPTH_STENCIL_BINDING } from “./UboDeffined”;

const { ccclass, property } = _decorator;

@ccclass(“DefferRPCustom”)

export class DefferRPCustom extends DeferredPipeline {

public activate (swapchain: gfx.Swapchain): boolean {

    const result = super.activate(swapchain);

    this._generateCustomData(swapchain);

    return result;

}

private _generateCustomData(swapchain: gfx.Swapchain) {

    this._width = swapchain.width;

    this._height = swapchain.height;

    const deferredData =  this.getPipelineRenderData();

    //将深度图传入全局中

    this.descriptorSet.bindTexture(UNIFORM_DEPTH_STENCIL_BINDING, deferredData.gbufferFrameBuffer.depthStencilTexture!);

    const sampler = this.globalDSManager.pointSampler;;

    this.descriptorSet.bindSampler(UNIFORM_DEPTH_STENCIL_BINDING, sampler);

}

}

然后就是把标准shader拷贝出来改一下透明物体的片元着色器代码罗!

太长了就只贴关键代码了

#if(CC_PIPELINE_TYPE == CC_PIPELINE_TYPE_DEFERRED)

  #pragma builtin(global)

  layout (set = 0, binding = 7) uniform sampler2D cc_depth_stencil;

  #endif

然后在surf中加入处理逻辑:

#if (CC_PIPELINE_TYPE == CC_PIPELINE_TYPE_DEFERRED)

vec2 v_uv2 = vec2(v_clipDepth.x,v_clipDepth.y);

vec4 depthColor = texture(cc_depth_stencil, v_uv2);



float depth = depthColor.x;//0-1



//  vec3 worldPos = screen2WS(vec3(gl_FragCoord.xy, depth)).xyz;

// s.albedo = vec4(worldPos.xyz,1.0);

// return;



if(v_clipDepth.z>depth) discard;



#endif

其中v_clipDepth由顶点传递过来的。。。顶点里面关键代码。咋定义,自己看effect文档。其中posClip为齐次裁剪坐标,取值范围为【-1,1】,所以*0.5+0.5就到【0,1】区间了。

#if(CC_PIPELINE_TYPE == CC_PIPELINE_TYPE_DEFERRED)

v_clipDepth = posClip.xyz/posClip.w*0.5+0.5;

#endif
1赞

我咋看了3.6的源码还是一样的勒。。。可能是我使用的姿势不太对??

查了些资料,不特殊处理下半透明物体,延迟渲染本身就会有这个问题

好像是的,等官方完善延迟渲染管线吧,maybe 3.6.2?

light-stage 里面改下,减一半的UI drawcall

if(CC_EDITOR) self._uiPhase.render(camera, renderPass);

大佬 这个修改的哪个地方?