全国免费咨询:

13245491521

VR图标白色 VR图标黑色
X

中高端软件定制开发服务商

与我们取得联系

13245491521     13245491521

2022-10-14_Three.js 进阶之旅:模型光源结合生成明暗变化的创意页面

您的位置:首页 >> 新闻 >> 行业资讯

Three.js 进阶之旅:模型光源结合生成明暗变化的创意页面 本文为稀土掘金技术社区首发签约文章,14天内禁止转载,14天后未获授权禁止转载,侵权必究! 摘要本篇文章将根据专栏前两章《Three.js 进阶之旅:基础入门(上)》、《Three.js 进阶之旅:基础入门(下)》学到的基础知识以及Tween补间动画相关的知识,创建一个使用光源和模型结合而成的3D创意页面。通过本文内容,你将学到的知识包括:使用Blender压缩模型、使用模型加载管理器管理加载进度、使用模型加载器加载压缩过的模型、优化渲染器的输出效果、使用TWEEN实现位移动画和镜头补间动画、点光源随鼠标移动动画、鼠标光标悬浮到导航栏时虚拟光标动画、监听页面元素可见性以及CSS动画效果等。 效果开始之前,我们先来看看最终页面的实现效果。页面共分为两页,首先是第1页,主体是一个头部雕像??模型,使用鼠标在头像周围移动可以观察到由光源??引起的明暗变化效果以及模型朝相反的方向发生位移。页面其余部分是导航栏和一些装饰性文字。 preview_0.gif向下??滚动到第2页,左侧是一个Tab菜单及下方的诗句文案,点击??菜单选项可以引起右侧雕像位置和角度的变化,在右侧雕像上移动鼠标,同样能观察到由光源??引起的明暗变化效果。两个页面使用同一个模型,只是因为相机角度的不同因而呈现不用的视觉效果。 preview_1.gif打开以下链接中的任意一个就可以在线预览效果,大屏访问效果更佳。 ?????在线预览地址1:https://dragonir.github.io/3d/#/shadow?????在线预览地址2:https://3d-eosin.vercel.app/#/shadow本专栏系列代码托管在Github仓库【threejs-odessey】,后续所有目录也都将在此仓库中更新。 ??代码仓库地址:git@github.com:dragonir/threejs-odessey.git 码上掘金代码片段 准备工作本页面的开发,涉及到外部模型??的加载,开发之前,需要准备所需的模型资源。如果你已经具备较强的建模能力,可以使用3D建模软件Blender、3D Max创建模型并导出前端开发需要的模型格式备用。如果只想练习Three.js开发技能,可以直接在网络上找一些免费的模型练手。 下载模型本案例中使用的雕像模型??来源于Sketchfab的免费模型。Sketchfab是一个发布、共享、发现3D、VR和AR内容的平台,它提供了基于WebGL和WebVR技术的查看器,可以在Web上显示3D模型,并可以在任何移动浏览器,桌面浏览器或虚拟现实头盔上进行查看。大家也可以在上面找一些自己喜欢的模型下载练习。 sketchfab.png??下载网络上的模型时一定要注意模型的使用许可范围,不要用于商业用途,避免产生版权纠纷! 压缩模型下载到模型后,就会发现即使是一些简单模型体积一般都有30M-50M,模型体积过大不仅会占用大量存储空间,而且会影响页面加载速度和性能。此时就需要对模型进行压缩优化。我们可以直接使用Blender软件进行优化。基本操作步骤是: 在Blender中导入下载好的模型;在右侧场景集合栏中删掉场景、光源、摄像机等多余元素,只留模型主体网格;导出模型,导出时勾选压缩选项、去选动画等选项。模型压缩后体积一般可以缩小到几百K以内,压缩效果近乎百倍。若此时使用Windows自带的3D查看器等软件预览压缩之后的模型时会报错,不用担心,在Three.js中有用于专门加载压缩模型的加载器,使用专门的Loader即可在页面正常渲染预览,在本文后续内容中将详细介绍。 blender.png??Blender 是一款跨平台可以在Linux、macOS以及Windows系统下运行的免费开源三维图形图像软件。它体积小巧,便于分发,提供了大量的基础工具,包括 建模、渲染、动画绑定、视频编辑、视觉效果、合成、贴图以及多种类型的模拟等。 实现页面结构先来开发页面整体布局,页面主要由以下几部分构成:#loading-text-intro是Loading加载页,用于在模型加载完成之前展示;nav.header是页面的导航栏;如以下示例图所示,两页内容主体区域分别是section.first和section.second它们的宽都为100vw,高都为100vh充满整个可视区,位于其中的.webgl分别用于加载和渲染两页内容上的3D模型。 divclass='shadow_page' divid="loading-text-intro"pLoading/p/div divclass="content"style="visibility:hidden" navclass="header"/nav sectionclass="sectionfirst" divclass='info'/div canvasid='canvas-container'class='webgl'/canvas /section sectionclass="sectionsecond" divclass="second-container"/div canvasid='canvas-container-details'class='webgl'/canvas /section /div /div layout.png资源引入在代码顶端引入开发页面功能必需的库和资源。style.css是全局样式表;Three.js中的类和方法在本示例中采用按需引入的方式加载;TWEEN是用于生成页面补间动画的库,在本页面中使用它实现场景镜头切换和元素位移动画效果;DRACOLoader用于加载压缩过的模型,是提高页面加载速率和性能的关键;GLTFLoader用于加载.gltf或.glb格式的模型。 import'./style.css'; import{Clock,Scene,LoadingManager,WebGLRenderer,sRGBEncoding,Group,PerspectiveCamera,DirectionalLight,PointLight,MeshPhongMaterial}from'three'; import{TWEEN}from'three/examples/jsm/libs/tween.module.min.js'; import{DRACOLoader}from'three/examples/jsm/loaders/DRACOLoader.js'; import{GLTFLoader}from'three/examples/jsm/loaders/GLTFLoader.js'; 场景初始化场景初始化流程和前两节内容基本一致,需要注意?的是本示例页面有上下两页,每页上面都各自有一个三维场景需要渲染,因此渲染器、和相机??都需要创建两个,Scene只需创建一个即可。当页面产生缩放事件时,所有相机和渲染器也都需要同时更新,不然可能会产生模型变形和错位问题。 constsection=document.getElementsByClassName('section')[0]; letwidth=section.clientWidth; letheight=section.clientHeight; //初始化渲染器1 constrenderer=newWebGLRenderer({ canvas:document.querySelector('#canvas-container'), antialias:true, alpha:true, //提示用户代理怎样的配置更适用于当前的WebGL环境 powerPreference:'high-performance' }); renderer.setSize(width,height); renderer.setPixelRatio(Math.min(window.devicePixelRatio,2)); //定义渲染器是否在渲染每一帧之前自动清除其输出 renderer.autoClear=true; //定义渲染器的输出编码 renderer.outputEncoding=sRGBEncoding; //初始化渲染器2 constrenderer2=newWebGLRenderer({ canvas:document.querySelector('#canvas-container-details'), antialias:false }); renderer2.setPixelRatio(Math.min(window.devicePixelRatio,2)); renderer2.setSize(width,height); renderer2.outputEncoding=sRGBEncoding; //初始化场景 constscene=newScene(); //初始化相机1 constcameraGroup=newGroup(); scene.add(cameraGroup); constcamera=newPerspectiveCamera(35,width/height,1,100) camera.position.set(19,1.54,-.1); cameraGroup.add(camera); //初始化相机2 constcamera2=newPerspectiveCamera(35,width/height,1,100); camera2.position.set(3.2,2.8,3.2); camera2.rotation.set(0,1,0); scene.add(camera2); //页面缩放事件监听 window.addEventListener('resize',()={ letsection=document.getElementsByClassName('section')[0]; camera.aspect=section.clientWidth/section.clientHeight camera.updateProjectionMatrix(); camera2.aspect=section.clientWidth/section.clientHeight; camera2.updateProjectionMatrix(); renderer.setSize(section.clientWidth,section.clientHeight); renderer2.setSize(section.clientWidth,section.clientHeight); renderer.setPixelRatio(Math.min(window.devicePixelRatio,2)); renderer2.setPixelRatio(Math.min(window.devicePixelRatio,2)); }); ?? 知识点渲染编码outputEncodingoutputEncoding属性控制输出渲染编码。 默认情况下,值为THREE.LinearEncoding,这种线性编码的缺点是看起来不够真实。此时可以将值设置为THREE.sRGBEncoding提升渲染输出效果的真实性。此外还有另一个可选属性值为THREE.GammaEncoding,它是一种存储颜色的方法, 这种编码的优点在于它允使用一种表现像亮度的gammaFactor值,根据人眼的敏感度优化明暗值的存储方式。当使用sRGBEncoding时,其实就像使用默认gammaFactor值为2.2的GammaEncoding。加载管理模型??加载会使页面产生一段空白时间,此时可以添加一个Loading加载页展示页面资源加载进度?,缓解等待时间。使用THREE.LoadingManager即可实现加载进度管理。在正式加载模型前,我们先定义好模型加载后的回调方法,在本示例中的回调方法中,使用TWEEN添加了两个补间动画效果:一个是当模型资源加载完成时,隐藏Loading加载页面;另一个是当第一个页面场景加载时,镜头由近到远的镜头拉升动画。 //初始化加载管理器 constloadingManager=newLoadingManager(); loadingManager.onLoad=()={ document.querySelector('.content').style.visibility='visible'; constyPosition={y:0 constftsLoader=document.querySelector('.lds-roller'); constloadingCover=document.getElementById('loading-text-intro'); //隐藏加载页面动画 newTWEEN.Tween(yPosition) .to({y:100},900) .easing(TWEEN.Easing.Quadratic.InOut) .start() .onUpdate(()={loadingCover.style.setProperty('transform',`translate(0,${yPosition.y}%)`)}) .onComplete(function(){ loadingCover.parentNode.removeChild(document.getElementById('loading-text-intro')); TWEEN.remove(this); //第一个页面相机添加入场动画 newTWEEN.Tween( camera.position.set(0,4,2)) .to({x:0,y:2.4,z:5.8},3500) .easing(TWEEN.Easing.Quadratic.InOut) .start() .onComplete(function(){ TWEEN.remove(this) document.querySelector('.header').classList.add('ended') document.querySelector('.description').classList.add('ended') }) ftsLoader.parentNode.removeChild(ftsLoader); window.scroll(0,0) } step_0.png?? 知识点THREE.LoadingManager其功能是处理并跟踪已加载和待处理的数据。如果未手动设置加强管理器,则会为加载器创建和使用默认全局实例加载器管理器。以下是加载器的基本使用方法: //初始化加载器 constmanager=newTHREE.LoadingManager(); //此函数在加载开始时被调用 manager.onStart=function(url,itemsLoaded,itemsTotal){ console.log('Startedloadingfile:'+url+'.\nLoaded'+itemsLoaded+'of'+itemsTotal+'files.' }; //所有的项目加载完成后将调用此函数。默认情况下,该函数是未定义的,除非在构造函数中传入 manager.onLoad=function(){ console.log('Loadingcomplete!'); }; //此方法加载每一个项,加载完成时进行调用 manager.onProgress=function(url,itemsLoaded,itemsTotal){ console.log('Loadingfile:'+url+'.\nLoaded'+itemsLoaded+'of'+itemsTotal+'files.' }; //此方法将在任意项加载错误时,进行调用 manager.onError=function(url){ console.log('Therewasanerrorloading'+url }; constloader=newTHREE.OBJLoader(manager //加载模型 loader.load('file.obj',function(object){}); ?? 知识点补间动画TWEENTween.js是附加在Three.js库中的一个扩充动画库,它可以平滑的修改元素的属性值,使一个对象在一定时间内从一个状态缓动变化到另外一个状态,配合动画函数实现丝滑的动画效果TWEEN.js本质就是一系列缓动函数算法,结合Canvas、Three.js很简单就能实现很多动画效果。本专栏项目示例中将多次使用它实现创意的动画效果。 基本使用: vartween=newTWEEN.Tween({x:1})//position:{x:1} .delay(100)//等待100ms .to({x:200},1000)//1s时间,x到200 .onUpdate(render)//变更期间执行render方法 .onComplete(()={})//动画完成 .onStop(()={})//动画停止 .start();//开启动画 要让动画真正动起来,需要在requestAnimationFrame中调用update方法。 TWEEN.update() 缓动类型: TWEEN.js最强大的地方在于提供了很多常用的缓动动画类型,由api easing()指定。如示例中用到的: tween.easing(TWEEN.Easing.Cubic.InOut); 链式调用: TWEEN.js支持链式调用,如在动画A结束后要执行动画B,可以这样tweenA.chain(tweenB)利用链式调用创建往复来回循环的动画: vartweenA=newTWEEN.Tween(position).to({x:200},1000); vartweenB=newTWEEN.Tween(position).to({x:0},1000); tweenA.chain(tweenB); tweenB.chain(tweenA); tweenA.start(); 加载雕像模型定义好模型加载管理器,就可以将它作为参数传递给模型加载器来进行加载进度管理了。本文示例中使用的模型是.glb格式,因此需要使用对应的模型加载器GLTFLoader来加载模型。在前面步骤中我们为减小模型体积对其进行了压缩,此时直接使用GLTFLoader加载模型会发生报错,需要配合使用DRACOLoader才能正常加载模型。模型??加载完成后,为实现雕像光滑反光的效果,可以将它的材质替换为MeshPhongMaterial来创建光亮的表面。 //使用dracoLoader加载用blender压缩过的模型 constdracoLoader=newDRACOLoader(); dracoLoader.setDecoderPath('/draco/'); dracoLoader.setDecoderConfig({type:'js' constloader=newGLTFLoader(loadingManager); loader.setDRACOLoader(dracoLoader); //模型加载 letoldMaterial; loader.load('/models/statue.glb',function(gltf){ gltf.scene.traverse((obj)={ if(obj.isMesh){ oldMaterial=obj.material; obj.material=newMeshPhongMaterial({ shininess:100 }) } }) scene.add(gltf.scene); oldMaterial.dispose(); renderer.renderLists.dispose(); }); ?? 知识点DRACOLoaderDRACOLoader是使用Draco库压缩的几何图形加载器。Draco是一个开源库,用于压缩和解压缩3D网格和点云。压缩后的几何图形可以明显更小,代价是客户端设备上的额外解码时间。独立的Draco文件具有.drc扩展名,并包含顶点位置、法线、颜色和其他属性。Draco文件不包含材质、纹理、动画或节点层次结构,要使用这些功能,需要将Draco几何图形嵌入到glTF文件中。 可以使用glTF-Pipeline将普通的glTF文件转换为Draco压缩的glTF文件。 当使用带有glTF的Draco时,GLTFLoader将在内部使用DRACOLoader的实例。DRACOLoader依赖于IE11不支持的ES6 Promises,要在IE11中使用加载器,必须包含一个提供Promise替换的polyfill。开源3D建模工具Blender可以生成使用Draco压缩过的模型。使用DRACOLoader时,必须在静态资源目录引入以下文件,并在初始化时设置正确的资源路径。 constdracoLoader=newDRACOLoader(); dracoLoader.setDecoderPath('/draco/'); dracoLoader.setDecoderConfig({type:'js' draco.png添加光源模型加载完成之后,为了模型可以显示在场景中,就需要添加光源??。本示例中添加了两种初始光源:白色?的直射光和偏绿色??的点光源。其中直射光作为照亮场景的基础光源;点光源用来实现鼠标略过模型时明暗变化效果。点光源是一种单点发光,照射所有方向的光源,它的发光效果类似于夜空中发射的照明弹,在点光源的照射下,物体的迎光面会亮一些,背光面会暗一些。 //直射光 constdirectionLight=newDirectionalLight(0xffffff,.8); directionLight.position.set(-100,0,-100); scene.add(directionLight); //点光源 constfillLight=newPointLight(0x88ffee,2.7,4,3); fillLight.position.set(30,3,1.8); scene.add(fillLight); step_1.png使用不同的点光源颜色,可以产生不同的视觉效果,本文中装饰主色调是#03c03c??,因此选取了一种偏绿的点光源,大家实践时可以调整成自己喜欢的颜色??,下图是使用偏蓝色??的点光源效果。 step_1_1.png通过调整两个页面对应的相机参数,调整好不同的模型的初始位置,页面2的模型初始位置如下图。 step_2.png动画效果示例页面虽然只有两个页面,但是涉及到的动画效果种类较多,需要结合TWEEN、页面重绘动画requestAnimationFrame以及CSS来共同实现,可以按以下几个步骤分别实现本示例中的所有动画效果??。 ① 鼠标移动时添加虚拟光标监听鼠标在页面上的移动事件并记录鼠标在页面上的位置信息??,以供在页面重绘动画中通过动态修改点光源的方式实现点光源跟随鼠标效果。可以观察到本页面是隐藏默认鼠标光标的,而是显示个性化样式的圆形光标?。在此步骤中实时修改圆形光标元素的位置,实现自定义鼠标光标。 constcursor={x:0,y:0 document.addEventListener('mousemove',event={ event.preventDefault(); cursor.x=event.clientX/window.innerWidth-.5; cursor.y=event.clientY/window.innerHeight-.5; //鼠标移动时添加虚拟光标 document.querySelector('.cursor').style.cssText=`left:${event.clientX}px;top:${event.clientY}px;`; },false); ② 点光源随鼠标移动动画现在实现当鼠标在模型上方悬浮移动时点光源跟随变化的动画效果。 首先,我使用IntersectionObserver来检测当前页面是页面1还是页面2,以便对两个页面对应的相机和渲染器分别更新。接着,获取通过Clock().getElapsedTime()获取两次重绘的时间间隔。使用时间间隔作为种子更新点光源在X轴和Y轴的位置,效果是当鼠标上下左右移动时光源随鼠标相同方向移动。使用时间间隔作为种子更新相机组在X轴和Z轴的位置,效果是当鼠标左右移动时模型往相反方向移动,当鼠标上下移动是模型变大或缩小。最后,别忘了还需要在页面重绘动画中调用TWEEN.update来更新镜头补间动画,添加的所有TWEEN动画都无法生效。letsecondContainer=false; constob=newIntersectionObserver(payload={ secondContainer=payload[0].intersectionRatio0.05; },{threshold:0.05 ob.observe(document.querySelector('.second')); //页面重绘动画 constclock=newClock() letpreviousTime=0; consttick=()={ constelapsedTime=clock.getElapsedTime(); constdeltaTime=elapsedTime-previousTime; previousTime=elapsedTime; constparallaxY=cursor.y; constparallaxX=cursor.x //点光源位置 fillLight.position.y-=(parallaxY*9+fillLight.position.y-2)*deltaTime; fillLight.position.x+=(parallaxX*8-fillLight.position.x)*2*deltaTime; //相机组位置 cameraGroup.position.z-=(parallaxY/3+cameraGroup.position.z)*2*deltaTime; cameraGroup.position.x+=(parallaxX/3-cameraGroup.position.x)*2*deltaTime; TWEEN.update(); secondContainer?renderer2.render(scene,camera2):renderer.render(scene,camera); requestAnimationFrame(tick); } tick(); step_3.gif鼠标在页面2的模型上方移动,也同样有光线明暗变化的效果。 step_4.gif③ 鼠标光标悬浮到导航栏时虚拟光标动画为了有更好的交互和视觉体验,也添加了一些当鼠标??在页面1导航栏菜单上移动时的动画效果:当鼠标悬浮到导航菜单栏时,鼠标光标放大,在选中的菜单上移动光标菜单会跟随发生部分偏移;当离开菜单时,鼠标光标恢复原状。这种效果是通过动态修改菜单元素的样式cssText属性以及结合CSS实现的。 constbtn=document.querySelectorAll('nav.a'); functionupdate(e){ constspan =this.querySelector('span'); if(e.type==='mouseleave'){ span.style.cssText=''; }else{ const{offsetX:x,offsetY:y}= const{offsetWidth:width,offsetHeight:height}=this; constwalk=20; constxWalk=(x/width)*(walk*2)-walk,yWalk=(y/height)*(walk*2)-walk; span.style.cssText=`transform:translate(${xWalk}px,${yWalk}px);` } } btn.forEach(b=b.addEventListener('mousemove',update)); btn.forEach(b=b.addEventListener('mouseleave',update)); CSS中给菜单栏选项和鼠标光标添加:hover动画效果。 nav.header.a:hover{ cursor:pointer; color:#afafaf; transform:scale(1.1); } nav.header.a:hover~.cursor{ transform:translate(-50%,-50%)scale(5); opacity:0.1; } step_5.gif④ 点击Tab菜单栏时模型旋转动画在页面2上,点击左侧的三个Tab菜单可以产生模型转动的效果,这部分就实现这个功能。页面2上模型??的旋转,其实是通过改变相机2??的位置和角度来实现的。为了旋转动画丝滑不生硬,同样使用TWEEN补间动画来实现此效果。首先定义一个相机旋转动画,当Tab标签被点击时,相机2??根据传入的参数变换到对应的角度和位置,视觉上就产生了模型发生旋转的效果。 functionanimateCamera(position,rotation){ //相机2的位置动画 newTWEEN.Tween(camera2.position) .to(position,1800) .easing(TWEEN.Easing.Quadratic.InOut) .start() .onComplete(function(){ TWEEN.remove(this) }) //相机2的旋转动画 newTWEEN.Tween(camera2.rotation) .to(rotation,1800) .easing(TWEEN.Easing.Quadratic.InOut) .start() .onComplete(function(){ TWEEN.remove(this) }) } //点击第一Tab菜单 document.getElementById('one').addEventListener('click',()={ document.getElementById('one').classList.add('active') document.getElementById('three').classList.remove('active') document.getElementById('two').classList.remove('active') document.getElementById('content').innerHTML='昨夜西风凋碧树。独上高楼,望尽天涯路。' animateCamera({x:3.2,y:2.8,z:3.2},{y:1 }); //点击第二个Tab菜单和第三个菜单的交互逻辑与第一个类似 document.getElementById('two').addEventListener('click',()={/*...*/ document.getElementById('three').addEventListener('click',()={/*...*/ step_6.gif?? 知识点THREE.Clock本文中使用Clock提供的getElapsedTime方法来获取页面重绘两帧之间的时间间隔。Clock本质上就是对Date进行封装,提供了一些方法和属性,在Three.js使用过程中涉及到时间相关的方法时不用对Date进行封装,直接使用Clock提供的方法即可。在骨骼动画、变形动画、粒子动画等功能的开发中常常需要调用Clock的方法。 两个常用方法: getElapsedTime():获取自时钟启动后的秒数,同时将.oldTime设置为当前时间。 如果.autoStart设置为true且时钟并未运行,则该方法同时启动时钟。getDelta():获取自.oldTime设置后到当前的秒数。 同时将.oldTime设置为当前时间。 如果.autoStart设置为true且时钟并未运行,则该方法同时启动时钟。?? 知识点Intersection Observer本文中使用Intersection Observer来辨识当前处于哪个页面以更新相机位置。IntersectionObserver接口提供了一种异步观察目标元素与其祖先元素或顶级文档视窗viewport交叉状态的方法。可以使用它来检测元素在页面上的可视状态或者两个元素之间的相对可视状态。应用这一特性可以用它来实现页面滚动加载、图片懒加载等功能。??其他使用方法详情可访问文章末尾提供的参考链接。 页面装饰为了使页面更具艺术气息,可以添加一些额外的页面装饰。本示例中DRAGONIR和THREE.JS ODESSEY字样使用了免费字体abduction.ttf,可以在CSS中使用如下方式定义,并在需要的选择器当中使用font-family引用。 @font-face{ font-family:abduction; src:url('../static/fonts/abduction.ttf'); } step_7.png最后再为页面添加一些百万诗句文案,升华一下页面氛围,“光与影之诗”的页面主题就瞬间跃然屏上?。文案引用的是王国维先生的人生的三个境界,细细品之,Three.js 进阶之旅、前端学习之路何尝不是这三种境界呢??? "昨夜西风凋碧树。独上高楼,望尽天涯路。"此第一境也。"衣带渐宽终不悔,为伊消得人憔悴。"此第二境也。"众里寻他千百度,蓦然回首,那人却在灯火阑珊处。"此第三境也。 源码地址:https://github.com/dragonir/threejs-odessey 总结本文中主要包含的知识点包括: 使用Blender压缩模型;使用LoadingManager管理模型资源加载进度;使用DRACOLoader和模型对应格式加载器加载压缩过的模型;修改outputEncoding设置渲染器的编码输出;使用TWEEN实现位移动画和镜头补间动画;使用点光源照亮模型;隐藏默认鼠标光标创建虚拟光标;点光源随鼠标移动动画;鼠标光标悬浮到导航栏时虚拟光标动画;点击Tab菜单栏时模型旋转动画;THREE.Clock基本使用方法;Intersection Observer监听页面元素可见性;使用@font-face添加字体装饰页面等。想了解其他前端知识或其他未在本文中详细描述的Web 3D开发技术相关知识,可阅读我往期的文章。如果有疑问可以在评论中留言,如果觉得文章对你有帮助,不要忘了一键三连哦 ??。 附录[1].?? Three.js 打造缤纷夏日3D梦中情岛[2].?? Three.js 实现炫酷的赛博朋克风格3D数字地球大屏[3].?? Three.js 实现2022冬奥主题3D趣味页面,含冰墩墩[4].?? Three.js 实现3D开放世界小游戏:阿狸的多元宇宙[5].?? 掘金1000粉!使用Three.js实现一个创意纪念页面...【Three.js 进阶之旅】系列专栏访问 ??更多往期【3D】专栏访问 ??更多往期【前端】专栏访问 ??参考[1]. https://www.blender.org[2]. https://sketchfab.com[3]. https://threejs.org[4]. https://threejs.org/docs/#examples/zh/loaders/DRACOLoader[5]. https://developer.mozilla.org/zh-CN/docs/Web/API/IntersectionObserver 阅读原文

上一篇:2022-09-15_送月亮的人,这支中秋广告好浪漫! 下一篇:2021-06-24_以裸眼3D为例 , 未来户外数字媒体形式将如何演变

TAG标签:

20
网站开发网络凭借多年的网站建设经验,坚持以“帮助中小企业实现网络营销化”为宗旨,累计为4000多家客户提供品质建站服务,得到了客户的一致好评。如果您有网站建设网站改版域名注册主机空间手机网站建设网站备案等方面的需求...
请立即点击咨询我们或拨打咨询热线:13245491521 13245491521 ,我们会详细为你一一解答你心中的疑难。
项目经理在线

相关阅读 更多>>

猜您喜欢更多>>

我们已经准备好了,你呢?
2022我们与您携手共赢,为您的企业营销保驾护航!

不达标就退款

高性价比建站

免费网站代备案

1对1原创设计服务

7×24小时售后支持

 

全国免费咨询:

13245491521

业务咨询:13245491521 / 13245491521

节假值班:13245491521()

联系地址:

Copyright © 2019-2025      ICP备案:沪ICP备19027192号-6 法律顾问:律师XXX支持

在线
客服

技术在线服务时间:9:00-20:00

在网站开发,您对接的直接是技术员,而非客服传话!

电话
咨询

13245491521
7*24小时客服热线

13245491521
项目经理手机

微信
咨询

加微信获取报价