Unity_Shader自学笔记

第二章 渲染流水线

渲染流水线分为3个阶段:应用阶段、几何阶段、光栅化阶段。

2.1 应用阶段

这一阶段由CPU负责,开发者拥有这个阶段的控制权。

1.在这个阶段有三个任务,我们需要场景准备数据,例如相机的位置、视椎体、场景中包含的模型、光源等。

2.为了提高渲染性能,需要做一个粗粒度剔除,把不可见的面剔除,设置模型的渲染状态。

3.渲染状态包括(漫反射、高光反射颜色、纹理、使用的Shader等),这一阶段输出所需要的渲染需要的几何信息,即渲染图元(点、线、面)。

这些信息会传递给下一个阶段——几何阶段。

2.2 几何阶段

这一阶段主要把顶点坐标变换到屏幕空间中,然后交给光栅器处理(在GPU上进行)。

2.3 光栅化阶段

这一阶段接收上一个阶段传过来的数据来在屏幕上产生像素,并渲染出最终图像(在GPU上进行)。

2.4 GPU与CPU的通信

渲染流水线的起点是CPU,也就是应用阶段。

应用阶段大致3个步骤:

(1) 把数据加载到显存中

(2) 设置渲染状态

(3) 调用Draw Call

2.4.1 如何把数据加载到显存中

2.4.2 渲染状态

渲染状态就是定义场景中的网格是如何被渲染的。

例如使用哪个顶点着色器,光源,材质等。

如何没有改变渲染状态,那么都将使用同一种渲染状态。

2.4.3 调用Draw Call

Draw Call 是一个命令,发起方是CPU,接收方是GPU。

当给定一个Draw Call时,GPU就会根据渲染状态,材质、纹理、着色器和所有输入的顶点数据进行计算。

GPU流水线

2.5.1  GPU渲染流水线

当GPU从CPU那得到渲染命令后,就会进行一系列流水线操作,最终把图元渲染到屏幕上。

顶点着色器是完全可编程的,它通常用于实现顶点的空间变换、顶点着色等功能。

曲面细分着色器是一个可选的着色器,它用于细分图元。

结合着色器同样是一个可选着色器,它可以被用于执行逐图元的着色操作,或者产生更多图元。

裁剪这一阶段是将那些不在摄像机视野内的顶点裁剪掉。

屏幕映射这一阶段是不可配置和编程的,它负责把每个图元的坐标转换到屏幕坐标系中。

光栅化阶段的三角形设置和三角形遍历是固定函数阶段。

片元着色器阶段,是完全可以编程的,用于实现逐片元着色操作。

逐片元操作,例如修改颜色、深度缓冲、混合等,是不可编程的,但可配置。

2.5.2 顶点着色器

顶点着色器的输入来自CPU。输入进来的每一个顶点都会调用一次顶点着色器。

顶点着色器不可以被创建和销毁任何顶点,而且无法得到顶点与顶点之间的关系。

顶点着色器的任务有:坐标变换和逐顶点光照及后面阶段需要的数据。

2.5.3 坐标变换

就是对顶点的坐标(位置)进行某种变换。

顶点着色器可以在这一步中改变顶点的位置,在顶点动画中非常有用。

无论怎么改变顶点坐标,顶点着色必须完整一个最基本的工作,把顶点坐标从模型空间转换到齐次裁剪空间

 =>这句话意思是把顶点坐标转换到齐次裁剪坐标系下

2.5.4 裁剪

不在视野内的物体不需要被处理。

这一步是不可编程的,而是由硬件固定完成,但我们可以自定义一个裁剪操作来对这一步进行配置。

2.5.5 屏幕映射

这一步输入的坐标任然是三维坐标系的坐标。

屏幕映射的任务是把每个图元的x和y坐标转换到屏幕坐标系下。

屏幕坐标系是一个二维坐标系,它和我们用于显示画面的分辨率有很大关系。

输出三角形顶点、深度值(z坐标)、法线方向、视角方向等。

这一步的值会传到下一个光栅化阶段。

OpenGL 和 DirectX之间的差异:

如果你发现得到的图像是倒转的,那么很有可能是这个原因造成的。

2.5.6 三角形设置

为了能够计算边界像素的坐标信息,我们就需要得到三角形边界的表示方式。这样一个计算三角网格表示数据的过程就叫做三角形设置。

由这一步进入光栅化阶段。

光栅化第一个流水线阶段是三角形设置。

光栅化阶段两个重要的目标是:

(1)计算每个图元覆盖了哪些像素。

(2)以及为这些像素计算颜色。

这个阶段会计算一个三角形网格所需要的信息。

2.5.7 三角形遍历

三角形遍历 阶段将会检查每个像素是否被一个三角网格所覆盖。

如果覆盖的话,就会生成一个片元(fragment)。

而这样一个找到哪些像素被三角网格覆盖的过程就是三角形遍历,这个阶段也成为扫描变换。

这些输出就是一个片元序列。一个片元并不是真正意义上的像素,而是包含了很多状态的集合,这些状态用于计算每个像素的最终颜色。

这些状态包括屏幕坐标、深度信息、及其他几何阶段输出的顶点信息,如法线、纹理坐标等。

2.5.8 片元着色器

片元着色器 是一个可编程着色器阶段。

前面的光栅化不会影响屏幕的每个像素颜色值,而是会产生一系列的数据信息,用来表述一个三角网格是怎样覆盖每个像素的。

真正对像素产生影响的是下一个流水线阶段--逐片元操作。

片元着色器的输入是上一个阶段对顶点信息插值得到的结果,也就是根据那些从顶点着色器中输出的数据插值得到的。

而片元着色器的输出是一个或多个颜色值。

为了在片元着色器中进行纹理采样,我们通常会在顶点着色器阶段输出每个顶点对应的纹理坐标进行插值后,就可以得到其覆盖的片元的纹理坐标。

2.5.9 逐片元操作

渲染流水线的最后一步。

这一阶段的主要几个任务:

(1)决定每个片元的可见性。这涉及了很多测试工作,例如深度测试、模版测试等。

(2)如果一个片元通过了所有的测试,就需要把这个片元的颜色值和已经存储在颜色缓冲区中的颜色进行合并,或者说是混合。

这一阶段可配置性高。

一般深度测试放在片元着色器之前进行,可以节省性能的消耗。

当模型的图元经过上面层层计算和测试后,就会显示到我们的屏幕上。

我们的屏幕显示的就是颜色缓冲区中的颜色值。

为了避免我们看到那些正在进行光栅化的图元,GPU会使用双重缓冲的策略,即在后置缓冲中。

一旦场景已经已经被渲染到后置缓冲区中,GPU就会交换后置缓冲区和前置缓冲中的内容,而前置缓冲区是之前显示在屏幕上的图像。

由此,保证了我们看到的图像总是连续的。
 

CPU、OpenGL/DirectX、显卡驱动和GPU之间的关系

造成渲染过慢性能降低的原因是CPU 而不是GPU

为了减少Draw Call 的性能开销,需要注意两点:

(1)避免使用大量很小的网格。当不可避免地需要使用很小的网格结构时,考虑是否合并他们。

(2)避免使用过多的材质。尽量在不同的网格之间共用同一个材质。

Unity Shader基本代码结构

Properties、SubShader、Fallback是ShaderLab语义

Properties语义块的定义通常如下:

为了在材质面板中能够方便的调整各个材质属性。

如果需要在Shader中访问,就需要使用每个属性的名字(Name)。

显示名称(display name)则是出现在材质面板上的名字。

(PropertyType)是每个属性的类型。

ShaderLab属性的类型:

Int、Float、Range是数字类型的属性,默认值其实是一个单独的数字。

Color、Vector默认值是用圆括号包围的一个四维向量。

2D、Cube、3D是纹理类型,默认值是通过一个字符串后跟一个花括号来指定的,其中字符串要么是空的,要么是内置的纹理名称,如"while"、"black"、"gray"、"bump"

SubShader语义块中包含的定义通常如下:

SubShader 中定义了一系列Pass以及可选的状态([RenderSetup])和标签([Tags])设置。

状态和标签可以在Pass声明。

SubShader中的一些标签设置是特定的。

如果我们在SubShader进行了这些设置,那么将会用于所有的Pass。

每个Pass定义了一次完整的渲染流程,如果Pass的数目过多,就会造成渲染性能下降。

ShaderLab ([RenderSetup]) 状态设置

SubShader的标签 ([Tag]) 

SubShader的标签(Tags)是一个键值对,它的键和值都是字符串类型。

注意,上述标签仅可以在SubShader中声明,而不可以在Pass块中声明。

Pass语义块:

可以在Pass中定义该Pass的名称,例如:

Name "MyPassName"

可以使用ShaderLab的UsePass命令来使用其他UnityShader中Pass,例如:

UsePass "MyShader/MYPASSNAME"

(Unity内部会把所有Pass的名称转换成大写字母,因此,使用UsePass命令时必须使用答谢形式的名字)

Pass标签类型

Fallback

跟在SubShader语义块后面。

如果其他SubShader都不能使用在这块显卡上,就使用Fallback指定的Shader。

语义如下:

Fallback会影响阴影的投射。在渲染阴影纹理时,Unity会在每个Unity Shader中寻找一个阴影投射的Pass。

Unity Shader 的形式

着色器代码可以写在SubShader语义块中(表面着色器),也可以写在Pass语义块中(顶点/片元着色器和固定函数着色器的做法)。

编写Unity Shader的3种形式:

表面着色器:

表面着色器被定义在SubShader语义块(而非Pass语义块)中的CGPROGRAM和ENDCG之间。

原因是,表面着色器不需要开发者关心使用了多少个Pass,每个Pass如何渲染等问题,Unity会在背后为我们做好了这些事情,我们只需要写代码。

顶点/片元着色器

和表面着色器类似,顶点/片元着色器等代码需要定义在CGPROGRAM和ENDCG之间,但不同的是,顶点/片元着色器写在Pass语义块内,而非SubShader内。我们可以自定义每个Pass需要使用的Shader代码。

灵活性更高,可以控制渲染实现的细节。

固定函数着色器

固定函数着色器的代码定义在Pass语义块中,这些代码相当于Pass中的一些渲染设置,对于固定函数着色器我们完全使用ShaderLab的语法(使用ShaderLab的渲染设置命令)来编写。

选择哪种Unity 着色器开发

学习Shader需要的数学基础

笛卡尔坐标系

三位笛卡尔坐标系

左手坐标系

右手坐标系

点和矢量

点是n维空间中的一个位置,它没有带小、宽度这类概念。

表示方法例如:

二维空间里点:P = (Px,Py)

三维空间的点:P=(Px,Py,Pz)

失量(Vector,也称为向量)

失量是一个包含模和方向的有向线段。

失量的模指的是这个失量的长度。一个失量的长度可以是任意的非负数。

失量的方向则描述了这个失量在空间中的指向。

点是一个没有大小之分的空间中的位置。

而失量是一个有模和方向但没有位置的量。

失量运算

相乘

只需要把失量的每个分量和标量相乘即可:

相除

一个失量可以被一个非零的标量除。这等同于和这个标量的倒数相乘:

例如:

几何意义:

一个失量v和一个标量k相乘,意味着对失量v进行一个大小为|k|的缩放。

当k<0时,失量的方向也会取反。

失量的加法和减法

只需要把两个失量的对应分量进行相加或相减即可。

加法:

减法:

例如:

注意:

一个失量不可以和一个标量相加减,或者是和不同维度的失量进行运算。

如果想要计算点b相对于a点位移,就可以通过把a和b相减得到。

失量的模

失量是有模长和方向组成的一个标量。

可以理解为是失量在空间中的长度。

三维失量的模长计算公式:

其他维度的失量的模计算类似,都是对每个分量的平方相加后再开根号得到。

例如:

单位失量

在只关心失量的方向而不是模的时候,例如光照模型,就需要计算单位失量。

单位失量指的是那些模为1的失量。

单位失量也被称为归一化的失量。

对任何给定的非零失量,把它转换成单位失量的过程就被称为归一化。

失量归一化公式:

对失量进行归一化,可以用失量的模除以该失量得到。

例如:

零失量(即失量的每个分量值都为0,如v=(0,0,0)) 是不可以被归一化的。

这是因为做除法运算时分母不能为0。

几何意义:

二维空间,可以画一个圆,那么单位失量就可以是从圆心出发、到圆边界的失量。

三维空间,单位失量就是一个单位球的球心出发、到达球面的失量。

一般法线方向、光源方向 使用前进行归一化。

失量的点积

失量之间可以进行乘法

两个三维失量的点积是把两个失量对应分量相乘再取和,最后结果是一个标量。

公式1:

例子:

失量的点积满足交换律,即

几何意义:

其中一个几何意义是投影。

假如有一个光源,那么,我们就可以使用点积.b来得到b在方向上的有符号的投影。

投影的值可能是负数。投影结果的正负号与和b的方向有关:

当他们的方向相反(夹角大于90度)时,结果小于0;

当他们的方向互相垂直(夹角为90度)时,结果等 于0;

当他们的方向相同(夹角小于90度)时,结果大雨0。

性质1:点积可结合标量乘法。

对点积其中一个失量进行缩放的效果,相当于对最后的点积结果进行缩放。

性质2:点积可结合失量加法和乘法,和性质一类似。

性质3:一个失量和本身进行点积的结果,是该失量的模的平方。

公式2:

cos=邻边/斜边

当夹角小于90度时,cos >0

当夹角等于90度时,cos =0

当夹角大于90度时,cos <0

利用这个公式还可以求得两个向量之间的夹角(0度~180度):

假设a和b是单位失量。

其中,arcos是反余弦操作。

失量的叉积

叉积的结果仍然是一个失量,而非标量。

公式:

例子:

需要注意的是,叉积不满足交换律,即axb != bxa

满足反交换律axb=-(bxa) 

不满足结合律(axb) xc != ax(bc)

叉积的几何意义:

对两个失量进行叉积的结果会得到一个同时垂直于这两个失量的新失量。

叉积的模公式:

axb的长度等于a和b的模的乘积再乘以他们之间夹角的正弦值。

使用a和构建一个平行四边形。

平行四边形的面积可以使用|b| h来得到,即底乘以高。

而h又可以使用|a|和夹角得到,即

a x b 得到两个方向的失量

根据是左手坐标系 还是 右手坐标系选择 失量的方向

使用左手坐标系 还是 右手坐标系 不会对计算结果产生任何影响

矩阵(matrix)

在三维数学中,我们通常会使用矩阵来进行变换。

一个矩阵可以把一个失量从一个坐标空间转换到另一个坐标空间。

例如在顶点着色器中我们需要把顶点坐标从模型空间变换齐次裁剪坐标系中。

Mij 表示这个元素在矩阵M的第i行、第j列。

失量可以看成nx1的列矩阵或1xn的行矩阵,其中n对应失量的维度。

例如,失量v=(3,8,6)可以写成行矩阵


或列矩阵

矩阵的运算

和失量类似,矩阵也可以和标量相乘,它的结果仍然是一个相同维度的矩阵。

矩阵相乘

矩阵的乘法,就是矩阵的每个元素和该标量相乘。

例如:

矩阵和矩阵的乘法

两个矩阵的乘法结果是一个新的矩阵,并且这个矩阵的维度和两个原矩阵的维度都有关系。

一个rxn的矩阵A和一个nxc的矩阵B相乘,他们的结果AB将会是一个rxc大小的矩阵。

第一个矩阵的列数必须和第二个矩阵的行数相同才能相乘。

例如:

矩阵A的维度是4x3,矩阵B的维度是3x6,那么AB的维度就是4x6。

他们相乘得到的矩阵的行数是第一个矩阵的行数,而列数是第二个矩阵的列数。

假设A的大小是4x2,B的大小是2x4。

那么如果计算C的元素C23的话,先找到对应的行矩阵和列矩阵,即A中的第2行和B中的第3列,他们进行失量的点积=>

矩阵乘法的性质:

性质1:矩阵乘法并不满足交换律。

性质2:矩阵乘法满足结合率。

特殊矩阵

1.方块矩阵

简称方阵,是指那些行和列数目相等的矩阵。

例如:

3x3和4x4的方正。

矩阵的一些运算和性质只有方阵才有。

例如:对角元素。

方阵的对角元素指的是行号和列号。

方阵的对角元素指的是行号和列号相等的元素,例如:

m11、m22、m33

如果一个矩阵除了对角元素外的所有元素都为0,那么这个矩阵就叫做对角矩阵。

例如,4x4的对角矩阵:

2.单位矩阵

一个特俗的对角矩阵是单位矩阵。

用In来表示,一个3x3的单位矩阵如下:

任何矩阵和上图矩阵相乘的结果都还是原来的矩阵。

这就和标量中的数字1一样!

3.转置矩阵

转置矩阵实际是对原矩阵的一种运算,即转置运算。

给定一个rxc的矩阵M,它的转置可以表示成,是一个cxr的矩阵。

也就是说,原矩阵的第i行变成了第i列,而第j列变成了第j行。数学公式为:

例如:

转置矩阵也有一些常用的性质。

性质1:矩阵转置的转置等于原矩阵。

性质2:矩阵串接的转置,等于反向串接各个矩阵的转置。

4.逆矩阵

逆矩阵,不是所有矩阵都有逆矩阵,第一个前提就是,该矩阵必须是一个方阵。

给定一个方阵M,它的逆矩阵用来表示。

如果我们把M和相乘,那么他们的结果将会是一个单位矩阵。

所有元素为0的矩阵没有逆矩阵。

如果一个矩阵有逆矩阵,那么就说这个矩阵是可逆的或者说是非奇异的。

相反,如果一个矩阵没有逆矩阵,那么就说这个矩阵是不可逆的或者说是奇异的。

如何判断一个矩阵是否可逆?

一个矩阵的行列式不为0,那么他就是可逆的。

Unity Shader 里通常调用第三方库(C++数学库Eigen)来求得,不需要我们手动计算。

逆矩阵的性质。

性质1:逆矩阵的逆矩阵是原矩阵本身。

性质2:单位矩阵的逆矩阵是它本身。

性质3:转置矩阵的逆矩阵是逆矩阵的转置。

性质4:矩阵串接相乘后的逆矩阵等于反向串接各个矩阵的逆矩阵。

一个矩阵可以表示一个变换,而逆矩阵允许我们还原这个变换,或者说是计算这个变换的反向变换。

如果我们使用变换矩阵M对失量v进行一次变换,然后再使用它的逆矩阵进行另一次变换,那么我们会得到原来的失量。

5.正交矩阵

特殊的方正正交矩阵。

正交是矩阵的一种属性。

如果一个方阵M和它的转置矩阵的乘积是单位矩阵的话,我们就说这个矩阵是正交的。反过来也是成立的。

如果一个矩阵是正交的,那么它的转置矩阵和逆矩阵是一样的。

3x3点正交矩阵:

这样就有了九个等式:

我们可以得到以下结论:

矩阵的每一行,即c1、c2和c3是单位失量,因为只有这样它们与自己的点积才能是1。

矩阵的每一行,即c1、c2和c3之间相互垂直,因为只有这样它们之间的点才能是0。

上述的两条结论对矩阵的每一列同样适用,因为如果M是正交矩阵的话, 也会是正交矩阵。

行矩阵还是列矩阵

假设有一个失量v=(x,y,z),我们可以把它转换成行矩阵v=[xyz]或列矩阵,有另一个矩阵M:

那么M分别和行矩阵以及列矩阵相乘,先看一下M和行矩阵的相乘。由矩阵乘法的定义可知,需要把行矩阵放在M的左边。

结果:

如果和列矩阵相乘的话,结果是:

发现结果出了行列矩阵的区别外里面的元素也不一样。

行列矩阵相乘结果值和书写次序是不一样的。

在Unity中,常规做法是把失量放在矩阵的右侧,即把失量转换成列矩阵来进行运算。

矩阵的几何意义:变换

一般变换包含了旋转、缩放、缩放和平移(这些变换可以通过使用矩阵实现)。

变换指的是,我们把一些数据,如点、方向失量甚至是颜色等,通过某种方式进行转换等过程。

线性变换

线性变换指的是那些可以保留失量加和标量乘的变换。

数学公式表示:

缩放就是一种线性变换。

例如:

f(x)=2x

可以表示一个大小为2的统一缩放,即经过变换后失量x的模将被放大两倍。

如果第一个三维的失量进行变换,那么仅仅使用3x3的矩阵就可以表示所有的线性变换。

线性变换除了包括旋转和缩放外,还包括错切、镜像、正交投影等。

看一个例子:令x = (1,1,1) 那么

可见,两个运算得到的结果是不一样的。因此,我们不能用一个3x3的矩阵来表示一个平移变换。

仿射变换就是合并线性变换和平移类型。

仿射变换可以使用一个4x4的矩阵来表示,因此,我们需要把失量扩展到四维空间下,这就是齐次坐标空间。

齐次坐标

由于3x3矩阵不能表示平移操作,我们就把其扩展到4x4的矩阵(只要多一个维度就可以实现对平移对表示)。

在平移中,齐次坐标是一个四维失量。

对于方向失量来说,从三维坐标转换成齐次坐标需要把w分量设为0。

平移矩阵

我们可以使用矩阵乘法来表示对一个点进行平移变换:

点点x、y、z分量分别增加了一个位置偏移。在3D中的可视化效果是,把点(x,y,z)在空间中平移了(tx,ty,tz)个单位。

方向失量进行平移变换,结果如下:

可以发现,平移变换不会对方向失量产生任何影响,因为失量没有位置属性,也就是说它可以位于空间中的任意一点。

缩放矩阵

对方向失量可以使用同样对矩阵进行缩放:

如果缩放系数kx = ky = kz,我们把这样的缩放称为统一缩放,否则称为非统一缩放。

从外观上看,统一缩放是扩大整个模型,而非统一缩放会拉伸或挤压模型。

统一缩放不会改变角度和比例信息,而非统一缩放会改变与模型相关的角度和比例。

例如在对发现进行变换时,如果存在非统一缩放,直接使用用于变换顶点的变换矩阵的话,就会得到错误的结果。

缩放矩阵的逆矩阵是使用原缩放系数的倒数来对点或方向失量进行缩放,即

缩放矩阵一般不是正交矩阵。

上面的矩阵只适用于沿坐标方向进行缩放。

如果我们希望在任意方向上进行缩放,就需要使用一个符合变换。

先将缩放轴变换成标准坐标轴,然后进行沿坐标轴的缩放,再使用逆变换得到原来的缩放轴朝向。

旋转矩阵

如果我们需要把点绕着x轴旋转度,可以使用下面点矩阵:

绕y轴度旋转也是类似:

最后是绕z轴度旋转:

旋转矩阵的逆矩阵是旋转相反角度得到的变换矩阵。

旋转矩阵是正交矩阵,多个旋转矩阵之间的串联同样是正交。

复合变换

把平移、旋转、缩放组合起来,复合变换可以通过矩阵的串联来实现。

由于使用的是列矩阵.所以阅读顺序是从右到左,即先进行缩放变换,再进行旋转,最后进行平移变换。

变换结果是依赖于变换顺序的,也就是说不同的变换顺序得到的结果可能是不一样的。

大多数情况下约定的变换顺序就是先缩放,再旋转,最后平移。

旋转的顺序不同,导致的结果也会不同。

坐标空间

坐标空间的变换

在渲染流水线中,我们往往需要把一个点或方向失量从一个坐标空间转换到另一个坐标空间。

想要定义一个空间,必须指明其原点位置和3个坐标轴的方向。

每个空间都一个父坐标空间。

对坐标空间的变换实际上就是在父空间和子空间之间对点和失量进行变换。

当给定一个坐标空间以及其中一点(a,b,c)时,我们如何知道该点的位置?

(1)从坐标空间的原点开始

(2)向x轴方向移动a个单位

(3)向y轴方向移动b个单位

(4)想z轴方向移动c个单位

Unity内置的变换矩阵

UNITY_MATRIX_MV是正交矩阵 

UNITY_MATRIX_T_MV是UNITY_MATRIX_MV的逆矩阵

可以使用UNITY_MATRIX_T_MV把顶点和方向失量从观察空间变换到模型空间。

Unity内置的摄像机和屏幕参数

Unity中常用的包含文件

 

UnityCG.chinc中一些常用的结构体

 

UnityCG.chinc中一些常用的帮助函数

Unity支持的语义

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值