Dual Kawase Blur Cover

几乎连续的双Kawase模糊

2023年6月3日修订 发现还是在降采样升采样后进行线性插值来获取中间程度的模糊效果比较好,所以后面的代码也都改过来了,不过理论上的部分倒是没必要改。顺便也花了点时间写了一个Shadertoy作为演示: Dual Kawase Blur 写这篇文章的原因 网上已经有了很多很多的双Kawase模糊的现成的案例,但是存在以下几个问题:1. 绝大部分的文章都只给了代码,没有相应的解释,至多会给一张直接从Arm的pdf截取的图示,而这张图示画了一堆方框和符号,却没有说明这些图案代表的含义。2. 绝大部分的文章通过修改采样的距离来控制模糊的程度,这个的缺点我们后续再谈。3. 绝大部分的文章并没有考虑模糊程度从0开始逐渐增大的动态过程,使用降采样和升采样往往会破坏整个画面的连续性。 如果只是想要获得一个模糊的画面,只需要做几次降采样和升采样就能完成了,但我希望能有一个连续地逐渐地变模糊的过程,因此我开始了量化双Kawase模糊的想法。 双Kawase模糊(Dual Kawase Blur) 双Kawase模糊是2015年Arm在Kawase模糊的基础上提出的一种通过降采样和升采样来快速且高效地进行高质量大半径模糊的一种方法,具体的pdf可以从这里找到。 这里是一张双Kawase模糊的图示,表示了双Kawase模糊在降采样和升采样时的操作。细的黑线对应的格子是原始的像素(或是升采样后的像素),粗的黑线对应的格子是降采样后的像素。叉对应的是当前模糊的像素,圆对应的是当前模糊的像素所需要采样点。粉色对应的是降采样时的模糊的像素和采样点,绿色对应的是升采样时的模糊的像素和采样点。 从这张图中也可以看到双Kawase模糊利用双线性采样来节省采样数的操作。在降采样时实际采样了当前像素周围一共十六个像素的颜色;在升采样时实际采样了当前像素周围一共十三个像素的颜色。而如果在做降采样时,对于奇数个像素除以二向下取整,或者是在降采样时使用了不恰当的偏移(比如1.5倍的偏移),会导致降采样的采样点落在原始像素的中心,这时即使使用了双线性采样,也只等价于采样一个像素。 因此为了让每一个像素都能对模糊做到应用的贡献,为了达到比较好的模糊效果,我们这里限制双Kawase模糊的采样偏移为一倍(也就是严格按照采样点进行最优的双线性采样)。而通过多次降采样和升采样达到合适的模糊半径。 量化双Kawase模糊 降采样和升采样有一个缺点,就是只要发生了降采样和升采样,就必然会带来模糊。这时有两种方法,一种是在原始分辨率下通过消耗更大的方式进行加权模糊来逼近双Kawase模糊配合降采样带来的模糊;另一种是在零次和一次双Kawase之间线性插值得到一张介于两者之间模糊程度的图像。综合两者来看,线性插值得到的效果更为平滑,效果上稍“错误”一些,但完全在可接受的范围内。 我这边写了一个小小的脚本,去计算原始分辨率下值为1的像素点,在经过一次双Kawase模糊后,其他像素的值。通过多项式拟合这些模糊后的值,就能利用这些值来逼近双Kawase模糊的效果了。我这边对8x8的像素做了计算(实际上模糊的核心应该更大一些,不过我懒得改之前的代码了)。计算出的权重如下: 0.0003255208 0.001464844 0.003092448 0.004231771 0.004231771 0.003092448 0.001464844 0.0003255208 0.001464844 0.004882813 0.009440104 0.01204427 0.01074219 0.007486979 0.004231771 0.001464844 0.004394531 0.01334635 0.02311198 0.02701823 0.0218099 0.01334635 0.007486979 0.003092448 0.01009115 0.02571615 0.03808594 0.04329427 0.03678386 0.0218099 0.01074219 0.004231771 0.01529948 0.03222656 0.04069011 0.04589844 0.04329427 0.02701823 0.01204427 0.004231771 0.01416016 0.0296224 0.03678386 0.04069011 0.03808594 0.02311198 0.009440104 0.003092448 0.007324219 0....

May 29, 2023 · zznewclear13
Circular Blur Cover

Unity两个Pass的圆形模糊

圆形模糊 圆形模糊,在Photoshop里又称镜头模糊(Lens Blur),和景深结合在一起的时候被称作散景(Bokeh),是指在摄影时失焦的区域产生的和光圈的形状一致的模糊效果,五边形八边形或是圆形都有可能。 在计算机图形学中实现景深效果基本上有两种方法:第一种也是最常用的,通过黄金率生成一系列的采样点,使得其形状接近想要模糊的形状,这种方法需要很多很多的采样点,基本上找到的都是60次以上的采样次数,由于采样点的分布不一定正好在像素点中心,也不能轻易地使用Group Shared Memory进行优化,事实上大的模糊半径很可能导致Group Shared Memory的大小不够;另一种是针对于特殊的模糊形状,比如正六边形,可以使用三次(MRT的话可以认为是两次)1D的模糊来组合而成,可以在Colin Barré-Brisebois的博客Hexagonal Bokeh Blur Revisited中看到详细的说明,值得一提的是他此前也在EA工作过(看来EA是真的很喜欢散景啊)。 EA的渲染工程师Kleber Garcia在2018年的GDC演讲Circular Separable Convolution Depth of Field中提到了通过复数的运算来实现圆形模糊的算法,其背后的数学我这里就不再赘述了,感兴趣的话可以参考Circularly symmetric convolution and lens blu这篇文章。圆形模糊的参数的生成的代码可以在Kleber Garcia的公开仓库里找到。Kleber Garcia本人也在Shadertoy上写了具体的圆形模糊的代码。 Circular Dof 由于景深效果相对来说比较复杂,这里就只考虑对整个屏幕施加相同程度的圆形模糊效果。 具体的实现方法 其实大部分和之前的高斯模糊没有什么差别。在分离卷积圆形模糊的算法中,圆形的效果是通过多个Filter叠加而成的,每个Filter对应实部和虚部两个参数。以本文为例,本文使用了两个Filter,对于一个Filter的一个颜色分量,需要储存实部虚部两个数据,总体就需要2x3x2=12个通道,使用三张R16G16B16A16_SFloat就能储存所有的数据。在Kleber Garcia的演讲中他还提到了,可以使用bracket的方法,将颜色的卷积数据储存到另一张图中,这样中间的Filter的结果就会落在[0, 1]的范围内,就能使用R8G8A8B8来储存了,可以节省一半的带宽(但是颜色的卷积数据不也要一张32位的图吗,这里我没太懂,感觉优化了但又没那么优化,索性就没那么做)。 整体的操作是:1. 采样源图片,对每个Filter和每个颜色分量计算实部和虚部的值,水平累加后储存到中间贴图中;2. 采样中间贴图,对每个Filter和每个颜色分量计算实部和虚部的值,竖直累加后乘上各自的权重就得到最终的颜色了。由于我没有使用bracket的方法,Filter中会有负值存在,在仅使用两个Filter且半径较大且像素颜色过亮的时候,由于banding的存在会使最终的颜色出现负值,解决方法是在读取颜色的时候做一次Clamp或者是ToneMapping到合理范围。 CircularBlurFilterGenerator.cs 改写自Kleber Garcia的公开仓库。 /* Copyright 2023 zznewclear13 (zznewclear@gmail.com) Copyright 2016 Kleber A Garcia (kecho_garcia@hotmail.com) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software....

April 9, 2023 · zznewclear13
Gaussian Blur Cover

再议高斯模糊

将近两年之后再回过头来制作高斯模糊 虽然两年前已经写过了使用Group Shared Memory加速高斯模糊这篇文章了。但当时写的时候仍有一些遗憾的地方,由于使用的是长度为17的静态的高斯模糊的数组(实际上只有9个权重),虽然在一定程度上能够达到任意调节高斯模糊的程度的效果,但在较低程度的高斯模糊时,是通过手动线性插值找到合适的采样颜色,且一定会有17次的颜色和权重的运算;而在较高程度的高斯模糊时,由于仅有十七个有效的颜色点,会有明显的采样次数不足的瑕疵。 而这两年之间我也曾考虑使用不同的方法来制作一个既能满足很高程度的高斯模糊,又能兼顾很小程度的高斯模糊,性能上也相对高效,且使用同一套通用的代码,的高斯模糊效果。下面便是我之前在Shadertoy上写的通过随机采样和历史混合的高斯模糊效果。 Stochastic Gaussian Blur 但随机带来的噪声和历史混合带来的限制,决定了这种方法终究不能真正地使用在项目中,于是我又开始回到了使用Compute Shader和Group Shared Memory来计算高斯模糊效果的老路子上。不同的是,这次我使用了Compute Buffer把高斯模糊的参数传给Shader,这样就能确保范围内的每一个采样点都能够对最后的颜色产生应有的贡献。 正态分布(Normal Distribution) 和之前不同的是,这次我们要先从正态分布入手,从正态分布的特性来考虑我们的计算方式。正态分布的概率密度函数(probability density function)如下所示: $$ f(x) = \frac 1 {\sigma \sqrt{2 \pi}} e^{- \frac 1 2 (\frac {x-\mu} \sigma)^2} $$ 使用正态分布对信号进行过滤,被称作高斯滤波器(Gaussian Filter)。我们在使用的时候会把\(\mu\)设成0,这样永远是最中心的信号带来最大的贡献。但是这个概率密度函数的\(x\)的范围是\((-\infin, \infin)\),我们不可能对所有的信号都进行采样,于是我们一般对\(3\sigma\)范围内的信号进行采样,对1D的正态分布来说,\((-3\sigma, 3\sigma)\)占据了约99.7%的面积。因此我们往往使用三倍的\(sigma\)作为采样的半径,事实上在2D的时候,可能需要更大的采样半径才能消除明显的采样半径过小的瑕疵。 有一点值得一提的是,虽然我并不会具体的微积分的计算,但据我所知先后执行两个\(\sigma\)值分别为\(x\)和\(y\)高斯模糊,等价于执行一次\(\sigma\)值为\(\sqrt {x^2+y^2}\)的高斯模糊。 另一个有趣的点是,在普通的模糊操作是我们往往会用降采样再升采样的方式来减少采样的次数。对于半分辨率的线性1D降采样和升采样,中心像素保留了\(\frac 3 8\)的之前像素的信息,我们可以找到那么一个\(\sigma\)的值使得其在\((-0.5, 0.5)\)之间的面积约等于\(\frac 3 8\),这样我们就能说我们通过线性降采样和升采样做到了近似对应\(\sigma\)的高斯模糊的效果。可惜这个\(\sigma\)不太好算,有Group Shared Memory也没有必要去做额外的降采样和升采样了。 在本文中,会通过横竖两个1D高斯滤波器来等效一个2D的高斯滤波器,使用Group Shared Memory的话,倒是一个2D的高斯滤波器效率更高一些,不过为了后续的扩展性,本文拆成了两个滤波器。 具体的实现方法 剩下的就和之前大同小异了,为了确保每个像素只会进行至多两次采样,需要限制高斯模糊的最大半径GAUSSIAN_BLUR_MAX_RADIUS为THREAD_GROUP_SIZE的一半。而为了2D的高斯模糊在比较极端的情况下也能有比较好的效果,我的高斯模糊的半径会是\(\sigma\)的3.8倍向上取整。 GaussianBlurComputeShader.compute 这是一个横竖两次高斯模糊的Compute Shader,通过Group Shared Memory优化了原本高斯模糊的每个像素的采样操作(至多两次)。最大模糊半径为128个像素。 #pragma kernel GaussianH #pragma kernel GaussianV Texture2D<float4> _SourceTex; RWTexture2D<float4> _RW_TargetTex; StructuredBuffer<float> _GaussianWeights; float4 _TextureSize; #define GAUSSIAN_BLUR_MAX_RADIUS 128 #define THREAD_GROUP_SIZE 256 const static int CACHED_COLOR_SIZE = THREAD_GROUP_SIZE +GAUSSIAN_BLUR_MAX_RADIUS*2; groupshared half3 cachedColor[CACHED_COLOR_SIZE]; void SetCachedColor(half3 color, int index) { cachedColor[index] = color; } half3 GetCachedColor(int threadPos) { return cachedColor[threadPos + GAUSSIAN_BLUR_MAX_RADIUS]; } void CacheColor(int2 groupCacheStartPos, int cacheIndex, int isHorizontal) { int2 texturePos = groupCacheStartPos + cacheIndex * int2(isHorizontal, 1 - isHorizontal); texturePos = clamp(texturePos, 0, _TextureSize....

April 5, 2023 · zznewclear13
Tile Rendering Cover

模仿缺氧的瓦片渲染方法

缺氧的瓦片渲染的特点 很可惜我没有在RenderDoc里截到缺氧的帧,不过我还是能从渲染表现上来分析一下缺氧的瓦片渲染的特点。经过一段时间的游玩和从下面这张图中可以看到,缺氧的游戏逻辑是把整个2D的地图分成一个一个格子,每个格子记录了气体、液体、固体和建筑物的信息。气体只是一个扭曲的Shader,液体渲染和计算比较复杂,这里暂时不考虑,建筑物中的墙和管线虽然也有程序化生成再渲染的效果,但和场景中资源类型的固体格子是硬相接的关系,这里也不考虑。本文的研究重点放在资源类型的固体格子的渲染上(不包括这些格子的程序化生成)。 资源类型的固体格子(这里就简称瓦片了)的特点如下: 有多种类型的瓦片 仅在不同类型的瓦片相接时会有黑色的描边 瓦片之间会有排序,优先级高的瓦片会更多地扩张 瓦片之间黑色的描边呈现周期性规律 模仿这种渲染的思路 最简单的思路肯定就是在CPU中计算每一个瓦片应当有的形态,然后找到对应的贴图,把瓦片在GPU中绘制出来了。但是这样子做的话就失去了本文的意义,也太过无趣了。我想的是尽量多地用GPU来计算每个瓦片的形态,同时使用Instancing的方式,绘制每一个瓦片。 第一个问题是,不规则的瓦片应当如何绘制。如果是正方形的瓦片,能够很轻易地使用一个Quad和纹理来绘制,但是不规则的瓦片,势必会使用透明度混合的方式来绘制,这时对应的模型就会超出瓦片的游戏逻辑上的位置。因此,我想的是绘制的Quad的数量是瓦片实际数量的两倍加一,如下图所示: 在这张图中,ABC代表了不同类型的瓦片,左边是游戏游玩的时候逻辑上的瓦片分布,ABC是相接的,右边是在渲染的时候的瓦片的分布,在原有瓦片中间插入新的瓦片,专门用来渲染接缝。对于2号瓦片,其左上角右上角右下角左下角(顺时针的顺序)分别是ABCC,决定了这是一块三块相接的瓦片;对于1号瓦片,对应的编号是AACC(通过一些对2取模取余的运算可以排除掉B),决定了这是一块两块相接的瓦片;而对于3和4号瓦片,其编号为CCCC,决定了这两块是没有接缝的瓦片。这时我们又考虑到了瓦片之间优先级的关系,假设C>B>A,则AACC和AABB的接缝应当是相同的,ABCC和BCCA是旋转了九十度的关系。考虑到必定会有一个瓦片处于最低优先级,我们只需要将最低优先级的瓦片固定到左上角,讨论剩下三个瓦片的优先级与顺序即可。循着这个思路,我们可以把所有可能的接缝画在一张图上,这张图的RGBA通道记录了瓦片的优先级(R优先级最低,A优先级最高,接缝我使用了一个统一的灰色以便后续渲染),图片如下所示,为了比较容易观察,我对A通道做了反向,且对应的在下方标注了优先级顺序。同时我们还对应的写好一个函数用于根据优先级顺序找到对应的接缝类型从而在渲染时找到接缝在图上的位置(见ONITileRender.hlsl中的GetMode(uint a, uint b, uint c))。 由于会有优先级的比较,不可避免地会在GPU中进行排序,使用MergeSort的话,4个元素会有5次比较,由于我们还需要获得每个瓦片在四个瓦片中排序的序号,这里就硬写了手动比较,6次比较和MergeSort的5次也差不太多。我们绘制的图上仅有最低优先级瓦片在左上角的情况,因此我们还需要找到最低优先级瓦片初始的序号,从而在渲染时旋转我们的接缝图(这里就体现了我们使用顺时针编号的优势,方便了旋转的操作,如果是左上角右上角左下角右下角的顺序,就不太好旋转了)。 知道了每一个接缝图的旋转,我们还需要为其每一个部分(通道)渲染不同的贴图。这里使用了DrawProceduralIndirect来进行Instancing的渲染,DrawCall数量会和瓦片类型的数量一样多。对于一种瓦片,需要渲染的总瓦片数相当于是这类瓦片的图形向外扩展一个瓦片的数量,我们可以通过判断左上右上右下左下的瓦片类型来轻易地判断当前瓦片是否应该和目标瓦片类型一起渲染。我们会使用一个数据数量为瓦片类型数量*(2*地图宽高+1)的StructuredBuffer来统计所有应当绘制的瓦片(实际使用的大小不会大于4*(2*地图宽+1)*(2*地图高+1))。同时我们会使用一个数据数量为瓦片类型数量*5的ByteAddressBuffer来统计每种瓦片类型Instancing时需要的参数。 本文中的岩石的2D无缝贴图来自OpenGameArt.org 具体的代码和相关的解释 由于会用到CommandBuffer进行瓦片的绘制,我就把相关的代码放到Universal RP的Package里了。CPU代码,ONITileRenderManager.cs放在Packages/com.unity.render-pipelines.universal/Runtime/Overrides/下,ONITileRendererFeature.cs放在Packages/com.unity.render-pipelines.universal/Runtime/RendererFeature/下,ONITileRenderPass.cs放在Packages/com.unity.render-pipelines.universal/Runtime/Passes/下;GPU代码,ONITileRender.hlsl,ONITileComputeShader.compute和ONITileRenderShader.shader放在Packages/com.unity.render-pipelines.universal/Shaders/ONITile/下。 ONITileRenderManager用于地图的设置、计算和Buffer的获取。ONITileRendererFeature和ONITileRenderPass用于在Unity URP中渲染瓦片,ONITileComputeShader用于瓦片相关的计算,ONITileRenderShader用于瓦片的渲染。 ONITileRenderManager.cs 这里尤其需要注意每个Buffer的大小。在这个脚本里使用Compute Shader做了三件事:1. 对地图每一个点生成一个随机数作为瓦片类型;2. 从地图中计算每一种瓦片类型需要绘制的数量、位置、解封类型、旋转和应当采样的通道;3. 把ByteAddressBuffer中的数据复制到IndirectArgumentBuffer里。事实上我感觉ComputeShader.Dispatch应该做成一个异步的方法,不过这个调用频率不高,就这样好了。 using UnityEngine; [ExecuteInEditMode] public class ONITileRenderManager : MonoBehaviour { [HideInInspector] public static ONITileRenderManager Instance { get; private set; } public ComputeShader oniTileComputeShader; public int tileTypeCount = 4; public Vector2Int tileCount = new Vector2Int(16, 16); public Vector2 tileSize = Vector2.one; public Vector3 tileStartPos; public Vector2 randomSeed; public Texture[] mainTextures = new Texture[] {}; public Vector4 mainTextureST = new Vector4(1....

February 20, 2023 · zznewclear13
Ground Truth Ambient Occlusion Cover

Unity使用ComputeShader计算GTAO

环境光遮蔽 环境光遮蔽,在很久很久以前玩刺客信条的时候就看到过这个词语,但是并不懂什么意思,本着画质拉满的原则总是会勾选这个选项。后来才知道环境光遮蔽翻译自Ambient Occlusion(还真是直白的翻译),用来表现角落里阴暗的效果。 环境光遮蔽作用在光线计算的间接光照的阶段,由于光栅化渲染的局限性,间接光照往往分为漫反射间接光照和高光间接光照,因此环境光遮蔽也分漫反射和高光两种,这里暂时只讨论作用于漫反射间接光照的漫反射环境光遮蔽。而又由于前向渲染的局限性,屏幕空间的环境光遮蔽不分差别地作用于直接光照和间接光照,因此其强度还需要特别地留意。 Ground Truth Ambient Occlusion是Jorge Jimenez在他的文章Practical Real-Time Strategies for Accurate Indirect Occlusion中介绍的一种在主机上能够符合事实环境光遮蔽效果的一种屏幕空间环境光遮蔽的算法。我认为这个算法相较于其他的环境光遮蔽的算法最大的优点是,暗部够暗,在很窄的缝隙中能够很黑很黑,这是别的算法做不到的。 本文极大地参考了英特尔的XeGTAO开源代码。 具体的操作 这篇文章着重要讲的是使用Compute Shader来加速计算的操作方式,因此不会具体涉及到GTAO算法本身,感兴趣的话可以去SIGGRAPH 2016 Course上阅读GTAO的ppt。 GTAO的计算需要视空间法线和深度两个数据,如果是延迟管线的话能轻易得拿到所有数据,但对于前向渲染来说,需要从深度数据还原出视空间法线。正好我之前的文章介绍了一些从深度图计算视空间法线的方法。但在原有文章的基础上,我们还能使用Group Shared Memory对采样数进行一系列的优化。 由于GTAO相对来说算是比较低频的信息,我们可以考虑使用下采样的方式只用半分辨率甚至是更低的分辨率来计算GTAO。这里使用的方法是对NxN大小的区域,每一帧只取一个采样点,最后通过TAA来进行混合。 GTAO本身的采样数也能使用时空噪声来生成较少的采样点,最后通过TAA来进行混合。但是实际使用中发现,如果使用较多的时间混合,当场景中的物体发生移动之后,会露出一部分白色的画面,和较深的AO有比较明显的对比,因此考虑尽量多地使用空间的混合。 得益于Group Shared Memory,可以在非常大的范围内进行空间的混合,这里使用高斯模糊的方式进行混合,能够尽量保持暗部较暗的颜色。如果直接对所有的采样进行平均的话,会导致暗部变得很亮,失去了GTAO最出众的优点。对水平和竖直方向做两次高斯模糊的话,由于本身还会根据深度和法线算出额外的几何上的权重,在下采样程度较大的时候会产生比较明显的瑕疵,可以用全分辨率的深度图和法线来解决,但这会带来额外的采样。 在高斯模糊的阶段,由于模糊是作用在低分辨率的图像上的,在我们的上采样的操作中,还需要根据上采样的位置进行双线性插值(实际上只要一个方向线性插值就好了)。 Render Texture的精度上,GTAO最后的值可以用8位通道来储存,如果不需要额外的视空间法线的话,可以把GTAO值和24位的深度一起存到RGBA32的RT中。这里就偷懒使用R16G16B16A16_SFloat来储存了。 如此一来整个路线图就比较清晰了 下采样深度图获取深度数据 使用深度图计算视空间的法线,或者从G Buffer直接获取法线数据 使用深度图和法线计算GTAO的值 横向上采样,计算水平高斯模糊后的GTAO的值 纵向上采样,计算垂直高斯模糊后的GTAO的值 相关代码和说明 GTAOComputeShader.compute 重中之重就是Compute Shader了。分了四个Kernel:第一个计算GTAO的值,同时还储存了深度图和法线(除了直接储存法线的两个分量,也可以Encode成八面体来储存);第二个和第三个分别是水平和竖直方向的模糊;最后一个用来可视化,实际项目中可以不用这个。 和XeGTAO不同的是,我增加了一个USE_AVERAGE_COS的宏,正常是在每一个Slice中选择最大的cos值,但是考虑到场景中有栅格这样的物体,在时空混合程度不是很大的时候,可以计算cos的平均值来降低栅格对GTAO的影响(也就是减弱了噪声),这个宏完全可以不用开启。 本文为了尽量多的使用空间混合(亦即不使用时间混合),在XeGTAO的时空平均噪波中限制了时间的参数为13,这样GTAO就不会随着时间而变化了,实际上可以传入_FrameIndex充分利用时空噪波的优势。 主要是用groupIndex来储存和读取Group Shared Memory,每个点至多采样两次。计算法线时会采样5x5的区域,因此NORMAL_FROM_DEPTH_PIXEL_RANGE的值是2;计算模糊时既有高斯模糊的采样,还有后续手动线性插值的采样,所以CACHED_AO_NORMAL_DEPTH_FOR_BLUR_SIZE会有两者之和。线性插值还需要注意subpixelBias对线性插值的权重产生的影响。 本文使用了宽度为29的高斯核,可以在demofox的网站上轻松的计算很大的高斯核。 可能会有报寄存器使用数量超过限制的问题,感觉是const array和循环导致的,不过reimport之后就不会报这个警告了。 #pragma kernel GTAOMain #pragma kernel BlurHorizontalMain #pragma kernel BlurVerticalMain #pragma kernel VisualizeMain #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" Texture2D<float4> _ColorTexture; Texture2D<float> _DepthTexture; Texture2D<float4> _GTAOTexture; RWTexture2D<float4> _RW_NormalTexture; RWTexture2D<float4> _RW_GTAOTexture; RWTexture2D<float4> _RW_BlurTexture; RWTexture2D<float4> _RW_VisualizeTexture; SamplerState sampler_LinearClamp; SamplerState sampler_PointClamp; //region Parameters uint _FrameIndex; uint _DownsamplingFactor; float _Intensity; float _SampleRadius; float _DistributionPower; float _FalloffRange; float2 _HeightFogFalloff; float4 _TextureSize; float4 _TAAOffsets; //endregion //region Pre-defined Marcos #define SQRT2_2 0....

December 2, 2022 · zznewclear13
zznewclear13 技术美术 图形学 个人博客 technical art computer graphics