Unreal材质添加ZWrite控制
可能是使用Unity太长时间的缘故,感觉Unity经常使用得一些东西,Unreal没有就很不方便,也不知道为啥这些普遍功能Unreal都不提供,也不知道我这么改是不是Unreal所期望的。总之,就当实验吧。先说怎么做,再说为什么。
打开Material.h
添加ZWrite属性,以及加上在编辑器上显示的修饰宏。
打开Material.cpp在UMaterial构造函数初始化
做完这些,面板上就有显示了
打开MaterialShared.h,在FMaterial和FMaterialResource中定义GetWrite
在MaterialShared.cpp中实现
打开MobileBasePass.cpp,在FMobileBasePassMeshProcessor的Process中,根据材质是否有ZWrite设置不同的DepthState。
按理来讲这样就应该可以了,但是打开Unreal发现不对,并没有写深度,看来问题没有那么简单,我会在下面讲为什么。
打开MobileShadingRenderer.cpp,找到Render函数,在渲染透明物体的地方,把DepthRT打开。
这样就可以了。
左边是写了深度的透明效果,右边是无深度效果,无深度是没法做到两个物体的重叠透明关系的,虽然左边也是不对的。由于Unreal的网格是最后画的,导致最初看到这个效果,可能会认为是不透明,拿个桌子验证一下,也从另一方面说明,这种方式实现透明物体的里外关系是有一些问题的。
比较费解的是最后一步,把整个通道的DepthWrite给打开了。那其他的不需要写深度的透明物体怎么办,在这里,详细说一下Unreal在移动上是怎么控制读写深度的。
CustomDepth我就不说了,那个就相当于只写深度,不做其他处理,没啥好说的。Unreal渲染通过FRHICommandListImmediate这个类来执行的,这个会调用底层渲染API,如D3D,OpenGL等。在MobileShadingRenderer的Render中,变量名为RHICmdList,这个类在调试的时候可能会给我们带来费解,这也是我在确保我改深度正确性带来了很大的困扰,他调用Command的时候不会直接调用而是缓存起来,当直接Flush的时候,才执行我们刚刚的所有Command。
我们直接跳到渲染透明物体的这一段
我们先姑且认为这里就是直接调用,因为最后收集起来他也是按照顺序调用的。那么这个BeginRenderPass到底做了什么呢?我们只看跟Depth相关的东西,BeginRenderPass调用RHIBeginRenderPass,在这里复制一份FRHIRenderPassInfo,然后继续往下调用RHISetRenderTargetsAndClear这是个跨平台函数,这里 以D3D11为例,在D3D11Commands.cpp中
调用RHISetRenderTargets,这里设置深度的TargetView。
这里会根据CurrentDSVAccessType获取DepthView,接着下面会设置让GPU知道我这个View会写深度(关闭读写应该会提升性能)。如果DepthView都没有切换过来,你在DrawCommand里打开深度绘制肯定不会起任何效果。
好,在设置完DepthView后,回到MobileShadingRenderer的Render中,我们进入到RenderTranslucency里渲染透明物体了
这里,前两章的知识了,够着DrawCommand,重点关注Depth的处理,继续调用到
然后
接着
最终到达MeshPassProcessor.cpp的
这里是取出之前在生成DrawCommand的时候创建的MeshPipelineState。
然后接着往下调用
这个又是一个跨平台函数,找到D3D11的这个接口
再往下
终于找到我们的DepthState的设置了
最终调用D3D11的API
-----------------------------------------------------------------------------------------------分割线---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
在修改了这些后,会有个bug。就是在透明通道那个渲染序列里,如果出现使用深度图,并且又没有对深度图进行处理,诸如MSAA这种,那么就获取不到深度图。
先说为什么
这是因为,在上面我们在BeginRender的地方设置深度View的地方
这个地方,我们正常的透明通道渲染获取的是Read的深度View,为了更加理解这个东西,我直接找到设置这个东西给显卡的操作。还是这个函数往下
会调到这个地方
这个地方是设置DepthViewTarget的D3D入口API。配合我们上面说的每个Pass会自己设置自己的RenderState
通过这两个地方控制渲染深度。
那么问题来了,当我们把透明通道的深度View设置成跟不透明通道的深度View设置成同一个,即能写深度的View。我们需要用这个View提供给透明通道作Out操作,而在贴花处理中,取的也是Write这个flag对应的View。即读写都是这个View,一个View不可能既写又读,这样会导致设置深度贴图失败。
那么我们应该怎么修改呢?
其实也比较简单,就是在需要使用深度贴图的地方,再重新启用一个新RenderPass。比如贴花的处理。
我们在RenderDoc里看看效果
在Unreal中贴花也能正常显示
经过这个风波后,我对暴露ZWrite这件事持怀疑态度了,这样可能会影响深度图相关的东西,我这样修改又增加了主渲染的RenderState切换压力。要实现上面的效果可以在不透明渲染里插入一个只写深度不写颜色的批次,然后在渲染也能实现类似效果 ,并且暴露ZWrite实现的这个效果也不是真实半透明。暴露ZWrite可能还有其他功能,但是需要处理的地方也比较多,就看权衡把,我觉得是如果就实现本文的需求,没必要做这件事情。
页:
[1]