有着关于Unity Shader Graph使用以及特效实现的专栏,能够直接到达,去到那儿。
于 Unity URP Shader Graph 里面,Texture 3D Asset 节点是个基础且关键的资源定义节点,它专门用来在着色器程序里定义并引用三维纹理资源。跟传统的 2D 纹理不一样,3D 纹理在三个维度上都存有纹理数据,这致使它在处理体积数据、复杂材质过渡以及动态效果方面具备独特优势。该节点自身并非直接去执行纹理采样活动,而是当作纹理资源的声明之处以及引用的点,给后续的Sample Texture 3D节点供应必需的纹理数据源头。
三维纹理,在计算机图形学里头,常常被称作体积纹理,它把纹理数据,组织于三维空间里的规则网格之上,每一个纹素,都有着对应的(x, y, z)坐标。这样的一种结构,致使3D纹理极为适宜用来表示体积数据,像是医学成像里的CT扫描数据、云层以及烟雾的密度场、金属材质的微观结构,又或者是复杂材质的内部变化。在实时渲染之中,3D纹理的运用,能够营造出更为丰富且真实的视觉效果,尤其是在那些需要展现材质内部结构或者复杂过渡效果的场景当中。
Texture 3D Asset节点的关键功用是构筑Shader Graph与项目里3D纹理资源间的连接通道,借助此节点,开发者能够把项目以内导入或是创建的3D纹理资源引入到着色器图内,并且让其能够用于后续的纹理采样以及处理行为,这般设计契合现代着色器开发过程中的资源跟逻辑分离准则,致使同一个3D纹理资源能够在不同的采样节点之中以不一样的参数反复运用,提升了资源的利用效率以及着色器开发的灵活性。
在Unity的渲染管线里,3D纹理的处理方式跟2D纹理不尽相同,因为涵盖额外维度,3D纹理一般需要更多内存以及计算资源,所以在性能优化层面要格外留意,Texture 3D Asset节点借助统一接口对这些底层细节予以抽象,致使开发者能够聚焦于材质的外观与效果,而无需过度操心底层的实现复杂性。
描述
将Texture 3D Asset节点的主要功能,定义为在着色器里使用的常量3D纹理资源。这里所说的“常量”,意思是在着色器执行进程中,该纹理资源自身不会产生变化,即便通过各异的采样参数,能够从同一纹理里提取不一样的信息。这样的设计模式,契合现代图形编程的最佳实践,也就是尽可能地把资源与计算逻辑分隔开,进而提升代码的可维护性以及执行效率,从而提高代码的可维护性和执行效率。
在所涉及的Shader Graph工作流程里,Texture 3D Asset节点起着资源绑定的关键作用,当于项目内创建或者导入3D纹理之后,需借由该节点把它引入至着色器图中,该节点自身并不含有任何采样逻辑,它只是对纹理资源的引用声明,这样的设计致使同一个纹理资源能够在着色器图里的多个地方被重复运用,每个运用位置均可应用不一样的采样参数以及变换操作,而不必于内存中创建多个纹理副本。
无论是 3D 纹理,还是 2D 纹理,它们在内部结构方面,有着十分大的差异。2D 纹理,能被看成是平面里的像素网格,然而 3D 纹理,却是三维空间当中的体素网格。每一个体素,并不仅仅含有颜色有关的信息,还能够去存储别的一些数据,像是密度、材质属性,或者是其他自定义的信息。这样的一种结构,让 3D 纹理极其适合用来表示体积方面的效果,以及复杂材质的内部结构变化。比如说,于模拟大理石材质之际,能够运用 3D 纹理去存储石材内部的矿物分布状况以及脉络结构,借助适当的采样方式,可渲染出具备真实内部结构的大理石表面。
想要针对 3D 纹理资源开展实际采样,那就一定要把它跟 Sample Texture 3D 节点搭配着使用。这两个节点的分工是清晰明确的:Texture 3D Asset 节点承担着声明纹理资源的职责,而 Sample Texture 3D 节点负责去执行具体的采样操作。这种分离设计具备优势,优势在于提高了资源的可行性复用性,当使用单个Texture 3D Asset节点的时候,可以运用不同的参数对3D纹理开展多次采样,不需要对3D纹理自身进行多次定义,这样不但降低了内存占用,还让着色器结构变得更加清晰以及易于维护。
于实际应用场景里,3D纹理用途极为广泛,能够用以创建复杂材质效果,像木材的年轮、石材的纹理、金属的晶粒结构等,在特效制作当中,3D纹理常被用于模拟体积效果,例如烟雾、云层、火焰等,除此之外,在科技可视化领域,3D纹理也常常被用于显示医学影像数据、地质勘探数据、流体模拟结果等科学数据。
Unity针对3D纹理的支持涵盖了一系列优化特性,诸如mipmapping、纹理压缩以及流式加载等等,这些特性借助Texture 3D Asset节点对开发者透明地予以提供,从而使得开发者能够专注于材质效果的达成,而无需过多去关注底层的纹理管理以及优化细节,与此同时,Unity还给出了完善的工具链支持,其中包含3D纹理的导入设置、预览以及调试工具,进一步让3D纹理的使用流程变得简化。
端口
输出端口
Texture 3D Asset节点,其中仅包含一个输出端口,此端口的设计,反映了节点的单一职责原则,该原则专注于提供3D纹理资源的引用。
对于该节点而言,Out输出端口是其绝对唯一的输出接口,其数据类型确切来说被精准给定为“3D纹理”。此数据类型不但涵盖着纹理自身的图像数据引用,而且还对与纹理相关的所有元数据信息进行了封装,像纹理尺寸、格式、mipmap级别等等。于Shader Graph的内部数据流当中,这个输出端口实际传递的其实是一个纹理对象的句柄或者引用,并非纹理数据真实的本身。这般设计切实保障了数据传输的高效率,有效规避了毫无必要的数据复制操作。
特别要留意输出端口的连接规则,Out端口仅能连接至可接受3D纹理类型输入的节点,其中Sample Texture 3D节点最为典型,要是试着把它连接到不兼容的节点类型,Shader Graph便会显示连接错误,以此防止出现不合理的节点连接组合,这种类型安全检查机制保障了着色器图的正确性与稳定性。
从数据流方面去看,Out端口所输出的3D纹理引用,在着色器执行进程里维持不变,这表明,于渲染一帧期间,纹理资源恒定,不会产生改变,这种不变特性致使Unity的渲染管线能够开展各类优化,像纹理数据的预取、缓存策略的优化等,并且,这也契合现代图形API的设计准则,也就是在绘制调用之间尽量保持资源状态的稳定。
于实际运用当中,Out端口之连接方式对着色器的性能及效果有着直接的影响,倘若同一个Texture 3D Asset节点的Out端口被连接至多个Sample Texture 3D节点,这便意味着同一纹理资源会被多次采样。在这种情形下,Unity的渲染后端通常会识别出此种模式并加以优化,像是借助纹理绑定点的复用来降低API调用开销。只是,开发者还是得留意这种用法或许会造成的性能方面的影响,尤其是在对性能比较敏感的移动平台之上。
输出端口的特性,也涵盖其对动态分支的支持,在有着动态分支的着色器里,哪怕某个分支路径上的Sample Texture 3D节点事实上不会被执行,然而只要它连接到了Texture 3D Asset节点的Out端口,那么对应的纹理资源依旧会被绑定到着色器上,这种行为保证了着色器执行的一致性,但同时也表明要留意纹理资源的绑定数量,防止超出目标平台的限制。
控件对象字段控件
Texture 3D Asset 节点的核心控件之中含有一个 3D 纹理对象字段,此控件给出了和 Unity 项目资源系统的交往门路。对象字段的设计依照了 Unity 编辑器的常规样式,这致使熟悉 Unity 的开发者能够迅速上手来运用。
在节点界面里以资源选择区域形式呈现的对象字段控件,一般涵盖纹理预览、资源名称以及选择按钮等要素。对于此字段,开发者能够借由多种途径来指定3D纹理资源:将项目窗口中的3D纹理资源直接拖至字段区域,点击字段右侧的选择按钮从资源选择窗口选取,或者借助字段的上下文菜单展开操作。这样一种灵活的资源指定办法适应了各异的工作流程与习惯。
对对象字段展开验证的机制,保障了只限适配得当的所属资源类型得以被指定,当着手尝试分配并非3D纹理规格之资源时,系统都会进行拒绝该给定操作,并且给出与之相应的错误提示信息,这样的经由类型予以安全保障的机制,防止了出现并非正确的资源分配情况,从而为此减缩了进行调试所需利用的时间,另外,该字段还会针对作为纹理之用的资源所开展的导入设置予以检查,保证其契合3D纹理付诸使用时所必备的要求,诸如正确无误的纹理尺寸、需遵循的格式以及mipmap应予设置的相关内容等等。
于资源管理范畴内,对象字段维系着针对项目里3D纹理资源的引用,此引用关系于Shader Graph序列化之际会被留存,从而确保了着色器材质在再度加载之时能够精准恢复与纹理资源的关联,与此同时,这种引用机制还致使资源的重命名、移动等行为能够被准确追踪,进而降低了资源遗失的风险。
对象字段具备能快速进行资源访问以及导航的功能,借着字段的上下文菜单,开发者能够迅速于项目窗口里来定位当下指定那个纹理资源,还能重新去导入纹理,或者把纹理导入设置界面给打开,这些便利功能极大地提升了开发着色器的工作效率,尤其在那种需要频繁对纹理设置作出调整的工作流程当中。
对于团队协作以及资源管理而言,对象字段的引用机制保证了 Shader Graph 跟纹理资源之间的依赖关系,能够被 Unity 的资产数据库准确跟踪,这致使资源打包、依赖分析还有内存管理等功能得以正常运行。在构建项目之际,所有借助 Texture 3D Asset 节点引用的 3D 纹理资源,都会被自动纳入构建当中,无需手动去管理这些依赖关系。
它对默认资源的支持,是对象字段的另一个重要特性。创建新的Texture 3D Asset节点那时候,字段初始状态为空,这个时候节点不会输出有效的纹理引用。开发者必须显式地指定一个3D纹理资源,不然在着色器编译时会生成错误。这种显式的要求确保了着色器行为的明确性,避免了因缺少资源从而导致的意外行为。
生成的代码示例
当Shader Graph被编译成实际的着色器代码之际,Texture 3D Asset节点会去生成相应的HLSL代码。明白这些被生成的代码,对于深入地掌握节点的工作原理进而开展高级着色器开发而言,具有重要的意义。
基础代码结构
Texture 3D Asset,其较为典型的那种节点,会去生成这样形式的代码,此代码是HLSL类型的。
HLSL
TEXTURE3D(_Texture3DAsset);
SAMPLER(sampler_Texture3DAsset);
这一段代码里,有着两个关键的部分,分别是纹理声明,以及采样器声明。TEXTURE3D(_Texture3DAsset)这种宏,它声明了一个3D纹理对象,这里面的 _Texture3DAsset,是纹理的标识符名称。这个标识符,在默认情形下,是由系统自动生成的,不过呢,也能够依据命名规则去进行预测。在实际的编译过程当中,Unity会按照Shader Graph的整体结构以及设置,来确定最终的标识符命名。
SAMPLER(sampler_Texture3DAsset)宣称出了跟纹理相衔接的采样器状态,采样器界定了纹理采样之际的各类参数,像过滤模式、寻址模式等,在当代图形API里,纹理和采样器一般是分开的,这样的设计准许多个纹理共同享有同一个采样器状态,提升了资源的灵活性。
编译时处理
在进行着色器编译这个过程当中,Unity会针对Texture 3D Asset节点开展一连贯的处理以及优化。首先呢,系统会去检查所指定的3D纹理资源是不是存在并且有效。要是资源丢失了或者类型不正确,那么编译器就会生成错误,然后停止编译。这样一种严格的验证保证了最终着色器的可靠性。
关于纹理的导入设置,像是 mipmap、压缩格式、各向异性过滤等,Unity 在编译时会把这些设置纳入考量,进而生成相应的采样代码。举例来讲,要是纹理开启了 mipmap,那生成的采样代码会自动涵盖 mipmap 级别的计算以及选择逻辑。这些细节对于 Shader Graph 用户而言是透明的,但知晓其背后的机制有助于更优地优化纹理设置。
运行时行为
运行之际,所生成的着色器代码借由Unity的材质系统同实际的纹理资源予以绑定。材质被渲染之时,Unity的渲染管线会确保于绘制调用之前,全部引用的纹理资源皆被正确设置至对应的纹理单元当中。此过程是自动管理的,开发者通常无需关心具体的实现细节。
关于不一样的渲染管线以及平台,Unity 兴许会产出互不相同的底层代码,比如说,在对 Bindless Texture 予以支持的平台之上,有可能会运用更为高效的纹理绑定方式,而这些平台所特有的优化是由 Unity 自行处理的,从而保证了着色器放在不同环境里均能够获取最佳性能。
高级用法代码模式
处在繁杂的着色器里头,极有可能碰到各种类型纹理3D资产当中较多项目共同开展工作的状况,在此种状况下所产生的代码会涵盖多个纹理相关声明以及采样器声明。
HLSL
// 第一个3D纹理
TEXTURE3D(_Texture3DAsset);
SAMPLER(sampler_Texture3DAsset);
// 第二个3D纹理
TEXTURE3D(_SecondaryVolumeTexture);
SAMPLER(sampler_SecondaryVolumeTexture);
这种模式准许在同一着色器里运用多个3D纹理,像一个用于基础颜色,另一个用来法线或高度信息。在性能层面要留意,一同使用多个3D纹理有可能增添内存带宽需求,尤其是在移动设备那儿要慎重使用。
与 Properties 的关联
虽 Texture 3D Asset 节点于 Shader Graph 里呈现为常量资源引用,然而在某些情形下,开发者有可能期望把 3D 纹理展露成材质的可配置属性。在此种状况下,需运用 Texture 3D 类型的 Property 节点,而非 Texture 3D Asset 节点。两者的代码生成存有差异:
HLSL
// 通过Property暴露的3D纹理
TEXTURE3D(_CustomVolumeTexture);
SAMPLER(sampler_CustomVolumeTexture);
float4 _CustomVolumeTexture_ST; // 自动生成的缩放偏移参数
Property节点会额外生成被叫做纹理的缩放偏移参数(_ST),这致使材质能够在运行的时候动态调整纹理变换参数,明白这种区别对于挑选出正确的节点类型来讲极为重要。
优化考虑
从生成的代码视角而言,存在一些性能优化的考量要点。其一,尽量复用同一 Texture 3D Asset 节点,而非去创建多个引用相同资源的节点。如此一来,便减少了着色器里纹理声明的数量,这或许会带来些许的性能提升。
第二,要留意纹理的采样器的相关设置,借助 Unity 的采样器状态管理,能够保证相似的样本设置共同享有采样器状态,进而降低状态切换所产生的开销,在 Shader Graph 里,这通常是经由采样器节点的设置去实施控制的。
首先,鉴于有别的平台的特性,所生成的代码或许会存在不同。于编写跨平台着色器之际,应当测试于不同设备上的表现情形,保证性能特征契合预期。Unity给出的帧调试器以及渲染诊断工具能够协助剖析纹理相关的性能问题。
实际应用示例基础体积材质创建
首先,创建一个简单的体积材质,这是能够理解Texture 3D Asset节点用法的良好起始点。其次,假设我们有创建一个具有内部结构的大理石材质的需求,那么可以运用3D纹理去模拟石材内部的矿物分布。
第一步,于项目里预备或者打造一个恰当的3D纹理资源。此纹理应当涵盖大理石内部构造的体积数据 ,一般能够借由程序化生成或者从真实数据扫描而获取。在Unity中导入该纹理之际 ,要保证纹理类型设定成“3D Texture” ,并按照需求配置mipmap 、压缩格式等导入设置。
于Shader Graph里创建新的着色器图,接着 adding Texture 节点的操作来添加Texture 3D Asset节点。凭借节点的对象字段控件去向上面导入的3D纹资源明确指定。这时节点的Out端口已然就能输出出对该纹理的引用。随后添加Sample Texture 3D节点,把它的Texture输入端口连接到Texture 3D Asset节点的输出。对于Sample Texture 3D节点,其UVW输入,是需要去提供三维纹理坐标的,而这通常是源于物体的世界位置,或者是物体空间位置在经过适当变换之后所产生的那个结果。
把 Sample Texture 3D 节点的 RGB 输出,朝着主着色器的 Base Color 输入去连接,如此这般便能瞧见 3D 纹理针对物体着色的基础成效。借由对 Sample Texture 3D 节点的采样参量以及 UVW 输入的变换方式实施调整,便能够把控住纹理于物体表面的表现样式与繁密程度。
复杂效果组合
3D纹理所具备的真正厉害之处,在于跟其他着色器功能一道组合运用,比如说呀,能够把多个 Texture 3D Asset 节点结合起来用,以此去打造复杂的多层体积效果。
思索一个具备高级特性的云层渲染方面的示例,首先呢,需要两个3D纹理资源才行,其中一个是用来呈现云层的基础密度分布状况的,另一个则是用于体现云层的细节扰动情况的,接着,要创建两个Texture 3D Asset节点,让它们分别援引这两个纹理要素,之后,针对基础密度纹理展开采样操作,以此获取基本的云层形状模样,再往后,对细节纹理开展采样活动,并且借助时间变量使其具备动画化效果,最后,将这两者按照恰当的比例予以混合处理。
在于 3D 纹理采样坐标,去进行变换以及组合的理解,这是这种技术的核心所在。基础纹理能够运用较大尺度的世界坐标,而细节纹理得以使用具备较小尺度以及加上时间变量的坐标。借助适当的混合函数,像是加法、乘法以及屏幕混合等,来组合那两个采样得出的结果,以此能够创建出动态且充满富有细节的云层效果。
更进一步而言,能够把结果同光照计算相融合,将3D纹理采样所得结果用作体积散射计算的输入,联合方向光信息去计算光照衰减以及散射效果,这样这般的技术可生成尤为逼真的体积光照,适用于烟雾、云层以及其他参与介质效果的渲染了。
性能优化实践
使用Texture 3D Asset节点之际,性能优化是一项较为重要的需考虑因素。3D纹理因涵盖更多的数据量,一般相较于同等分辨率的2D纹理,需耗用更多的内存以及采样开销。
有一种关键的优化策略,是对3D纹理的分辨率进行合理选择。针对那些不需要高频细节的体积数据,能够采用较低的分辨率。比如说,64×64×64的3D纹理,在多数情形下已然能够给出不错的质量,然而其内存占用仅仅是同等2D纹理的1/64(相较于4096×4096的2D纹理)。
还有一个关键的优化是 mipmap 的运用,针对于在深度方向存在较大波动变化的体积效果这一情况方面,启用 mipmap 能够明显地提升采样的缓存效率,然而要留意 mipmap 将增加大约 33% 的内存占用,这就需要在质量与性能之间去进行权衡。
在Shader Graph里,能够借助恰到好处的组织节点架构来实现性能的优化。拿多个Sample Texture 3D节点来说,要是它们运用相同的纹理然而采样参数不一样,那就应当使它们共同享用同一个Texture 3D Asset节点,而并非让每个采样节点都去连接自身独立的纹理资源节点。这样的共享降低了着色器当中纹理绑定的数量,有可能带来性能方面的提升。
对于移动平台而言,还得格外留意纹理压缩以及格式选取。ASTC 压缩格式针对 3D 纹理一般有着不错的支持,能够在维持可接受质量的情形下,显著削减内存占用。与此同时,应当规避在片段着色器里开展过于繁杂的 3D 纹理采样操作,特别是在低端移动设备之上。
调试和问题排查
虽说在运用Texture 3D Asset节点之际,极有可能遭遇各式各样的问题,可言掌握有效的调试办法是相当关键的。其中一个较为常见的情况是,纹理会呈现为粉色,而这一般意味着纹理资源出现丢失或者类型并不匹配的状况。要去查证Texture 3D Asset节点的对象字段是否切实无误地指定了3D纹理资源,并且还要确认该资源于项目当中确实是存在的,同时导入设置也是正确的。
常见的另一个问题是,纹理采样给出的结果并不符合事先所预计的那样。它能够存在由采样坐标出现不正确状态所导致形成的可能性。针对这个问题,能够借助可视化采样坐标的方式来处置调试。即将UVW坐标的各个不同分量分别输出转化成颜色,以此检查坐标范围是不是处于一种合理的情形。在正常的状况之下,采样坐标应当是处于相应范围之内的。越过并且不在这个范围之中的行为,则是由纹理的Wrap Mode设置来决定的。
需被关注的重点之中也是涵盖着性能问题的。要是察觉到在运用3D纹理之后帧率出现了显著的下降,那么能够选用Unity的Profiler工具来开展渲染耗时的分析。要格外去关注纹理采样指令的数量以及耗时,还有纹理内存的占用状况。要是发现有问题了,便可思索着去降低纹理分辨率、优化采样次数或者采用更具高效性的纹理格式。
就高级用户而言,能够运用RenderDoc等图形调试工具,去深入剖析着色器的执行情形。这些工具能够展现出每个绘制调用里纹理的实际绑定状况以及采样成果,助力查找复杂问题的根源所在。
最佳实践和高级技巧资源管理策略
有效的资源管理,对于成功使用 Texture 3D Asset 节点而言极其关键。首先,要确立一种统一的 3D 纹理命名以及组织规范。因为 3D 纹理在项目当中,不如 2D 纹理那般频繁出现,所以清晰的组织结构能够防范混淆,进而提升工作效率。
关于内存管理这块,要留意 3D 纹理加载以及卸载的时间点,大型 3D 纹理会占据相当可观的内存空间,得借助 Unity 的资源管理系统来保证它们仅在有需求的时候才加载,能够运用 Addressables 系统或者传统的 Resources 文件夹去管控 3D 纹理加载的生命周期。
针对那一些必须要动态去生成的3D纹理,Unity那儿给出了Texture3D.Create方法以及支持Compute Shader的更新途径。这些较为高级的用法能够准许在运行的时候去生成或者是修改3D纹理。
【Unity Shader Graph 使用与特效实现】专栏-直达