问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

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

发布网友 发布时间:2024-09-17 01:50

我来回答

1个回答

热心网友 时间:2024-10-18 23:49

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

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

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

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

Three.js相关基础

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

constscene=newTHREE.Scene();scene.add(xxx);constgroup=newTHREE.Group();group.add(yyy);group.add(zzz);scene.add(group);

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

constrenderer=newTHREE.WebGLRenderer();functionrender(){renderer.render(scene,camera);requestAnimationFrame(render);}render();

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

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

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

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

平行光:平行的光线

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

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

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

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

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

const圆柱几何体=newTHREE.CylinderBufferGeometry(上圆半径,下圆半径,高度,侧面分段数量);const侧面材质=newTHREE.MeshBasicMaterial({map:纹理图片});const上面材质=newTHREE.MeshBasicMaterial({color:'red'});const下面材质=newTHREE.MeshBasicMaterial({color:'red'});const圆柱=newTHREE.Mesh(圆柱几何体,[侧面材质,上面材质,下面材质]);

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

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

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

之后就可以显示文字了:

constfontLoader=newTHREE.FontLoader();fontLoader.load('./font/xxx.typeface.json',function(font){vartextGeometry=newTHREE.TextGeometry('文字',参数);consttextMaterial=[newTHREE.MeshBasicMaterial({color:'字体颜色'}),newTHREE.MeshBasicMaterial({color:'侧面颜色'}),];consttext=newTHREE.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去加载他们:

constcakeTexture1=newTHREE.TextureLoader().load('img/cake1.png');constcakeTexture2=newTHREE.TextureLoader().load('img/cake2.png');constcakeTexture3=newTHREE.TextureLoader().load(`img/cake3.png`);constcakeTexture4=newTHREE.TextureLoader().load('img/cake4.png');

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

constcakeMaterail1=newTHREE.MeshBasicMaterial({map:cakeTexture1});constcakeMaterail2=newTHREE.MeshBasicMaterial({map:cakeTexture2});constcakeMaterail3=newTHREE.MeshBasicMaterial({map:cakeTexture3});constcakeMaterail4=newTHREE.MeshBasicMaterial({map:cakeTexture4});

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

constpinkMaterial=newTHREE.MeshBasicMaterial({color:'pink'});

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

constcakeGeometry1=newTHREE.CylinderBufferGeometry(100,100,70,40);constcakePart1=newTHREE.Mesh(cakeGeometry1,[cakeMaterail1,pinkMaterial,pinkMaterial]);

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

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

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

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

constcakeGeometry1=newTHREE.CylinderBufferGeometry(100,100,70,40);constcakePart1=newTHREE.Mesh(cakeGeometry1,[cakeMaterail1,pinkMaterial,pinkMaterial]);cakePart1.translateY(45)constcakeGeometry2=newTHREE.CylinderBufferGeometry(120,120,70,40);constcakePart2=newTHREE.Mesh(cakeGeometry2,[cakeMaterail3,pinkMaterial,pinkMaterial]);cakePart2.translateY(-25)constcakeGeometry3=newTHREE.CylinderBufferGeometry(140,140,60,40);constcakePart3=newTHREE.Mesh(cakeGeometry3,[cakeMaterail2,pinkMaterial,pinkMaterial]);cakePart3.translateY(-90)constcakeGeometry4=newTHREE.CylinderBufferGeometry(160,160,10,40);constcakePart4=newTHREE.Mesh(cakeGeometry4,[cakeMaterail4,cakeMaterail4,cakeMaterail4]);cakePart4.translateY(-120)cake.add(cakePart1)cake.add(cakePart2)cake.add(cakePart3)cake.add(cakePart4)

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

constaxisHelper=newTHREE.AxisHelper(2500);scene.add(axisHelper);

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

constrenderer=newTHREE.WebGLRenderer();functionrender(){renderer.render(scene,camera);requestAnimationFrame(render);}render();0

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

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

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

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

constrenderer=newTHREE.WebGLRenderer();functionrender(){renderer.render(scene,camera);requestAnimationFrame(render);}render();1

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

constrenderer=newTHREE.WebGLRenderer();functionrender(){renderer.render(scene,camera);requestAnimationFrame(render);}render();2

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

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

constrenderer=newTHREE.WebGLRenderer();functionrender(){renderer.render(scene,camera);requestAnimationFrame(render);}render();3

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

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

constrenderer=newTHREE.WebGLRenderer();functionrender(){renderer.render(scene,camera);requestAnimationFrame(render);}render();4

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

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

我们来看下效果:

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

全部代码:

<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><title>生日蛋糕</title><style>body{margin:0;overflow:hidden;}</style><scriptsrc="./js/three.js"></script><scriptsrc="./js/OrbitControls.js"></script></head><body><script>constwidth=window.innerWidth;constheight=window.innerHeight;//窗口宽高比constk=width/height;//三维场景显示范围的宽度consts=200;constcamera=newTHREE.OrthographicCamera(-s*k,s*k,s,-s,1,1000);constfontLoader=newTHREE.FontLoader();constscene=newTHREE.Scene();constcake=newTHREE.Group();constrenderer=newTHREE.WebGLRenderer();functioncreate(){renderer.setSize(width,height);//设置背景颜色renderer.setClearColor(0xFFFFFF,1);document.body.appendChild(renderer.domElement);camera.position.set(0,100,500)camera.lookAt(scene.position);constlight=newTHREE.AmbientLight(0xCCCCCC);scene.add(light);constaxisHelper=newTHREE.AxisHelper(2500);scene.add(axisHelper);constcakeTexture1=newTHREE.TextureLoader().load('img/cake1.png');constcakeTexture2=newTHREE.TextureLoader().load('img/cake2.png');constcakeTexture3=newTHREE.TextureLoader().load(`img/cake3.png`);constcakeTexture4=newTHREE.TextureLoader().load('img/cake4.png');constcakeMaterail1=newTHREE.MeshBasicMaterial({map:cakeTexture1});constcakeMaterail2=newTHREE.MeshBasicMaterial({map:cakeTexture2});constcakeMaterail3=newTHREE.MeshBasicMaterial({map:cakeTexture3});constcakeMaterail4=newTHREE.MeshBasicMaterial({map:cakeTexture4});constpinkMaterial=newTHREE.MeshBasicMaterial({color:'pink'});constcakeGeometry1=newTHREE.CylinderBufferGeometry(100,100,70,40);constcakePart1=newTHREE.Mesh(cakeGeometry1,[cakeMaterail1,pinkMaterial,pinkMaterial]);cakePart1.translateY(45)constcakeGeometry2=newTHREE.CylinderBufferGeometry(120,120,70,40);constcakePart2=newTHREE.Mesh(cakeGeometry2,[cakeMaterail3,pinkMaterial,pinkMaterial]);cakePart2.translateY(-25)constcakeGeometry3=newTHREE.CylinderBufferGeometry(140,140,60,40);constcakePart3=newTHREE.Mesh(cakeGeometry3,[cakeMaterail2,pinkMaterial,pinkMaterial]);cakePart3.translateY(-90)constcakeGeometry4=newTHREE.CylinderBufferGeometry(160,160,10,40);constcakePart4=newTHREE.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){vartextGeometry=newTHREE.TextGeometry('光光',{font:font,size:30,height:5,bevelEnabled:true,bevelSize:10,});consttextMaterial=['white','red'].map(color=>newTHREE.MeshBasicMaterial({color}));consttext=newTHREE.Mesh(textGeometry,textMaterial);text.translateY(90)text.translateX(-45)cake.add(text);});scene.add(cake);}functionrender(){renderer.render(scene,camera);cake.rotation.y+=0.005;requestAnimationFrame(render)}create()render()constrenderer=newTHREE.WebGLRenderer();functionrender(){renderer.render(scene,camera);requestAnimationFrame(render);}render();4</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画个蛋糕送给他(她),或许会有不一样的收获哦。

声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
说课包括哪些方面 说课内容包括()。 如何在手机百度上删除对话记录? 结核病是什么样的疾病? 曹丕17岁得了肺痨,明知自己命不长久,还要强争王位,是不是很自私呢?_百... 古代小说常出现的病名 急求一篇"生活小窍门"(500字)的作文 至今最有什么小妙招 健康的戒烟方法 笔记本电池锁死是什么原因引起的? 我是如何用 Three.js 在三维世界建房子的(详细教程) Three.js 手写跳一跳小游戏(上) three.js运用详解 使用Three.js 的 3D 制作动画场景:飞行者 肾功能不全早期能恢复吗 慢性肾功能不全怎么治疗 老年人急性肾功能衰竭可以治愈吗? 肾功能不全可以治好吗 肌酐120能治愈吗 电瓶充电用什么牌子的充电器好,多少参数的合适! 为什么我的手机号发不了短信? 月经期吃巧克力的危害 经期吃巧克力会怎么样 一百毫升酒是多少 歌词中有"年轻的心不会再有浪潮"的歌叫什么名字 月经期吃巧克力会怎么样 幻想嘉年华爱因兹贝伦城堡是哪一集 月经期吃巧克力的危害 来月经为什么不能吃巧克力 玩fate hollow ataraxia到风云伊莉雅城的时候,因为小游戏输了,一不小心... 来月经吃巧克力的危害 通过“漫天花雨”来入门Three.js 如何用Three.js + Blender打造一个web 3D展览馆 苹果12pro max电池多少毫安 苹果promax电池多少毫安 1990年版50元 冠号FZ98224378人民币 市场价值多少 还有1980年10元一... word怎么设置首行缩进两个字符啊! 羊男比牛女大6岁相合吗 肺癌的肺外症状有哪些? 肺癌最常见的临床表现 肺癌有哪些症状前兆 肺癌晚期的症状 晚期肺癌症状表现有哪些 肺癌的临床表现有哪些? 肺癌的症状会有哪些 怀孕喝红糖水流产了是怎么回事 孕妇喝红糖水会流产吗 26个英文字母最好记方法 26个英文字母表的一些有趣记忆方法_百度... 干货推荐 | 高效记英语单词的13种方法 脚手架搭设安全技术规范有什么规定呢? ipadpro开机设置 ipadpro如何省电