寄存器分配图着色_着色基础------抗锯齿与半透明

7a2527782e058a450cd0dc058b8c6598.png

4. 抗锯齿

当绘制一个三角形时在三角形边缘位置会有明显的不连贯性的像素出现也就是锯齿,这是因为GPU对边缘像素采样时像素要么在三角形内要么在三角形为,而一般判断像素在三角形内是依靠像素中心位置判定的,因此就会出现锯齿状边缘,如下图所示,产生锯齿的本质是在采样和重构时的精度不够所引起的。(参考《计算机图形学》信号处理和滤波器两章内容,同时结合3D数据基础的卷积和傅里叶变换)

0bd14e86d17a7c4ac7efb744e8965d25.png

4.1 采样与滤波理论

采样与滤波在《计算机图形学》中有做介绍,在此在做补足和回顾。渲染物体本质上就是采样的过程,对3D连续的物体采样(到像素)以获取每个像素颜色值的过程。为了使得采样出来的像素不会过于失真(如:锯齿),需要从采样数据重建成不太失真的信号,如下图:

b3cdfad83a4f688c62d0e42ad24a5832.png

说到采样就不得不提到Nyquist 采样定理,该定理指出 采样频率只有超出信号最高频一倍以上才能准确获得原信号,这时候采样不发生频谱混叠,通过理想滤波器能够还原得到原信号,否则就会测出一个不准确的信号。这要怎么理解呢,请看下面的示例:

bd2c585e66b061910c4987e415670e88.png

经过这个示例我们可以了解采样定理的基本含义,以频率来说可能比较抽象,如果换一种说法可能采样定理更容易理解 : 对于一个周期性信号采样时,至少要以这个信号每个周期内采样2次以上的频率进行采样,才能恢复原始信号。(如果是个混合信号则需要在周期最短(频率最大)的一个周期内至少采样2次以上,才可恢复原始信号)

当我们在绘制场景中物体边界,阴影边界时,边界位置的颜色时突变的(如:由三角形的黑色突然变成了白色)此时的信号变化率是无穷大(因为是突变所以变化周期可以认为是0),变化频率是无穷大,因此根据采样定理,无论我们如何增大采样频率都无法避免这种情况下的走样/混叠现象。虽然我们无法完全避免走样/混叠现象,但我们可以用采样理论判断出是否需要采用滤波器对走样/混叠现象进行处理。(如果采样频率大于原始信号的两倍则无需处理否则则需要使用滤波器对采样型号进行重建(Reconstruction))

重建(Reconstruction)

接下来我们将介绍如何从采样的离散信号重建原信号,为此我们必须使用滤波器,此处介绍三种常见滤波器(滤波器曲线的面积一般为1,此时才能保证在此范围内信号总量没有被放大/缩小):盒式滤波器(box filter), 帐篷滤波器(tent filter) 和sinc滤波器。(更多滤波器介绍请参见《计算机图形学》)

ee75ec7185e6b70b5cea4cef3a297b91.png

其中Sinc滤波器是之前没有介绍过的滤波器其函数为: sinc(x) = sin(πx) / πx 。 滤波器函数可以对离散的采样点重建的基本原理是使用 采样函数f(x)与滤波器函数g(x)的卷积,具体原理请参见《计算机图形学---滤波器》《3D数学基础---卷积》。 实际上,对sinc函数做傅里叶变化,将其转化到频域空间就可以明显发现sinc滤波器是最理想的低通滤波器(低频率信号可通过,高频信号被阻拦)

287134746d8b7bc1ee23b48cb7d7a560.png

傅里叶变化是将波动函数由时域空间转化到频域空间,具体内容请参见《3D数学基础-----傅里叶变换》。

由上述傅里叶变化的频域空间结果可以知道该滤波器完美的将频率大于π的信号全部滤掉。这是什么意思呢?假设离散采样的频率是2π,根据采样定理原始信号的频率最大是2π/2(只有原始信号频率小于π时,频率为2π的离散采样才能恢复原始信号)。因此我们把频率高于π的信号过滤掉就可以完美的获取原始信号。当然可以用真实的频率fs代替π,sinc(fsx)是对频率为fs的完美低频滤波器。

实际应用中对sinc滤波器的使用较少,主要因为其范围是到无穷远并且某些领域的值为负数。更多使用的平滑过渡的滤波器是高斯滤波器。其傅里叶变化后仍然是个高斯函数。

重采样(Resampling)

重采样一般用于对采样信号的缩放,假设原始的采样信号频率为每一个像素做一次采样,如果希望进行重采样重采样的频率为a,a > 1 为缩小(降采样), a < 1 为放低(升采样)。放大比较简单只需按照新的采样频率采样即可,如下图:

20fc3fa7cbd276fb705c02f3028a955e.png

如果以上变化是对图像的处理,原来采样频率为1,如图总共采样5次相当于一个包含5个像素的图片,后面采样10次相当于从该图片中采样10此放入包含10个像素的图片中,因此也就是放大。

降采样就没有这么简单了,为了避免走样需要对降采样在此使用滤波器处理,如下图:

6647e63e37e556810312715bb0118a09.png

4.2 屏幕空间反走样

之前内容我们已经了解了产生走样/锯齿的原理和理论上的处理方式(增加采样频率和使用滤波器函数)。在本节中我们会介绍几种常见的基于屏幕空间的全屏抗锯齿Full-scene Antialiasing(FSAA)的技术实现方案(。这些方案基本都是采用一定的采样模式对平面上的信息进行采样(增加采样频率),然后通过加权求和的方式(使用滤波器函数)计算出该像素的最终颜色值 p, 即:

7679f9b3efaff2a4ad9fda1c9582c93b.png

其中 p(x, y) 为位于(x,y)位置的最终像素颜色值, n 为采样次数,wi为第 i 次采样值的权重,c(i, x, y)为对(x,y)像素第 i 次采样得到的颜色值。当然该公式不是一个觉得的公式而是一个思维方式的提炼,具体不同算法中的实现可能稍有不同。我们将分别介绍几种常见的FSAA的方式。

超级采样抗锯齿 SuperSampling Antialiasing(SSAA)

该方法是早起使用的抗锯齿的方法,性能较差但方法简单即: 先以更高的分辨率渲染场景,然后针对相邻采样通过使用过滤器算法计算出最终图像。举个例子,比如要渲染一张1280*1024的图片,那么先绘制一张2560*2048的图片,然后对2*2区域取平均数显示到屏幕上(本质上就是使用box滤波器对信号进行处理)。但这种方式的开销很大,如上述例子相当于以4倍大小的Z-Buffer并且有4倍的PS计算量。

此外还有一个基于累积寄存器(accumulation buffer)的超级采样的方案,(累积寄存器将一次绘制的颜色缓存区数据拷贝到多个累积寄存器中并对多个累积寄存器中的颜色处理操作计算出最终颜色,常被用来做motionblur,抗锯齿等效果)此方案无需创建2*2倍的后台缓存区大小,而是通过将原颜色缓存区中的屏幕图像拷贝到4个积累寄存器,并根据需要在屏幕x或者y方向移动半个像素。最后在计算每个像素颜色值时可以从四个积累寄存器中采样计算。该方法相对(SSAA)性能好一些, 而且更为灵活,可以根据不同的方式采样,如下图:

683cf011def4de5550d888941dcc1341.png

无论是直接的SSAA还是通过使用累积缓存区的方案都是通过完整计算的作色和深度得到的样本值,由于每个采样样本都必须通过PS计算,因此总体来说SSAA的方案实现难度低但是运行效率也比较低。

多重采样抗锯齿 Multisampling Antialiasing (MSAA)

在前面提到的SSAA中,每个子采样点都要进行单独的着色,这样在片断(像素)着色器比较复杂的情况下还是很费的。MSAA 针对每个像素只会计算一次表面着色,并在样本之间共享该结果,这样可以减少高计算成本。MSAA的基本做法是:将每个像素设定为n个采样片元(例如 4个采样片元),每个片元在光栅化时有自己的颜色深度等信息,但对于片元/像素着色器的执行,则是以像素为单位执行的。最终该像素的颜色值对n个采样片元使用滤波器函数处理(默认是box滤波器也就是求平均值)。这样做的好处是即保留了SSAA的提高采样频率,又避免了对多余采样的PS计算。这样概念性的描述可能比较抽象难以理解,我们用以下图片中的示例来对比分析SSAA和MSAA的区别:

09329df4dc6fc659452227df4e383e19.png

22c435ada3e17fe1281b585e505bed25.png

86b0a8901f125d7f30a5f488e4af93d2.png

MSAA的第一步是设置采样级别,在OpenGL中创建窗口前可以使用 glfwWindowHint(GLFW_SAMPLES, 4);指定采样级别。之后光栅化时,GPU会将顶点数据按照采样级别栅格化到每个采样点。默认MSAA的每个采样点都用于自己的颜色和深度缓存,NVIDIA引入了coverage sampling anialiasing (CSAA), AMD引入enhanced quality antialiasing (EQAA)来优化采样信息的保存方式,如下图:

0aa61491f9c178dc9ecabcf7fd28f5c7.png

之后在开始绘制之前开启多重采样,在OpenGL中调用 glEnable(GL_MULTISAMPLE); 即可。当逐像素渲染时,GPU会根据当前图元对采样点的覆盖情况,以及该采样点的深度值绝对是否对该像素执行PS。如果执行PS则将PS计算的颜色值存入该图元所覆盖的采样点颜色列表中。最终对采样的使用滤波器函数(默认使用box滤波器即平均值)处理即可得到抗锯齿后的像素颜色值信息。现代GPU中PS或者CS支持访问MSAA的采样样本并根据需要使用任意的滤波器函数处理。

虽然MSAA针对SSAA做了性能上的提升(减少PS/FS的执行次数),但它仍然存在缺陷,比如对HDR颜色进行MultiSampling时,可能会出现问题。更重要的是延迟渲染无法很好的兼容MSAA(代价太高,且最终效果可能是错误的)。除此之外MSAA只能处理几何走样(即由于绘制几何图形时采用不足导致的走样), 不能处理Shading走样(例如在对光照进行计算时由于某些部分(法线方向突变等)变化较快采用不足时导致的走样)。

时域性(历史帧)抗锯齿 Temporal Antialiasing(TAA)

Temporal Antialiasing(TAA) 是一种通用技术,他的核心是使用以前帧的结果来改善图像。MSAA,SSAA这种抗锯齿的方式是在每一帧增加采样次数然后通过滤波器由多个采样数据计算出滤波后的像素颜色。TAA的思路是将多次采样分摊到多帧,每帧只对采样样本在像素内进行一些偏移,然后将之前几帧的图片与当前融合计算。(本质上是将一帧的多次采样分摊到了连续的n帧中)

这样做的好处也很明显,既避免了每帧的多次采样,又可以避免SSAA,MSAA只能对几何走样处理的尴尬境遇。

先通过MSAA或者其它算法生成一张图片,然后将之前的图片与其混合。通常只会使用2到4 张图片进行混合。旧图片的权重可以采取指数形式减小,但是,如果观看者和场景不动,这种权重方案可能会产生帧闪烁的效果,因此,通常只用当前帧和上一帧以权重相同的方式进行混合。由于每帧的采样位置使用的是同一个像素中的 不同位置,所以这样的加权和比单一帧在边缘上的处理效果更好。因此,可以说使用最近两帧平均在一起可以得到更好的结果。每帧不需要额外的采样样本,这正是这种方法如此吸引人的原因。

虽然TAA解决了不兼容延迟渲染的问题,提升了执行效率,但它也存在自己的缺陷。如快速移动的物体或者快速移动相机时可能出现重影。解决重影问题影的一个办法是只对移动缓慢的对象执行这种抗锯齿。还有一个办法是使用 reprojection重投影,更好的将前一帧和当前帧的对象关联起来。这种方案中,将物件的移动向量保存在一个单独的velocity buffer中。这些向量是用于将前一帧与当前帧进行关联使用的。比如,从当前像素位置减去该向量以找到该对象的表面位置在前一帧 上的颜色像素。那些上一帧有,而在当前帧没有出现的表面,在前一帧中的颜色像素则会被丢弃。

这里仅仅是一些基本的原理和思路并不涉及具体实现方式,在具体实现时会有更多细节和问题,本书并未详细提及,会在其他笔记中整理各种AA的具体实现方案。

Sampling Patterns 采样模式

上述的所有AA方式中都需要改变对每个像素点的采样次数,采样位置。不同的采样方式也会到抗锯齿产生影响,研究表明人类最容易受到水平垂直方向的锯齿干扰,其次是斜45°方向。 Rotated Grid Supersampling旋转网格超级采样(RGSS使用旋转的方形图案在像素内提供了更多的垂直和水平采样点。下图中展示了包含RGSS在内的多种采样模式:

72e1b377a3ace7bc881d3bc0da84da95.png

RGSS是一种Latin hypercube拉丁超立方体或者N-rooks的采样,其中N个采样样本被放置到n*n 的网格中,每行和每列都有一个采样样本。这种采样模式能较好的捕获近似水平垂直方向的采样信息,但对于斜对角线的走样捕获会比较差。为了获得更好的采样,我们需要尽量避免两个样本彼此靠近。我们还需要一个均匀的分布,将样本均匀的分布在整个区域。为了形成这样的模式,分层采样技术,比如拉丁超立方体采样将于其它方法相结合,比如jittering抖动,halton sequences哈尔顿序列,以及Poisson disk泊松盘采样等。但这种规律性较强的采样方式也存在缺陷,当对细小的物件以相对较为恒定的间隔对其采样时会产生摩尔纹现象。当然可以通过随机采样的方式规避这些问题。实际上GPU厂商会将这些采样模式写入硬件,我们可以根据不同情况选用适合的采样模式。

Morphological Methods 形态学方法处理AA

锯齿通常是由边缘造成,比如几何体、尖锐的阴影或者明亮的高光。利用这一点(锯齿与结构有关),可以更好的得到一个抗锯齿的结果。2009年,Reshetov提出了一种算法,称之为morphological antialiasing形态学抗锯齿(MLAA)。morphological形态学是指与结构或者形状有关, 强调了对边沿的检测和重建。下图展示了MLAA确定边缘方向,修正走样的基本过程:

f7a26c174f0a70d4363b20d027015638.png

MLAA的方式是基于每个像素的后处理的方式,这里仅简单提到利用该方式的两种比较流行的算法 FXAA 和 SMAA 并未做详细介绍,同样会在其他笔记中整理各种AA的具体实现方案。

5. 半透明 Alpha Blend

半透明物件允许光线通过的方式有很多种。对于渲染算法,它们大致上可以分为基于灯光的半透明效果和基于视图的半透明效果。基于灯光的效果是指半透明对象导致灯光减弱或者转向,从而导致场景中的其他对象以不同的方式被照亮和渲染的效果。基于视图的半透明效果是半透明对象本身被渲染的效果。本节讨论的半透明是基于视图的半透明效果,灯光半透明会在后续章节中提及。

有一种和渲染顺序无关(无需先渲染不透明再渲染半透明)也不需要特殊硬件的处理半透明的方式,它把对应像素全部当做不透明处理(因此半透明导致的问题也就消失了)这种处理半透明效果的思路被称为alpha to coverage。 alpha to coverage源自Screen-Door transparency(纱门透明度)理论。其基本思想是将alpha值换成mask来绝对像素是否写入颜色缓冲区。这类技术的主要目的是避免alpha blending所需的排序问题,是渲染顺序无关的半透明渲染的古典解决方案。

例如:alpha为0.5时,mask值为0.5也就是说绘制时有一般像素会被绘制另一半不会绘制,alpha越小被绘制的像素越少,如下图:

3c4dc65e9f2a4b8d3441e50b5e8cfd03.png

如果对Alpha的mask也是用随机位置采样的话甚至连抗锯齿的操作都可以省略了,该方案绘制alpha的好处很明显,即与渲染顺序无关(后处理可用),又无需blend操作,也不需要再做抗锯齿计算,但其缺点就是包含明显的像素图案(像素感很强)。

大多数半透明的算法是将半透明物件的颜色与其后面物件的颜色混合在一起。当在屏幕上渲染半透明对象的时候,根据深度值判断是否需要融合,需要融合的部分执行alphablend操作。

5.1 Blending顺序

为了使物件看起来是透明的,需要将其以alpha小于1.0的形式绘制在已有场景上。该物件所覆盖 到的每个像素都会收到一个由PS计算得到的RGBA值。将该值与像素上原有值进行混合,混合的 时候通常使用over操作,如下:

2919bae92f71db91930f12dfcb37658d.png

当然这种alpha Blend的计算方式不是固定的,操作方式支持选择性设置,例如当使用一个红色的半透明滤光片(只有红色光可以透过)看一个蓝色物体时,通常会使得蓝色物件看起来很暗,这是因为蓝色物件反射的光线在穿过红色滤光片的时候,基本无法穿过。如下图。而如果使用over的话,结果是红色和蓝色的部分相加。这里其实最好是将两种颜色相乘,并添加上半透明对象本身的颜色。

b595c0b740f7cffbd912cb1ac3919ea1.png

当然还有其他混合的操作方式,如

6c440cf169480a0b76945e9e401c84cd.png

这种blending模式比较适合用于发光效果,比如闪电或者火花,它们不会衰减后面的像素,只会使得像素变得更亮。

Zbuffer的一个限制是针对每个像素只能存储一个物件。如果多个半透明物件重叠在相同的像素中,单靠Zbuffer无法保存并resolve所有可见物件的效果。在任意像素点上绘制半透明物件的时候,需要按照从后到前的顺序。如果不这样做的话可能会出现错误的效果。实现这种排序的一个 方法是根据单个对象的质心沿视图方向的距离对其进行排序。这种粗略的排序可以完成工作,但很多情况下会出问题,因为这个排序是近似的,不能保证每个面的顺序都是物体的排序顺序。如下图中左侧的飞机,可以半透看到飞机内部,明显是错误的。

2a7a29e6bbe1217187fcb3f41b387197.png

虽然存在问题但在很多情况下仍然使用近似排序的方式,毕竟这种方式最为简单高效。为尽量保证这种方式的绘制正确,要以正确的顺序绘制多层半透明物体首先绘制不透明物体,然后关闭ZBuffer写入在对半透物体进行绘制,所有半透物体近进行ZTest但不写入深度值。此外还有些方式对其进行改善,如对半透物体进行两次绘制,线渲染背面再渲染证明。或者修改blend计算方式改为从前层向后层绘制。

df9543e249d2c214df7032cafdf7664d.png

此时无论对像素的Alpha Blend计算还是对采样点的计算对最终结果都没有影响,因为alpha blend公式与source和des的执行顺序无关,下图为一个像素被两个不同alpha值覆盖不同采样点的计算:

171f05562ad4a98b40a05ee385256437.png

5.2 顺序无关的半透明渲染 Order Independent Transparency

所谓顺序无关的半透明渲染就是不需要考虑对半透明物体的排序问题,纱门透明度就是一种OIT的渲染方式,而5.1中讲述的blending排序实际上是顺序有关的渲染方式,并且5.1的排序方式实际上不能完全避免多层alpha叠加渲染效果错误的问题。本节会讲述一些常见的OIT的半透明渲染算法如: 深度剥离Depth Peeling, A-Buffer, K-Buffer 。

Depth Peeling算法的基本原理是使用两个ZBuffer和多个pass每次将离相机最近的半透明层"剥离"出来,将所有半透明测叠加后再和不透明物体blend。具体步骤如下: 1. 第一个pass绘制所有物体(包含半透明和不透明物体)的深度信息(仅绘制深度不会只颜色)保存在ZBuffer1中。 2. 第二个pass对所有半透明物体深度信息。如果当前物体深度于ZBuffer1中深度值相同,我们就知道这个这是离相机最近的透明物体就要对其进行"剥离"(将它的RGBA值保存到一个颜色缓冲中)。绘制深度信息的方式是将当前像素于ZBuffer1中的深度值比较,小于等于ZBuffer1深度的被丢弃(if(pos.z<=texture(depthTexture,vec2(pos.x,pos.y))) discard;),并将当前半透明的深度值写入ZBuffer2中。(仍然是深度测试,但在该pass中对深度小于等于ZBuffer1值的像素颜色记录深度丢弃,目的是将第二层最靠近相机的深度记录)。 3, 将剥离出的当前层半透明颜色缓存于之前剥离的颜色缓存数据进行under operator(5.26)。 4, 交换ZBuffer1和ZBuffer2并继续执行第 2,3,4三个步骤直到最大pass数或者所有半透明曾绘制完成。 5, 将颜色缓冲中半透明颜色于不透明颜色表面融合。

Depth Peeling虽然优先但是其性能差,因为每层alpha剥离都要执行一个pass,因此有个对其优化的方案被称为 dual depth peeling,该方案在每个pass中同时剥离出离相机最近和最远的两个层,有效的使pass数降低一半。

A-Buffer的基本思路是为每个采样片元创建一个coverage mask, 完全不透明的片元数据会剔除其后面的片元数据(类似深度缓冲区),半透明片元信息被保存起来,然后对所有存储的片元信息采样计算半透明blend,得出最终结果。 A-Buffer的问题在于你不知道每个片元需要占用多少内存,在某些特殊情况下(eg:大量半透明粒子特效)可能每个采样点有几十上百个半透明片元数据。但通过Multi-Layer Alpha Blending的方案可以解决内存消耗的问题,但同时带来的是额外的性能损耗。

Multi-Layer Alpha Blending是基于K-Buffer的方案,K-Buffer的基本理论是对半透明的前几个(如:前三层)可见层保存下拉排好序,对后面的半透明层(如: 从第四层半透开始)采用加权平均的方式在一个pass中计算出所有半透明颜色的blend值。对后几层的加权计算公式如下:

f6844788ca3a33f231cdb3f0888da5ec.png

163e66092f2e886b974482be0c4112a7.png

5.3 Alpha颜色表示及颜色校正

对于带Alpha通道的颜色的表达有两种方式,一种方式及使用非alpha预乘的颜色表达即使用RGBA四个维度分别表示颜色值和alpha值如半透明度为0.4的白色为(1,1,1,0.4),其好处是直观并且全面的保存来颜色原本的信息和半透明信息,此外为了融合计算方便会使用预乘的颜色表达方式,即预先将alpha通道的值乘以颜色值,上述白色半透明将变为(0.4,0.4,0.4) 这会使得alphaBlend计算时更加方便(此时Blend和Filter可以同时进行)。

由于我们在理论和逻辑计算时一般认为颜色值是线性的,例如: R 为1时的红色值的亮度是R为0.5时的一倍。理论上没有什么问题,但由于硬件设备(显示器)的显示是非线性的(实际上是指数曲线变化,也就是说R=0.5和R=1是表现出的亮度并不是一倍的关系)因此需要对颜色进行校正(校正到线性空间),这也就是我们常说的gamma校正。幸运的是大多数GPU支持sRGB的颜色空间规范定义,会在从纹理读取数值或者向颜色缓存区写入sRGB格式的数据值自动进行转换。如果你使用的颜色缓冲区的格式是RGB而非sRGB,则需要在使用这些采样颜色之前手动将其转化到线性空间,转换公式如下:

b7e697f90baa8b3e22d303790d5bac7a.png

有时也会被简化为

cb8abe0718a6fe69248d8f5eb8d60413.png

常用的指数为2.2次幂。

多数情况下使用sRGB的颜色格式GPU会自动进行颜色线性空间转化而无需手动计算处理。

6. 总结

  • 所谓着色器模型是指如果根据光照及表面信息最终得出表面颜色值的过程。
  • 点光源的衰减和距离的平方成反比(原因可以参考球的表面积计算),为了点光源在最大影响范围Rmax附近不会有突变截断的现象,可以使用修正函数如

52fdcb121832ef0b569eacfc2ab4238e.png

来保证其平滑衰减,这在lightingmap等采样光照中十分有用(避免在Rmax附近的越变)。

  • 现代的材质系统很少使用为每个效果编写一套Shader代码的方式实现引擎中所有效果。更多的是采用将着色器功能拆分并使用组合变体等形式根据需求组装着色器的方式。
  • 锯齿的本质是由于采样不足导致的走样,而大多数抗锯齿的方法是通过提高采样精度使用滤波器函数将采样数据重构的方式减少和降低走样。
  • SSAA和MSAA的抗锯齿方式很类似,都是提升采样频率,但SSAA直接暴力的将颜色缓冲区提升n倍导致显存,PS计算量都提升n倍。MSAA的方式仅仅是将每个像素的采样点提升n倍,PS计算还是逐像素进行,因此性能比SSAA要高。
  • TAA FXAA等方式略有提及原理需要自己补充其他AA方式。TAA的基本思路是将多次采样平摊到不同帧计算,然后在从多帧中采样重建。
  • 半透明的主要问题在于渲染顺序可能会影响结果,本章介绍了些处理方式包括:顺序有关的Blend排序+under operator的方式和 OIT的 Screen-Door transparency 深度剥离 ABuffer KBuffer等方式。
  • gamma矫正和sRGB
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值