第七章-基础纹理

基础概念

  • 纹理映射技术(texture mapping)
  • 纹理映射坐标(texture-maping coordinates,uv坐标)
  • 纹素(texel)

单张纹理

  • _MainTex_ST:Unity中使用{纹理名}_ST的方式来声明某个纹理的属性,其中ST是缩放(scale)和平移(translation)的缩写,通过_MainTex_ST.xy来获取缩放值,通过_MainTex_ST.zw获取偏移值,可通过面板进行调节
    在这里插入图片描述
  • albedo:材质的反射率,作用于环境光照和漫反射光照中
  • tex2D:传入纹理和uv坐标进行采样获取颜色
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'

Shader "Unity Shaders Book/Chapter 7/Single Texture"{
    Properties {
        _Color ("Color", Color) = (1, 1, 1, 1)
        _MainTex ("Main Tex", 2D) = "white" {}
        _Specular ("Specular", Color) = (1, 1, 1, 1)
        _Gloss ("Gloss", Range(8.0, 256)) = 20
    }
    SubShader{
        Tags {
            "LightMode" = "ForwardBase"
        }
        Pass{
            CGPROGRAM
            
            #pragma vertex vert
            #pragma fragment frag

            #include "Lighting.cginc"
            
            fixed4 _Color;
            sampler2D _MainTex;
            float4 _MainTex_ST;
            fixed4 _Specular;
            float _Gloss;

            struct a2v {
                float4 vertex: POSITION;
                fixed3 normal: NORMAL;
                float4 texcoord: TEXCOORD0;
            };

            struct v2f {
                float4 pos: SV_POSITION;
                float3 worldNormal: TEXCOORD0;
                float3 worldPos: TEXCOORD1;
                float2 uv: TEXCOORD2;
            };


            v2f vert(a2v i){
                v2f o;
                o.pos = UnityObjectToClipPos(i.vertex);
                
                //o.worldNormal = normalize(mul(i.normal, (float3x3)unity_WorldToObject));
                o.worldNormal = normalize(UnityObjectToWorldNormal(i.normal));
                
                o.worldPos = mul(unity_ObjectToWorld, i.vertex).xyz;
                
                //错误写法
                //o.uv = i.texcoord;
                //o.uv = i.texcoord * _MainTex_ST.xy + _MainTex_ST.zw;
                o.uv = TRANSFORM_TEX(i.texcoord, _MainTex);
                return o;
            }

            fixed4 frag(v2f i): SV_Target {
                float3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));

                fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
                
                fixed3 h = normalize(viewDir + worldLightDir);

                fixed3 albedo = tex2D(_MainTex, i.uv).rgb * _Color.rgb;
                
                // 环境光也需要 * albedo
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb * albedo;
                
                // _Diffuse.rgb改为albedo
                fixed3 diffuse = _LightColor0.rgb * albedo * saturate(dot(worldLightDir, i.worldNormal));

                fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(i.worldNormal, h)), _Gloss);

                fixed3 color = ambient + diffuse + specular;

                return fixed4(color, 1.0);
            }

            ENDCG
        }
    }
}

在这里插入图片描述

纹理的属性

在这里插入图片描述

  • Texture Type(纹理类型):在这里插入图片描述
  • Alpha Source(透明度来源):在这里插入图片描述
Wrap Mode(平铺模式):在这里插入图片描述
- 选择Per-Axis相当于可以单独对UV定制化平铺模式

在这里插入图片描述

  • 效果对比:
    在这里插入图片描述
  • 通过调整纹理偏移查看效果
    在这里插入图片描述
    在这里插入图片描述
    • 注意:若shader中没有考虑纹理_ST的作用则调整偏移则无效果,例如在这里插入图片描述在这里插入图片描述
Filter Mode(纹理过滤)
  • 资源导入设置
    在这里插入图片描述
  • 三种模式的效果对比:用64 x 64的图片放大成256 x 256进行渲染
    在这里插入图片描述
Mipmap:多级渐远纹理
  • 环境搭建:
    • Scene中添加Plane对象
    • 新增材质文件TextureFilterMat,同时新增Unity Shader文件TextureFilter
    • Plane对象的MeshRender组件设置材质TextureFilterMat
      在这里插入图片描述
    • TextureFilterMat材质属性中,纹理的缩放设置(30,30)
      在这里插入图片描述
  • No Mipmap效果:摩尔纹(走样)
    在这里插入图片描述
  • 带Mipmap效果,其中Filter Mode
    • Point在这里插入图片描述
    • Bilinear:在这里插入图片描述
    • Trilinear:在这里插入图片描述
纹理其他信息
  • 最大尺寸:如果纹理大小超过最大尺寸,则Unity会将该纹理进行缩放至满足最大尺寸范围下

  • 纹理格式:在这里插入图片描述

  • NPOT纹理:占用更多内存空间,而且GPU读取纹理的速度也下降;

凹凸映射(bump mapping)

  • 目的:使用一张纹理来修改模型的表面法线,来为模型提供更多细节(看起来好像是凹凸不平,实际在特定角度上能看出破绽)
  • 方案:
    • 高度映射(height mapping):高度纹理(height map)模拟表面位移得到一个修改后的法线值,可以理解为增量改动原法线;
    • 法线映射(normal mapping):法线纹理(normal map)直接存储表面法线,可以理解为直接替换原法线;
  • 法线纹理
    • 模型空间的法线纹理:
      • 为何颜色看起来五颜六色:主要是模型空间下存储的法线跟当前顶点强相关,每个顶点的法线在模型空间下差异较大,因此对应的模型空间下的法线纹理也差异较大;
    • 切线空间的法线纹理:
      • 为何看起来都是偏蓝色:由于在切线空间下z轴为顶点法线方向,而法线纹理存储的则是每个点的法线扰动方向,如果一个点的发现方向是(0,0,1),则对应的颜色值为(0.5,0.5,1)为浅蓝色,因此看上去一大片都是浅蓝说明大部分顶点的法线是没有变化;
        在这里插入图片描述
  • 为何选择使用切线空间的法线纹理?
    • 讲道理每太看明白书中介绍的优点(P147)
    • 自由度高
    • 在切线空间下的法线纹理更容易复用,而模型空间下的法线纹理是跟特定模型绑定(复用性较差)
    • 兼容UV动画
    • 可压缩

实践演练(切线空间的法线纹理)

  • 选择哪个坐标系进行光照计算:切线空间,还是世界空间
    • 回答:效率上看是切线空间,通用性(其他计算需要用到世界空间的情况下)上看是世界空间
在切线空间下计算
  • float4 tangent: TANGENT;告诉Unity将顶点的切线方向填充到tangent变量中,而为什么最终结果是float4(normal变量是float3)是因为分量w决定副切线的方向性(讲道理需要认真学习下切线空间下的相关知识,没太理解这块)
  • 坑点
    • 通过内建函数TRANSFORM_TEX根据顶点的uv坐标换算(考虑纹理 平铺 和 偏移 参数)为最终传入fragment shader的uv坐标时,如果在Pass中没有提前声明_MainTex_ST则会出现编译异常,说明内建函数最终还是会依赖Pass中声明的变量;
      在这里插入图片描述
    • 使用UnpackNormal(tex2D("_BumpMap", i.uv.zw))的原因?看上去应该是要将法线转换到切线方向进行计算
    • 使用TANGENT_SPACE_ROTATION 宏时发现编译错误提示变量v不存在
      在这里插入图片描述
      • 查看UnityCG.cginc中宏事实上是对顶点着色器中的输入变量名字是有限制(蛋疼)
        在这里插入图片描述
      • 解决方案:v2f vert(a2v i)中调整输入变量名为v,猜测其他宏也有类似的依赖,可以养成习惯默认在顶点着色器中的输入变量名字为v更合适;
    • albedo的变量类型误写成float(正常应该是float3)导致最终表现效果异常,类似变成一个灰度图(读取单分量后跟向量进行乘积相当于缩放)
      • 在这里插入图片描述
  • 最终效果
    在这里插入图片描述
    在这里插入图片描述
  • 代码实现
Shader "Unity Shaders Book/Chapter 7/Normal Map In Tangent Space" {
    Properties
    {
        _Color ("Color", Color) = (1, 1, 1, 1)
        _MainTex ("Main Tex", 2D) = "white" {}
        _BumpMap ("Normal Map", 2D) = "bump" {} // 法线纹理,默认值"bump"是内置的法线纹理
        _BumpScale ("Bump Scale", Float) = 1.0 // 控制凹凸程度,0则代表法线纹理不会对光照产生任何影响
        _Specular ("Specular", Color) = (1,1,1,1) // 材质高光反射的颜色
        _Gloss ("Gloss", Range(8.0, 256)) = 20 // 高光区域的大小
     }
     SubShader
     {
        Pass{
            Tags {"LightMode" = "ForwardBase"}

            CGPROGRAM

            #pragma vertex vert
            #pragma fragment frag

            #include "Lighting.cginc" // 感觉涉及光照就基本要导入下这个
            #include "UnityCG.cginc" // TANGENT_SPACE_ROTATION内建变量需要导入

            fixed4 _Color;
            sampler2D _MainTex;
            float4 _MainTex_ST;
            sampler2D _BumpMap;
            float4 _BumpMap_ST;
            float _BumpScale;
            fixed4 _Specular;
            float _Gloss;


            struct a2v {
                float4 vertex : POSITION;
                float3 normal: NORMAL;
                float4 tangent: TANGENT;
                float4 texcoord: TEXCOORD0;
            };

            struct v2f {
                float4 pos: SV_POSITION;
                float4 uv: TEXCOORD0;
                float3 lightDir: TEXCOORD1;
                float3 viewDir: TEXCOORD2;
            };

            v2f vert(a2v v){
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.uv.xy = TRANSFORM_TEX(v.texcoord, _MainTex);
                o.uv.zw = TRANSFORM_TEX(v.texcoord, _BumpMap);
                // float3 binormal = cross( normalize(v.normal), normalize(v.tangent.xyz) ) * v.tangent.w;
                // float3x3 rotation = float3x3(v.tangent.xyz, binormal, v.normal);
                TANGENT_SPACE_ROTATION;
                o.lightDir = mul(rotation, ObjSpaceLightDir(v.vertex)).xyz;
                o.viewDir = mul(rotation, ObjSpaceViewDir(v.vertex)).xyz;


                return o;
            }

            fixed4 frag(v2f i): SV_Target{
                
                float3 tangentLightDir = normalize(i.lightDir);
                float3 tangentViewDir = normalize(i.viewDir);

                // 从normal map中获取法线向量
                fixed4 packedNormal = tex2D(_BumpMap, i.uv.zw);

                fixed3 tangentNormal;

                tangentNormal = UnpackNormal(packedNormal);
                tangentNormal.xy *= _BumpScale;
                tangentNormal.z = sqrt(1.0 - saturate(dot(tangentNormal.xy, tangentNormal.xy)));

                float3 tangentHalfDir = normalize(tangentViewDir + tangentLightDir);


                float3 albedo = tex2D(_MainTex, i.uv.xy).rgb * _Color.rgb;

                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb * albedo;

                fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(tangentLightDir, tangentNormal));

                fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(tangentHalfDir, tangentNormal)), _Gloss);
                
                return fixed4(ambient + diffuse + specular, 1.0);
            }

            ENDCG
        }
     }
     FallBack "Specular"
}
  • 不同BumpScale的对比
    在这里插入图片描述在这里插入图片描述在这里插入图片描述
世界空间下计算
  • 注意点:
    • 法线贴图从切线空间转换世界空间,这里的矩阵计算有点复杂
    • fragment shader中的法线方向是直接通过法线贴图获取;
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'

Shader "Unity Shaders Book/Chapter 7/Normal Map In World Space" {
    Properties
    {
        _Color ("Color", Color) = (1, 1, 1, 1)
        _MainTex ("Main Tex", 2D) = "white" {}
        _BumpMap ("Normal Map", 2D) = "bump" {} // 法线纹理,默认值"bump"是内置的法线纹理
        _BumpScale ("Bump Scale", Float) = 1.0 // 控制凹凸程度,0则代表法线纹理不会对光照产生任何影响
        _Specular ("Specular", Color) = (1,1,1,1) // 材质高光反射的颜色
        _Gloss ("Gloss", Range(8.0, 256)) = 20 // 高光区域的大小
     }
     SubShader
     {
        Pass{
            Tags {"LightMode" = "ForwardBase"}

            CGPROGRAM

            #pragma vertex vert
            #pragma fragment frag

            #include "Lighting.cginc" // 感觉涉及光照就基本要导入下这个
            #include "UnityCG.cginc" // TANGENT_SPACE_ROTATION内建变量需要导入

            fixed4 _Color;
            sampler2D _MainTex;
            float4 _MainTex_ST;
            sampler2D _BumpMap;
            float4 _BumpMap_ST;
            float _BumpScale;
            fixed4 _Specular;
            float _Gloss;


            struct a2v {
                float4 vertex : POSITION;
                float3 normal: NORMAL;
                float4 tangent: TANGENT;
                float4 texcoord: TEXCOORD0;
            };

            struct v2f {
                float4 pos: SV_POSITION;
                float4 uv: TEXCOORD0;
                float4 TtoW0: TEXCOORD1;
                float4 TtoW1: TEXCOORD2;
                float4 TtoW2: TEXCOORD3;
            };

            v2f vert(a2v v){
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.uv.xy = TRANSFORM_TEX(v.texcoord, _MainTex);
                o.uv.zw = TRANSFORM_TEX(v.texcoord, _BumpMap);
                
                float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
                float3 worldNormal = UnityObjectToWorldNormal(v.normal).xyz;
                float3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz);
                float3 worldBinormal = cross(worldNormal, worldTangent) * v.tangent.w;
                
                o.TtoW0 = float4(worldTangent.x, worldBinormal.x, worldNormal.x, worldPos.x);
				o.TtoW1 = float4(worldTangent.y, worldBinormal.y, worldNormal.y, worldPos.y);
				o.TtoW2 = float4(worldTangent.z, worldBinormal.z, worldNormal.z, worldPos.z);
                return o;
            }

            fixed4 frag(v2f i): SV_Target{
                float3 worldPos = float3(i.TtoW0.w, i.TtoW1.w, i.TtoW2.w);
                
                float3 lightDir = normalize(UnityWorldSpaceLightDir(worldPos));
                float3 viewDir = normalize(UnityWorldSpaceViewDir(worldPos));

                float3 halfDir = normalize(lightDir + viewDir);
                
 				// Get the normal in tangent space
				fixed3 bump = UnpackNormal(tex2D(_BumpMap, i.uv.zw));
				bump.xy *= _BumpScale;
				bump.z = sqrt(1.0 - saturate(dot(bump.xy, bump.xy)));
				// Transform the narmal from tangent space to world space
				bump = normalize(half3(dot(i.TtoW0.xyz, bump), dot(i.TtoW1.xyz, bump), dot(i.TtoW2.xyz, bump)));
				
                float3 albedo = tex2D(_MainTex, i.uv.xy).rgb * _Color.rgb;

                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb * albedo;

                fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(lightDir, bump));

                fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(halfDir, bump)), _Gloss);
                
                return fixed4(ambient + diffuse + specular, 1.0);

            }

            ENDCG
        }
     }
     FallBack "Specular"
}
法线纹理类型
  • 用到UnpackNormal内建函数时需要将纹理类型设置为Normal map
    在这里插入图片描述
  • 高度图设置
    • 勾选:Create from Grayscale
    • Bumpiness:控制凹凸程度
    • Filtering:控制计算凹凸程度的方式
      • Sharp:使用Sobel滤波生成法线
      • Smooth:生成的法线纹理相对光滑
        在这里插入图片描述

渐变纹理

  • 冷到暖色调(cool-to-warm tones)着色技术:插画风格的渲染效果,很多卡通风格的渲染都使用这种技术
  • 代码实现
    • 半兰伯特光照模型:表现增强技术,在这里将计算结果作为渐变纹理的uv坐标
    • 注意点
      • 用到TRANSFORM_TEX函数时需要提前声明_RampTex_ST
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'

Shader "Unity Shaders Book/Chapter 7/Ramp Texture"{
    Properties{
        _Color ("Color Tint", Color) = (1,1,1,1)
        _RampTex ("Ramp Tex", 2D) = "white" {}
        _Specular ("Specular", Color) = (1,1,1,1) // 材质反射的高光颜色
        _Gloss ("Gloss", Range(8.0, 256)) = 20 // 材质高光区域大小
    }

    SubShader{
        Pass{
            Tags {"LightMode" = "ForwardBase"}

            CGPROGRAM

            #pragma vertex vert
            #pragma fragment frag

            #include "Lighting.cginc"
            
            float4 _Color;
            sampler2D _RampTex;
            float4 _RampTex_ST;
            float4 _Specular;
            float _Gloss;

            struct a2v{
                float4 vertex: POSITION;
                float3 normal: NORMAL;
                float4 texcoord: TEXCOORD0;
            };

            struct v2f{
                float4 pos: SV_POSITION;
                float3 worldNormal: TEXCOORD0;
                float3 worldPos: TEXCOORD1;
                float2 uv: TEXCOORD2;
            };

            v2f vert(a2v v){
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.worldNormal = UnityObjectToWorldNormal(v.normal);
                o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
                o.uv = TRANSFORM_TEX(v.texcoord, _RampTex);
                return o;
            }

            fixed4 frag(v2f i): SV_TARGET{
                float3 worldNormal = normalize(i.worldNormal);
                float3 lightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
                float3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
                float3 halfDir = normalize(lightDir + viewDir);

                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;

                fixed halfLambert = 0.5 + 0.5 * dot(worldNormal, lightDir);
                fixed3 diffuseColor = tex2D(_RampTex, fixed2(halfLambert, halfLambert)).rgb * _Color.rgb;
                fixed3 diffuse = _LightColor0.rgb * diffuseColor;

                fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(worldNormal, halfDir)), _Gloss);

                return fixed4(ambient + diffuse + specular, 1.0);

            }

            ENDCG
        }
    }
}

  • 效果展示
    在这里插入图片描述
    在这里插入图片描述

不同渐变纹理的效果

  • 效果1:
    在这里插入图片描述
    在这里插入图片描述
  • 效果2:
    在这里插入图片描述
    在这里插入图片描述
  • 效果3
    在这里插入图片描述在这里插入图片描述

渐变纹理的平铺模式影响

  • Repeat模式:halfLambert计算结果可能存在超过1.0的情况,如果用repeat模式则采样渐变纹理会存在突变的情况最终导致异常的情况
    在这里插入图片描述在这里插入图片描述
  • 解决方案:将渐变纹理的Wrap Mode改为Clamp模式,以防止对纹理采样时由于浮点数精度而造成的问题
    在这里插入图片描述

遮罩纹理(mask texture)

  • 应用场景:像素级别的控制表面属性,利用N张遮罩纹理的不同通道(1张有4个通道,N张则有N * 4个通道来存储信息)来分别存储,例如:高光反射的强度、边缘光照的强度、高光反射的指数等信息,使得材质的自由度较高;
    • 书中提到了《Dota2》中的人物模型使用了4张纹理,官方也提供了不同模型的下载链接
  • 注意点
    • a2v.normal是float3,而a2v.tangent是float4
    • TRANSFORM_TEX中第一个参数是v.texcoord,第二个参数是_MainTex
  • 代码实现
Shader "Unity Shaders Book/Chapter 7/Mask Texture"{
    Properties{
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Main Tex", 2D) = "white" {}
        _BumpMap ("Normal Map", 2D) = "dump" {}
        _BumpScale ("Bump Scale", Float) = 1.0
        _SpecularMask ("Specular Mask", 2D) = "white" {}
        _SpecularScale ("Specular Scale", Float) = 1.0
        _Specular ("Specular", Color) = (1,1,1,1)
        _Gloss ("Gloss", Range(8.0, 256)) = 20
    }

    SubShader{
        Pass{
            Tags {"LightMode" = "ForwardBase"}
            
            CGPROGRAM

            #pragma vertex vert
            #pragma fragment frag

            #include "Lighting.cginc"
            #include "UnityCG.cginc"

            fixed4 _Color;
            sampler2D _MainTex;
            float4 _MainTex_ST;
            sampler2D _BumpMap;
            float4 _BumpMap_ST;
            float _BumpScale;
            sampler2D _SpecularMask;
            float4 _SpecularMask_ST;
            float _SpecularScale;
            float4 _Specular;
            float _Gloss;


            struct a2v {
                float4 vertex: POSITION;
                float3 normal: NORMAL;
                float4 tangent: TANGENT;
                float4 texcoord: TEXCOORD0;
            };

            struct v2f {
                float4 pos: SV_POSITION;
                float2 uv: TEXCOORD0;
                float3 lightDir: TEXCOORD1;
                float3 viewDir: TEXCOORD2;
            };


            v2f vert(a2v v){
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);

                TANGENT_SPACE_ROTATION; // 获得rotation
                o.lightDir = mul(rotation, ObjSpaceLightDir(v.vertex)).xyz;
                o.viewDir = mul(rotation, ObjSpaceViewDir(v.vertex)).xyz;
                
                return o;
            }

            fixed4 frag(v2f i): SV_TARGET{
                fixed3 tangentLightDir = normalize(i.lightDir);
                fixed3 tangentVieDir = normalize(i.viewDir);

                fixed3 tangentNormal = UnpackNormal(tex2D(_BumpMap, i.uv));
                tangentNormal.xy *= _BumpScale;
                tangentNormal.z = sqrt(1.0 - saturate(dot(tangentNormal.xy, tangentNormal.xy)));
                
                float3 halfDir = normalize(tangentLightDir + tangentVieDir);

                fixed3 albedo = tex2D(_MainTex, i.uv).rgb * _Color.rgb;

                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;

                fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(tangentLightDir, tangentNormal));

                fixed specularMask = tex2D(_SpecularMask, i.uv).r * _SpecularScale;
                fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(halfDir, tangentNormal)), _Gloss) * specularMask;

                return fixed4(ambient + diffuse + specular, 1.0);

            }
            ENDCG
        }
    }

    Fallback "Specular"
}
  • 效果展示

在这里插入图片描述
在这里插入图片描述

对比无遮罩纹理的效果

在这里插入图片描述在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值