Untiy Decal 贴花

目录

特殊文件夹

SetRenderTarget

Decal

        DBuffer Decal

UE

Unity

Forward Decal


特殊文件夹

Unity 保留了一些项目文件夹名称来指示内容具有特殊用途。其中一些文件夹会影响脚本编译的顺序。这些文件夹名称为:

  • Assets

  • Editor

  • Editor default resources

  • Gizmos

  • Plugins

  • Resources

  • Standard Assets

  • StreamingAssets

Editor文件夹 (不会打包),Editor文件夹可以在根目录下,也可以在子目录。Editor下面放的所有资源文件或者脚本文件都不会被打进发布包中,并且脚本也只能在编辑时使用

编译阶段如下:

阶段程序集名称脚本文件
1Assembly-CSharp-firstpass名为 Standard Assets、Pro Standard Assets 和 Plugins 的文件夹中的运行时脚本。
2Assembly-CSharp-Editor-firstpass名为 Editor 的文件夹(位于名为 Standard Assets、Pro Standard Assets 和 Plugins 的顶级文件夹中的任意位置)中的 Editor 脚本。
3Assembly-CSharp不在名为 Editor 的文件夹中的所有其他脚本。
4Assembly-CSharp-EditorAll remaining scripts (those that are inside a folder called Editor).

注意:标准资产仅适用于资产 根文件夹。

SetRenderTarget

  1. 屏幕的渲染目标可以通过Graphics.activeColorBuffer和Graphics.activeDepthBuffer获取。其中Graphics.activeDepthBuffer当中包括了stencilbuffer和depthbuffer。

  2. commandbuffer当中同样可以使用BuiltinRenderTextureType.Color和BuiltinRenderTextureType.depth来访问对应的渲染目标,但是BuiltinRenderTextureType.depth是不包括stencilbuffer的。

  3. 如果想要复用包含stencilbuffer的Graphics.activeDepthBuffer,是不能通过SetRenderTarget和自定义的ColorBuffer组合的,会报错。因为screen的buffer和rendertarget的buffer不能混合使用

  4. 如果只需要复用同一张depth,只需要使用BuiltinRenderTextureType.depth和自定义的colorbuffer即可。

  5. 如果需要复用同一张depth/stencil,必须要在一开始修改渲染目标为自定义的RenderTarget,例如:SetRenderTarget(selfColorBuffer, selfDepthBuffer) ,同时selfDepthBuffer的深度位数必须为24或32. 如果是16 则不支持stencilbuffer。

A: 举一些例子来说明。

例子1:比如,你先在RenderTexture A绘制了一个三角形,然后在RenderTexture B绘制任意一个东西,再然后想继续往RenderTexture A绘制东西,但是要求是A上的那个三角形还要在,那么就需要RenderBufferLoadAction.Load。这个操作会导致RenderTexture A需要从local memory复制到tile memory,这样就多了一倍的带宽。带宽是移动游戏发热的根本原因。

例子2:比如,你在一个刚申请出来的RenderTexture A绘制一个三角形,那么就需要RenderBufferLoadAction.Clear。否则,谁都不知道刚申请出来的这个RenderTexture是什么样子,所以需要Clear一下。Clear操作只是给这个RenderTexture打个标记,虽然有代价,但是比较小。

例子3:比如,在一个刚申请出来的RenderTexture A绘制一个三角形,但是你自己知道,你绘制的这个三角形是全屏的,会覆盖满整个屏幕,这个时候就不需要Clear了,直接RenderBufferLoadAction.DontCare。这样毫无代价,效果也完全没问题。

例子4:比如,你将RenderTexture A作为Color RenderTexture,RenderTexture D作为Depth RenderTexture,绘制两个有前后顺序的三角形。然后,你将RenderTexture A blit到RenderTexture B上,这个时候,要对RenderTexture A设置为RenderBufferStoreAction.Store,对RenderTexture D设置为RenderBufferStoreAction.DontCare。这个操作,RenderTexture A会从tile memory复制到local memory,而RenderTexture D则会直接被抛弃,不会产生额外的带宽。

当然这个操作还有个前提,RenderTexture D被通过的RenderTextureMemoryless.Depth设置为memoryless。

Decal

DBuffer Decal

UE

UE使用DBufferA、DBufferB、DBufferC三张RT,将贴花信息渲染到这三张DBuffer RT并非直接在GBuffer上。一旦dbuffer填充好了数据,我们就可以在后面的basepass中做贴花的应用了。DBuffer的渲染放在basepass之前因为ue4会在basepass里做一些光照的计算,并同时把计算的结果输出到scenecolor中。这些光照的计算需要用到材质的若干属性,最常见的有basecolor,normal,以及roughness等。而decal存在的意义就是为了修改原始geometry表面的材质属性,然后在basepass中与原生的材质属性进行混合叠加,生成最终的属性值,这时再进行光照计算才能保证结果的正确。

在渲染Gemoetry的BasePass阶段结合PrePass阶段得到的EarlyZ信息,如果满足条件后对DBuffer进行采样,混入到GBuffer中。这是在光照流程前渲染(这里可以修改BaseColor,法线,金属度、高光强度等),可以支持烘培光,PBR;

  • DBufferA 的rgb通道代表贴花的base色彩,a通道代表不透明度,GBuff色彩计算公式为

BaseColor = BaseColor * DBufferData.ColorOpacity + DBufferData.PreMulColor;

  • DBufferB 的rgb通道代表世界法线,a通道代表该像素原来世界法线强度,GBuff法线计算公式

WorldNormal = normalize(WorldNormal * DBufferData.NormalOpacity + DBufferData.PreMulWorldNormal);

  • DBufferC 的r通道代表贴花金属度,g通道代表贴花高光强度,b通道代表贴花粗造度,a通道代表该像素原粗造度强度。

Unity

Unity 将贴花渲染到DBuffer,然后在渲染不透明对象期间将 DBuffer 的内容覆盖在不透明对象之上。

先在 depthprepass 渲染深度图,然后将贴花(Decal)渲染到类似 GBuffer 的 DBuffer 上,然后再渲染 GBuffer 的时候,对 DBuffer 进行采样,融合到 GBuffer 中。之后根据渲染路径的不同:

前向 -在渲染不透明对象期间,每个对象将 _DBufferTexture0-3 应用于其表面数据 Surface Data。

延迟 -在渲染 GBuffer 期间,每个对象都将 _DBufferTexture0-3 应用于其表面数据 Surface Data。

为什么不用DBuffer Decal

depth prepass预先把整个场景所有物体的深度渲染一次,只写深度不做fragment shading,后面再进行不透明物体渲染的时候直接使用深度的结果进行深度测试。

DBuffer的引入会造成带宽和存储空间的增加,而且depth-prepass会渲染所有的geometry

将物体的渲染拆分为两步(在同一个render pass内),第一步,渲染depth,第二步,渲染颜色,所以物体的geometry部分是需要做两遍的。

depth prepass不适合移动开发:所有顶点都需要处理两次,移动设备的顶点处理会在PC耗费更多的时间,会增加的GPU时间。

Deferred Decal的基本思路就是,在G-Buffer建立之后,把每一个decal当作一个立方体迭加到G-Buffer上。对于G-Buffer中被立方体覆盖的像素,用decal的color和normal来替换掉。为了确定每个Decal所能够影响到的范围,一般需要绘制出Decal所对应的一个包围几何体,本DecalMesh选用CubeMesh,默认绑有DecalMat材质,默认使用shader为DeferredDecal。

计算贴Decal的那些Pixel所对应的Decal uv,需要知道Decal box所对应的局部坐标系,在局部坐标中计算出对应的Decal UV进行纹理读取。

// 计算Decal屏幕空间位置,返回的是齐次坐标下的屏幕坐标值
output.screenPos = ComputeScreenPos(output.positionCS);

// 获取View space中相机到顶点的射线
float3 viewRay = vertexInput.positionVS;
// unity's camera space 为右手系
viewRay *= -1;


// 将摄像机坐标和摄像机到贴花射线转换到贴花空间
// 相当于在Decal的位置上放置一个Camera,即从Decal的角度来观察整个场景
output.viewRayOS.xyz = mul((float3x3) ViewToObjectMatrix, viewRay);
output.cameraPosOS.xyz = mul(ViewToObjectMatrix, float4(0, 0, 0, 1)).xyz;
// 从GBuffer里获取深度图,深度有压缩需要先解压
half4 depthOcclution = SAMPLE_TEXTURE2D(_CameraDepthTexture, s_point_clamp_sampler, screenSpaceUV);
float depth = UnpackDepth(depthOcclution.xyz);

// 从深度缓存重建世界空间坐标(因为采样_CameraDepthTexture的结果是非线性的)
// TO = FROM + Dir * Length
float sceneDepth = LinearEyeDepth(depth, _ZBufferParams);
float3 decalSpacePos = input.cameraPosOS.xyz + input.viewRayOS.xyz * sceneDepth;

// 计算decal纹理坐标,模型空间[-0.5,0.5]转到[0,1]
float2 uv = decalSpacePos.xz + 0.5;

// 使用ddx,ddy计算法线解决拉伸
float3 decalSpaceHardNormal = normalize(cross(ddy(decalSpacePos), ddx(decalSpacePos))); 
float shouldClip = decalSpaceHardNormal.y > _ClipThread ? 0 : 1;
  
// 计算GI
bakedGI = SampleSH(normalWS);
half3 color = GlobalIllumination(...)

//处理法线效果
#if _GBUFFER_NORMALS_OCT        
    half2 octNormalWS = PackNormalOctQuadEncode(normalWS); 
    half2 packedNormalWS = octNormalWS.xy * 0.5f + 0.5f;           
#endif        


// 写入GBuffer
GBuffer0 = GI
GBuffer1 = COLOR
GBuffer2 = NORMAL

在延迟渲染中完成正常的G-Buffer渲染后,即可得到对应的AlbedoMaterialFlag buffer,以及NormalMetallicSmoothness Buffer,在G-Buffer后再增加一个Pass,把每一个decal当作一个立方体迭加到G-Buffer上。对于G-Buffer中被立方体覆盖的像素,用decal的color和normal来替换掉。需读取Depth并写入GBuffer的Albedo、GI、Normal

Forward Decal

Forward Decal本质上的实现方法同Deferred Decal,都是从深度缓存重建物体空间位置,然后使用其中的xz轴作为纹理坐标进行的采样,和Deferred不同的地方在于Deferred Decal从GBuffer里读取Depth并且计算后的结果写进GBuffer的GI,Albedo,Normal,Forward Decal从DepthBuffer中取出DepthTexture然后输出ColorBuffer。

关于ddx/ddy:在光栅化的时刻,GPUs会在同一时刻并行运行很多Fragment Shader,但是并不是一个pixel一个pixel去执行的,而是将其组织在2x2的一组pixels分块中,去并行执行。对于一个着色点,只需要求出他的上下左右的位置信息,然后利用叉乘来近似计算该点的法线。ddx 就是右边的像素块的值减去左边像素块的值,而ddy就是下面像素块的值减去上面像素块的值。其中的x,y代表的是屏幕坐标。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值