Posts

一篇文章说透计算着色器的SV_DispatchThreadID

请尊重原作者的工作,转载时请务必注明转载自:www.xionggf.com SV_DispatchThreadID 是HLSL(High-Level Shading Language)中的一个语义(Semantic),用于在Compute Shader中获取当前线程的全局线程ID。它是Compute Shader中非常重要的一个概念,用于确定每个线程处理的数据或任务。 SV_DispatchThreadID 的含义 全局线程ID: SV_DispatchThreadID 是一个 uint3 类型的变量,表示当前线程在全局线程空间中的ID。 它的三个分量(x、y、z)分别表示线程在X、Y、Z三个维度上的全局索引。 线程的全局位置: SV_DispatchThreadID 的值范围由程序语言层(例如C#) 的Dispatch 调用,和HLSL语言中的 numthreads 语句定义。 例如,如果调度了 Dispatch(10, 20, 30),并且 numthreads 是 [8, 8, 1],那么: SV_DispatchThreadID.x 的范围是 [0, 10*8-1](即 [0, 79])。 SV_DispatchThreadID.y 的范围是 [0, 20*8-1](即 [0, 159])。 SV_DispatchThreadID.z 的范围是 [0, 30*1-1](即 [0, 29])。 用途: SV_DispatchThreadID 通常用于确定当前线程处理的数据索引。 例如,在处理2D纹理时,SV_DispatchThreadID.xy 可以直接用作纹理坐标。 在处理1D数组时,SV_DispatchThreadID.x 可以直接用作数组索引。 SV_DispatchThreadID 的计算方式 SV_DispatchThreadID 的值是由以下两个因素决定的: 线程组的大小(由 numthreads 定义): 例如,[numthreads(8, 8, 1)] 表示每个线程组有 8x8x1 = 64 个线程。即是说,numthread语句,是声明“线程”的。 线程组的数量(由 Dispatch 调用定义):

层次化Z缓冲区拣选算法学习笔记

1 Nick Darnell的Hiz算法总结和分析笔记 基于层次化Z缓冲区的遮挡拣选(Hierarchical Z-buffer occlusion culling)的实现方式有多种,其中有一种是基于论文Real-Time Rendering (Section 3.3.3)。 1.1 层次化Z缓冲区(Hierarchical Z-buffer,HiZ)剔除步骤 让美术准备好用来遮挡住其他物体的遮挡体(occlusion geometry) CPU端:获取到所有的遮挡体,且将在视截体之外的给剔除掉 GPU端:把上一步剔除剩下的遮挡体,将其深度值渲染到深度缓冲区D-RT0 GPU端:对D-RT0进行采样,并填充整个mipchain CPU端:对当前场景中的所有可见物体,获取到其物体的包围球 GPU端:把上一步获取到的物体包围球,计算该包围球对应的屏幕空间宽度,然后使用该宽度计算 mip 级别,从步骤 4 中生成HiZ 贴图中进行采样 CPU端:读回计算着色器的缓冲区输出 1.2 对分层Z缓冲区进行降采样的HLSL代码 上述步骤中的第4步用到的降采样的方式如下: 取当前的,右边一个、下方一个和右下方一个像素进行比较,取其值最大者,作为降采样像素中的新深度值。下面两个图是降采样前后版本的示例,黑色表示较近的深度,像素越白,深度值越远/越高。 降采样HLSL代码如下所示 float4 vTexels; vTexels.x = LastMip.Load( nCoords ); vTexels.y = LastMip.Load( nCoords, uint2(1,0) ); vTexels.z = LastMip.Load( nCoords, uint2(0,1) ); vTexels.w = LastMip.Load( nCoords, uint2(1,1) ); float fMaxDepth = max( max( vTexels.x, vTexels.y ), max( vTexels.z, vTexels.w ) ); 1.3 层次化Z缓冲区的剔除核心算法 cbuffer CB { matrix View; matrix Projection; matrix ViewProjection; float4 FrustumPlanes[6]; // 视截体的6个面,基于世界坐标,每个面的法线朝外 float2 ViewportSize; // 视口的高和宽,基于像素 float2 PADDING; }; // GPU只读的缓冲区,因为声明了使用t0着色器,故而每一个点是一个float4 // float4的xyz分量记录了包围球的球心坐标,w分量记录了球的半径。基于 // 世界坐标系 StructuredBuffer Buffer0 : register(t0); // Is Visible 1 (Visible) 0 (Culled) // GPU可读写的缓冲区,每一个点是一个uint,值为1时该点可视,0时该点不可视 RWStructuredBuffer BufferOut : register(u0); Texture2D HizMap : register(t1); SamplerState HizMapSampler : register(s0); // 计算给定的点vPoint到平面方程的距离 // vPlane: 四个xyzw分量,分别包含了平面方程的四个系数abcd: ax + by + cz = d // vPoint: Point to be tested against the plane.

使用mipmap streaming来优化GPU的纹理内存

请尊重原作者的工作,转载时请务必注明转载自:www.xionggf.com 本文所涉及到的API和Unity3D编辑器都是基于6000.0.25f1c1版本 1 mipmap streaming 简介 使用 mipmap 流来限制 GPU 内存中纹理的大小。 1.1 mipmap streaming的工作原理 对于每个被摄像机观察到的纹理,Unity会自动计算并加载其特定级别的mipmap,而不是加载所有级别。这意味着 Unity 只会将每个纹理的指定mipmap level的内容从磁盘传输到 CPU 和 GPU。 Unity 会以尽可能高的分辨率加载 mipmap level,但如果较高分辨率的 mipmap level不符合您设置的内存限制,则使用较低的 mipmap level。mipmap可预配置 Unity 在 GPU 上缓存指定mipmap level的纹理内容,以避免重复加载。 1.2 mipmap streaming的限制 Unity不支持对地形纹理使用mipmap streaming,因为 Unity 始终需要最高分辨率的 mipmap level。 Unity不支持对纹理数组、cubemap,3D纹理使用mipmap streaming。 如果使用Graphics.DrawMeshNow等 API来渲染纹理,Unity 将无法获得计算 mipmap level所需的信息。使用Texture2D.requestedMipmapLevel API 手动设置纹理的 mipmap level,或者禁用mipmap streaming。 如果纹理有tiling和offset属性,但又没指定使用_ST属性,Unity可能无法正确计算这种纹理的mipmap level。 2 启用 mipmap streaming 要启用 mipmap streaming,按照以下步骤操作: 点击【Edit|Project Settings】菜单项,弹出【Project Settings】面板,选中【Quality】项 在【Texture】部分中,启用【Mipmap Streaming】 在默认情况下,这时候为场景中的所有摄像机都打开了mipmap streaming。在编辑器中,mipmap streaming在edit mode或者play mode下都处于开启状态。

Software Virtual Texture 学习笔记

在MegaTexture的基础上,id Software进一步提出了Virtual Texture的概念,这个概念取自于Virtual Memory。与虚拟内存类似的是,一个很大的Texture将不会全部加载到内存中,而是根据实际需求将需要的部分加载;与虚拟内存不同的是,它不会阻塞执行,可以使用更高的Mipmap来暂时显示,它对基于block的压缩贴图有很好的支持。 基本思路是:将纹理的Mipmap chain分割为相同大小的Tile或Page,这里的纹理是虚纹理,然后通过某种映射,映射到一张内存中存在的纹理,这里的纹理是物理纹理,在游戏视野发生变化的时候,一部分物理纹理会被替换出去,一部分物理纹理会被加载。 这样的机制不仅仅减少了带宽消耗和内存(显存)消耗,也带来了其它好处。比如有利于合批,而不用因为使用不同的Texture而打断合批,这样可以根据需求来组织几何使得更利于Culling。当然合批的好处是states change变少,LightMap也可以预计算到一张大的Virtual Texture上用来合批。 1 地址映射 地址映射在Virtual Texture中是一个很重要的环节,即是“如何将一个virtual texture上的texel,对应地映射到phyiscal texture上去”。同时还需要处理“假如高分辨率的page没有加载的话,如何获得已加载的相对应的低分辨率的page” 1.1 四叉树映射 使用四叉树主要是为了和Mipmap对应,也就是每个低MIP的Map会对应有四个高MIP的Map,四叉树中只存储加载的Mipmap信息。这里的对应关系就是每个加载的Virtual Texture的Page对应一个四叉树的节点,具体的计算如下: 这里存在每个四叉树的节点中的内容就是bias和scale,这样就可以将虚拟纹理的地址转换成物理纹理的地址。如果没有找到,也可以用父节点的地址来得到低分辨率。但是这里要找到对应的节点需要搜索这个四叉树,搜索的速度取决于树的高度,也就是Mipmap的层级,在差的低MIP的地址上效率会比较低。 1.2 单像素对应虚纹理的一个Page的映射 为了减少索引,首先容易想到的就是,为每个虚纹理的Page都存储一份信息,这样就能直接转换了。这个方案就是创建一个带Mipmap的Texture,一个Texel对应虚纹理的一个Page,Texel的内容就是四叉树映射里面的bias和scale。 假如对应的MIP没有加载,存储的就是高MIP的转换信息,这样显然就提高了地址转换的效率。但是这会带来内存增加,因为我们需要每个虚纹理的Page都对应一个Texel。其中bias和scale都是二维的向量,即使设计虚纹理和物理纹理的比例一致,我们也需要至少scale、SBias、TBias三个量,而且这三个量的精度要求很高,至少需要16bit的浮点数精度。如果要达到这样的精度就需要F32*4的纹理格式,那么必然会产生一个巨大的映射纹理,因此需要减小映射纹理的大小。 1.3 双纹理映射 这个方案仍然有一个对应每个虚纹理Page的Texture,但是不同的是,纹理的内容存储的是物理纹理Page的坐标,用这个坐标再去索引另外一张Texture。另外一张贴图的内容才是bias和scale,但不是每个虚拟纹理,而是每个物理纹理Page一个Texel。下图是虚拟纹理对应的Texture: 这样就减少了映射纹理的大小,但是同时多了一次纹理查询。 2.1.4 Page和MIP level映射 总结上面两个基于映射纹理的方案,要么是纹理需要很大的存储,要么是需要多次查询。如果从映射纹理比较大的角度考虑优化,可以考虑适当减少每个像素的大小,这个方案就是从这个角度出发的。在这个方案中,仍然是每个虚拟纹理的Page对应一个texel,但是存储的内容是物理纹理Page的Offset和虚拟纹理所在的MIP level。 这样存储的好处就是,Page Offset对精度的要求没有那么大,用32bit的Texture即可。当然也可以压缩到更小格式的纹理中,如RGB565。这种方案的使用最广泛,基本各家引擎的实现都使用了这种方案。 2.1.5 HashTable映射 这是最直接的方法,好处是节省内存,查询速度快,但是当遇到没有加载的virtual Page的时候,需要多次查询。这个和四叉树还有一个问题,即如何设计一个GPU友好的数据结构。 2.2 Texture Filtering 由于虚拟纹理并没有完整加载,所以各种采样过滤在Page的边界会有问题,我们需要自己设计解决这些问题的方法,适当的使用软实现的采样。 2.2.1 Bi-linear Filtering 这个解决方案比较简单,就是给Physical Page加上一个像素的border。 2.2.2 Anisotropic Filtering Anisotropic Filtering可能需要更多的相邻像素,假如我们需要支持8倍的Anisotropic Filtering,那我们需要采样步长为4的相邻像素,也就是我们的border要增加到4个像素。增加4个像素的border会增加Physical Texture的大小,但是带来了一个好处,就是适配了block compression。 具体实现可以分为软实现和硬实现,硬实现放到下文的Tri-linear说,这里说软实现。软实现其实就是在Shader中实现Anisotropic Filtering的算法,在决定采样的MIP level的时候,需要把虚纹理相对于物理纹理的比例考虑进去,剩下的就是正常的Anisotropic Filtering。 2.2.3 Tri-linear Filtering Tri-linear Filtering的实现方案可以分为两种:一种是软实现,一种是硬实现。 所谓的软实现与Anisotropic Filtering一样,在Shader中实现Tri-linear Filtering。也就是说,需要在Shader中计算MIP level,然后进行两次地址的转换,采样两个物理纹理的Page后进行插值。 硬实现的方法是直接给物理纹理生成一个一层的Mipmap,然后利用硬件去直接采样。同样的,对于Anisotropic Filtering,也打开Anisotropic Filtering直接进行采样。这样的好处当然是由于硬件的加速,采样的效率会提升,但是这样同时会导致增加25%的纹理大小,而且由于Mipmap的边界会变成两个像素,对于block compression和超过4倍的Anisotropic Filtering来说,在遇到Page的边界时都会出现问题。

Sparse virtual textures 学习笔记

请尊重原作者的工作,转载时请务必注明转载自:www.xionggf.com Nathan Gauër的Sparse virtual textures 稀疏虚拟纹理概述 sparse virtual texture(SVT)方案是由Id Software提出,用来解决Texture加载问题的一套解决方案,解决了带宽和内存不足的问题。 在Id Software的方案之后,业界又相继提出以下的的方案: Procedural Virtual Texture Adaptive Procedural Virtual Texture Hardware Virtual Texture 稀疏虚拟纹理的历史沿革 最早期的SVT思想,是受Mesh的LOD启发。不同LOD的Mesh对应着不同的网格面数,对应地也应加载不同大小的texture。 在1998年,Tanner发表了一篇《The Clipmap: A Virtual Mipmap》的论文,文中提到了一种叫clipmap的技术。后来被Id Software引入并改进,称之为MegaTexture clipmap的基本思想是:设置一个mipmap大小的上限,超过这个上限的mipmap会被clip掉,也就是不会加载到内存中 Software Virtual Texture 在MegaTexture的基础上,Id Software提出了virtual texture的概念,这个概念取自于virtual memory。与virtual memory似的是,一个很大的texture将不会全部加载到内存中,而是根据实际需求将需要的部分加载。如下图所示: 基本思路是:将虚拟纹理的Mipmap chain分割为相同大小的Tile或Page,这里的纹理是虚纹理,然后通过某种映射,映射到一张内存中存在的纹理,这里的纹理是物理纹理,在游戏视野发生变化的时候,一部分物理纹理会被替换出去,一部分物理纹理会被加载。 稀疏虚拟纹理(sparse virtual texture)。简单地说,就是在着色器中重新实现分页(pagination),可以拥有无限的纹理,同时保持 GPU 内存使用量恒定。 1 步骤 1 - 手工制作分页 1.1 分页概述 要理解 SVT 如何工作,首先要了解什么是分页: 在大多数计算机上,数据存储在 RAM 中。RAM 是一个线性缓冲区,其第一个字节位于地址 0,最后一个字节位于地址N。因为某些原因,使用真实地址并不方便。因此有人发明了分段(segmentation),后来演变为分页。这个想法很简单:使用虚拟地址(virtual address),由 CPU 将其转换为实际 RAM 地址。 在上图中:左侧是虚拟内存,可以看见虚拟内存的地址是线性连续的。共分为4个page。虚拟内存中每个page,都线性映射到page table中的一个entry。 下面举例说明,利用page和page table,如何将一个virtual RAM address(VRA)映射到physical RAM address(PRA)。例子中的page的大小是4KB,即4096个字节。每个虚拟地址VRA,可以用公式表示为:

HBAO

请尊重原作者的工作,转载时请务必注明转载自:www.xionggf.com HBAO(Horizon-Based Ambient Occlusion,基于水平的环境光遮蔽) 是一种用于实时渲染的环境光遮蔽(Ambient Occlusion, AO)技术。它的目的是模拟物体表面因周围几何体的遮挡而产生的阴影效果,从而增强场景的深度感和真实感。 1. 环境光遮蔽(AO)简介 环境光遮蔽是一种全局光照技术,用于模拟物体表面因周围几何体的遮挡而产生的阴影效果。它可以增强场景的深度感和真实感,尤其是在角落、缝隙和物体接触处。 常见的 AO 技术包括: SSAO(Screen Space Ambient Occlusion,屏幕空间环境光遮蔽):基于屏幕空间信息计算 AO。 HBAO(Horizon-Based Ambient Occlusion,基于水平的环境光遮蔽):改进的 AO 技术,基于水平角计算遮蔽。 GTAO(Ground Truth Ambient Occlusion,真实环境光遮蔽):更精确的 AO 技术,基于物理模拟。 2. HBAO 的工作原理 HBAO 的核心思想是通过计算每个像素周围几何体的水平角(Horizon Angle)来确定遮蔽程度。具体步骤如下: 2.1 采样周围几何体 对于每个像素,在其法线方向的半球范围内采样周围的几何体。 采样点的深度值用于计算遮蔽。 2.2 计算水平角 对于每个采样方向,计算几何体的水平角(Horizon Angle)。 水平角表示几何体在某个方向上的最大遮挡角度。 2.3 计算遮蔽强度 根据水平角和采样方向,计算当前像素的遮蔽强度。 遮蔽强度越大,表示该像素被周围几何体遮挡得越多。 2.4 应用遮蔽效果 将计算出的遮蔽强度应用到像素的颜色值上,生成 AO 效果。 3. HBAO 的优势 高质量 AO 效果: HBAO 能够生成更真实、更平滑的 AO 效果,尤其是在复杂几何体周围。 性能优化: 相比传统的 SSAO,HBAO 通过优化采样和计算方式,减少了性能开销。 可配置性强: HBAO 提供了多种参数(如采样半径、遮蔽强度等),可以根据需求调整 AO 效果。 兼容性:

什么是UV density

请尊重原作者的工作,转载时请务必注明转载自:www.xionggf.com UV Density(UV密度)是指在3D建模和纹理映射过程中,每单位面积上UV坐标的密集程度。它描述了UV坐标如何在模型的表面上分布,直接影响纹理的细节和清晰度。 UV坐标是将三维模型的表面(即网格)展开到二维平面上所使用的坐标系。每个顶点都有一个对应的UV坐标,用于在纹理贴图中定位该顶点上的纹理图像。 UV密度高意味着同样的纹理会在模型的更多区域覆盖,因此每个单位面积上的纹理细节更丰富,通常会表现出更高的纹理分辨率。 UV密度的影响: 纹理清晰度:UV密度越高,纹理的细节越清晰,尤其在大模型或复杂形状的区域。 纹理失真:如果UV密度不均匀,可能会导致某些区域的纹理出现拉伸或压缩,影响视觉效果。 性能:较高的UV密度意味着更多的纹理数据,可能会影响渲染性能,尤其是在低性能的设备上。 如何控制UV密度: 在3D建模时,合理分配UV空间,确保模型表面的大致区域都有足够的纹理空间。 在纹理贴图时,可以通过调整纹理的分辨率或者UV映射方式来优化UV密度。 总之,UV密度是纹理映射的重要指标,合理控制它能确保纹理效果的质量和性能。

Unity3D的内置宏 DYNAMICLIGHTMAP_ON

请尊重原作者的工作,转载时请务必注明转载自:www.xionggf.com 在Unity中,DYNAMICLIGHTMAP_ON 是一个与 动态光照贴图(Dynamic Lightmaps) 相关的内置变体关键字(Shader Variant Keyword)。它的核心作用是 控制着色器是否处理动态光照贴图数据,主要用于实现动态物体的间接光照混合效果。 核心含义 动态光照贴图标识 当该宏被激活时(#if defined(DYNAMICLIGHTMAP_ON)),表示当前渲染的物体 启用了动态光照贴图,需要在着色器中采样动态光照贴图数据。 若未激活,则物体仅使用静态光照贴图或实时光照。 技术目的 允许动态物体(如可移动的物件)与烘焙的静态场景光照进行间接光交互。 结合 Enlighten 或 Progressive Lightmapper 系统,实现动态物体的间接光实时更新。 触发条件 该宏在以下情况下自动激活: 物体设置 物体的 Mesh Renderer 组件中勾选 Lightmap Static 为 Contribute GI,同时设置 Lightmap Parameters 为支持动态更新。 光照系统配置 项目中启用了 Realtime Global Illumination(实时全局光照)。 使用 Baked Indirect 或 Shadowmask 混合光照模式。 Shader编译指令 在Shader中通过 #pragma multi_compile 或 #pragma shader_feature 声明动态光照贴图支持: #pragma multi_compile __ DYNAMICLIGHTMAP_ON 技术实现机制 1. 数据传递 动态光照贴图信息通过以下方式传递到着色器: 顶点数据:动态光照贴图坐标通过 TEXCOORD2 传递。 Uniform变量:动态光照贴图纹理(unity_DynamicLightmap)和采样器(samplerunity_DynamicLightmap)。 2. 着色器代码示例 struct v2f { float2 uv : TEXCOORD0; #if defined(LIGHTMAP_ON) float2 lightmapUV : TEXCOORD1; // 静态光照贴图UV #endif #if defined(DYNAMICLIGHTMAP_ON) float2 dynamicLightmapUV : TEXCOORD2; // 动态光照贴图UV #endif }; half3 GetLightmapColor(v2f i) { half3 staticGI = 0; half3 dynamicGI = 0; #if defined(LIGHTMAP_ON) staticGI = SampleLightmap(unity_Lightmap, samplerunity_Lightmap, i.

Unity3D的内置宏 REQUIRES_VERTEX_SHADOW_COORD_INTERPOLATOR

请尊重原作者的工作,转载时请务必注明转载自:www.xionggf.com 在Unity URP中,REQUIRES_VERTEX_SHADOW_COORD_INTERPOLATOR 是一个与阴影计算相关的内置宏(Shader Variant Keyword),它的作用是: 核心含义 控制顶点着色器是否需要生成并传递阴影坐标插值器 当该宏被激活时,Unity会在顶点着色器阶段计算主光源的阴影坐标,并通过顶点着色器输出结构体(Varyings)传递到片元着色器。这使得片元着色器可以直接使用插值后的阴影坐标进行阴影采样,避免在片元阶段重复计算。 触发条件 该宏会自动激活,当以下条件满足时: 项目中启用了 主光源阴影(Main Light Shadows) 使用了 逐顶点阴影坐标插值 的优化模式(而非逐片元计算) 在Shader中通过 #pragma multi_compile 启用了阴影相关功能(例如 _MAIN_LIGHT_SHADOWS) 典型应用场景 在URP的Shader中,你会在顶点着色器输出结构体 Varyings 中看到类似代码: struct Varyings { float4 positionCS : SV_POSITION; float3 positionWS : TEXCOORD0; // 当 REQUIRES_VERTEX_SHADOW_COORD_INTERPOLATOR 激活时,自动添加阴影坐标字段 #if defined(REQUIRES_VERTEX_SHADOW_COORD_INTERPOLATOR) float4 shadowCoord : TEXCOORD1; #endif }; 技术原理 阴影坐标生成 顶点着色器通过 GetShadowCoord(vertexInput) 计算阴影坐标,并存储在 shadowCoord 中。 插值优化 阴影坐标在顶点阶段计算后,通过插值器传递到片元着色器,减少片元阶段的计算量。 自动编译控制 Unity会根据项目设置和Shader特性,在编译时自动决定是否启用该宏,生成不同的Shader变体。 对比其他阴影模式 宏名称 计算阶段 性能消耗 精度 REQUIRES_VERTEX_SHADOW_COORD_INTERPOLATOR 顶点着色器 较低 中等 REQUIRES_FRAGMENT_SHADOW_COORD_INTERPOLATOR 片元着色器 较高 较高 调试与手动控制 强制启用/禁用(不推荐)

Unity3D的内置宏 _LIGHT_LAYERS

Unity3D的内置宏 ADDITIONAL_LIGHTS_VERTEX 请尊重原作者的工作,转载时请务必注明转载自:www.xionggf.com 在Unity URP/HDRP中,_LIGHT_LAYERS 是一个与 光源层级过滤(Light Layer Masking) 相关的内置变体关键字(Shader Variant Keyword)。它的核心作用是 实现基于层级的光源与物体交互控制,允许开发者通过层掩码(Layer Masks)精确控制哪些光源可以影响特定物体。 核心含义 1. 功能定位 光源筛选机制:激活该宏后,物体会根据自身的 Light Layer Mask 和光源的 Light Layer Mask 进行匹配,只有匹配层的光源才会对该物体产生光照影响。 渲染优化:通过层级过滤减少无效光源计算,提升复杂场景性能。 2. 技术目标 精细化光源控制:例如让装饰性灯光只影响场景道具,不影响角色 多世界渲染支持:用于分屏游戏或VR中不同视角的独立光照逻辑 触发条件 该宏的激活需要同时满足以下条件: 项目设置启用 URP/HDRP Asset 中开启光源层支持: URP: Lighting > Light Layers > Enable Light Layers HDRP: Lighting > Light Layers Shader声明 在着色器中通过#pragma指令声明支持: #pragma multi_compile _ _LIGHT_LAYERS 光源与物体配置 光源的 Light Layer Mask 设置为非"Everything"值 物体的 Renderer组件 > Rendering Layer Mask 设置为与光源匹配的层级 技术实现机制 1.