Loading [MathJax]/jax/input/TeX/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >一文搞懂如何在Three.js里创建阴影效果 |《Three.js零基础直通14》

一文搞懂如何在Three.js里创建阴影效果 |《Three.js零基础直通14》

作者头像
大帅老猿
发布于 2022-11-11 08:38:16
发布于 2022-11-11 08:38:16
7.7K06
代码可运行
举报
文章被收录于专栏:大帅老猿大帅老猿
运行总次数:6
代码可运行

微信的规则进行了调整

希望大家阅读时点点 “ 在看 ”

觉得不错也请点个 “ 分享 ”

这样大帅的教程才能继续出现在你的订阅列表里

前言

经过上一小节,我们学会了如何使用各种类型的灯光。既然有了光,那还得有阴影,这样看起来才会更加真实。

无论使用什么引擎,阴影一直是实时3D渲染的一项挑战。需要有技巧的,以合理的性能消耗来显示更加逼真的阴影效果。

实现阴影的方法有很多种,Three.js有一个内置的解决方案。需要注意的是,这个解决方案很方便,使用很简单,但它并不完美。

它是如何工作的

本课程不会详细说明阴影是如何在内部工作的,我们主要学习了解有关阴影的基础知识。

Three.js在进行渲染时,首先会对每个需要投射阴影的光源进行计算。和相机的工作原理有点类似,对光线可“见”范围里的3D对象进行渲染,在此期间,会使用MeshDepthMaterial来替换所有的材质。

将这些渲染的结果存储为纹理贴图,并且在需要接收阴影的几何体材质上进行投影。

three.js的官方文档中有一个平行光和聚光灯阴影的示例: https://threejs.org/examples/webgl_shadowmap_viewer.html

准备工作

在场景中创建一个球体,一个平面,再创建一个平行光和一个环境光。

我们可以在Dat.GUI中控制这些灯光的位置和强度以及材质的金属度和粗糙度。

/assets/lessons/16/step-01.png

如何启用阴影

首先,我们需要将渲染器shadowMapenabled属性设置为true

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
renderer.shadowMap.enabled = true

然后,我们需要想清楚两件事:

  • 哪些对象需要计算阴影,将需要计算阴影的对象的castShadow属性设置为true
  • 哪些对象需要接受阴影,将需要接受阴影的对象的receiveShadow属性设置为true

“例如:场景的球体需要用于计算阴影,而平面需要接受阴影 ”

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 球体计算阴影
sphere.castShadow = true

// 平面接受阴影
plane.receiveShadow = true

最后,我们还需要开启灯光对象的castShadow属性。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
directionalLight.castShadow = true

请注意,仅有以下三种灯光可以启用计算阴影:

  • PointLight
  • DirectionalLight
  • SpotLight

再次提醒大家,实时阴影的计算非常消耗性能,场景中如果有大量的灯光,请务必想清楚哪些灯光需要用于计算实时阴影,而不是全部启用阴影计算。不需要进行实时阴影计算的灯光可以在3D渲染软件中将阴影的效果烘焙到贴图上。

/assets/lessons/16/step-02.png

现在我们的平面上可以看到这个球体的影子了,虽然它看起来还很粗糙。

接下来让我们学习如何改善阴影效果。

阴影优化

渲染尺寸

要知道,Three.js里阴影的本质其实是通过计算实时生成阴影贴图。您可以使用灯光上的shadow属性访问此阴影贴图:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
console.log(directionalLight.shadow)

默认情况下,阴影贴图大小仅为512x512。我们可以改变这个尺寸,不可随意设置哦,必须是2的幂:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
directionalLight.shadow.mapSize.width = 1024
directionalLight.shadow.mapSize.height = 1024

/assets/lessons/16/step-03.png

当我们把阴影贴图的尺寸设置为1024后,看上去好一些了。

Near and far

Three.js使用相机来帮助计算阴影贴图。这些相机与我们前面学到的相机具有相同的属性。比如我们必须定义相机的近视距离和远视距离。这两个参数并不会真正提高阴影的质量,但调整这两个参数有可能会修复一些看不到阴影或者阴影突然出现的错误。为了帮助我们调试灯光对象中阴影贴图的相机,为了更方便预览近视远视两个参数的变化,我们可以使用相机辅助工具。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const directionalLightCameraHelper = new THREE.CameraHelper(directionalLight.shadow.camera)
scene.add(directionalLightCameraHelper)

/assets/lessons/16/step-04.png

现在,我们是不是可以直观地看到这些用于渲染阴影贴图的相机了。尝试找到适合场景的值:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
directionalLight.shadow.camera.near = 1
directionalLight.shadow.camera.far = 6

/assets/lessons/16/step-05.png

光照范围Amplitude

通过相机辅助工具,可以观察到这些相机的视角过大。因为我们使用的是平行光,所以Three.js在为它渲染阴影贴图时使用的是正交相机。如果您还记得相机课程,我们可以通过顶部,右侧,底部和左侧属性控制相机在每一侧可以看到的距离。让我们调整减小一下这些属性:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
directionalLight.shadow.camera.top = 2
directionalLight.shadow.camera.right = 2
directionalLight.shadow.camera.bottom = - 2
directionalLight.shadow.camera.left = - 2

/assets/lessons/16/step-06.png

这些值越小,阴影就会越精确。但是太小也不行,阴影会被裁剪。

现在我们把相机辅助工具隐藏起来看一看

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
directionalLightCameraHelper.visible = false

/assets/lessons/16/step-07.png

模糊

我们可以通过设置阴影的radius属性从而让阴影的边缘看起来是模糊的,这将使阴影看上去更柔和。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
directionalLight.shadow.radius = 10

/assets/lessons/16/step-08.png

这种模糊并没有去计算灯光和物体的距离(比如近则清晰,远则模糊),这只是一个简单的模糊,但大部分时候挺出效果的。

阴影贴图算法

three.js中有几种内置的阴影贴图算法供我们选择:

  • THREE.BasicShadowMap 性能很好,但质量很差
  • THREE.PCFShadowMap 性能较差,但边缘更光滑
  • THREE.PCFSoftShadowMap 性能较差,但边缘更柔软
  • THREE.VSMShadowMap 更低的性能,更多的约束,可能会产生意想不到的结果

我们可以通过设置 renderer.shadowMap.type 来改变阴影贴图的算法。这个属性的默认值是 THREE.PCFShadowMap 我们可以使用 THREE.PCFSoftShadowMap 来获得更好的阴影效果。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
renderer.shadowMap.type = THREE.PCFSoftShadowMap

/assets/lessons/16/step-09.png

不过启用THREE.PCFSoftShadowMap后,shadow.radius属性就不可用了。选择好难~

聚光灯

现在我们在场景中添加一个聚光灯Spotlight,并将castShadow属性添加为true

同样的,我们也为聚光灯添加一个阴影相机的辅助工具:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// Spot light
const spotLight = new THREE.SpotLight(0xffffff, 0.4, 10, Math.PI * 0.3)

spotLight.castShadow = true

spotLight.position.set(0, 2, 2)
scene.add(spotLight)
scene.add(spotLight.target)

const spotLightCameraHelper = new THREE.CameraHelper(spotLight.shadow.camera)
scene.add(spotLightCameraHelper)

如果现在场景过亮的话,可以降低其他灯光的强度:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const ambientLight = new THREE.AmbientLight(0xffffff, 0.4)

// ...

const directionalLight = new THREE.DirectionalLight(0xffffff, 0.4)

/assets/lessons/16/step-10.png

现在两个阴影看上去是两种效果。不过就目前而言,没有太多参数能把他们调整得更和谐。

但我们至少能给他们设置一样的参数来获得相同的阴影质量。

设置相同的 shadow.mapSize属性:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
spotLight.shadow.mapSize.width = 1024
spotLight.shadow.mapSize.height = 1024

/assets/lessons/16/step-11.png

因为这是一个聚光灯SpotLight,所以它使用的是透视相机 PerspectiveCamera。要改变它的生效范围就必须更改fov属性,而不是top, right, bottomleft属性。尝试在不裁剪阴影的情况下找到尽可能小的角度:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
spotLight.shadow.camera.fov = 30

/assets/lessons/16/step-12.png

同样需要改变一下 nearfar 属性:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
spotLight.shadow.camera.near = 1
spotLight.shadow.camera.far = 6

/assets/lessons/16/step-13.png

让我们再把这个相机辅助工具隐藏起来:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
spotLightCameraHelper.visible = false

/assets/lessons/16/step-14.png

确实比刚才好了一点,就一点点,不仔细看可能还看不大出来:P。

点光源

现在让我们试一试最后一种支持计算阴影的灯光,点光源:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// Point light
const pointLight = new THREE.PointLight(0xffffff, 0.3)

pointLight.castShadow = true

pointLight.position.set(- 1, 1, 0)
scene.add(pointLight)

const pointLightCameraHelper = new THREE.CameraHelper(pointLight.shadow.camera)
scene.add(pointLightCameraHelper)

如果场景太亮,可以降低其他灯光强度:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const ambientLight = new THREE.AmbientLight(0xffffff, 0.3)

// ...

const directionalLight = new THREE.DirectionalLight(0xffffff, 0.3)

// ...

const spotLight = new THREE.SpotLight(0xffffff, 0.3, 10, Math.PI * 0.3)

/assets/lessons/16/step-15.png

点光源的光线应该是向四面八方发射出去,但我们可以通过相机辅助工具只能看到一个透视相机 (和聚光灯一样),但这个相机是朝下的。这是由于在Three.js中点光源的阴影贴图要依赖6个方向上的相机来实现-_-!。

正因为点光源会在每个方向上发光,所以Three.js必须通过6个方向的渲染才能创建出多维数据集阴影贴图。而我们通过相机辅助工具看到的相机是最后一个方向,朝下的相机。

要同时在六个方向计算阴影,这当然会更加消耗性能,所以请尽可能避免启用点光源来计算阴影。

和其它阴影一样可以改变的属性有mapSizenearfar

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
pointLight.shadow.mapSize.width = 1024
pointLight.shadow.mapSize.height = 1024

pointLight.shadow.camera.near = 0.1
pointLight.shadow.camera.far = 5

/assets/lessons/16/step-16.png

让我们再把这个相机辅助工具隐藏起来:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
pointLightCameraHelper.visible = false

/assets/lessons/16/step-17.png

阴影烘焙

通过前面的学习,我们知道了在Three.js实时计算阴影是很消耗性能的。

有另一种很好的选择是烘焙阴影。我们在上一小节中了解过灯光烘焙,其实它和阴影烘焙是一个意思。阴影的效果会被整合到我们应用于材料的纹理贴图上。

之前为阴影写的代码,我们可以直接在渲染器中停用它们,并不需要注释所有与阴影相关的代码行:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
renderer.shadowMap.enabled = false

/assets/lessons/16/step-18.png

现在我们可以用TextureLoader加载器加载位于 textures/bakedShadow.jpg中的阴影纹理贴图。

/assets/lessons/16/bakedShadow.jpg

在创建物体和灯光之前添加下面的代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * Textures
 */
const textureLoader = new THREE.TextureLoader()
const bakedShadow = textureLoader.load('/textures/bakedShadow.jpg')

最后,我们使用MeshBasicMaterial来创建一个基本材质的平面即可,并不需要使用MeshStandardMaterial

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const plane = new THREE.Mesh(
    new THREE.PlaneGeometry(5, 5),
    new THREE.MeshBasicMaterial({
        map: bakedShadow
    })
)

/assets/lessons/16/step-19.png

现在我们应该能看到一个很漂亮很逼真的阴影效果。需要注意的是,这不是实时计算的阴影,所以当球体或灯光移动时,阴影不会随之改变。

/assets/lessons/16/step-20.png

假~阴影

无论是计算阴影或者是阴影烘焙,至少都需要依赖物体和灯光的计算,只是实时和非实时的区别。

还有一种方式可以非常简单高性能的模拟出类似阴影的效果,注意,真的只是类似。

/assets/lessons/16/simpleShadow.jpg

纹理贴图是一个简单的光晕效果。白色部分将可见,黑色部分将不可见。

首先,让我们去掉原本的阴影烘焙贴图,恢复使用MeshStandardMaterial的状态:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const plane = new THREE.Mesh(
    new THREE.PlaneGeometry(5, 5),
    material
)

然后,我们可以加载位于textures/bakedShadow.jpg中的基本阴影纹理。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const simpleShadow = textureLoader.load('/textures/simpleShadow.jpg')

加载好贴图后,我们可以通过使用一个平面来创建阴影,平面是创建出来时默认是面向Z轴正方向。我们需要将其旋转90度并放置在地板上方。基础材质的颜色必须是黑色的,再将alphaMap设置为刚才加载的阴影贴图。还需要将透明属性更改为true,最后将平面添加到场景中:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const sphereShadow = new THREE.Mesh(
    new THREE.PlaneGeometry(1.5, 1.5),
    new THREE.MeshBasicMaterial({
        color: 0x000000,
        transparent: true,
        alphaMap: simpleShadow
    })
)
sphereShadow.rotation.x = - Math.PI * 0.5
sphereShadow.position.y = plane.position.y + 0.01

scene.add(sphere, sphereShadow, plane)

/assets/lessons/16/step-21.png

好了,现在这个球体有了一个说不上很假,但也凑合能看的阴影效果。

虽然假了一点,但这种方式拥有很高的性能。并且这个阴影的位置和大小还可以根据球体的位置来动态调整,比如球体离地面越高,阴影越淡;球体离地面越近,阴影越浓。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const clock = new THREE.Clock()

const tick = () =>
{
    const elapsedTime = clock.getElapsedTime()

    // Update the sphere
    sphere.position.x = Math.cos(elapsedTime) * 1.5
    sphere.position.z = Math.sin(elapsedTime) * 1.5
    sphere.position.y = Math.abs(Math.sin(elapsedTime * 3))

    // Update the shadow
    sphereShadow.position.x = sphere.position.x
    sphereShadow.position.z = sphere.position.z
    sphereShadow.material.opacity = (1 - sphere.position.y) * 0.3

    // ...
}

tick()

如何选择阴影实现方式

three.js中实现阴影的三种方式(计算,烘焙,假阴影)都教给大家了,在实战中请根据项目对性能和视觉效果的需求来灵活选择,当然,这几种方式也可以结合起来使用。

微信的规则进行了调整

希望大家阅读时点点 “ 在看 ”

觉得不错也请点个 “ 分享 ”

这样大帅的教程才能继续出现在你的订阅列表里

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-10-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 大帅老猿 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Three.js教程(5):光源
Three.js的作用就是做3D效果,一说到3D就绕不过一个话题,那就是阴影。而要出现阴影的效果,那么就要涉及光源。本章介绍Three.js中光源相关的知识。
kai666666
2020/10/17
2.8K0
【愚公系列】2023年08月 Three.js专题-光源
光源是能够发出光的物体或设备,它能够发出光线,使我们看到周围的环境和物体。常见的光源包括太阳、灯泡、蜡烛、火把、手电筒等。根据发光方式不同,光源可以分为自发光源和非自发光源。自发光源是指能够自行发出光线的物体,如太阳等;而非自发光源是指需要外部能量刺激才能发出光线的物体,如灯泡、荧光棒等。
愚公搬代码
2025/05/28
640
【愚公系列】2023年08月 Three.js专题-光源
ThreeJs 基础学习
如果你要实现一个机器人在跑步,那么机器人的头、四肢、躯干等都是要整体移动的,group可以将一系列的mesh模型组成一个整体,这样就可以实现整体移动了。
心安事随
2024/07/29
2910
Three.js中光源
官方文档:https://threejs.org/docs/index.html#api/zh/lights/SpotLight
小刀c
2024/04/03
2270
Three.js中光源
Three.js 这样写就有阴影效果啦
在学习 Three.js 时,很多知识点其实记住几个重要的步骤就能实现相应的效果。
德育处主任
2022/09/23
2.7K0
Three.js 这样写就有阴影效果啦
Three.js光照效果实战案例解析
在当今数字化的时代,3D图形在网页、游戏、虚拟和增强现实等多个领域发挥着重要作用。Three.js作为一款强大且流行的JavaScript 3D库,极大地简化了在网页上创建和展示3D内容的难度。而光照效果在3D图形中犹如灵魂一般的存在,它能够赋予场景真实的质感和立体感,让虚拟的世界栩栩如生。本文将通过一系列实战案例深入解析Three.js光照效果的实现与应用,带您领略Three.js在光照处理方面的独特魅力。
Front_Yue
2025/03/15
1690
Three.js光照效果实战案例解析
Three.js 场景创建基础
在当今数字化的时代,Web 开发已经不再局限于传统的二维界面。随着技术的不断进步,越来越多的开发者开始探索将 3D 图形引入到网页中,为用户带来更加沉浸式和交互性的体验。而 Three.js 作为一款广受欢迎的 JavaScript 3D 库,为开发者提供了强大而便捷的工具,使得在网页上创建和展示复杂的 3D 场景变得不再困难。本文将深入探讨 Three.js 场景创建的基础知识,包括其简介、引入方式,以及场景构建的关键要素。
Front_Yue
2025/03/12
3080
Three.js 场景创建基础
如何实现一个3d场景中的阴影效果(threejs)?
跟OpenGL不同,在threejs中实现一个阴影效果很简单,只需要简单的几个设置。
程序你好
2021/07/23
2.9K0
如何实现一个3d场景中的阴影效果(threejs)?
【愚公系列】2023年08月 Three.js专题-基本概念和使用
Three.js是一个用于创建和呈现3D图形的JavaScript库。它提供了一组易于使用的工具和API,使得开发人员可以轻松地在Web应用程序中创建复杂的3D场景和动画。Three.js可以与其他JavaScript库和框架一起使用,如jQuery和React等。它是一个开源项目,有一个活跃的社区在支持和扩展它。
愚公搬代码
2025/05/28
1110
【愚公系列】2023年08月 Three.js专题-基本概念和使用
Three.js基础
通过scene.overrideMaterial = new THREE.MeshLambertMaterial({ color: 0xffffff });来强制设置场景中对象的材质,极端情况可以做性能优化。
小刀c
2024/04/03
3190
Three.js基础
threejs中实现物体阴影
在Three.js中实现阴影需要几个步骤,包括设置渲染器、光源以及物体的材质等。以下是一个基本的实现阴影的步骤:
用户8703799
2024/08/20
1360
Three.js深入浅出:4-three.js中的光源
本篇文章将带你深入了解Three.js中的光源类型、属性和使用方法,助你在创建虚拟世界时获得更加生动逼真的效果
用户6297767
2023/11/26
6900
Three.js深入浅出:4-three.js中的光源
学习Three.js
window resize 需要设置camera的aspect 属性,设置renderer的尺寸
小刀c
2024/04/03
2370
【愚公系列】2023年08月 Three.js专题-纹理
纹理是指由于物体内部或表面的微观结构、形态、组成、排列等因素所形成的外观或质感特征。它可以是线性的、非线性的、均匀的或不均匀的,可以在物体的表面或内部出现。纹理可以是自然界中的,也可以是人工制造的,是视觉艺术和设计中一个非常重要的元素。在数字图像处理中,纹理是图像分析和识别的一个重要因素。
愚公搬代码
2025/05/28
820
【愚公系列】2023年08月 Three.js专题-纹理
Threejs入门之十七:给物体添加阴影
在前面的章节中,我们已经实现了将物体添加到场景中,并设置了灯光等效果,但是,这并不是很真实,在真实的世界中,被灯光照射的物体是有阴影的,这一节我们就来给物体添加阴影。 在Threejs中给物体添加阴影,需要注意以下几点 1.要选择具有投射阴影效果的材质 我们前面也提到过,基础网格材质MeshBasicMaterial是不受光照影响的,我们如果需要有阴影效果,就不能选择该材质 2.需要投射阴影的物体要设置castShadow属性 castShadow属性用于设置物体是否被渲染到阴影贴图中,默认为false,如果需要投影,则设置为true 3.接收阴影的物体要开启receiveShadow属性 receiveShadow属性用于设置材质是否接收阴影,默认为false,如果需要接收物体的投影,设置为true 4.灯光开启投射阴影castShadow属性 灯光也要设置castShadow为true,默认为false 5.渲染器设置允许在场景中使用阴影贴图 将渲染器的shadowMap.enabled属性设置为true,允许场景中使用阴影贴图 经过上面五步的设置,就可以开启物体的阴影效果了,具体实现代码如下
九仞山
2023/04/30
7300
Threejs入门之十七:给物体添加阴影
【带着canvas去流浪(11)】Three.js入门学习笔记
官方文档中的新手示例过于简单,所以本节对Three.js中的概念进行一些补充描述:
大史不说话
2019/07/15
4K0
【带着canvas去流浪(11)】Three.js入门学习笔记
three.js中的重要基础概念
Three.js 是一个功能强大的 JavaScript 库,用于创建和展示基于 WebGL 的三维图形。在学习使用Three.js来构建3D世界之前,有一些基本概念是需要牢记的,否则,在你绘制3D世界时,思绪会是杂乱无章的:
fastmock
2025/04/26
1310
【愚公系列】2023年08月 Three.js专题-几何体
几何体是指由点、线、面所构成的空间实体。其中,点、线、面是几何体的基本元素,几何体包括球体、立方体、圆锥体、圆柱体、棱柱、棱锥、棱台等。这些几何体都有自己的特定形状和特征,可以应用于各种数学、物理和工程领域。
愚公搬代码
2025/05/28
640
【愚公系列】2023年08月 Three.js专题-几何体
Three.js可视化企业实战WEBGL网-2024入门指南
Three.js 是一个功能强大的 JavaScript 库,用于在 Web 浏览器中创建和显示动画 3D 图形。它的丰富 API 和模块化设计使得开发者可以轻松构建复杂的 3D 场景和动画效果。本文将详细介绍 Three.js 中的一些重要组件和模块,包括场景、相机、几何体、材质、光源、渲染器和控制器等。
用户11130883
2024/05/31
3730
Three.js 手写跳一跳小游戏(上)
玩家从一个方块跳到下一个方块,如果没跳过去就算失败,跳过去了就会再出现下一个方块。
神说要有光zxg
2023/08/28
7250
Three.js 手写跳一跳小游戏(上)
相关推荐
Three.js教程(5):光源
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验