.. 2024年了,前端人是时候给予页面一点 Hero Section 魔法了!!! (Three.js)(稀土掘金技术社区2024年10月05日文章)_众股360

您的位置 : 首页 > 公众号 > 稀土掘金技术社区

2024年了,前端人是时候给予页面一点 Hero Section 魔法了!!! (Three.js)(稀土掘金技术社区2024年10月05日文章)

分享到:
作者:稀土掘金技术社区 | 更新时间:2024-10-08 09:56:12

点击关注公众号,“技术干货”及时达!0.前置条件本篇文章为 Threejs微进阶课程,需要您提前了解 Threejs。但您不用担心,此次课程完全不会涉及到 shader。可放心食用1.前言 什么是 Hero Section?Hero Sec...

A股板块轮动加剧,跨年大妖来袭,这几只票主力已明显介入!微信搜索关注【研讯小组】公众号(可长按复制),回复666,领取代码!

点击关注公众号,“技术干货”及时达!

0.前置条件

本篇文章为 Threejs微进阶课程,需要您提前了解 Threejs。但您不用担心,此次课程完全不会涉及到 shader。可放心食用

1.前言 什么是 Hero Section?

Hero Section 是网页设计中的一个术语,通常指页面顶部的一个大型横幅区域。但对于开发人员而言,这个概念可以更直观地理解为用户在访问网站的瞬间所感受到的视觉冲击,或者促使用户停留在该网站的关键原因因素。让我们来看看今天的 Hero Section,看看它是否引起您的兴趣。

(结尾处会附赠下图源码)


在线预览: https://hero-profile-five.vercel.app/

2.环境准备

在 Three.js 中,三要素是场景 (Scene)相机 (Camera)渲染器 (Renderer) 。这些是 Three.js 中最基本的构建块,用于创建 3D 场景并将其渲染到屏幕上。那么让我们先来搭建一个可以显示出三维场景的基础框架。

import * as THREE from 'three'; import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; const device = { width: window.innerWidth, height: window.innerHeight, pixelRatio: window.devicePixelRatio }; export default class Three { constructor(canvas) { this.canvas = canvas; this.scene = new THREE.Scene(); this.camera = new THREE.PerspectiveCamera( 75, device.width / device.height, 0.1, 100 ); this.camera.position.set(0, 0, 2); this.scene.add(this.camera); this.renderer = new THREE.WebGLRenderer({ canvas: this.canvas, alpha: true, antialias: true, preserveDrawingBuffer: true }); this.renderer.setSize(device.width, device.height); this.renderer.setPixelRatio(Math.min(device.pixelRatio, 2)); this.controls = new OrbitControls(this.camera, this.canvas); this.clock = new THREE.Clock(); this.setLights(); this.setGeometry(); this.render(); this.setResize(); } setLights() { this.ambientLight = new THREE.AmbientLight(new THREE.Color(1, 1, 1, 1)); this.scene.add(this.ambientLight); } setGeometry() { this.planeGeometry = new THREE.PlaneGeometry(1, 1, 128, 128); this.planeMaterial = new THREE.MeshBasicMaterial({ color: 0x00_ff_00 }); this.planeMesh = new THREE.Mesh(this.planeGeometry, this.planeMaterial); this.scene.add(this.planeMesh); } render() { const elapsedTime = this.clock.getElapsedTime(); this.planeMesh.rotation.x = 0.2 * elapsedTime; this.planeMesh.rotation.y = 0.1 * elapsedTime; this.renderer.render(this.scene, this.camera); requestAnimationFrame(this.render.bind(this)); } setResize() { window.addEventListener('resize', this.onResize.bind(this)); } onResize() { device.width = window.innerWidth; device.height = window.innerHeight; this.camera.aspect = device.width / device.height; this.camera.updateProjectionMatrix(); this.renderer.setSize(device.width, device.height); this.renderer.setPixelRatio(Math.min(device.pixelRatio, 2)); } }

此时运行代码,呈现出来的界面效果为


看起来太朴素了。那么,现在让我们把场景变得高大上起来吧。我们可以去一些拥有免费数字资产的网站,看看有哪些不错的模型能够添加到当前的 3D 场景当中。在这里,我前往的是Free3D以及Meshy这两个网站,去寻找喜爱的白模(未上色的雕塑人物造型的胚模)。

这里我选择的是自己在 3D 生成网站中生成的狗头


然后我们将模型导入场景替换掉正常显示的绿色平面

//构造器中添加 模型加载器 constructor(canvas) { //... this.gltfLoader = new GLTFLoader(); //... } //在 class 中创建一个新的方法 setObject() { this.gltfLoader.load('goutou.glb', (gltf) => { this.object = gltf.scene; this.scene.add(this.object); }); } //最后别忘了在构造器中加载此类方法、 constructor(canvas) { this.setLights(); this.setObject(); // this.setGeometry(); 已删除 this.render(); this.setResize(); }

加载进入场景是这样的


还记得我最开始提到的三要素吗?那就是场景 (Scene)相机 (Camera)渲染器 (Renderer) ,为什么灯光不是其中的 “四要素” 之一呢?在这些要素里唯独没有提到灯光 (Light) 。虽然灯光也是非常重要的元素,但它并不被视为三要素之一。原因在于在 Three.js 中,灯光并不是唯一能赋予物体可见性的元素。在 Three.js 里,环境贴图 (Environment Map) 和某些材质类型(例如MeshBasicMaterial)同样能够让物体可见,无需使用灯光。

而这次,我们将采用一些特殊的 环境贴图 (Environment Map) 来为这个平平无奇的模型增添魔法。

3.开始亮起来了

3.1 PMREMGenerator

THREE.PMREMGenerator 是用于处理环境贴图、生成预计算辐射环境映射的工具。它在 PBR 渲染中能大大提升反射效果的质量。其主要用法是将环境贴图(如球形投影贴图或立方体贴图)处理成 预计算的辐射环境贴图,用于 PBR 材质(如 MeshStandardMaterialMeshPhysicalMaterial)的环境反射和光照。

简单来看看您可以如何去使用它:

1.初始化 PMREMGenerator:需要先创建一个渲染器,然后用它来实例化 PMREMGenerator

const pmremGenerator = new THREE.PMREMGenerator(renderer); pmremGenerator.compileEquirectangularShader(); // 可选:预编译着色器

2.加载环境贴图:通过 TextureLoaderCubeTextureLoader 来加载环境贴图。

const textureLoader = new THREE.TextureLoader(); textureLoader.load('path/to/envmap.jpg', (texture) => { // 生成 PMREM 环境贴图 const envMap = pmremGenerator.fromEquirectangular(texture).texture; });

3.应用环境贴图到材质:将生成的 envMap 应用到 PBR 材质的 envMap 属性上。

const material = new THREE.MeshStandardMaterial({ color: 0xffffff, metalness: 1, roughness: 0.2, envMap: envMap // 应用环境贴图 });

4.清理资源:在处理完毕后,可以调用 pmremGenerator.dispose() 来释放内存。

pmremGenerator.dispose();

这里我选用一张纯白的图片作为环境贴图 ( equirectangular texture),那么实际作用到您刚刚写的代码就是这样

white.png

setObject() { this.pmremGenerator = new THREE.PMREMGenerator(this.renderer); this.pmremGenerator.compileEquirectangularShader(); this.envMap = new THREE.TextureLoader().load( './img/white.png', (texture) => { this.envMap = this.pmremGenerator.fromEquirectangular(texture).texture; this.envMap.encoding = THREE.SRGBColorSpace; this.dogMaterial = new THREE.MeshStandardMaterial({ //注意是 PBR 材质哦 color: '#a7c6b1', metalness: 1, roughness: 0.28 }); this.dogMaterial.envMap = this.envMap; this.gltfLoader.load('./goutou.glb', (gltf) => { const { scene } = gltf; this.dog = scene.children[0]; this.dog.geometry.center(); this.dog.material = this.dogMaterial; this.scene.add(this.dog); }); this.pmremGenerator.dispose(); } ); }

在此时,模型已经被环境所照亮,这样我们也就掌握了PMREMGenerator的基本用法。但看起来距离我们的目标还存在着很大的差距。


先别着急!让我们尝试换一张环境贴图(equirectangular texture) ,这是一张由 AI 生成的 PBR 环境贴图。


此时的狗子就变成。。。。青铜狗子?


虽然颜色有点难以言喻。但也算是初见成效了。那么怎么去强化这种光照效果呢?

这里有这两种方法

  • 第一,根据相应的环境光照来调节狗子本身的材质颜色,让漫反射能够发挥更大的作用。

this.dogMaterial = new THREE.MeshStandardMaterial({ //注意是 PBR 材质哦 color: '#a7c6b1', //因为是绿色环境光,这里要偏绿且偏白 (白色反射光线更多哦) metalness: 1, roughness: 0.28 });

  • 第二,则是后处理效果

3.2 Postprocessing

Postprocessing(后期处理)是在 3D 渲染完成后,对生成的图像进行进一步处理,以添加各种视觉效果的过程。在此我们选用

UnrealBloomPass 来实现辉光效果(bloom effect)进而让画面更加的明亮。

setPost() { this.renderScene = new RenderPass(this.scene, this.camera); this.bloomPass = new UnrealBloomPass( new THREE.Vector2(device.width, device.height), 0.06, 0.4, 0.85 ); this.outputPass = new OutputPass(); this.composer = new EffectComposer(this.renderer); this.composer.addPass(this.renderScene); this.composer.addPass(this.bloomPass); this.composer.addPass(this.outputPass); }

不要忘记改用 this.composer 渲染场景 以及 resize 时需要对 canvas 进行重新渲染

render() { const elapsedTime = this.clock.getElapsedTime(); this.composer.render(this.scene, this.camera); // 改用我来输出场景哦! requestAnimationFrame(this.render.bind(this)); } onResize() { device.width = window.innerWidth; device.height = window.innerHeight; this.camera.aspect = device.width / device.height; this.camera.updateProjectionMatrix(); this.renderer.setSize(device.width, device.height); this.renderer.setPixelRatio(Math.min(device.pixelRatio, 2)); this.composer.setSize(device.width, device.height);// 别忘了我 this.composer.setPixelRatio(Math.min(device.pixelRatio, 2)); }

此时我们的狗子模型已经浑身散漫着健康的颜色。

(Tips: 这里给大家一个展示 Euler 如何工作的网站: [Euler]

4. 是旋转!我加了旋转!envMapRotation (重要)

目前还剩下最后一个问题,对吧?原作中的光线是流动的,但目前光线仅仅照射在模型上。我们要如何让光线流动起来呢?

答案是:旋转。

旋转什么呢?我们当然可以同时旋转物体和相机,这会给人一种环境在旋转的印象,并且这在 threejs 的 r162 版本之前也是最佳选择。

然而,在 162 版本更新了以下细则:

envMapRotation用于所有支持envMap的材质。
新特性的类型为Euler。现在可以旋转等矩形、立方体贴图和cuveUV(PMREM)格式的环境贴图。

没错,我们可以像操作万向轮一样操作环境贴图。让我们来试一试。

只需要将原先 render 中的代码修改为:

render() { const elapsedTime = this.clock.getElapsedTime(); if (this.dog) { // 更新时间 this.dog.material.envMapRotation = new THREE.Euler( 0 + elapsedTime * 3, 0 + elapsedTime * 2, 0 ); this.dog.material.needsUpdate = true; } this.renderer.render(this.scene, this.camera); this.composer.render(this.scene, this.camera); requestAnimationFrame(this.render.bind(this)); }

此时,我们就得到了流光狗子。


(这里推荐一个可视化 Euler 的网站 https://quaternions.online/))

5.最后再加上一些细节吧

最后我们可以为整体添加一个有趣的背景以及一些鼠标阻尼小互动。就完成了最终的页面效果

setBackground() { const textureLoader = new THREE.TextureLoader(); const starSprite = textureLoader.load('./img/circle.png'); const stars = getStarfield({ numStars: 4500, sprite: starSprite }); this.scene.add(stars); } setMouseMoveEvent() { document.addEventListener('mousemove', function (event) { mouse.x = (event.clientX / window.innerWidth) * 2 - 1; mouse.y = -(event.clientY / window.innerHeight) * 2 + 1; }); }

当然 我最近也在利用 figma 的 原型 AI 和莱昂纳多AI等可以帮助我产出原型图的AI 配合 spline & Claude 来帮助我构建一个较为完善的网站。如果我将这个工作流完善到一定程度。我会出一篇文章分享给大家。

6.源码

最后附上最开始介绍 Hero Section 的源码地址。

https://github.com/hexianWeb/hero-profile.git

看到这里可能您更多会想,通篇示例的效果与源码效果不能说一模一样,只能说是大相径庭。为什么不直接通过源码中作为实例来演示整个项目呢?我想说的是 3D 场景其实不同于普通的 CSS 动效。很多时候一个看起来简单的场景也许会消耗您大量的时间去调试,所以一开始效果不尽如人意是非常正常的。一个好的场景需要多方面的打磨。更多的是反复的调试以及对各种细节的优化。

7.即将到来

我即将开启一个全新的专栏 ——Hero Section,在这个专栏里,我会向您介绍一些或许能对您有所帮助的3D 最小可实例。希望能够为前端开发者在 3D 前端设计方面提供一些启发和参考,也期待在即将到来的专栏中为读者带来更多关于 3D 实例的有趣内容。如果您认为这篇文章可能对您有用的话,可以点赞或者收藏这篇文章,我想系统会在我后续更新的时候向您推送相关内容的。

如果您对 Threejs 很感兴趣,也欢迎加入 ice 图形学社区.这里是国内Web图形学最全的知识库,致力于打造一个全新的图形学生态体系! 您可以在认证达人里找到我这个 Threejs 爱好者和其他大佬。

此外,如果您很喜欢 Threejs 又在烦恼其原生开发的繁琐,那么我诚邀您尝试 TresjsTvTjs, 他们都是基于 Vue 的 Threejs 框架。TvTjs 也为您提供了大量的可使用案例,让您的开发变得轻松。


题外话

我深信 Web3D 将会成为未来前端设计与发展的趋势之一,这也是我想要创建整个专栏的初衷。

这一信念的形成不光源于当前市面上普遍存在的情况:

  • 单一的交互方式极大地限制了用户体验的丰富性与多样性。用户在与各种前端界面交互时,往往只能进行一些常规的操作,缺乏更为新颖、多元且富有创意的交互模式。

  • 许多设备的硬件配置已经远超实际需求如果仅仅只是为了玩0神未免有点可惜;

还因为 WebGPU 对 Safari 的适配能够在一定程度上缓解以往 Web3D 从业者在众多苹果设备上一直疲于进行性能优化的现状。降低一些门槛让玩家能少掉两根头发。懂得都懂。

总之,我是何贤。期待和您一起进步!

点击关注公众号,“技术干货”及时达!

A股板块轮动加剧,跨年大妖来袭,这几只票主力已明显介入!微信搜索关注【研讯小组】公众号(可长按复制),回复666,领取代码!

本站内容转载请注明来源并提供链接,数据来自互联网,仅供参考。如发现侵权行为,请联系我们删除涉嫌侵权内容。

展开

相关文章

更多>>

反馈与咨询

关于本站 反馈中心 版权声明 网站地图

  版权投诉请发邮件到1191009458#qq.com(把#改成@),我们会尽快处理

  Copyright©2023-2024众股360(www.zgu360.com).AllReserved|备案号:湘ICP备2023009521号-3

  本站资源均收集整理于互联网,其著作权归原作者所有,如有侵犯你的版权,请来信告知,我们将及时下架删除相应资源

Copyright © 2024-2024 EYOUCMS. 易优CMS 版权所有 Powered by EyouCms