百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

通过漫天花雨来入门 Three.js(漫天花雨落)

toyiye 2024-07-03 01:58 10 浏览 0 评论

随着元宇宙概念的火爆,3D 渲染相关的技术频繁被提及,而 Three.js 是基于 WebGL 的 api 封装的用于简化 3D 场景的开发的框架, 是入门 3D 的不错的抓手,今天我们就来入门下 Three.js。


我们基于 Three.js 来实现一个花瓣雨的效果。


Three.js 的基础

Three.js 用于渲染一个 3D 的场景,里面会有很多物体,比如立方体、圆柱、圆环、圆锥等各种几何体(以 Geometry 为后缀),比如点(Points)线(Line)面(Sprite)等基础物体。这些所有的物体怎么管理呢?


用一个场景 Scene 来承载,所有的物体都会被添加到 Scene 里。


所以有这样的 api:


const scene = new THREE.Scene();

scene.add(xxx);

scene.add(yyy);

当然,物体之间可以做分组 Group,组内的物体可以统一管理,之后再添加到 Scene 里。


const scene = new THREE.Scene();


const group = new THREE.Group();


group.add(xxx);

group.add(yyy);


scene.add(group);

这种场景、物体、分组的概念,在很多游戏引擎中也有类似的 api,大家都是这么来管理的。


可以添加到 Scene 中的物体,除了几何体(Geometry)、点线面等,还有辅助工具,比如坐标系工具(AxisHelper)。其实这些工具也是用集合体、点线面封装出来的,只不过是作为工具来临时添加到 Scene 中。


const axisHelper = new THREE.AxisHelper(max);

scene.add(axisHelper)

有了场景和场景中的各种物体,怎么渲染出来呢?


调用 Renderer,这个类是专门负责渲染 Scene 中各种物体的。


但是还有个问题,三维的世界(scene)怎么渲染到二维的屏幕呢?


图片

图片

如图,从一个点找个角度来看三维世界,或者从一个平面来平行的看三维世界,看到的就是二维的。


这两种方式,第一种叫做透视、第二种叫做正交。


生成二维图像,就像照相机的功能一样,所以这种概念叫做 Camera。


在 Three.js 里面有 PerspectiveCamera (透视相机)和 OrthographicCamera(正交相机),分别对应上面两种三维转二维的方式。


这两个 Camera 的参数还是挺多的,但是理解了也挺简单:


new Three.PerspectiveCamera( fov, aspect, near, far )

new Three.OrthographicCamera( left, right, top, bottom, near, far )

先看透视相机的,它要看三维世界,那就要有一个最近和最远两个位置,然后从一个点看过去会有一个视野的角度,看到的画面还有个宽高比。


这就是为什么 PerspectCamera 有 near、far、fov、aspect 这四个参数。


图片

正交相机的参数也是差不多的意思,不过因为不是从一个点,看的,而是从一个面做的投影,那么就没有角度的参数,而是有上下左右的四个面位置的参数。


图片

正交相机的上下左右位置也不是随便的,比例要和画面的宽高比一样,所以一般都是这么算:


const width = window.innerWidth;

const height = window.innerHeight;

//窗口宽高比

const k = width / height;

//三维场景的显示的上下范围

const s = 200;


// 上下范围 s 再乘以宽高比 k 就是左右的范围,而远近随便设置一个数就行

const camera = new THREE.OrthographicCamera(-s * k, s * k, s, -s, 1, 1000);

上面的正交相机的参数里面,远近可以设置为 1 和 1000,上下设置为 200,左右就可以根据宽高比算出来。这就是相机所看到的二维画面的范围。


有了场景 Scene 中的各种物体,有了照相机 Camera,就可以用渲染器 Renderer 渲染出画面来了。


const renderer = new THREE.WebGLRenderer();

//设置渲染区域尺寸

renderer.setSize(width, height)


renderer.render(scene, camera)

不过,一般不会只渲染一帧,有动画效果的话,会使用 requestAnimationFrame 的 api 一帧帧的不停渲染。


function render() {

renderer.render(scene, camera)


requestAnimationFrame(render)

}

render();

这就是 Three.js 的大概流程:Scene 中有几何体Geometry、点线面、辅助工具等各种物体,物体还可以做分组,然后通过正交或者透视相机来设置看到的二维画面,之后用 Renderer 渲染出来。有动画效果的话,要用 requestAnimationFrame 来一帧帧的渲染。


下面我们来实现一下花瓣雨的效果。


花瓣雨实现

首先我们要创建场景 Scene 中的物体,也就是各种花瓣,这个需要显示的是一个平面,可以用 Sprite。


Sprite 是精灵的意思,在 Three.js 中,它就是一个永远面向相机的二维平面。


我们给 Sprite 贴上花瓣的纹理就可以了。


我们先准备一些花瓣的贴图,类似这种:


图片

图片

花瓣的数量有很多,我们生成 400 个,加到花瓣分组里,然后添加到场景中:


const scene = new THREE.Scene();

/**

* 花瓣分组

*/

const petal = new THREE.Group();


function create() {

var texture1 = new THREE.TextureLoader().load("img/h1.png");

var texture2 = new THREE.TextureLoader().load("img/h2.png");

var texture3 = new THREE.TextureLoader().load("img/h3.png");

var texture4 = new THREE.TextureLoader().load("img/h4.png");

var texture5 = new THREE.TextureLoader().load("img/h5.png");

var imageList = [texture1, texture2, texture3, texture4, texture5];


for (let i = 0; i < 400; i++) {

var spriteMaterial = new THREE.SpriteMaterial({

map: imageList[Math.floor(Math.random() * imageList.length)],//设置精灵纹理贴图

});

var sprite = new THREE.Sprite(spriteMaterial);

petal.add(sprite);


sprite.scale.set(40, 50, 1);

sprite.position.set(2000 * (Math.random() - 0.5), 2000 * Math.random(), 0)

}

scene.add(petal)

}


create();

400 个 Sprite 随机贴上了不同的花瓣的纹理贴图,然后设置了下放缩,之后随机设置了一个在场景中的位置。


我们在 Scene 中加入坐标系辅助工具来看下坐标:


const axisHelper = new THREE.AxisHelper(1000);

scene.add(axisHelper)

图片

红色是 x 轴,向右是递增的,绿色是 y 轴,向上是递增的。z 轴我们暂时用不到。


所以,根据代码,花瓣的 x 的范围就是随机的 -1000 到 1000,y 的范围就是 0 到 2000。


然后,我们创建正交相机:


const width = window.innerWidth;

const height = window.innerHeight;

//窗口宽高比

const k = width / height;

//三维场景的显示的上下范围

const s = 200;

const camera = new THREE.OrthographicCamera(-s * k, s * k, s, -s, 1, 1000);

设置下相机的位置和方向:


camera.position.set(0, 200, 500)

camera.lookAt(scene.position)

我们创建相机的时候指定了二维能显示的范围,相机在这个范围内的哪个位置都行。


然后创建渲染器,设置下大小和背景颜色,把渲染到的 canvas 元素插入到 dom 中。


const renderer = new THREE.WebGLRenderer();

//设置渲染区域尺寸

renderer.setSize(width, height)

//设置背景颜色

renderer.setClearColor(0xFFFFFF, 1)

//body元素中插入canvas对象

document.body.appendChild(renderer.domElement)

之后就用 requestAnimation 不断地一帧帧渲染就行了。


function render() {

petal.children.forEach(sprite => {

sprite.position.y -= 1;

sprite.position.x += 0.5;

if (sprite.position.y < -400) {

sprite.position.y = 800;

}

if (sprite.position.x > 1000) {

sprite.position.x = -1000

}

});


renderer.render(scene, camera)


requestAnimationFrame(render)

}

每次重新渲染之前,我们修改下花瓣的位置,产生下落效果,如果超出了范围,就移到上面去重新开始落,这样就是不间断的花瓣雨效果。


图片

完整代码如下:


<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="UTF-8">

<title>花瓣雨</title>

<style>

body {

margin: 0;

overflow: hidden;

}

</style>

<script src="js/three.min.js"></script>

</head>

<body>

<script>


const scene = new THREE.Scene();

/**

* 花瓣分组

*/

const petal = new THREE.Group();


const width = window.innerWidth;

const height = window.innerHeight;

//窗口宽高比

const k = width / height;

//三维场景的显示的上下范围

const s = 200;

const camera = new THREE.OrthographicCamera(-s * k, s * k, s, -s, 1, 1000);


const renderer = new THREE.WebGLRenderer();


function create() {

//设置相机位置

camera.position.set(0, 200, 500)

camera.lookAt(scene.position)

//设置渲染区域尺寸

renderer.setSize(width, height)

//设置背景颜色

renderer.setClearColor(0xFFFFFF, 1)

//body元素中插入canvas对象

document.body.appendChild(renderer.domElement)


// const axisHelper = new THREE.AxisHelper(1000);

// scene.add(axisHelper)


var textureTree1 = new THREE.TextureLoader().load("img/h1.png");

var textureTree2 = new THREE.TextureLoader().load("img/h2.png");

var textureTree3 = new THREE.TextureLoader().load("img/h3.png");

var textureTree4 = new THREE.TextureLoader().load("img/h4.png");

var textureTree5 = new THREE.TextureLoader().load("img/h5.png");

var imageList = [textureTree1, textureTree2, textureTree3, textureTree4, textureTree5];


for (let i = 0; i < 400; i++) {

var spriteMaterial = new THREE.SpriteMaterial({

map: imageList[Math.floor(Math.random() * imageList.length)],//设置精灵纹理贴图

});

var sprite = new THREE.Sprite(spriteMaterial);

petal.add(sprite);


sprite.scale.set(40, 50, 1);

sprite.position.set(2000 * (Math.random() - 0.5), 2000 * Math.random(), 0)

}

scene.add(petal)

}


function render() {

petal.children.forEach(sprite => {

sprite.position.y -= 1;

sprite.position.x += 0.5;

if (sprite.position.y < -400) {

sprite.position.y = 800;

}

if (sprite.position.x > 1000) {

sprite.position.x = -1000

}

});


renderer.render(scene, camera)


requestAnimationFrame(render)

}


create()

render()

</script>

</body>

</html>

总结

Three.js 是为了简化 3D 渲染的框架,它提供了场景 Scene 的 api,里面可以包含各种可渲染的物体:立方体、圆锥等各种几何体 Geometry、点线面、坐标系等辅助工具。这些物体还可以通过 Group 分组来统一管理。


Sence 要渲染出来需要指定一个相机,分为从点去看的透视相机 PerspectiveCamera,从平面去投影的正交相机 OrthographicCamera。理解了它们的原理才能理解 Camera 的参数。


之后通过 Renderer 渲染出来,如果有动画需要用 requestAnimationFrame 来一帧帧的渲染。


这是 Three.js 的大概渲染流程。


之后我们实现了一个花瓣雨的案例。用到了 Sprite 这种物体,它是一个永远面向相机的平面,用来做这种效果很合适。


当然,Three.js 的东西还是比较多的,这篇文章只是入下门,后面我们会继续深入,做更多的有意思的 3D 场景和效果。

相关推荐

为何越来越多的编程语言使用JSON(为什么编程)

JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

何时在数据库中使用 JSON(数据库用json格式存储)

在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

JSON对象花样进阶(json格式对象)

一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

深入理解 JSON 和 Form-data(json和formdata提交区别)

在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...

JSON 语法(json 语法 priority)

JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...

JSON语法详解(json的语法规则)

JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...

MySQL JSON数据类型操作(mysql的json)

概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...

JSON的数据模式(json数据格式示例)

像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...

前端学习——JSON格式详解(后端json格式)

JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...

什么是 JSON:详解 JSON 及其优势(什么叫json)

现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...

PostgreSQL JSON 类型:处理结构化数据

PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...

JavaScript:JSON、三种包装类(javascript 包)

JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...

Python数据分析 只要1分钟 教你玩转JSON 全程干货

Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...

比较一下JSON与XML两种数据格式?(json和xml哪个好)

JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码