Three.js:JavaScript 3D游戏开发入门与实战指南

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:本文详细介绍了使用Three.js进行WebGL基础的3D游戏开发流程。Three.js库简化了复杂3D场景的创建和交互式应用开发,涵盖从基础概念到性能优化的全方位内容。内容包括three.js的基本元素如场景、相机、渲染器和几何体,以及3D对象和材质的应用、光照和阴影效果、动画和交互性、纹理和贴图技巧、3D模型加载、性能优化策略、实例应用展示和调试测试工具的使用。本文旨在帮助开发者构建自己的3D项目,并通过社区资源和教程深入学习。

1. Three.js基础概念介绍

1.1 Three.js的定位与发展

Three.js是一个轻量级的3D库,它为WebGL提供了一个简洁的API。它使得开发者能够在网页上创建和显示3D图形。Three.js的发展始于2010年,并迅速成为最流行的WebGL库之一。它提供了一套丰富的工具来简化场景创建、物体渲染、动画制作和光照管理等工作。

1.2 Three.js与其他3D技术的比较

与传统的3D编程技术相比,Three.js的易用性是其主要优势。它抽象了许多复杂的WebGL概念,如着色器编程和矩阵变换,使得开发者不需要深入了解图形学的专业知识也能创建出复杂的3D场景。同时,Three.js也在不断进化,引入了如PBR(物理基础渲染)等先进的渲染技术,保持与现代3D图形技术的同步。

1.3 Three.js核心组件概览

Three.js的核心包括场景(Scene)、相机(Camera)、渲染器(Renderer)、几何体(Geometry)、材质(Material)和光源(Light)。场景用于组织和渲染所有对象;相机定义了视角;渲染器负责最终的渲染输出;几何体定义了对象的形状;材质决定了对象的外观;光源为场景提供了照明。这些组件相互协作,共同构建了Three.js的应用场景和使用方法。

通过上述内容,读者可以初步了解到Three.js的基础概念和核心组件,为进一步学习Three.js奠定了基础。接下来的章节将深入探讨这些组件的具体应用以及如何结合它们创建出生动的3D世界。

2. 3D对象与材质的创建

2.1 Three.js中的基础3D对象

2.1.1 几何体(Geometry)与形状(Shape)

在Three.js中创建3D对象是一个基础且核心的过程,首先需要理解几何体和形状的概念和区别。几何体可以被视为3D世界中的“骨架”,它是用来定义形状的顶点、边和面的集合,而形状通常指的是二维平面的轮廓,可以从形状中派生出更加复杂的几何体。

Three.js提供了各种内置的几何体,例如立方体(BoxGeometry)、球体(SphereGeometry)和圆环体(TorusGeometry)等。这些几何体使用标准的WebGL几何表示方法,可以方便地创建常见的3D形状。形状(Shape)类允许我们手动定义二维平面图形的轮廓,例如使用 Shape 类可以绘制一个心形或者自定义的轮廓,然后可以通过拉伸(ExtrudeGeometry)或其他方式转换为三维形状。

// 使用BoxGeometry创建一个立方体几何体
const geometry = new THREE.BoxGeometry(1, 1, 1);

// 创建一个圆形形状
const circleShape = new THREE.Shape();
circleShape.moveTo(1, 0);
circleShape.bezierCurveTo(1, 0, 1, 1, 0.5, 1);
circleShape.bezierCurveTo(0, 1, 0, 0, 0, 0);
circleShape.bezierCurveTo(0, 0, 0, 0, 1, 0);

// 将二维形状转为三维几何体
const geometryShape = new THREE.ExtrudeGeometry(circleShape, { depth: 1, bevelEnabled: true });

在上面的代码示例中,首先创建了一个标准的立方体几何体 BoxGeometry ,接着定义了一个 Shape 来创建一个圆形轮廓。使用 ExtrudeGeometry ,将这个圆形轮廓“拉伸”成为一个三维几何体。

2.1.2 基本几何体的创建和应用场景

基础几何体在Three.js中通常用于快速搭建3D场景的基本元素。根据不同的应用场景,选择适当的几何体可以大大提高开发效率和渲染性能。

  • BoxGeometry :创建立方体或长方体,常用于构建箱子、盒子、箱子组成的地面等。
  • SphereGeometry :创建球体或椭球体,用于模拟行星、地球仪、眼睛等圆形物体。
  • CylinderGeometry :创建圆柱体,可以用来构建树干、柱子、管道等。
  • TorusGeometry :创建圆环体,适用于模拟戒指、甜甜圈等形状。
  • TorusKnotGeometry :创建一种特殊的环面结,可以用来创建复杂的装饰性模型。

每种几何体都有自己的参数,比如长宽高、直径、半径、圆环体的径向细分和环向细分等,这些参数可以根据具体的需求进行调整,以达到最佳的视觉效果和性能平衡。

// 创建一个复杂的圆环结几何体用于装饰性模型
const torusKnotGeometry = new THREE.TorusKnotGeometry(0.5, 0.1, 100, 16);

上述代码中,创建了一个 TorusKnotGeometry ,其中参数分别代表了主要半径、管半径、径向细分数量和环向细分数量。这些参数使得我们能够创建出一个具有复杂几何细节的3D对象。

在实际开发中,创建基础几何体只是第一步,为了使场景更加真实,我们需要通过材质赋予几何体不同的外观,并且添加光照、阴影等效果来增强立体感和真实感。在下一节中,我们将深入探讨材质和着色器的概念及其在Three.js中的应用。

3. 光照和阴影效果的实现

3.1 光照(Light)的基本概念

3.1.1 光照的种类及其作用

在Three.js中,光照是创建真实3D效果的关键元素,不同的光照类型可以模拟出多样的视觉效果。Three.js支持多种光照类型,包括:

  • DirectionalLight(平行光) :模拟来自无限远处的光源,如太阳光,光线相互平行,可以用于模拟远距离的光效果,适用于表现如阴影等效果。
  • PointLight(点光源) :从一个点向所有方向发散光线,类似于灯泡发出的光。其特点是光源周围会有渐变的光圈效果,可以根据距离影响照明区域。

  • SpotLight(聚光灯) :从一点向特定方向发射锥形光线,类似于手电筒或舞台灯的效果,具有明确的边缘,能够控制光束角度,产生聚焦的光束效果。

  • HemisphereLight(半球光) :模拟天空和地面的环境光,适合创建天空和地面的渐变效果,适用于室外场景。

  • AmbientLight(环境光) :模拟周围环境中的散射光,适用于创建较为均匀的背景光效果,不会产生阴影。

每种光照类型都有其独特的作用,开发者可以根据需要选择合适的光照类型,以达到预期的视觉效果。

3.1.2 光照属性的调节方法

Three.js中的光照属性是影响物体渲染效果的关键因素,开发者可以通过调整光照的属性来实现不同的视觉效果。光照属性包括但不限于:

  • intensity(强度) :控制光源的亮度,通过增大或减小这个值可以增加或减少光照对场景的影响。

  • color(颜色) :设定光照的颜色,可以模拟不同的光线氛围,比如温暖的黄色光或冷调的蓝光。

  • distance(距离)和decay(衰减) :点光源和聚光灯具有距离和衰减属性,可以通过调整这些参数模拟光源随着距离增加而逐渐减弱的效果。

  • angle(角度)和penumbra(半影) :聚光灯具有控制其角度和半影的属性,角度定义了光束的最大宽度,而半影则描述了光线边缘模糊的程度。

调节这些属性可以让场景中的光照效果更加丰富和细腻,满足不同的艺术和技术需求。

// 示例代码:创建不同类型的光照并设置属性
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(10, 10, 10);
scene.add(directionalLight);

const pointLight = new THREE.PointLight(0xffffff, 1);
pointLight.position.set(5, 5, 5);
scene.add(pointLight);

const spotLight = new THREE.SpotLight(0xffffff, 1);
spotLight.position.set(5, 5, 5);
spotLight.target.position.set(0, 0, 0);
scene.add(spotLight, spotLight.target);

const hemisphereLight = new THREE.HemisphereLight(0xffffff, 0x444444, 1);
hemisphereLight.position.set(0, 20, 0);
scene.add(hemisphereLight);

const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
scene.add(ambientLight);

以上代码展示了如何在Three.js场景中添加不同类型的光源,并设置了一些基本属性。通过调整光源位置、颜色和强度等参数,可以模拟出不同的光照效果。

3.2 阴影的生成与控制

3.2.1 阴影的开启与效果优化

阴影能够给3D场景带来更多的深度感和真实感,Three.js通过阴影贴图(Shadow Maps)技术来实现阴影效果。要开启阴影功能,需要设置场景的光照和物体的阴影属性:

  • 光源的shadow属性 :在光源对象上设置shadow属性为true,启动阴影的生成。

  • 物体的castShadow和receiveShadow属性 :在需要产生或接收阴影的物体上设置castShadow和receiveShadow属性,决定物体是否能够投射或接收阴影。

阴影效果优化涉及多个方面,以下是一些常用的优化技巧:

  • 分辨率(Shadow Map Resolution) :阴影贴图的分辨率决定了阴影的清晰度,提高分辨率会使阴影更清晰,但也会消耗更多资源。

  • PCF(Percentage-Closer Filtering) :这种技术通过在阴影边缘进行多次采样并取平均值来软化阴影边缘,减少硬边阴影的出现。

  • 调整bias(偏差) :阴影贴图的偏差值调整阴影与实际物体的贴合度,过大的偏差会导致阴影错位,过小则可能出现阴影渗漏。

下面的代码展示了如何设置光源和物体,使得它们可以产生和接收阴影,并对阴影效果进行基本优化。

// 示例代码:配置光源和物体以生成和接收阴影
// 开启光源阴影
directionalLight.castShadow = true;
pointLight.castShadow = true;
spotLight.castShadow = true;

// 设置阴影贴图的分辨率和PCF软化程度
directionalLight.shadow.mapSize.width = 2048;
directionalLight.shadow.mapSize.height = 2048;
directionalLight.shadow.camera.near = 1;
directionalLight.shadow.camera.far = 100;
directionalLight.shadow.radius = 4; // PCF软化

pointLight.shadow.mapSize.width = 1024;
pointLight.shadow.mapSize.height = 1024;
pointLight.shadow.camera.near = 0.1;
pointLight.shadow.camera.far = 50;

spotLight.shadow.mapSize.width = 1024;
spotLight.shadow.mapSize.height = 1024;
spotLight.shadow.camera.near = 1;
spotLight.shadow.camera.far = 100;
spotLight.shadow.radius = 4; // PCF软化

// 配置物体产生和接收阴影
const sphereGeometry = new THREE.SphereGeometry(0.5, 32, 32);
const sphereMaterial = new THREE.MeshStandardMaterial({color: 0x333333});
const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
sphere.position.set(2, 1, 0);
sphere.castShadow = true;
sphere.receiveShadow = true;
scene.add(sphere);

// ... 其他物体配置略 ...

// 渲染场景
renderer.shadowMap.enabled = true;
renderer.render(scene, camera);

此代码段中,我们开启了光源的阴影投射功能,并对每个光源进行了详细的阴影贴图设置,以产生更逼真的阴影效果。同时,我们也对一些物体设置了接收和产生阴影的能力。

3.2.2 阴影的渲染技术及其应用场景

Three.js提供了不同的阴影渲染技术,比如硬阴影和软阴影。软阴影是通过多重采样和混合阴影贴图中的颜色来生成的,比硬阴影更加真实,但相对计算量更大。

  • 硬阴影 (Hard Shadows):硬阴影边缘清晰,适合模拟硬质光源,如直射太阳光。

  • 软阴影 (Soft Shadows):软阴影边缘模糊,适合模拟面积较大的光源,如日光透过云层的效果。

阴影渲染技术的选择取决于所需渲染的场景和视觉效果需求,不同类型的应用可能适合不同类型的阴影。例如,在室内场景中可能需要更柔和的阴影效果,而在室外场景中,则可能需要更加锐利的阴影效果。

Three.js支持多种渲染器,如WebGLRenderer、CanvasRenderer等。不同的渲染器在性能和特性上有所差异,可以根据不同的应用场景和平台需求选择合适的渲染器。

mermaid流程图:

graph TD;
A[开始渲染场景] --> B{阴影是否开启};
B -- 是 --> C[设置光源阴影属性];
B -- 否 --> D[渲染物体];
C --> E[设置阴影贴图分辨率和PCF];
E --> D;
D --> F[配置物体产生和接收阴影];
F --> G{选择渲染器};
G -- WebGLRenderer --> H[WebGL渲染场景];
G -- CanvasRenderer --> I[Canvas渲染场景];
H --> J[渲染完成];
I --> J;

通过上述流程图,我们可以看到渲染阴影的整体流程,包括开启阴影、设置阴影参数、配置物体阴影属性,以及选择渲染器。

3.3 环境光与特殊效果

3.3.1 环境光(AmbientLight)的使用

环境光(AmbientLight)在Three.js中用于创建非方向性的、全局均匀的光照效果,它并不产生阴影,主要用于减少场景中的全黑区域,增加场景的整体亮度。

// 示例代码:添加环境光
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
scene.add(ambientLight);

环境光对于场景中的所有物体都有均匀的影响,因此,它的颜色和强度对于场景的总体感觉非常重要。尽管环境光不会产生阴影,但它有助于改善整体的视觉效果,使得渲染的图像更加平滑和自然。

3.3.2 特殊光照效果的实现技巧

在Three.js中,除了基础的光照类型外,还可以通过组合和参数调整实现更特殊的效果:

  • 多重光源 :通过组合不同的光照类型,可以模拟更复杂的光照场景,如同时使用多个平行光和点光源,营造丰富的光照层次感。

  • 自定义着色器 :通过修改材质的着色器代码,可以创建特殊的光照效果,比如使用顶点着色器和片元着色器来实现光晕、辉光等效果。

  • 使用外部光源贴图 :通过加载环境贴图作为光源,可以实现动态的光照效果,如使用HDRI贴图模拟真实世界的光照环境。

  • 光照渐变效果 :通过调整光照的颜色和强度,并使用着色器实现渐变效果,可以创建如日出日落等动态变化的光照效果。

这些技巧的应用可以大大丰富Three.js创建的3D场景,提升视觉效果的多样性。

// 示例代码:使用多重光源创建复杂光照效果
const ambientLight = new THREE.AmbientLight(0x444444);
scene.add(ambientLight);

const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(10, 10, 10);
scene.add(directionalLight);

const pointLight = new THREE.PointLight(0xffffff, 1);
pointLight.position.set(5, 5, 5);
scene.add(pointLight);

通过上述代码,我们展示了如何在场景中结合环境光和定向光以及点光源,创建出更加丰富的光照层次感和细节。

4. 动画和交互性编程

4.1 动画的实现

4.1.1 Three.js的动画基础

在Three.js中,动画通常是通过更新场景中的对象属性来实现的。动画可以是简单的对象移动,也可以是复杂的状态变化。Three.js提供了多种方法来实现动画效果,包括使用关键帧动画、动画面板(AnimationClip)、动画混合器(AnimationMixer)等。

关键帧动画是最基础的动画形式,Three.js的 AnimationAction 类和 AnimationMixer 类用于处理复杂的动画序列。每个动画动作都可以控制一个或多个对象,并且可以通过时间和速度进行调节。

// 示例:创建一个简单的动画动作
// 获取动画混合器
const mixer = new THREE.AnimationMixer(geometry);
// 创建动画剪辑(AnimationClip)
const clip = THREE.AnimationClip.parseAnimation(json);
// 创建动画动作
const action = mixer.clipAction(clip);
// 播放动画
action.play();

4.1.2 动画控制器(AnimationController)的使用

动画控制器允许开发者控制动画的时间线,比如开始、结束、倒放等。Three.js为动画控制器提供了一个API,通过 AnimationController 类可以实现更精细的动画控制。

// 示例:控制动画播放状态
// 假设action是已经创建的动画动作
// 开始播放
action.reset().play();
// 暂停动画
action.pause();
// 停止动画
action.stop();

4.1.3 动画事件

除了基本的控制之外,Three.js还允许开发者在动画中设置关键事件(animation events)。这些事件可以用于同步动画与游戏逻辑或其他复杂场景。

// 示例:在关键帧上设置事件
// 假设我们有一个动作action和一个动画剪辑clip
clip.addEventListener('loop', () => {
  console.log('Animation has looped');
});

4.2 交互性的增强

4.2.1 事件监听与响应

为了增强用户交互性,Three.js支持各种事件监听器,包括鼠标事件、键盘事件和触摸事件。通过监听器,开发者可以在用户与3D场景交互时触发特定的响应。

// 示例:监听鼠标移动事件
function animate() {
  requestAnimationFrame(animate);

  // 更新场景和摄像机位置等...

  renderer.render(scene, camera);
}

// 添加事件监听器
window.addEventListener('mousemove', onMouseMove, false);

function onMouseMove(event) {
  // event.clientX 和 event.clientY 为当前鼠标位置
  console.log('Mouse moved to: ', event.clientX, event.clientY);
  // 可以使用这些数据来更新场景中的对象,如相机、灯光等
}

animate(); // 开始动画循环

4.2.2 用户输入处理与反馈机制

处理用户输入是实现交互性的关键。Three.js允许开发者通过事件监听器来获取用户输入,并且可以使用WebGL的 hitTest 方法来检测与特定3D对象的交互。

// 示例:点击一个3D对象时触发事件
let raycaster = new THREE.Raycaster();
let mouse = new THREE.Vector2();

window.addEventListener('click', onClick, false);

function onClick(event) {
  // 将屏幕坐标转换为三维坐标
  mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
  mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;

  raycaster.setFromCamera(mouse, camera);

  // 计算与场景中对象的交集
  const intersects = raycaster.intersectObjects(scene.children);

  if (intersects.length > 0) {
    console.log('Object hit: ', intersects[0].object);
    // 根据交集触发特定的动画或效果
  }
}

4.3 物理引擎的集成

4.3.1 物理引擎的作用与选择

物理引擎为3D世界提供了真实的物理行为,包括重力、碰撞检测、力学反应等。在Three.js项目中集成物理引擎,如Cannon.js或Oimo.js,可以让场景中的对象按照物理规则互动,大大增强真实感和互动性。

// 示例:使用Cannon.js创建一个物理世界
// 创建物理世界
const world = new CANNON.World();
world.gravity.set(0, -9.82, 0); // 设置重力

// 创建物理材料和形状
const material = new CANNON.Material();
const groundShape = new CANNON.Plane();
const groundBody = new CANNON.Body({ mass: 0, material: material });
groundBody.addShape(groundShape);

// 将物理身体添加到世界中
world.addBody(groundBody);

4.3.2 Three.js与物理引擎的交互实例

要将物理引擎与Three.js结合,需要设置物理世界,并将物理对象与Three.js中的几何体相关联。物理引擎会处理对象之间的相互作用,而Three.js则负责显示。

// 示例:同步物理引擎和Three.js对象位置
// 假设我们有一个物理世界world和Three.js场景scene

function updatePhysics() {
  // 更新物理世界
  world.step(1/60);

  // 同步物理对象和Three.js对象位置
  world.bodies.forEach(body => {
    if (body.threeObject) {
      body.threeObject.position.copy(body.position);
      body.threeObject.quaternion.copy(body.quaternion);
    }
  });
}

// 在动画循环中调用updatePhysics来同步位置
animate(); // 开始动画循环

function animate() {
  requestAnimationFrame(animate);
  updatePhysics(); // 更新物理状态
  renderer.render(scene, camera); // 渲染场景
}

以上章节通过实例代码、详细解析与应用场景,详细介绍了动画和交互性编程的相关概念与技术,通过这些内容,开发者可以更深入地理解和掌握Three.js中的动画和交互性编程,进而在自己的项目中实现更为丰富的功能和体验。

5. 纹理和贴图的应用

5.1 纹理映射的基本原理

纹理映射是三维图形渲染中不可或缺的一环,它能够为3D模型添加颜色、细节,甚至光照信息。了解纹理映射的基础原理,对于创建逼真的3D场景至关重要。

5.1.1 纹理的类型与应用场景

  • 颜色纹理(Diffuse Texture) :这是最常见的纹理类型,用于提供物体表面的颜色和图案信息。
  • 法线贴图(Normal Texture) :通过存储法线信息来模拟表面细节,可以用于增强模型的视觉复杂性,而不需要额外的几何细节。
  • 位移贴图(Displacement Texture) :与法线贴图不同,位移贴图会改变模型的实际几何形态,因此效果更为真实,但也更为消耗资源。
  • 光照贴图(Lightmap) :用于存储静态光照信息,常用于预先计算复杂光照的场景,提高渲染效率。

5.1.2 纹理坐标与映射方法

纹理坐标通常用UV坐标表示,其中U和V分别对应纹理图像的宽度和高度方向。正确地应用纹理坐标对于实现自然的纹理映射至关重要。

  • 平铺(Tiling) :纹理可以被平铺到模型表面以覆盖大范围区域。
  • 包裹(Wrapping) :控制纹理在超出模型边缘时的处理方式,通常有重复(Repeat)、镜像(Mirrored Repeat)、钳制(Clamp)等选项。

5.2 高级贴图技术

随着技术的发展,越来越多的高级贴图技术被引入到Three.js中,以实现更加逼真的视觉效果。

5.2.1 法线贴图(Normal Mapping)的应用

法线贴图是一种存储表面法线信息的贴图,能够在不增加几何细节的情况下,为模型表面添加复杂的光照响应效果。在Three.js中,通过设置材质的 normalMap 属性可以应用法线贴图。

const material = new THREE.MeshStandardMaterial({
  normalMap: new THREE.TextureLoader().load('path/to/normalmap.png')
});

5.2.2 高度贴图(Height Map)与位移贴图(Displacement Mapping)

高度贴图通过在黑色至白色之间渐变的灰度值来表达模型表面的高度变化,而位移贴图会根据高度贴图实际修改模型的几何形状。在Three.js中,实现位移贴图需要使用 THREE.TextureLoader 加载高度贴图,并设置材质的 displacementMap displacementScale 属性。

const loader = new THREE.TextureLoader();
const heightMap = loader.load('path/to/heightmap.png');
const material = new THREE.MeshPhongMaterial({
  displacementMap: heightMap,
  displacementScale: 0.1
});

5.3 纹理与光照的结合

纹理和光照是三维图形中塑造物体表面视觉效果的两个关键因素。正确结合使用它们可以产生令人信服的视觉体验。

5.3.1 纹理对光照效果的影响

纹理可以影响模型的光照效果,特别是对于具有高度贴图或位移贴图的模型,光照会根据模型表面的高度变化产生相应的阴影和高光效果。在Three.js中,控制光照与纹理的相互作用通常通过调整光照的属性和材质的纹理属性来实现。

5.3.2 高级渲染技术在纹理处理中的应用

除了基础的光照模型之外,一些高级的渲染技术也可以增强纹理的效果。例如,使用环境光遮蔽(Ambient Occlusion)可以增加纹理的深度感,而次表面散射(Subsurface Scattering)技术可以模拟皮肤等半透明材料的光照效果。

总结来说,纹理和贴图技术是实现高质量3D渲染不可或缺的部分。通过合理地使用不同类型和高级技术,开发者可以创建出更加丰富和逼真的三维场景。随着WebGL和Three.js的不断发展,这些技术的应用会变得更加高效和简便。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:本文详细介绍了使用Three.js进行WebGL基础的3D游戏开发流程。Three.js库简化了复杂3D场景的创建和交互式应用开发,涵盖从基础概念到性能优化的全方位内容。内容包括three.js的基本元素如场景、相机、渲染器和几何体,以及3D对象和材质的应用、光照和阴影效果、动画和交互性、纹理和贴图技巧、3D模型加载、性能优化策略、实例应用展示和调试测试工具的使用。本文旨在帮助开发者构建自己的3D项目,并通过社区资源和教程深入学习。


本文还有配套的精品资源,点击获取
menu-r.4af5f7ec.gif

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值