iis服务器助手广告广告
返回顶部
首页 > 资讯 > 精选 >怎么用Three.js实现雪糕地球
  • 330
分享到

怎么用Three.js实现雪糕地球

2023-07-02 15:07:43 330人浏览 安东尼
摘要

这篇“怎么用Three.js实现雪糕地球”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“怎么用Three.js实现雪糕地球”文

这篇“怎么用Three.js实现雪糕地球”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“怎么用Three.js实现雪糕地球”文章吧。

  • style

* {    -WEBkit-user-select: none;       -moz-user-select: none;        -ms-user-select: none;            user-select: none;  }  body {    height: 100vh;    background-color: hotpink;    margin: 0;    padding: 0;    overflow: hidden;  }  .loader {    display: flex;    color: white;    display: flex;    justify-content: center;    align-items: center;    font-size: 5em;    width: 100%;    height: 100%;    font-family: "Baloo Bhaijaan", cursive;  }  .loader span {    text-shadow: 0 1px #bbb, 0 2px #bbb, 0 3px #bbb, 0 4px #bbb, 0 5px #bbb,      0 6px transparent, 0 7px transparent, 0 8px transparent,      0 9px transparent, 0 10px 10px rgba(0, 0, 0, 0.4);    text-shadow: 0 1px #bbb, 0 2px #bbb, 0 3px #bbb, 0 4px #bbb,        0 5px #bbb, 0 6px #bbb, 0 7px #bbb, 0 8px #bbb, 0 9px #bbb,        0 50px 25px rgba(0, 0, 0, 0.2);      transfORM: translateY(-20px);  }
  •  script

let isLoaded = false; // 纹理资源是否加载完毕const loadingScreen = {  scene: new THREE.Scene(),  camera: new THREE.PerspectiveCamera(    75,    window.innerWidth / window.innerHeight,    0.1,    1000  ),  // 移除加载标志的函数  removeText() {    const loadingText = document.querySelector("#canvas-loader");    if (loadingText.parentnode) {      loadingText.parentNode.removeChild(loadingText);    }  },};// 初始化加载器let loadingManager = new THREE.LoadingManager();// 监听加载器 onLoad 事件loadingManager.onLoad = () => {  loadingScreen.removeText();  isLoaded = true;};// 创建场景const scene = new THREE.Scene();// 创建渲染器const renderer = new THREE.webGLRenderer({ antialias: true });// 渲染器基本设置renderer.setClearColor("hotpink");renderer.setPixelRatio(window.devicePixelRatio);renderer.setSize(window.innerWidth, window.innerHeight);// canvas 外部容器const canvasWrapper = document.querySelector("#canvas-wrapper");// 创建透视相机const camera = new THREE.PerspectiveCamera(  75,  window.innerWidth / window.innerHeight,  0.1,  1000);// 设置相机位置camera.position.set(0, 0, 220);// 创建平行光源const light = new THREE.DirectionalLight();light.position.set(0, 0, 1);scene.add(light);// 创建点光源const point = new THREE.PointLight(0xeeeeee);point.position.set(400, 200, 300); //点光源位置scene.add(point); //点光源添加到场景中// 创建球体const cRadius = 100;const geometry = new THREE.SphereBufferGeometry(  cRadius,  cRadius * 6.4,  cRadius * 6.4);// 纹理图const textureLoader = new THREE.TextureLoader(loadingManager);const textureSurface = textureLoader.load(  "https://s3-us-west-2.amazonaws.com/s.cdpn.io/249663/world-surface.jpg");const textureElevation = textureLoader.load(  "Https://s3-us-west-2.amazonaws.com/s.cdpn.io/249663/world-elevation.jpg");const textureSpecular = textureLoader.load(  "https://s3-us-west-2.amazonaws.com/s.cdpn.io/249663/world-specular.jpg");// 材质信息const materialOpt = {  map: textureSurface,  normalMap: textureElevation,  specularMap: textureSpecular,  shininess: 80,};const material = new THREE.MeshPhongMaterial(materialOpt);// 创建网格体const sphere = new THREE.Mesh(geometry, material);// 设置环境贴图的颜色深浅sphere.material.normalScale.set(0.5, 0.5);// 将模型添加到场景中scene.add(sphere);// 将 canvas 元素添加到页面中canvasWrapper.appendChild(renderer.domElement);let mouseX = 0;let mouseY = 0;const moveAnimate = {  coordinates(clientX, clientY) {    const limit = 270;    const limitNeg = limit * -1;    mouseX = clientX - window.innerWidth / 2;    mouseY = clientY - window.innerHeight / 2;    mouseX = mouseX >= limit ? limit : mouseX;    mouseX = mouseX <= limitNeg ? limitNeg : mouseX;    mouseY = mouseY >= limit ? limit : mouseY;    mouseY = mouseY <= limitNeg ? limitNeg : mouseY;  },  onMouseMove(e) {    moveAnimate.coordinates(e.clientX, e.clientY);  },  onTouchMove(e) {    const touchX = e.changedTouches[0].clientX;    const touchY = e.changedTouches[0].clientY;    moveAnimate.coordinates(touchX, touchY);  },};document.addEventListener("mousemove", moveAnimate.onMouseMove);document.addEventListener("touchmove", moveAnimate.onTouchMove);const onWindowResize = () => {  const w = window.innerWidth;  const h = window.innerHeight;  camera.aspect = w / h;  camera.updateProjectionMatrix();  renderer.setSize(w, h);};window.addEventListener("resize", onWindowResize);const createAnimRotation = () => {  const speed = 0.005;  sphere.rotation.z += speed / 2;  sphere.rotation.y += speed;};// 渲染函数const render = () => {  if (!isLoaded) {    renderer.render(loadingScreen.scene, loadingScreen.camera);    requestAnimationFrame(render);    return;  }  camera.position.x += (mouseX * -1 - camera.position.x) * 0.05;  camera.position.y += (mouseY - camera.position.y) * 0.05;  camera.lookAt(scene.position);  createAnimRotation();  renderer.render(scene, camera);  requestAnimationFrame(render);};render();
  • 在线体验(支持PC与移动端): 雪糕地球线上预览

  • 源码仓库: 雪糕地球

ThreeJS 基础&mdash;&mdash;实现转动的球体

Three.js 是一款运行在浏览器中的 3D 引擎,你可以用它创建各种三维场景,包括了摄影机、光影、材质等各种对象,大家或多或少应该都见识过 Three 的传说。这是小包第一次使用 Three,因此小包会围绕雪糕地球实现的各种细节讲起。

下面首先来看一下 Three 框架的基本组成要素

怎么用Three.js实现雪糕地球

Three 中最重要的三个对象即场景、相机和渲染器。场景即放置模型、光照的场地;相机设置以何种方式何种角度来观看场景,渲染器将效果渲染到网页中。这三个概念都不难理解,下面我们用代码实现这三个对象。

// 场景const scene = new THREE.Scene();// 透视相机const camera = new THREE.PerspectiveCamera(  75,  window.innerWidth / window.innerHeight,  0.1,  1000);// 渲染器const renderer = new THREE.WebGLRenderer();// 设置渲染区域尺寸renderer.setSize(window.innerWidth, window.innerHeight);// body元素中插入canvas对象document.body.appendChild(renderer.domElement);// 设置背景颜色renderer.setClearColor("hotpink");// 执行渲染操作   指定场景、相机作为参数renderer.render(scene, camera);

Three 中有多种相机,本文章主要使用透视相机(PerspectiveCamera),其原理与人眼所看的景象类似,共有四个参数:

PerspectiveCamera( fov : Number, aspect : Number, near : Number, far : Number )

fov: 表示能看到的角度范围,值为角度,类似于人的视角。

aspect: 表示渲染窗口的长宽比,如果网页中只有一个 canvas,其值通常设置为网页视口的宽高比

near/far: near/far 分别代表摄像机的近剪切面和远剪切面

文字有些难以理解,可以参考一下下图:

怎么用Three.js实现雪糕地球

打开浏览器,看一下会渲染出什么?目前只能看到全粉色的网页,这是因为目前的场景中并没有添加 3D 模型对象。

接下来我们来添加一个球体模型,作为地球的基底。

const cRadius = 100;const geometry = new THREE.SphereBufferGeometry(  cRadius,  cRadius * 6.4,  cRadius * 6.4);

SphereBufferGeometryThree 中实现球体的 api,参数非常多,这里只介绍前三个参数

radius: 球体半径

widthSegments: 沿经线方向分段数

heightSegments: 沿纬线方向分段数

为球体添加材质 Material,目前我们只添加一个颜色属性。

// 材质对象Materialconst material = new THREE.MeshLambertMaterial({  color: 0x0000ff,});

渲染网格体 Mesh,并将其添加到场景 Scene 中。

// 网格体 Mesh,两个参数分别为几何体和材质const sphere = new THREE.Mesh(geometry, material);scene.add(sphere);

重新打开网站,并没有看到球体,还是一片粉茫茫的寂寥,天理何在?

怎么用Three.js实现雪糕地球

Three 相机的初始位置默认为 (0,0,0),相机焦点默认为 Z 轴负半轴方向,球体的半径是 100,也就是说目前相机位于球体内部,因此我们需要调整相机位置。

// 设置相机的位置camera.position.set(0, 0, 220);// 设置相机焦点的方向camera.lookAt(scene.position);

当当当当,网页中就可以成功看到一个黑色球体了,额有点奇怪,我们明明设置的是 0x0000ff 颜色,怎么会显示一个黑色模型?

怎么用Three.js实现雪糕地球

小包苦思冥想: 万物本没有颜色,颜色是光的反射。在整个场景中,目前是没有光源的,因此下面分别添加平行光(DirectionalLight)和点光源(PointLight)

平行光是沿着特定方向发射的光,其表现类似无限远的阳光,文章使用它来模拟太阳光。点光源是从一个点向各个方向发射的光源,使用它来增加整体的亮度。

// 声明平行光const light = new THREE.DirectionalLight();// 设置平行光源位置light.position.set(0, 0, 1);// 将平行光源添加到场景中scene.add(light);// 声明点光源const point = new THREE.PointLight(0xeeeeee);// 设置点光源位置point.position.set(400, 200, 300);// 点光源添加到场景中scene.add(point);

怎么用Three.js实现雪糕地球

立体效果看起来不明显,没事,接下来我们让球体动起来。接下来,给球体添加一个 z 轴和 y 轴的转动。

const createAnimRotation = () =&gt; {  const speed = 0.005;  sphere.rotation.z += speed / 2;  sphere.rotation.y += speed;};const render = () =&gt; {  createAnimRotation();  renderer.render(scene, camera);  requestAnimationFrame(render);};render();

由于球体是对称的,转动看起来并不明显,如果你特别想看到转动效果,可以将 SphereBufferGeometry 暂时更换为 BoxBufferGeometry

ThreeJS 纹理&mdash;&mdash;实现转动的地球

上文已经成功实现地球,接下来我们来为地球披上衣服。本文实现的是雪糕地球,因此小包直接为其披上雪糕外衣。

Three 可以将一张纹理图映射到几何体上,具体的映射原理我们不做探究,映射的思想可以参考下图。

怎么用Three.js实现雪糕地球

选取一张雪糕地球的纹理图,使用下面的代码实现纹理贴图效果。

怎么用Three.js实现雪糕地球

// 纹理加载器对象const textureLoader = new THREE.TextureLoader();const textureSurface = textureLoader.load(  "https://s3-us-west-2.amazonaws.com/s.cdpn.io/249663/world-surface.jpg");// 设置纹理贴图const material = new THREE.MeshLambertMaterial({ map: textureSurface });

只使用普通贴图的雪糕地球看起来已经非常不错了,但还有进一步美化的空间,Three 提供了高光贴图,使用高光贴图,会有高亮部分显示。

const textureSpecular = textureLoader.load(  "https://s3-us-west-2.amazonaws.com/s.cdpn.io/249663/world-specular.jpg");const material = new THREE.MeshPhongMaterial({  map: textureSurface,  specularMap: textureSpecular,  shininess: 80, // 高光部分的亮度});

虽然动图录制的帧数太低,还是依稀可以看到一些高亮区域。

Three 还提供了环境贴图,环境贴图可以增加表面的细节,使三维模型更加立体。

const textureElevation = textureLoader.load(  "https://s3-us-west-2.amazonaws.com/s.cdpn.io/249663/world-elevation.jpg");const material = new THREE.MeshPhongMaterial({  map: textureSurface,  normalMap: textureElevation,  specularMap: textureSpecular,  shininess: 80,});

立体效果是有了,但是具体看起来一言难尽,颜色有些许暗淡,不符合雪糕的风格。

小包继续开始查看文档,环境贴图中有 normalScale 属性,可以设置颜色的深浅程度,减少对应属性值为 0.5,0.5

sphere.material.normalScale.set(0.5, 0.5);

交互式雪糕地球

给地球加一些交互效果:

  • 当鼠标靠近,地球放大;鼠标远离时,地球缩小

  • 地球随鼠标方向转动

上述动效我们可以通过移动相机位置实现。首先设定地球旋转的最大正负角度为 270

// 定义动效对象let mouseX = 0;let mouseY = 0;const moveAnimate = {  coordinates(clientX, clientY) {    const limit = 270;    const limitNeg = limit * -1;    mouseX = clientX - window.innerWidth / 2;    mouseY = clientY - window.innerHeight / 2;    mouseX = mouseX &gt;= limit ? limit : mouseX;    mouseX = mouseX &lt;= limitNeg ? limitNeg : mouseX;    mouseY = mouseY &gt;= limit ? limit : mouseY;    mouseY = mouseY &lt;= limitNeg ? limitNeg : mouseY;  },  onMouseMove(e) {    moveAnimate.coordinates(e.clientX, e.clientY);  },};document.addEventListener("mousemove", moveAnimate.onMouseMove);

通过上述事件计算出 mouseXmouseY 的值,在 render 函数中,修改 camera 的位置。

camera.position.x += (mouseX * -1 - camera.position.x) * 0.05;camera.position.y += (mouseY - camera.position.y) * 0.05;camera.lookAt(scene.position);

移动端同步监听 touchmove 事件,手机也可以看到雪糕地球的动态效果。

const moveAnimate = {  onTouchMove(e) {    const touchX = e.changedTouches[0].clientX;    const touchY = e.changedTouches[0].clientY;    moveAnimate.coordinates(touchX, touchY);  },};document.addEventListener("touchmove", moveAnimate.onTouchMove);

添加 loading 效果

纹理的加载需要一定的时间,因此添加一个转场 loading 效果。

loading 效果使用小包前面的实现跃动的文字中的效果。

.loader {  display: flex;  color: white;  display: flex;  justify-content: center;  align-items: center;  font-size: 5em;  width: 100%;  height: 100%;  font-family: "Baloo Bhaijaan", cursive;}.loader span {  text-shadow: 0 1px #bbb, 0 2px #bbb, 0 3px #bbb, 0 4px #bbb, 0 5px #bbb, 0 6px      transparent, 0 7px transparent, 0 8px transparent, 0 9px transparent, 0      10px 10px rgba(0, 0, 0, 0.4);  text-shadow: 0 1px #bbb, 0 2px #bbb, 0 3px #bbb, 0 4px #bbb, 0 5px #bbb, 0 6px      #bbb, 0 7px #bbb, 0 8px #bbb, 0 9px #bbb, 0 50px 25px rgba(0, 0, 0, 0.2);  transform: translateY(-20px);}

Three 提供了 LoadingManager,其功能是处理并跟踪已加载和待处理的数据。当所有加载器加载完成后,会调用 LoadingManager 上的 onLoad 事件。

因此我们定义一个 LoadingManager,当触发 onLoad 事件后,将页面中的加载标志移除。

const loadingScreen = {  scene: new THREE.Scene(),  camera: new THREE.PerspectiveCamera(    75,    window.innerWidth / window.innerHeight,    0.1,    1000  ),  // 移除加载标志的函数  removeText() {    const loadingText = document.querySelector("#canvas-loader");    if (loadingText.parentNode) {      loadingText.parentNode.removeChild(loadingText);    }  },};// 初始化加载器let loadingManager = new THREE.LoadingManager();// 监听加载器 onLoad 事件loadingManager.onLoad = () =&gt; {  loadingScreen.removeText();  isLoaded = true;};// 纹理图加载器传入 loadingManagerconst textureLoader = new THREE.TextureLoader(loadingManager);

以上就是关于“怎么用Three.js实现雪糕地球”这篇文章的内容,相信大家都有了一定的了解,希望小编分享的内容对大家有帮助,若想了解更多相关的知识内容,请关注编程网精选频道。

--结束END--

本文标题: 怎么用Three.js实现雪糕地球

本文链接: https://www.lsjlt.com/news/342776.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • C++ 生态系统中流行库和框架的贡献指南
    作为 c++++ 开发人员,通过遵循以下步骤即可为流行库和框架做出贡献:选择一个项目并熟悉其代码库。在 issue 跟踪器中寻找适合初学者的问题。创建一个新分支,实现修复并添加测试。提交...
    99+
    2024-05-15
    框架 c++ 流行库 git
  • C++ 生态系统中流行库和框架的社区支持情况
    c++++生态系统中流行库和框架的社区支持情况:boost:活跃的社区提供广泛的文档、教程和讨论区,确保持续的维护和更新。qt:庞大的社区提供丰富的文档、示例和论坛,积极参与开发和维护。...
    99+
    2024-05-15
    生态系统 社区支持 c++ overflow 标准库
  • c++中if elseif使用规则
    c++ 中 if-else if 语句的使用规则为:语法:if (条件1) { // 执行代码块 1} else if (条件 2) { // 执行代码块 2}// ...else ...
    99+
    2024-05-15
    c++
  • c++中的继承怎么写
    继承是一种允许类从现有类派生并访问其成员的强大机制。在 c++ 中,继承类型包括:单继承:一个子类从一个基类继承。多继承:一个子类从多个基类继承。层次继承:多个子类从同一个基类继承。多层...
    99+
    2024-05-15
    c++
  • c++中如何使用类和对象掌握目标
    在 c++ 中创建类和对象:使用 class 关键字定义类,包含数据成员和方法。使用对象名称和类名称创建对象。访问权限包括:公有、受保护和私有。数据成员是类的变量,每个对象拥有自己的副本...
    99+
    2024-05-15
    c++
  • c++中优先级是什么意思
    c++ 中的优先级规则:优先级高的操作符先执行,相同优先级的从左到右执行,括号可改变执行顺序。操作符优先级表包含从最高到最低的优先级列表,其中赋值运算符具有最低优先级。通过了解优先级,可...
    99+
    2024-05-15
    c++
  • c++中a+是什么意思
    c++ 中的 a+ 运算符表示自增运算符,用于将变量递增 1 并将结果存储在同一变量中。语法为 a++,用法包括循环和计数器。它可与后置递增运算符 ++a 交换使用,后者在表达式求值后递...
    99+
    2024-05-15
    c++
  • c++中a.b什么意思
    c++kquote>“a.b”表示对象“a”的成员“b”,用于访问对象成员,可用“对象名.成员名”的语法。它还可以用于访问嵌套成员,如“对象名.嵌套成员名.成员名”的语法。 c++...
    99+
    2024-05-15
    c++
  • C++ 并发编程库的优缺点
    c++++ 提供了多种并发编程库,满足不同场景下的需求。线程库 (std::thread) 易于使用但开销大;异步库 (std::async) 可异步执行任务,但 api 复杂;协程库 ...
    99+
    2024-05-15
    c++ 并发编程
  • 如何在 Golang 中备份数据库?
    在 golang 中备份数据库对于保护数据至关重要。可以使用标准库中的 database/sql 包,或第三方包如 github.com/go-sql-driver/mysql。具体步骤...
    99+
    2024-05-15
    golang 数据库备份 mysql git 标准库
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作