转 第十五章 3D 基础 (3)(as3.0)

实战指南:基于ELK与Grafana构建天融信防火墙日志可视化看板 本文详细介绍了如何基于ELK与Grafana构建天融信防火墙日志可视化看板,包括环境准备、日志收集与解析配置、Elasticsearch存储优化、Grafana看板设计等关键步骤。通过实战案例和优化技巧,帮助用户高效处理天融信防火墙日志,实现安全态势的可视化监控与分析。 阅读详情

坐标旋转
     接下来是 3D  坐标旋转。这里要比第十和十一章中的 2D  坐标旋转要稍微复杂一些。
我们不仅可以在三个不同的轴上旋转,还可以同时在两个以上的轴上进行旋转。
     在 2D 坐标旋转中物体是绕着 z  轴旋转的,如图 15-9  所示。想一想命运之轮(Wheel of
Fortune)这类游戏---轮轴从轮盘的中心穿过。轮轴就是 z 轴。只改变 x  和 y  坐标。

转 <wbr>第十五章 <wbr>3D <wbr>基础 <wbr>(3)(as3.0)

图 15-9  z 轴旋转
     在 3D 中,我们也可以在 y  或 z  轴上旋转。x  轴的旋转如同车轮向前滚动,如图 15-10
所示。这时轮轴就是 x 轴。只改变 y  和 z  上的点。

转 <wbr>第十五章 <wbr>3D <wbr>基础 <wbr>(3)(as3.0)

图 15-10 x  轴旋转
 y  轴上的旋转,请想象一下老的唱片机,如图 15-11  所示。轴心是 y  轴。只改变 x 和 z
上的点。

转 <wbr>第十五章 <wbr>3D <wbr>基础 <wbr>(3)(as3.0)

 

图 15-11 y  轴旋转
     因此,对于 3D  而言,当我们在某个轴上旋转一个物体时,将改变两个轴上的位置。
回顾一下第十章,我们找到 2D  旋转的公式如下:
      x1 = cos(angle) * x - sin(angle) * y;
       y1 = cos(angle) * y + sin(angle) * x;
在 3D  中,方法几乎相同,但是需要指定操作的是哪个轴:x, y, z。因此得到下面三个公式:
      x1 = cos(angleZ) * x - sin(angleZ) * y;
       y1 = cos(angleZ) * y + sin(angleZ) * x;
      x1 = cos(angleY) * x - sin(angleY) * z;
       z1 = cos(angleY) * z + sin(angleY) * x;
       y1 = cos(angleX) * y - sin(angleX) * z;
       z1 = cos(angleX) * z + sin(angleX) * y;


下面,试一下 y  轴的旋转。以下代码可在 RotateY.as  中找到。创建 50  个 Ball3D  的实例,
随机放置舞台上。根据鼠标 x  轴上的位置计算出 y  轴角度。鼠标越向右,角度值越高。物
体就像跟着鼠标旋转一样。
package {
 import flash.display.Sprite;
 import flash.events.Event;
  public class RotateY extends Sprite {
   private var balls:Array;
   private var numBalls:uint = 50;

   private var fl:Number = 250;
   private var vpX:Number = stage.stageWidth / 2;
   private var vpY:Number = stage.stageHeight / 2;
   public function RotateY() {
   init();
  }
   private function init():void {
   balls = new Array();
     for (var i:uint = 0; i < numBalls; i++) {
      var ball:Ball3D = new Ball3D(15);
    balls.push(ball);
      ball.xpos = Math.random() * 200 - 100;
      ball.ypos = Math.random() * 200 - 100;
      ball.zpos = Math.random() * 200 - 100;
    addChild(ball);
   }
   addEventListener(Event.ENTER_FRAME, onEnterFrame);
  }
   private function onEnterFrame(event:Event):void {
     var angleY:Number = (mouseX - vpX) * .001;
     for (var i:uint = 0; i < numBalls; i++) {

   var ball:Ball3D = balls[i];
    rotateY(ball, angleY);
   }
   sortZ();
  }
   private function rotateY(ball:Ball3D, angleY:Number):void {
   var cosY:Number = Math.cos(angleY);
     var sinY:Number = Math.sin(angleY);
     var x1:Number = ball.xpos * cosY - ball.zpos * sinY;
     var z1:Number = ball.zpos * cosY + ball.xpos * sinY;
   ball.xpos = x1;
   ball.zpos = z1;
     if (ball.zpos > -fl) {
    var scale:Number = fl / (fl + ball.zpos);
      ball.scaleX = ball.scaleY = scale;
      ball.x = vpX + ball.xpos * scale;
      ball.y = vpY + ball.ypos * scale;
    ball.visible = true;
   } else {

    ball.visible = false;
   }
  }
   private function sortZ():void {
   balls.sortOn("zpos", Array.DESCENDING | Array.NUMERIC);
     for (var i:uint = 0; i < numBalls; i++) {
    var ball:Ball3D = balls[i];
    setChildIndex(ball, i);
   }
   }
 }
}
     重要的部分加粗表示。获得一个角度,然后使用该角度调用 rotateY 的方法。该方法中,
获得角度的正弦和余弦值,执行旋转,将 x1 和 z1  再赋值给 ball.xpos  和 ball.zpos。接下
来,就是标准透视以及 z  排序。运行结果如图 15-12  所示。
转 <wbr>第十五章 <wbr>3D <wbr>基础 <wbr>(3)(as3.0)

图 15-12  y 轴旋转
     如果这个程序没问题,大家一定也可以将其转换为 x  轴的旋转。只需要改变
onEnterFrame 方法并加入 rotateX  方法:
private function onEnterFrame(event:Event):void {
  var angleX:Number = (mouseY - vpY) * .001;
  for (var i:uint = 0; i < numBalls; i++) {
   var ball:Ball3D = balls[i];
  rotateX(ball, angleX);
 }
 sortZ();
}
private function rotateX(ball:Ball3D, angleX:Number):void {
  var cosX:Number = Math.cos(angleX);
  var sinX:Number = Math.sin(angleX);
  var y1:Number = ball.ypos * cosX - ball.zpos * sinX;
  var z1:Number = ball.zpos * cosX + ball.ypos * sinX;
  ball.ypos = y1;
 ball.zpos = z1;
  if (ball.zpos > -fl) {

   var scale:Number = fl / (fl + ball.zpos);
   ball.scaleX = ball.scaleY = scale;
   ball.x = vpX + ball.xpos * scale;
   ball.y = vpY + ball.ypos * scale;
  ball.visible = true;
  } else {
  ball.visible = false;
 }
}
     现在,angleX  是根据鼠标的 y  坐标确定的。然后计算出正余弦的值,并使用它们算出

y1  和 z1,再赋值给 ypos  和 zpos 属性。
     接下来,我们将两种旋转组合起来。以下是代码(可以在 RotateXY.as  中找到):
package {
 import flash.display.Sprite;
 import flash.events.Event;
  public class RotateXY extends Sprite {
   private var balls:Array;
   private var numBalls:uint = 50;
   private var fl:Number = 250;
   private var vpX:Number = stage.stageWidth / 2;
   private var vpY:Number = stage.stageHeight / 2;
   public function RotateXY() {
   init();
  }
   private function init():void {
   balls = new Array();
     for (var i:uint = 0; i < numBalls; i++) {
      var ball:Ball3D = new Ball3D(15);
    balls.push(ball);
      ball.xpos = Math.random() * 200 - 100;

      ball.ypos = Math.random() * 200 - 100;
      ball.zpos = Math.random() * 200 - 100;
    addChild(ball);
   }
   addEventListener(Event.ENTER_FRAME, onEnterFrame);
  }
   private function onEnterFrame(event:Event):void {
     var angleX:Number = (mouseY - vpY) * .001;
     var angleY:Number = (mouseX - vpX) * .001;
     for (var i:uint = 0; i < numBalls; i++) {
    var ball:Ball3D = balls[i];
    rotateX(ball, angleX);
    rotateY(ball, angleY);
    doPerspective(ball);
   }
   sortZ();
  }
   private function rotateX(ball:Ball3D, angleX:Number):void {
   var cosX:Number = Math.cos(angleX);
     var sinX:Number = Math.sin(angleX);
     var y1:Number = ball.ypos * cosX - ball.zpos * sinX;
     var z1:Number = ball.zpos * cosX + ball.ypos * sinX;
   ball.ypos = y1;

   ball.zpos = z1;
  }
   private function rotateY(ball:Ball3D, angleY:Number):void {
   var cosY:Number = Math.cos(angleY);
     var sinY:Number = Math.sin(angleY);
     var x1:Number = ball.xpos * cosY - ball.zpos * sinY;
      var z1:Number = ball.zpos * cosY + ball.xpos * sinY;
   ball.xpos = x1;
   ball.zpos = z1;
  }
   private function doPerspective(ball:Ball3D):void {
     if (ball.zpos > -fl) {
    var scale:Number = fl / (fl + ball.zpos);
      ball.scaleX = ball.scaleY = scale;
      ball.x = vpX + ball.xpos * scale;
      ball.y = vpY + ball.ypos * scale;
    ball.visible = true;
   } else {
    ball.visible = false;
   }
  }
   private function sortZ():void {
   balls.sortOn("zpos", Array.DESCENDING | Array.NUMERIC);
     for (var i:uint = 0; i < numBalls; i++) {
    var ball:Ball3D = balls[i];
    setChildIndex(ball, i);
   }

  }
 }
}
     与前一个例子相比主要的变化用粗体表示。现在,我们计算出 angleY, angleX  ,并调
用 rotateX, rotateY。注意,我将透视的代码从 rotate  方法中分离到一个单独的方法中,因
为它不需要被调用两次。我相信根据前面的公式,您一定可以自行加入 rotateZ 方法。
     测试一下这个影片。通过将 3D  坐标旋转与前一节赛车游戏的“屏幕环绕”的概念相
结合,大家一定还可以创建出丰富的,交互性的 3D  环境。
 

 
碰撞检测
     本章的最后我要给大家介绍一下 3D  的碰撞检测。在 Flash  的三维空间中唯一可行的
方法就是距离碰撞检测。找出两个物体的距离(使用 3D 距离公式),如果小于两个物体的
半径之和,就产生了碰撞。
     我将前面 3D  反弹的例子改成了 3D  碰撞检测的例子,加入了少量的物体并扩大了一
些空间。代码中首先执行普通的 3D  运动及透视,然后做一个双重循环比较所有小球的位
置。如果有两个物体的距离小于它们的半径之和,就使用颜色转换代码将它们都设置为蓝色。
非常简单。以下是代码(Collision3D.as):
package {
 import flash.display.Sprite;
 import flash.events.Event;
 import flash.geom.ColorTransform;
  public class Collision3D extends Sprite {
   private var balls:Array;
   private var numBalls:uint = 20;
   private var fl:Number = 250;
   private var vpX:Number = stage.stageWidth / 2;

   private var vpY:Number = stage.stageHeight / 2;
   private var top:Number = -200;
   private var bottom:Number = 200;
   private var left:Number = -200;
   private var right:Number = 200;
   private var front:Number = 200;
   private var back:Number = -200;
   public function Collision3D() {
   init();
  }
   private function init():void {
   balls = new Array();
     for (var i:uint = 0; i < numBalls; i++) {
      var ball:Ball3D = new Ball3D(15);
    balls.push(ball);
      ball.xpos = Math.random() * 400 - 200;
      ball.ypos = Math.random() * 400 - 200;
      ball.zpos = Math.random() * 400 - 200;
      ball.vx = Math.random() * 10 - 5;
      ball.vy = Math.random() * 10 - 5;
      ball.vz = Math.random() * 10 - 5;
    addChild(ball);
   }

   addEventListener(Event.ENTER_FRAME, onEnterFrame);
  }
   private function onEnterFrame(event:Event):void {
     for (var i:uint = 0; i < numBalls; i++) {
    var ball:Ball3D = balls[i];
    move(ball);
   }
     for (i = 0; i < numBalls - 1; i++) {
      var ballA:Ball3D = balls[i];
       for (var j:uint = i + 1; j < numBalls; j++) {
     var ballB:Ball3D = balls[j];
        var dx:Number = ballA.xpos - ballB.xpos;
        var dy:Number = ballA.ypos - ballB.ypos;
     var dz:Number = ballA.zpos - ballB.zpos;
        var dist:Number = Math.sqrt(dx*dx + dy*dy + dz*dz);
        if (dist < ballA.radius + ballB.radius) {
      var blueTransform:ColorTransform =
         new ColorTransform(0, 1, 1, 1, 0, 0, 255, 0);
      ballA.transform.colorTransform = blueTransform;
      ballB.transform.colorTransform = blueTransform;
     }
    }
   }

 sortZ();
  }
   private function move(ball:Ball3D):void {
      var radius:Number = ball.radius;
   ball.xpos += ball.vx;
   ball.ypos += ball.vy;
   ball.zpos += ball.vz;
     if (ball.xpos + radius > right) {
      ball.xpos = right - radius;
    ball.vx *= -1;
     } else if (ball.xpos - radius < left) {
      ball.xpos = left + radius;
    ball.vx *= -1;
   }
     if (ball.ypos + radius > bottom) {
      ball.ypos = bottom - radius;
    ball.vy *= -1;
     } else if (ball.ypos - radius < top) {
      ball.ypos = top + radius;
    ball.vy *= -1;
   }
     if (ball.zpos + radius > front) {
      ball.zpos = front - radius;

    ball.vz *= -1;
     } else if (ball.zpos - radius < back) {
    ball.zpos = back + radius;
    ball.vz *= -1;
   }
     if (ball.zpos > -fl) {
    var scale:Number = fl / (fl + ball.zpos);
      ball.scaleX = ball.scaleY = scale;
      ball.x = vpX + ball.xpos * scale;
      ball.y = vpY + ball.ypos * scale;
    ball.visible = true;
   } else {
    ball.visible = false;
   }
  }
   private function sortZ():void {
   balls.sortOn("zpos", Array.DESCENDING | Array.NUMERIC);
     for (var i:uint = 0; i < numBalls; i++) {
    var ball:Ball3D = balls[i];
    setChildIndex(ball, i);
   }
  }
 }

}
     重要的部分用粗体表示。小球起初都是红色的,当它们碰撞后,颜色发生改变。最后,
全部变为蓝色。
 

本章重要公式
     本章的重要公式是3D透视,坐标旋转以及距离的计算。
 
 
 
基本 
scale = fl / (fl + zpos);
sprite.scaleX = sprite.scaleY = scale;
sprite.alpha = scale; //  可选
sprite.x = vanishingPointX + xpos * scale;
sprite.y = vanishingPointY + ypos * scale;

 

 
Z  排序:
//  假设有一个带有 zpos 属性的 3D 物体的数组
objectArray.sortOn("zpos", Array.DESCENDING | Array.NUMERIC);
for(var i:uint = 0; i < numObjects; i++) {
    setChildIndex(objectArray[i], i);
}
 

 
坐标旋转:
x1 = cos(angleZ) * xpos - sin(angleZ) * ypos;
y1 = cos(angleZ) * ypos + sin(angleZ) * xpos;
x1 = cos(angleY) * xpos - sin(angleY) * zpos;
z1 = cos(angleY) * zpos + sin(angleY) * xpos;
y1 = cos(angleX) * ypos - sin(angleX) * zpos;
z1 = cos(angleX) * zpos + sin(angleX) * ypos;
 
 
 
3D  距离:
dist = Math.sqrt(dx * dx + dy * dy + dz * dz); 
 

 

其实现在CS4 CS5都已经出来了,用过的朋友都知道从CS4开始已经引入了3D这个概念,直接增加Z这个坐标属性以及旋转,不过作为知识是不会过时的哈哈,大家可以结合CS4的特性来修改下上面所描述的Z得坐标。
 

(如果要转载请注明出处http://blog.sina.com.cn/jooi,谢谢)

【保姆级教程】基于UNet++&MobileNetv2的语义分割模型从训练到RK3588部署:让边缘AI落地效率提升300% 通过SMP库一键构建encoder_name="mobilenet_v2", # 选择MobileNetv2作为骨干encoder_weights="imagenet", # 加载预训练权重加速收敛in_channels=3, # 输入图像通道数(RGB)classes=29, # Cityscapes数据集类别数activation=None # 输出无激活,便于后续损失计算本教程从“模型设计-训练-转换-边缘部署”全链路拆解了。 阅读详情

相关推荐

CH341的I2C流模式(StreamI2C)到底怎么用?从EEPROM读写到自定义设备,一篇讲清参数配置逻辑

本文详细解析了CH341的I2C流模式(StreamI2C)的使用方法,从EEPROM读写到自定义设备的参数配置逻辑。通过深入分析I2C协议基础、StreamI2C参数映射关系及典型设备配置示例,帮助开发者快速掌握StreamI2C的应用技巧,解决实际开发中的常见问题。

weixin_30932215的博客 452

Away3d学习笔记4-View3D(视口)

前面<<三维世界的四个基本构件>>将Flash3D影片比喻成电视,力求能形象地阐述Away3D三维世界四个必备"物件"之间逻辑关系。其中"视口"被比喻成电视机屏幕。我们观众就是通过这个屏幕才能看到用摄影机拍摄下来的景物(3D物体)。本篇进一步讲解View3D几个重要的参数设置。 创建View3D Away3D里视口的类名是View3D。与as里创建任何实例...

dnukjz5627的博客 279

STM32玩转C++:从Arduino库到HAL库的封装思路(Clion环境配置指南)

本文介绍了如何在STM32开发中使用C++封装HAL库,从Arduino风格过渡到更高效的开发方式。通过Clion环境配置指南和详细的代码示例,展示了如何利用C++的面向对象特性简化GPIO、UART等外设操作,提升代码可读性和开发效率。特别适合嵌入式开发者和从Arduino转向STM32的开发者。

weixin_33737134的博客 336

“AS3.0高级动画编程”学习:第三章等角投影(上)

什么是等角投影(isometric)? 刚接触这个概念时,我也很茫然,百度+google了N天后,找到了一些文章: [转载]等角(斜45度)游戏与数学 [转载]使用illustrator和正交投影原理以及基本三视图制图 以及这篇ppt:http://files.cnblogs.com/yjmyzz/Isometric.rar 建议先耐心看完这三篇文章,再往下看: 在之前学习的3D基础、...

weixin_34348174的博客 197

告别龟速下载!保姆级教程:用离线包5分钟搞定Arduino IDE的STM32开发环境(附最新库文件)

本文提供了一套完整的Arduino IDE离线配置方案,帮助开发者5分钟快速搭建STM32开发环境。通过详细的板支持包安装指南、文件校验方法和常见问题解决方案,彻底解决GitHub下载慢的问题,提升开发效率。

weixin_26802933的博客 368

【算法】基于STM32的MPU6050卡尔曼滤波算法(入门级)

1. 简介 卡尔曼滤波(Kalman filtering)是一种利用线性系统状态方程,通过系统输入输出观测数据,对系统状态进行最优估计的算法。由于观测数据中包括系统中的噪声和干扰的影响,所以最优估计也可看作是滤波过程。详情见:卡尔曼滤波简介 MPU6050的解算主要有三种姿态融合算法:四元数法 、一阶互补算法和卡尔曼滤波算法。我们常用的DMP库使用的是四元数法,本文采用卡尔曼滤波算法,使用RT-Thread国产操作系统,利用env工具进行串口、模拟IIC环境配置,使用10ms的线程进行卡尔曼滤波解算。 2.

weixin_44549777的博客 3万+

JS Math.sin() 与 Math.cos()找x、y坐标

Math.sin(x) //x 的正玄值。返回值在 -1.0 到 1.0 之间; Math.cos(x) //x 的余弦值。返回的是 -1.0 到 1.0 之间的数; 这两个函数中的X 都是指的“弧度”而非“角度” Math.PI 相当于数学中的 π , 180° ; 角度为 a, 则弧度为a * (π / 180) ; 弧度为 a, 则角度为a * (180 / π ) ;...

前端小狮的博客 6077

查找【优快云】- 人工客服

【代码】查找【优快云】- 人工客服。

👨‍💻 前端开发者 | 技术分享者 1410

转 第十五章 3D 基础 (2)(as3.0)

Z  排序      在添加了多个物体后代码中显现出了一个新问题---称为z  排序。Z  排序就像它的名 字一样:物体如何在 z  轴上进行排序,或者说哪个物体在前面。由于物体都使用纯色,所 以看起来不是很明显。为了让效果更加明显,请将 Ball3D  的 init  方

roadProgram的专栏 913

手把手教你学 Simulink (1.3)--Simulink 的模块库及其模块介绍

Simulink 提供了丰富的模块库,涵盖了从基本的信号生成和处理到复杂的数学运算和系统建模的各种功能。通过熟悉这些模块库及其模块,你可以更高效地构建和仿真动态系统。

MHD0815的博客 5153

运放比较电路分析以及应用

摘要:本文介绍了基于LM321运放的同相输入型单限电压比较器电路。该电路通过运放开环工作模式实现电平判断:当输入信号Vs+>0V时输出+15V高电平,Vs+<0V时输出≈0V低电平。典型应用包括直流电压过零检测、交流信号整形、电池欠压报警和数字信号噪声过滤。电路具有结构简单(仅需1个运放和1个电阻)、成本低的优势,适用于传感器信号处理、波形转换等场景。使用时需注意参考电平调整、输出电平匹配等问题,如需抗噪声可增加正反馈电阻构成施密特触发器。该电路是运放实现简单电平逻辑的经典方案。

qq_37999850的博客 402

转 第十五章 3D 基础 (1)(as3.0)

前面我们做的一切都是二维的(有时只有一维),但是已经可以做出非常酷的东东了。 现在,将它们带入到下一个等级。      创建 3D  图形总是那么另人兴奋。新加入的这个维度似乎将物体真正地带入到了生活 中。如何在 Flash  中实现 3D  在无数的书籍和教学软件中都有介

roadProgram的专栏 972

Pulseaudio之同步/异步(十一)

PulseAudio是一个声音服务器,一个后台进程从一个或多个音源(进程或输入设备)接受声音输入 然后重定向声音到一个或多个槽(声卡,远程网络PulseAudio服务,或其他进程)。 PulseAudio的主要特点包括: 可对每一个应用程序进行音量控制Per-application volume controls 可扩展的插件与支持可装载模块架构 兼容性许多流行的音频应用程序 支持多重音源和多重输出 低延时操作和支持延迟测量 一个对处理器资源效率零拷贝内存架构 能够发现本地网络上使用PulseAu

Android系统攻城狮 1004

转 第十六章 3D 线条与填充(1) (as3.0)

第十五章我们介绍了 3D,但只是将物体置于 3D 空间中,设置大小与位置。物体实际 上还是 2D 的。这就像老的 3D 游戏中,我们可以绕着某个物体或人物走,这些对象会转 过来面对我们。这些物体或人物并不是真正的会转过来 —— 只是看上去是这样的,因为它 们都是 2D 物体

roadProgram的专栏 4118

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

转 第十七章 背面剔除与flash 3D 灯光 (as3.0)

第十六章介绍了所有建立 3D 立体模型的基础包括:如何创建点,线,用多边形组成 各种形状,以及如何为每个多边形设置颜色。但是,我们只能让颜色的透明度为 50%,才 能看到正确的效果。虽然制作出的 3D 立体模型也不差,但是这样做在真实度上还是有所 欠缺。     本章,通

roadProgram的专栏 4292

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

【as3】ActionScript 3.0体验试教学视频教程

很不错的视频教程 原文地址: [url=http://blog.sina.com.cn/s/blog_3ecb9b1101009nx0.html]http://blog.sina.com.cn/s/blog_3ecb9b1101009nx0.html[/url]   全屏观看地址: [url=http://vhead.blog.sina.com.cn/player/outer_pla...

hgfghwq13 113
上一篇: 转 第十五章 3D 基础 (2)(as3.0)
下一篇: 在flash cs4以上版本的3D基础 (as3.0)
coding_most
博客等级 码龄15年 13粉丝 38原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值