Tile Forward Shading

在现代化的GPU年代,现代化硬件得到了质的飞跃,由于晶体管的精仪程度越来越高,摩尔定律在近些年也有了失效。因此,我们将优化转化为在软件架构设计和最新技术的研发上,每一次一项新的技术提出,都是对性能的提高。

下图为在一个场景 10000个点光源所渲染的场景 在显卡为 3070的N卡上很稳定。

延迟着色有2个最主要的特征:decoupling of lighting and shading from geometry management and minimization of the number of lighting computations performed [Hargreaves and Harris 04].延迟着色要求有更好的几何图形提交和管理,并简化了着色器和着色器的资源管理。

传统前向渲染的管线通常一个接一个的渲染对象,并要考虑每个光栅化过程中与光的交互。在延迟着色中,我们将集合图形的表示渲染到屏幕大小的G-buffer缓冲区中 [Saito and Takahashi 90],它包含了每个像素的着色数据,如法线和深度信息。然后在一个单独的过程,使用光照和着色。例如,逐个渲染光源(其中每个光源有包围光线区域边界体表示)。对于在此传递过程中生成的每个片段,从G-buffer中提取相应的像素数据,计算像素值,并将结果混合到输出缓冲区中。执行的照明计算数量非常接近每个可见样本的每个灯光一个的最佳值(在某种程度上取决于用于表示光源的边界体积)。

因此,延迟着色成功减少了照明的计算量,但代价是增加了内存需求和更高的内存带宽的需求。Tiled deferred shading 修复了内存带宽的频繁使用,但仍需要较大的G-buffer.

概述 前向,延迟和Tiled着色

使用前向着色,我们指的是渲染过程,其中照明和着色计算在几何图形光栅化在同一过程执行。这对应于标准设置,包括转换几何图形顶点着色器和计算每个光栅化片段着色器上的颜色结果。

延迟着色器将这个过程分为两个通道。首先,几何图形被光栅化,但是与前向着色器相比,几何图形属性被输出到一组几何图形缓冲区(G-buffer)中,在以这种方式处理了所有的几何图形之后,将使用储存在G-buffer中的数据来执行一个计算照明和完全着色的额外过程。

Tiled 延迟着色通过将样本划分为N * N样本的tiles来实现这一点。然后将灯光分配给这些tiles,我们可以选择计算每个贴图的最小和最大的z边界,这允许我们进一步减少影响每个tile的灯光数量。

[Olsson and Assarsson 11]Tiled 的延迟着色有如下好处:

* G-buffer对每个照明样本只会读取一次。

* framebuffer只会写入一次。

* 渲染方程中的公共项可以分解并计算一次,而不是为每一束光重新计算他们。

* 每个贴图的工作将会变得一致,,贴图中的样本都需要相同的工作量(在相同的灯光下迭代),这允许在SIMD下更有效的实现。

对于大多数灯光有一个有限的范围,Tiled 延迟着色是值得的。如果所有灯光都有潜在地影响到所有的场景,那么对于这种,Tiled着色显然没有任何好处。

Tiled延迟着色可以推广到Tiled Shading,其中包括了2种延迟和前向变体。基本的Tiled 着色算法如下所示:

1 将屏幕细分为tiles。

2 可选:查找每个tile的最小和最大的Z边界。

3 为每个tile指定灯光。

4 对于每个采样点:处理影响当前采样贴图的所有灯光及着色。

步骤 1 屏幕细分大小基本上是随意的。步骤 2 为每个tile查找最小和最大Z边界是选的(步骤2)。例如,在一个具有低深度复杂度的场景上的一个自顶向下的视图可能不允许在z方向上剔除额外的光线。然而,其他情况也可以从更紧密的瓷砖z边界中受益,因为发现影响该瓷砖的灯光更少(下图)。

在tiled 延迟着色,步骤4中的采样样本实在G-buffer中萃取的,在tile前向着色中,样品实在光栅化过程中生成的。

为什么选择Tile 前向着色

延迟着色的主要优势(包括Tile延迟着色)是消除了over-draw中的over-shading,然而,与前向着色渲染相比,大多数延迟渲染都具有如下缺点:

* 对透明度/混合是绘制很麻烦,因为传统的G-buffer中只允许在缓冲区的每个位置存储一个样本。

* 使用MSAA和相关的一些技术,对内存的带宽要求更高。

另一方面,前向渲染就有很好的支持:

* 透明度通过alpha混合

* MSAA和相关技术得到很好的硬件支持(更少的内存储存)

此外,正向渲染支持不同的着色器和材质,延迟需要传递多个“超级着色器”.

基本的Tiled Forward Shading

1 将屏幕细分为tiles

2 可选:pre-z 通道渲染几何图形并在标准的Z-buffer中储存每个样本的深度值。

3 可选:查找每个tile的最小边界和最大边界。

4 为每个tile分配指定灯光

5 为每个生成的片段渲染几何图形并计算着色

屏幕细分: 我们使用规则的N * N像素贴图(例如32*32),拥有非常大的tile会产生更糟糕的光分配;每个tile都会受到更多光源的影响,这些光源会影响tile中较小的样本子集。创建非常小的贴图会使光分配更加昂贵,并增加所需的内存存储——特别是当贴图足够小,发现许多相邻的贴图受到相同光源的影响。

可选 pre-Z pass: 首先我们希望在下一步中找到每个Tile的z边界,则是必须的。其次,在最终的渲染过程中,它可以通过早期的z测试和类似的硬件特性来减少着色处理样本的数量。当然 ,z通道只应该包含不透明的几何图形。

可选的最小化和最大化Z-bounds 如果存在一个深度缓冲区,例如,从上面描述的pre-z通道,我们可以使用这些信息来查找(减少)Z方向(深度)上每个tile的范围。这会产生更小的每个块边界体积,减少了在灯光分配期间影响贴块的灯光数量。(下图)

光源分配:接下来,我们必须为tile分配灯光,基本上,我们想要有效地找到那些灯影响样本的tile,这需要一些选择和考虑。这可在CPU和GPU上实现,一些简单实现是为每个光源找到屏幕空间轴对齐的边界框(AABB),并循环AABB的2D区域中包含所有tile上,如果我们已经计算了每个tile的最小深度和最大深度,我们需要执行一个额外的测试,已丢弃z方向上的tile之外的灯光。

渲染额着色:最后一步是渲染所有的几何图元,和标准的前向渲染管道一样;不同着色器和相关的一些资源可能被附加到不同的几何块。除了Fragment Shader外其他没有任何区别。Fragment Shader对每一个生成的样本,通过分配给tile中的灯光来检测影响到的灯光。

透明度的支持

延迟渲染在处理透明度方面有很大的困难,因为传统的"G-buffer只在每个缓冲区位置储存单个样本的属性"[Thibieroz and Gr¨un 10],然而,对于前向渲染,我们不需要存储任何单个对应样本。相反我们只需要使用简单的Alpha混合生成的颜色。这里我们没有说明物体之间的前后顺序,所以,我们必须保证,按照正确的先后顺序绘制透明对象。我们将光列表分为透明和不透明光。除了分别为不透明和透明几何图形使用适当的光列表外,这里不需要进行特殊的修改。首先,应该渲染所有不透明的几何图形。然后将透明几何图形正确排序渲染。

对MSAA的支持

使用Tiled Forward Shading对MSAA兼容性很高,我们需要在确保所有渲染目标的动作都在MSAA中创建,此外我们在可选的 minimum和maximum Z-bound中采样更多的样本。

(a)渲染时间和(b)内存使用Tile前向和tile延迟阴影与不同的MSAA设置。我们无法为延迟分配8×MSAA框架缓冲区,这就是为什么该配置没有计时结果的原因。内存使用估计是基于32位深度、32位环境和64位正常、漫反射和漫反射镜面组件(使用RGBA16F格式)

总结和改善

我们已经探索了前向着色结合了Tile延迟着色和标准的前向渲染的优点。但它也有一定的问题,比如会导致性能和鲁棒性问题。解决这一问题的方法是Clustered Shading,如上图所示,在z方向具有不可预测的复杂性,硬件要求(严重依赖于计算着色器)由于既要吧屏幕细分,又要在z深度细分。

实现:

将屏幕按照 gl_GlobalInvocationID 显卡全局工作组来划分屏幕

ivec2 workUnit = ivec2(gl_GlobalInvocationID.xy

vec2 TexCoord = vec2(workUnit .x / 屏幕宽度, workUnit .y / 屏幕高度)

我们假设我们吧屏幕分为16 *16块的大小

uint TileMinx = 16 * gl_WorkGroupID.x;

uint TileMaxx = 16 * (gl_WorkGroupID.x + 1);

uint TileMiny = 16 * gl_WorkGroupID.y;

uint TileMaxy = 16 * (gl_WorkGroupID.y + 1);

将屏幕中每一Tile转化为视锥体

vec4 TileCorner[4];

TileCorners[0] = vec4((float(TileMinX) / u_WindowWidth) * 2.0f - 1.0f, (float(TileMinY) / u_WindowHeight) * 2.0f - 1.0f, 1.0f, 1.0f);

TileCorners[1] = vec4((float(TileMaxX) / u_WindowWidth) * 2.0f - 1.0f, (float(TileMinY) / u_WindowHeight) * 2.0f - 1.0f, 1.0f, 1.0f);

TileCorners[2] = vec4((float(TileMaxX) / u_WindowWidth) * 2.0f - 1.0f, (float(TileMaxY) / u_WindowHeight) * 2.0f - 1.0f, 1.0f, 1.0f);

TileCorners[3] = vec4((float(TileMinX) / u_WindowWidth) * 2.0f - 1.0f, (float(TileMaxY) / u_WindowHeight) * 2.0f - 1.0f, 1.0f, 1.0f);

TileCorners[0] = convertNDC2ViewSpace(TileCorners[0]);

TileCorners[1] = convertNDC2ViewSpace(TileCorners[1]);

TileCorners[2] = convertNDC2ViewSpace(TileCorners[2]);

TileCorners[3] = convertNDC2ViewSpace(TileCorners[3]);

vec4 TileFrustum[4];

for(int i = 0; i < 4; ++i)

TileFrustum[i] = createPlane(TileCorners[i], TileCorners[(i + 1) & 3]);

Tile视锥体和光源进行相交测试(视锥裁剪)

uint ThreadsNumInTile = LOCAL_GROUP_SIZE * LOCAL_GROUP_SIZE;

for(uint i = 0; i < u_TotalLightNum; i += ThreadsNumInTile)

//i加ThreadsNumInTile是为了让局部工作组里的所有线程并行的进行光源和Tile的求交

{

uint k = gl_LocalInvocationIndex + i;

if(k < u_TotalLightNum)

{

SPointLight PointLight = u_PointLights[k];

vec4 LightViewPos = u_ViewMatrix * PointLight.Position;

float LightRadius = PointLight.ColorAndRadius.w;

if((-LightViewPos.z + LightRadius < MinDepthInViewSpace) || (-LightViewPos.z - LightRadius > MaxDepthInViewSpace))

continue;

//小于还包括光源到Tile视锥平面的距离是负数

if((calcSignedDistanceFromPlane(LightViewPos, TileFrustum[0]) < LightRadius)

&& (calcSignedDistanceFromPlane(LightViewPos, TileFrustum[1]) < LightRadius)

&& (calcSignedDistanceFromPlane(LightViewPos, TileFrustum[2]) < LightRadius)

&& (calcSignedDistanceFromPlane(LightViewPos, TileFrustum[3]) < LightRadius))

{

uint CurrrentCulledLightCount = atomicAdd(s_CulledLightCount, 1);

s_CulledLightIndexs[CurrrentCulledLightCount] = k;

}

}

}

barrier();

计算光照,直接在这遍Pass里算的好处是可见光源索引都被存储到共享内存上,不用存到别的Buffer(主存)里了

vec3 Normal = texture(u_NormalTexture, TexCoord).xyz;

vec3 Albedo = texture(u_AlbedoTexture, TexCoord).xyz;

vec3 FragViewPos = texture(u_PositionTexture, TexCoord).xyz;

vec3 FragColor = Albedo * 0.1;

if(FragDepth != 1.0f)

{

for(uint i = 0; i < s_CulledLightCount; ++i)

{

uint LightIndex = s_CulledLightIndexs[i];

SPointLight PointLight = u_PointLights[LightIndex];

vec4 LightViewPos = u_ViewMatrix * PointLight.Position;

FragColor += calcPointLight(FragViewPos, Normal, LightViewPos.xyz, PointLight.ColorAndRadius.xyz, PointLight.ColorAndRadius.w);

}

}

FragColor *= Albedo;

[知乎] https://zhuanlan.zhihu.com/p/357275455

[Andersson 09] Johan Andersson. “Parallel Graphics in Frostbite - Current & Future.” SIGGRAPH Course: Beyond Programmable Shading, New Or

leans, LA, August 6, 2009. (Available at http://s09.idav.ucdavis.edu/talks/04-JAndersson-ParallelFrostbite-Siggraph09.pdf.)

[Balestra and Engstad 08] Christophe Balestra and P˚al-Kristian Engstad. “The Technology of Uncharted: Drake’s Fortune.” Presentation, Game Developer

Conference, San Francisco, CA, 2008. (Available at http://www.naughtydog.com/docs/Naughty-Dog-GDC08-UNCHARTED-Tech.pdf.)

[Enderton et al. 10] Eric Enderton, Erik Sintorn, Peter Shirley, and David Luebke. “Stochastic Transparency.” In I3D 10: Proceedings of the 2010 ACM SIGGRAPH Symposium on Interactive 3D Graphics and Games, pp. 157–164. New York: ACM, 2010.

[Engel 09] Wolfgang Engel. “The Light Pre-Pass Renderer: Renderer Design for Effiffifficient Support of Multiple Lights.” SIGGRAPH Course: Advances

in Real-Time Rendering in 3D Graphics and Games, New Orleans, LA, August 3, 2009. (Available at http://www.bungie.net/News/content.aspx?

type=opnews&link=Siggraph 09.)

[Hargreaves and Harris 04] Shawn Hargreaves and Mark Harris. “Deferred Shading.” Presentation, NVIDIA Developer Conference: 6800

Leagues Under the Sea, London, UK, June 29, 2004. (Available at http://http.download.nvidia.com/developer/presentations/2004/6800 Leagues/6800 Leagues Deferred Shading.pdf.)

[Kircher and Lawrance 09] Scott Kircher and Alan Lawrance. “Inferred Lighting:Fast Dynamic Lighting and Shadows for Opaque and Translucent Objects.”

In Sandbox ‘09: Proceedings of the 2009 ACM SIGGRAPH Symposium on Video Games, pp. 39–45. New York: ACM, 2009.

[Lauritzen 10] Andrew Lauritzen. “Deferred Rendering for Current and Future Rendering Pipelines.” SIGGRAPH Course: Beyond Programmable Shading,

Los Angeles, CA, July 29, 2010. (Available at http://bps10.idav.ucdavis.edu/talks/12-lauritzen DeferredShading BPS SIGGRAPH2010.pdf.)

[Olsson and Assarsson 11] Ola Olsson and Ulf Assarsson. “Tiled Shading.” Journal of Graphics, GPU, and Game Tools 15:4 (2011), 235–251. (Available at http://www.tandfonline.com/doi/abs/10.1080/2151237X.2011.621761.)

[Olsson et al. 12a] Ola Olsson, Markus Billeter, and Ulf Assarsson. “Clustered and Tiled Forward Shading: Supporting Transparency and MSAA.” In SIGGRAPH ‘12: ACM SIGGRAPH 2012 Talks, article no. 37. New York: ACM, 2012.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值