游戏开发论坛

 找回密码
 立即注册
搜索
查看: 3360|回复: 0

游戏中的Decal(贴花)

[复制链接]

1万

主题

1万

帖子

3万

积分

论坛元老

Rank: 8Rank: 8

积分
36572
发表于 2020-2-26 16:31:31 | 显示全部楼层 |阅读模式
v2-e0740d06989a2b619554c26e5c2acd43_1200x500.jpg

在游戏中,decal是一种非常常见的效果,常用来实现弹孔,血迹,涂鸦等效果。最近研究了下Decal在游戏引擎中的实现方式,大致总结了一下:

1.基于面片实现:

直接用一个Quat的mesh,加上一张贴图,简单直观的实现。

缺点:只能在平面上贴。

2.修改贴图:

将物体的材质贴图替换成原贴图和decal贴图的混合,适用于静态批量的物体

缺点:只适用于静态物体

3.基于SubMesh:

先获取跟目标投影相交的mesh,然后将mesh根据投影框进行裁剪

1.获取所有可能和投影框相交的mesh,一般游戏引擎都会有Octree或BVH保存mesh的aabb,这一步简单获取aabb相交的mesh即可。

2.将mesh的顶点数据变换到投影框的三维空间中,这样一来是方便裁剪,二来是裁剪完成后可以将变换后的坐标值直接作为uv值使用。

3.得到相交的三角形片:

判断每个点是否在投影框内,如果三角形有任意一个点在框内,则认为三角形与投影框相交。当然这种方法会漏掉一些三角形,比如这中情况:当然如果mesh较小以及要求不精细的话也没有太大问题。

image001.jpg
三角形顶点都不在框内,但是和框相交

比较严谨的求交算法可以使用SAT算法,参考 RealTimeRendering4 22.12 或者这个https://gdbooks.gitbooks.io/3dcollisions/content/Chapter4/aabb-triangle.html.

4.将所有相交的三角形片,合并成新的IndexBuffer,使用新的decal的纹理重新渲染一次,UV可以直接取映射到框中的xy值,当然要注意在shader中把uv 0~1之外的部分clip掉

5.如果你想的话,也可以对处在边界,不完全在框内部的三角形进行裁剪,最后整理顶点生成新的mesh.

方法如下:

简单的逐边裁剪:

image003.jpg
逐边裁剪

也可以一次性裁剪所有的方向,参考下面的算法(来自<计算机图形学第三版>6.8.1)

image004.jpg
一次性裁剪所有边

缺点:比较适用于静态的物体,创建过程可能耗时较长

4.基于Multi-Pass实现:

和上面方法很相似

1.获取所有相交的mesh;

2.在mesh正常渲染结束后,再渲染一次,使用decal的shader,向shader中传入一个ClipToDecal的矩阵(=ClipToWorld * WorldToDecal),在FS中计算计算映射到decal框中的坐标,取决于具体的实现,可以将xy坐标作为uv,以及裁剪掉uv0~1之外的部分,将decal渲染出来。

Unity的built-in管线中的Projector就是使用的这种方式。

缺点:如果投影框与多个mesh相交,或者mesh很大,则会产生很大的性能消耗。

5.修改渲染shader实现

判断decal框和某个mesh相交时,将decal标记为需要渲染。修改mesh的shader,传入一个或多个decal投影框矩阵+数张decal贴图。FS得到原始的输出颜色后,再根据decal拿到的颜色进行混合,如果同时有多个Decal,则需要不同数量改变shader变体。

缺点:需要大量调整shader,复杂繁琐,而且一个mesh上的decal数量在运行时发生变化时,需要动态编译shader变体。

6.基于后处理实现:

将decal整体作为一个长方体进行渲染两次来对目标进行贴花

1.首先正常渲染其他的物体,拿到正常渲染的buffer和depth buffer.

2.将投影框作为一个长方体进行渲染,关闭Face Cull,将depth test设置为GreatEqual,输出一个标志位到 stencil buffer(或者任意其他可以标志像素点的方式),不需要输出颜色值

3.再次将投影框作为长方体渲染,打开Face Cull(只绘制长方形靠前的三个面),再上一步中stencil buffer测试通过的位置绘制,与前面方法不同的是,当前像素点的 WorldPosition 通过从depth buffer中读取然后反变换获得(后处理中非常常用的方法).

两种情况下decal绘制示意图

image006.jpg
投影面在靠后的位置

image008.jpg
投影在靠前的位置

缺点:不支持光照

7.Deferred实现

大致和上面的方法相同

deferred 渲染管线中渲染所有gbuffer之后

和上一个方法中讲到的一样,同样是先渲染长方体,写入stencil buffer,然后再次渲染长方体,根据stencil buffer来改变gbuffer中的数据,根据需要选择修改basecolor,normal等;

因为gbuffer被修改,后面的光照计算会产生decal的效果。

UE4中的DefferedDecal,就是这种方法(未使用Dbuffer时)

缺点: 只能用于Deferred,不支持烘培光(因为烘培光是在渲染gbuffer时加上的).

8.Dbuffer

先进行depth prepass渲染深度图

用上面提到的方法将decal渲染到类似gbuffer的dbuffer上,然后在渲染gbuffer时(或者forward渲染时),直接应用同样位置对dbuffer进行采样,融合到gbuffer中,可以支持烘培光,支持deferred,forward管线。

Unity的HDRP以及UE4中的DeferredDecal(使用Dbuffer),使用该方法

image009.jpg
UE4中的Decal Material,当选择使用Dbuffer渲染时,会根据不同的类型来使用不同数量的Dbuffer Render Target

缺点:很大的性能消耗

9.常见的问题

1.垂直角度投影时可能出现这样的拉伸,可以加上一个角度判断,丢弃角度超过某个阈值的像素。

image010.jpg

2.可能需要在边缘处加上一些fade off的效果,防止突兀的边界。

3.blend方式,decal材质可以提供base color,normal等属性,decal是半透明时,需要仔细考虑和原图base color等的混合方式,normal 需要变换下切空间后再进行混合。

4.一般情况下decal无法支持带骨骼动画的物体。

作者:TC130
专栏地址:https://zhuanlan.zhihu.com/p/100748588

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

作品发布|文章投稿|广告合作|关于本站|游戏开发论坛 ( 闽ICP备17032699号-3 )

GMT+8, 2024-12-22 20:07

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表