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

用 Three.js 画个 3D 生日蛋糕送给他(她)(用3dmax生日蛋糕)

toyiye 2024-04-04 11:27 27 浏览 0 评论

作为整天和 UI 打交道的前端工程师,是否想在他(她)生日的时候用代码送上一份惊喜呢?


不妨用 Three.js 做个 3D 的蛋糕送给 ta,既浪漫又能展现你技术的魅力。


这篇文章我们就来学习下如何用 Three.js 画一个蛋糕。


图片


代码地址:https://github.com/QuarkGluonPlasma/threejs-exercize


Three.js 相关基础

Three.js 是通过场景 Scene 来管理所有的物体的,加到 Scene 的物体还可以分个组:


const scene = new THREE.Scene();


scene.add(xxx);


const group = new THREE.Group();

group.add(yyy);

group.add(zzz);


scene.add(group);

想要把 Scene 中的所有物体渲染出来,需要指定一个相机 camera,然后用 renderer 来渲染,如果有动画效果,要用 requestAnimationFrame 来一帧帧不断渲染。


const renderer = new THREE.WebGLRenderer();


function render() {

renderer.render(scene, camera);

requestAnimationFrame(render);

}

render();

相机 camera 分为从一个点去看的透视相机 PerspectiveCamera,还有从一个面去投影的正交相机 OrthographicCamera。


图片

图片

透视相机的特点是近大远小,而正交的则不是,就是一个平行投影,大小不变。


三维世界还需要指定一个光源,不然是全黑的,光源种类很多,常用的有这些:


点光源:从一个点发射光线,就像灯泡一样。

平行光:平行的光线

环境光:均匀照射每个地方

聚光灯:舞台聚光灯的光源

三维场景中的物体有很多种,比如永远面向相机的平面是 Sprite(我们做“漫天花雨”效果用的那个),还有由三角形构成的物体叫做 Mesh。


图片

Mesh 比较常用,它是由一个个三角形构成的几何体,还可以在每个面上贴图。所以,参数有两个,几何体 Geometry 和材质 Material。


比如圆柱体就是一个 Mesh,创建它的时候要指定圆柱几何体 CylinderBufferGeometry 和每个面的材质 Material。


const 圆柱几何体 = new THREE.CylinderBufferGeometry(上圆半径, 下圆半径, 高度, 侧面分段数量);


const 侧面材质 = new THREE.MeshBasicMaterial({map: 纹理图片});

const 上面材质 = new THREE.MeshBasicMaterial({color: 'red'});

const 下面材质 = new THREE.MeshBasicMaterial({color: 'red'});


const 圆柱 = new THREE.Mesh(圆柱几何体, [侧面材质, 上面材质, 下面材质]);

MeshBasicMaterial 是基础的材质,可以通过 color 来指定颜色,也可以通过 map 来指定纹理图片 texture。


各种 Mesh 中比较特殊是文字,它用的是 TextGeometry,文字需要从一个 xxx.typeface.json 中加载。


而这种 json 文件可以用字体文件 ttf 来转换得到。用ttf 转 typeface.json 的这个网站来转:


图片

之后就可以显示文字了:


const fontLoader = new THREE.FontLoader();


fontLoader.load('./font/xxx.typeface.json', function (font) {

var textGeometry = new THREE.TextGeometry('文字', 参数);

const textMaterial = [

new THREE.MeshBasicMaterial({color: '字体颜色'}),

new THREE.MeshBasicMaterial({color: '侧面颜色'}),

];


const text = new THREE.Mesh(textGeometry, textMaterial);

});

这些就是我们会用到的 Three.js 基础,简单做个小结:


Three.js 是通过 Scene 来管理各种物体的,物体还可以分下组。


物体中常见的有 Mesh 和 Sprite 等,Sprite 是永远面向相机的一个平面,Mesh 是由三角形构成的三维物体。Mesh 要指定几何体Geometry 和材质 Material,常用的材质可以是颜色或者纹理贴图。其中文字 TextGeometry 比较特殊,需要一个 typeface.json 的文件,这个可以由 ttf 转换得到。


场景中的物体准备好之后,还需要设置下光源 Light 和相机 Camera,相机主要有从点去看的透视相机和从一个平面去投影的正交相机,之后就可以通过渲染器 Renderer 渲染出来了,结合 requestAnimationFrame 来一帧帧的渲染。


基础学完之后,正式开始画蛋糕了。


画 3D 蛋糕

蛋糕其实就是由 4 个圆柱体加上文字构成的,每个圆柱体都设置了不同的位置,圆柱体的侧面和上下面都贴上不同的贴图,就是一个蛋糕。


我们先准备蛋糕的贴图:


图片

图片

图片

图片

使用纹理加载器 TextureLoader 去加载他们:


const cakeTexture1 = new THREE.TextureLoader().load('img/cake1.png');

const cakeTexture2 = new THREE.TextureLoader().load('img/cake2.png');

const cakeTexture3 = new THREE.TextureLoader().load(`img/cake3.png`);

const cakeTexture4 = new THREE.TextureLoader().load('img/cake4.png');

然后构成纹理贴图的材质:


const cakeMaterail1 = new THREE.MeshBasicMaterial({map: cakeTexture1});

const cakeMaterail2 = new THREE.MeshBasicMaterial({map: cakeTexture2});

const cakeMaterail3 = new THREE.MeshBasicMaterial({map: cakeTexture3});

const cakeMaterail4 = new THREE.MeshBasicMaterial({map: cakeTexture4});

除了纹理贴图的材质外,再准备个颜色构成的材质:


const pinkMaterial = new THREE.MeshBasicMaterial({color: 'pink'});

然后创建 4 个圆柱体的物体(Mesh),使用不同的贴图材质和颜色材质:


const cakeGeometry1 = new THREE.CylinderBufferGeometry(100, 100, 70, 40);

const cakePart1 = new THREE.Mesh(cakeGeometry1, [cakeMaterail1, pinkMaterial, pinkMaterial]);

圆柱体的几何体 CylinderBufferGeometry 的参数分别是 上面圆的半径,下面圆的半径,高度,侧面的分割次数。


上面圆半径保持一致,这样才是圆柱体。侧面分割次数设置为 40,这样比较圆滑。


之后还设置下位移,然后就可以加到蛋糕分组里了。


我们用同样的方式创建四个圆柱体,设置不同的大小和位置,贴不同的图:


const cakeGeometry1 = new THREE.CylinderBufferGeometry(100, 100, 70, 40);

const cakePart1 = new THREE.Mesh(cakeGeometry1, [cakeMaterail1, pinkMaterial, pinkMaterial]);

cakePart1.translateY(45)


const cakeGeometry2 = new THREE.CylinderBufferGeometry(120, 120, 70, 40);

const cakePart2 = new THREE.Mesh(cakeGeometry2,[cakeMaterail3, pinkMaterial, pinkMaterial]);

cakePart2.translateY(-25)


const cakeGeometry3 = new THREE.CylinderBufferGeometry(140, 140, 60, 40);

const cakePart3 = new THREE.Mesh(cakeGeometry3, [cakeMaterail2, pinkMaterial, pinkMaterial]);

cakePart3.translateY(-90)


const cakeGeometry4 = new THREE.CylinderBufferGeometry(160, 160, 10, 40);

const cakePart4 = new THREE.Mesh(cakeGeometry4, [cakeMaterail4, cakeMaterail4, cakeMaterail4]);

cakePart4.translateY(-120)


cake.add(cakePart1)

cake.add(cakePart2)

cake.add(cakePart3)

cake.add(cakePart4)

如果对坐标位置拿不准,可以在 Scene 中加上一个坐标的辅助工具 AxisHelper。参数是坐标轴长度。


const axisHelper = new THREE.AxisHelper(2500);

scene.add(axisHelper);

图片

然后是文字的部分,这个要先通过字体文件 ttf 转成 typeface.json 的文件,然后用 fontLoader 来加载,之后创建相应的 Mesh:


fontLoader.load('./font/guang.typeface.json', function (font) {

var textGeometry = new THREE.TextGeometry('光光', {

font: font,

size: 30,

height: 5,

bevelEnabled: true,

bevelSize: 10,

});

const textMaterial = ['white', 'red'].map(color => new THREE.MeshBasicMaterial({color}));


const text = new THREE.Mesh(textGeometry, textMaterial);

text.translateY(90)

text.translateX(-45)


cake.add(text);

});

TextGeometry 需要设置的参数有字体大小 size,厚度 height,以及边缘是否是曲面 bevelEnabled,和曲面的大小 bevelSize。


我们这里的效果是需要开启曲面的。


图片

4 个圆柱体画完了,文字也画完了,那蛋糕就算是画完了,之后设置下光源、相机,就可以用 Renderer 渲染了。


光源使用环境光,因为要均匀的照射:


const light = new THREE.AmbientLight(0xCCCCCC);

scene.add(light);

相机使用正交相机,因为不需要近大远小的透视效果:


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, 100, 500)

camera.lookAt(scene.position);

正交相机的参数分别是左右上下远近的三维视野范围,我们指定高度为 200,然后根据窗口的宽高比算出宽度。远近可以设置一个比较大的范围。


之后就可以用 Renderer 来渲染了。把渲染出的 canvas 的 dom 挂载到 body 上。


const renderer = new THREE.WebGLRenderer();


renderer.setSize(width, height);

//设置背景颜色

renderer.setClearColor(0xFFFFFF, 1);

document.body.appendChild(renderer.domElement);


function render() {

renderer.render(scene, camera);


cake.rotation.y += 0.005;


requestAnimationFrame(render)

}

render()


在每帧 render 之前,还做了个围绕 y 轴的自动旋转。


还要支持手动的旋转,这个直接使用 Three.js 的轨道控制器 OrbitControls 就行。


const controls = new THREE.OrbitControls(camera);

参数是相机,因为这种视野的改变就是通过改变相机位置和朝向来实现的。


创建了 Scene 中的蛋糕的每一部分,设置好了光源、相机,用渲染器做了一帧帧的渲染,并且添加了用鼠标来改变视角的轨道控制器之后,就完成了 3D 蛋糕的制作。


我们来看下效果:


图片


代码地址:https://github.com/QuarkGluonPlasma/threejs-exercize


全部代码:


<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="UTF-8">

<title>生日蛋糕</title>

<style>

body {

margin: 0;

overflow: hidden;

}

</style>

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

<script src="./js/OrbitControls.js"></script>

</head>

<body>

<script>

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 fontLoader = new THREE.FontLoader();


const scene = new THREE.Scene();


const cake = new THREE.Group();


const renderer = new THREE.WebGLRenderer();


function create() {

renderer.setSize(width, height);

//设置背景颜色

renderer.setClearColor(0xFFFFFF, 1);

document.body.appendChild(renderer.domElement);


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

camera.lookAt(scene.position);


const light = new THREE.AmbientLight(0xCCCCCC);

scene.add(light);


const axisHelper = new THREE.AxisHelper(2500);

scene.add(axisHelper);


const cakeTexture1 = new THREE.TextureLoader().load('img/cake1.png');

const cakeTexture2 = new THREE.TextureLoader().load('img/cake2.png');

const cakeTexture3 = new THREE.TextureLoader().load(`img/cake3.png`);

const cakeTexture4 = new THREE.TextureLoader().load('img/cake4.png');


const cakeMaterail1 = new THREE.MeshBasicMaterial({map: cakeTexture1});

const cakeMaterail2 = new THREE.MeshBasicMaterial({map: cakeTexture2});

const cakeMaterail3 = new THREE.MeshBasicMaterial({map: cakeTexture3});

const cakeMaterail4 = new THREE.MeshBasicMaterial({map: cakeTexture4});


const pinkMaterial = new THREE.MeshBasicMaterial({color: 'pink'});


const cakeGeometry1 = new THREE.CylinderBufferGeometry(100, 100, 70, 40);

const cakePart1 = new THREE.Mesh(cakeGeometry1, [cakeMaterail1, pinkMaterial, pinkMaterial]);

cakePart1.translateY(45)


const cakeGeometry2 = new THREE.CylinderBufferGeometry(120, 120, 70, 40);

const cakePart2 = new THREE.Mesh(cakeGeometry2,[cakeMaterail3, pinkMaterial, pinkMaterial]);

cakePart2.translateY(-25)


const cakeGeometry3 = new THREE.CylinderBufferGeometry(140, 140, 60, 40);

const cakePart3 = new THREE.Mesh(cakeGeometry3, [cakeMaterail2, pinkMaterial, pinkMaterial]);

cakePart3.translateY(-90)


const cakeGeometry4 = new THREE.CylinderBufferGeometry(160, 160, 10, 40);

const cakePart4 = new THREE.Mesh(cakeGeometry4, [cakeMaterail4, cakeMaterail4, cakeMaterail4]);

cakePart4.translateY(-120)


cake.add(cakePart1)

cake.add(cakePart2)

cake.add(cakePart3)

cake.add(cakePart4)


fontLoader.load('./font/guang.typeface.json', function (font) {

var textGeometry = new THREE.TextGeometry('光光', {

font: font,

size: 30,

height: 5,

bevelEnabled: true,

bevelSize: 10,

});

const textMaterial = ['white', 'red'].map(color => new THREE.MeshBasicMaterial({color}));


const text = new THREE.Mesh(textGeometry, textMaterial);

text.translateY(90)

text.translateX(-45)

cake.add(text);

});


scene.add(cake);

}


function render() {

renderer.render(scene, camera);


cake.rotation.y += 0.005;


requestAnimationFrame(render)

}


create()

render()


const controls = new THREE.OrbitControls(camera);

</script>

</body>

</html>

总结

本文我们用 Three.js 来实现了 3D 蛋糕的效果。


首先我们学习了下 Three.js 的基础:通过 Scene 来管理物体,物体可以分组,物体包括 Mesh、Sprite 等,Mesh 是三角形构成的 3D 物体,要分别指定几何体 Geometry 和材质 Material。材质可以是纹理(Texture)贴图、也可以是颜色。其中文字的 Mesh 需要做 ttf 到 typeface.json 的转换,加载这个 json 才能显示文字。


物体创建完了之后,还要设置相机、灯光等,然后通过渲染器来一帧帧的渲染。


调试的时候还可以添加 AxisHelper 坐标系辅助工具来辅助开发。


然后我们实现了 3D 蛋糕:


通过 4 个圆柱体 + 文字来画的,圆柱体用了不同的纹理贴图材质,设置了不同的位置,然后组成蛋糕的 group。


设置了环境光,使用了正交相机,还启用了轨道控制器 OrbitControls,来实现鼠标拖拽改变相机位置,进而改变视野角度的效果。


下个他(她)的生日,不妨试试用 Three.js 画个蛋糕送给他(她),或许会有不一样的收获哦。

相关推荐

为何越来越多的编程语言使用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)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码