用Three.js打造酷炫3D个人网站(含源码)

这篇具有很好参考价值的文章主要介绍了用Three.js打造酷炫3D个人网站(含源码)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

引言

个人网站是程序员的第二张简历。如果你有酷炫的个人网页,面试官对你的好感度会蹭蹭蹭往上涨。

在疫情隔离期间,我用Three.jsAmmo.js制作了一个可交互的3D个人网页。

在线预览地址: www.ryan-floyd.com/

Three.js的3D世界

当我在Google Experiments闲逛时,我发现非常多的作品都是用three.js写的。

three.js是一个让3D网页应用开发变得简单的库。它诞生于2010年,作者是Ricardo Cabello (Mr.doob),,在github上有超过1300多的贡献者,在所有仓库中star数排行第38。

当看到Google Experiments上那些酷炫的3D效果后,我决定开始学习three.js

Three.js的工作机制

(3D应用的组件结构,图片来自discoverthreejs.com)

Three.js使得在浏览器展示3D图像变得容易,它的底层是基于WebGL,它使浏览器能借助系统显卡在canvas中绘制3D画面。

WebGL自身只能绘制点(points)、线(lines)和三角形(triangles),而Three.jsWebGL进行了封装,使我们能够非常方便地创建 物体(objects), 纹理(textures), 进行 3D 计算等操作。

使用Three.js,我们将所有物体(objects)添加到场景(scene)中,然后将需要渲染的数据传递给渲染器(renderer),渲染器负责将场景在 <canvas> 画布上绘制出来。

(Three.js 应用架构,图片来自threejsfundamentals.org)

对于一个 Three.js 应用,最核心的就是场景(scene object),上面是一张场景图(scene graph)。

在一个3D引擎中,场景图是一个层级结构的树状图,树中的每一个节点代表空间中的一部分。这种结构有点像DOM树,但Three.js的场景(scene)更像虚拟DOM,它只更新和渲染场景中有变化的部分。而这一切的基础,是 Three.js 的 WebGLRenderer 类,它把我们的代码转换成 GPU 中的数据,浏览器再将这些数据渲染出来。

场景中的物体,也叫Mesh。在 Three.js 的世界中,Mesh 是由 几何体Geometry(决定物体形状) + 材质Material(决定物体外观)构成。

场景中的另一个重要元素,就是相机camera,它决定了场景中 哪些部分以怎样的视觉效果 被绘制在canvas画布上。

然后是动画,为了实现动画,渲染器(renderer)通常使用requestAnimationFrame()方法,以每秒60次的频率将场景更新绘制在canvas上。requestAnimationFrame()方法的原理和使用可以参考MDN。

下面这个例子来自Three.js官方文档,创建了一个旋转的 3D 立方体。

<html>
  <head>
    <title>My first three.js app</title>
    <style>
      body {
        margin: 0;
      }
      canvas {
        display: block;
      }
    </style>
  </head>
  <body>
    <script src="https://unpkg.com/three@0.119.0/build/three.js"></script>
    <script>
      //创建场景和相机
      var scene = new THREE.Scene();
      var camera = new THREE.PerspectiveCamera(
        75,
        window.innerWidth / window.innerHeight,
        0.1,
        1000
      );

      //创建渲染器,设置尺寸为窗口尺寸,并将渲染后的元素添加到body
      var renderer = new THREE.WebGLRenderer();
      renderer.setSize(window.innerWidth, window.innerHeight);
      document.body.appendChild(renderer.domElement);

      //创建一个Mesh(绿色的3D立方体),并添加到场景中
      var geometry = new THREE.BoxGeometry();
      var material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
      var cube = new THREE.Mesh(geometry, material);
      scene.add(cube);

      //设置照相机的位置
      camera.position.z = 5;

      //浏览器每次渲染的时候更新立方体的旋转角度
      var animate = function () {
        requestAnimationFrame(animate);

        cube.rotation.x += 0.01;
        cube.rotation.y += 0.01;

        renderer.render(scene, camera);
      };

      animate();
    </script>
  </body>
</html>
复制代码

效果如下:

转存失败重新上传取消

Ammo.js物理引擎

Ammo.js 是将 Bullet物理引擎 直接移植到JavaScript的产物(Bullet Physics是一个开源的物理模拟引擎)。我对物理引擎底层的工作原理理解得不太深入,简而言之,物理引擎根据你传入的参数(比如重力),创建循环,在每次循环中更新状态,从而模拟出自然的物理运动和碰撞等效果。

循环中的物体(通常也是刚体),具有力、质量、惯性、摩擦力等物理属性。每次循环,通过不断检查所有物体的位置、状态和运动来检测碰撞和交互。如果发生交互,对象位置将根据经过的时间和对象的物理属性进行更新。下面是我代码中的一个片段,显示了如何创建物理引擎循环以及如何将它添加到Three.js的sphere球体中。

//引入库
import * as THREE from "three";
import * as Ammo from "./builds/ammo";
import {scene} from "./resources/world";

//初始化 Ammo.js 物理引擎
Ammo().then((Ammo) => {

    // 创建物理世界
    function createPhysicsWorld() {

        //完全碰撞检测算法
        let collisionConfiguration = new Ammo.btDefaultCollisionConfiguration();

        // 重叠对/碰撞的调度计算
        let dispatcher = new Ammo.btCollisionDispatcher(collisionConfiguration);

        // 所有可能碰撞对的宽相位碰撞检测列表
        let overlappingPairCache = new Ammo.btDbvtBroadphase();

        // 使物体正确地交互,考虑重力、力、碰撞等
        let constraintSolver = new Ammo.btSequentialImpulseConstraintSolver();

        // 根据这些参数创建物理世界。 参考bullet physics文档
        let physicsWorld = new Ammo.btDiscreteDynamicsWorld(
            dispatcher,
            overlappingPairCache,
            constraintSolver,
            collisionConfiguration
        );

        // 添加重力
        physicsWorld.setGravity(new Ammo.btVector3(0, -9.8, 0));
    }

    //创建球体
    function createBall(){
        //球体参数
        let pos = {x: 0, y: 0, z: 0};
        let radius = 2;
        let quat = {x: 0, y: 0, z: 0, w: 1};
        let mass = 3;

        //three.js相关代码

        //创建球体并添加到场景中
        let ball = new THREE.Mesh(new THREE.SphereBufferGeometry(radius), new THREE.MeshStandardMaterial({color: 0xffffff}));
        ball.position.set(pos.x, pos.y, pos.z);
        scene.add(ball);

        //Ammo.js相关代码

        //设置位置和旋转
        let transform = new Ammo.btTransform();
        transform.setOrigin(new Ammo.btVector3(pos.x, pos.y, pos.z));
        transform.setRotation(
            new Ammo.btQuaternion(quat.x, quat.y, quat.z, quat.w)
        );

        //设置物体运动
        let motionState = new Ammo.btDefaultMotionState(transform);

        //设置碰撞边界框
        let collisionShape = new Ammo.btSphereShape(radius);
        collisionShape.setMargin(0.05);

        //设置惯性
        let localInertia = new Ammo.btVector3(0, 0, 0);
        collisionShape.calculateLocalInertia(mass, localInertia);

        //生成创建刚体(物体)的结构信息
        let rigidBodyStructure = new Ammo.btRigidBodyConstructionInfo(
            mass,
            motionState,
            collisionShape,
            localInertia
        );

        //基于上面的结构信息创建物体
        let body = new Ammo.btRigidBody(rigidBodyStructure);

        //当物体运动时,为其添加摩擦力
        body.setFriction(10);
        body.setRollingFriction(10);

        // 将物体添加到物理世界,这样Ammo.js引擎才能不断更新物体的状态
        physicsWorld.addRigidBody(body);
    }

    createPhysicsWorld();
    createBall()
}
复制代码

运动和交互

在Ammo.js模拟的物理世界中,交互是基于属性和力计算的。

每个对象有一个边界框(bounding box)属性,物理引擎会根据这个边界框来检测物体的位置。

在每个动画循环中检查所有对象的边界框后,如果任意两个对象的边界框位于同一位置,引擎将记录为“碰撞”,并相应地更新对象。 对于刚体来说,这意味着阻止两个物体处于同一位置。

下面是我的代码片段,显示了渲染循环和世界物理是如何更新的。

//渲染框架
function renderFrame() {

    //记录上一次渲染的时间
    let deltaTime = clock.getDelta();

    //基于用户输入,计算球会受到的力和产生的速度
    moveBall();

    //根据时间更新物理世界状态
    updatePhysics(deltaTime);

    //进行渲染
    renderer.render(scene, camera);

    // 循环
    requestAnimationFrame(renderFrame);
}

//更新物理世界状态的方法定义
function updatePhysics(deltaTime) {

    physicsWorld.stepSimulation(deltaTime, 10);

    //遍历“刚体”列表,并更新物理世界中的所有刚体状态
    for (let i = 0; i < rigidBodies.length; i++) {

        //变量定义:three.js需要的meshObject,和ammo.js需要的ammoObject
        let meshObject = rigidBodies[i];
        let ammoObject = meshObject.userData.physicsBody;

        //获取物体当前运动状态
        let objectMotion = ammoObject.getMotionState();

        //如果物体正在移动,则获取物体的当前位置和旋转信息
        if (objectMotion) {
            objectMotion.getWorldTransform(transform);
            let mPosition = transform.getOrigin();
            let mQuaternion = transform.getRotation();

            // 更新物体的位置和旋转状态
            meshObject.position.set(mPosition.x(), mPosition.y(), mPosition.z());
            meshObject.quaternion.set(mQuaternion.x(), mQuaternion.y(), mQuaternion.z(), mQuaternion.w());
        }
    }
}
复制代码

用户输入

我们希望用户在桌面和触摸屏移动设备上都能够在应用中移动球体。

对于键盘事件,当按下箭头键时,通过监听“keydown”和“keyup”事件对球体添加相应方向的力。

对于触摸屏,在屏幕上创建了一个操纵杆控制器。然后,我们将“touchstart”、“touchmove”和“touchend”事件监听器添加到用于控制的div元素(控制器)中。

控制器会跟踪用户手指移动的起始、当前和结束坐标,然后在每次渲染时相应地更新球的受力。

下面只是控制器代码的一个片段,展示了一些大致的概念。有关完整代码,请从本文底部的源代码地址获取。文章来源地址https://www.toymoban.com/news/detail-418512.html

// 在坐标平面上保持对当前球体运动的跟踪
let moveDirection = { left: 0, right: 0, forward: 0, back: 0 };

//控制器div在屏幕上的位置坐标
let coordinates = { x: 0, y: 0 };

//保存触摸事件的起始坐标的变量
let dragStart = null;

//创建控制器div元素
const stick = document.createElement("div");

//监听用户触摸点的移动
function handleMove(event) {
    //没有移动,返回
    if (dragStart === null) return;

    //有移动,获取新的触摸点的x、y坐标
    if (event.changedTouches) {
        event.clientX = event.changedTouches[0].clientX;
        event.clientY = event.changedTouches[0].clientY;
    }

    //根据触摸点的移动,计算出控制器div的实时坐标
    const xDiff = event.clientX - dragStart.x;
    const yDiff = event.clientY - dragStart.y;
    const angle = Math.atan2(yDiff, xDiff);
    const distance = Math.min(maxDiff, Math.hypot(xDiff, yDiff));
    const xNew = distance * Math.cos(angle);
    const yNew = distance * Math.sin(angle);
    coordinates = { x: xNew, y: yNew };

    //根据实时坐标更新样式
    stick.style.transform = `translate3d(${xNew}px, ${yNew}px, 0px)`;

    //根据坐标计算出球的运动方向
    touchEvent(coordinates);
}

//根据用户的触摸点移动坐标计算出球的运动方向
function touchEvent(coordinates) {

    // 向右运动
    if (coordinates.x > 30) {
        moveDirection.right = 1;
        moveDirection.left = 0;
    // 向左运动
    } else if (coordinates.x < -30) {
        moveDirection.left = 1;
        moveDirection.right = 0;
    } else {
        moveDirection.right = 0;
        moveDirection.left = 0;
    }

    //向前运动
    if (coordinates.y > 30) {
        moveDirection.back = 1;
        moveDirection.forward = 0;
    //向后运动
    } else if (coordinates.y < -30) {
        moveDirection.forward = 1;
        moveDirection.back = 0;
    } else {
        moveDirection.forward = 0;
        moveDirection.back = 0;
    }
}

到了这里,关于用Three.js打造酷炫3D个人网站(含源码)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 基于SSM+vue框架的个人博客网站源码和论文

    基于SSM+vue框架的个人博客网站源码和论文061  开发工具:idea   数据库mysql5.7+  数据库链接工具:navcat,小海豚等   技术:ssm (设计)研究背景与意义 关于博客的未来:在创办了博客中国(blogchina)、被誉为“博客教父”的方兴东接受了记者的专访。他认为,博客这一事物在中

    2024年02月11日
    浏览(47)
  • 2022最新个人发卡网站源码+支持傻瓜式安装/全开源的

    2022最新个人发卡网站源码+支持傻瓜式安装/全开源的,这发卡网站源码简约大气,看着还是挺不错的。 安装教程: 1.将源码上传至服务器根目录 2.将源码进行解压 3.域名/install安装程序 4.登录后台地址域名/admin 5.后台admin 123456 whgwu.lanzouw.com/ieomn06gt5if

    2024年02月12日
    浏览(100)
  • 大学生PHP个人博客网站源码 简单个人动态网站设计模板 PHP毕业设计成品 学生PHP MYSQL日志管理系统网页

    PHP MYSQL个人博客网站作品使用php+mysql开发,系统编码简单,大学生PHP毕业设计水平。系统随处可见增删改查等基本操作,有批量删除之功能,涉及的知识点比较全面。 数据库共6张数据表,表之间有关联,设计合理;系统具有管理员和会员两种用户角色,管理员(即日志的所

    2024年02月12日
    浏览(69)
  • 个人网站制作 Part 3 用JS添加高级交互(表单验证、动态内容更新) | Web开发项目

    欢迎回到基础Web开发练手项目系列! 在前两篇博文中,我们创建了个人网站的基本结构、样式、导航栏、项目展示、联系信息、表单交互和动画效果。 本篇将继续丰富你的网站,为其添加更高级的交互性功能,使用JavaScript进行操作。 🔨表单验证 🔧步骤 1: 添加JavaScript文件

    2024年02月01日
    浏览(44)
  • 基于HTML5的个人网页的网站设计与实现 毕业设计-附源码031623

    随着互联网的不断发展和中国网络人口的日益增长,建立个人网站,不但可以刚好的展示自己,而且可以提高自己在计算机应用方面的能力。故本次作业,我选择制作个人网页的网站。个人在设计时考虑的多为个人的兴趣喜好,而不注重商业的展示。内容以反映个人为中心,

    2024年02月05日
    浏览(52)
  • Three.js--》穿越虚拟门槛打造的3D登录界面

    今天简单实现一个three.js的小Demo,加强自己对three知识的掌握与学习,只有在项目中才能灵活将所学知识运用起来,话不多说直接开始。 目录 项目搭建 初始化three代码 添加背景与地球 星星动画效果 星云动画效果 实现登录框效果 项目搭建 本案例还是借助框架书写three项目,

    2024年04月23日
    浏览(28)
  • 一篇从零开始、步骤完整的网站搭建教程(全篇7000字、102张截图说明,力求每一个人都能看懂,附源码)

    从今年八月开始到现在自己也是从0开始做了有两个网站: 这中间也经常有不了解的地方需要去查。其实网上的资料也不少 但可能相对比较零散,需要反复的查来查去,费时又累心 那这次有时间就想着说写一篇从零开始、步骤完整的网站搭建教程 希望能帮助大家节省时间,不

    2023年04月09日
    浏览(45)
  • 如何用Three.js + Blender打造一个web 3D展览馆

    作者:vivo 互联网前端团队- Wei Xing  运营活动新玩法层出不穷,web 3D炙手可热,本文将一步步带大家了解如何利用Three.js和Blender来打造一个沉浸式web 3D展览馆。 3D展览馆是什么,先来预览下效果: 看起来像个3D冒险类手游,用户可以操纵屏幕中央的虚拟摇杆,以第一人称视角

    2024年02月16日
    浏览(58)
  • 六步快速搭建个人网站

    目录 第一步、选择搭建平台WordPress 第二步、选域名 1)域名在哪买? 2)域名怎么选? 3)以阿里云为例,讲解怎么买域名 第三步、选择服务器 第四步、申请主机、安装WordPress 第五步、选择WordPress模板 1. Blocksy(免费) 2. Astra(免费) 3. Kadence(免费) 第六步、安装WordPres

    2024年02月08日
    浏览(52)
  • 3D沉浸式旅游网站开发案例复盘【Three.js】

    Plongez dans Lyon网站终于上线了。 我们与 Danka 团队和 Nico Icecream 共同努力,打造了一个令我们特别自豪的流畅的沉浸式网站。 这个网站是专为 ONLYON Tourism 和会议而建,旨在展示里昂最具标志性的活动场所。观看简短的介绍视频后,用户可以进入城市的交互式风景如画的地图,

    2024年02月12日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包