草庐IT

【THREE.JS】网页中的炫酷3D

知心宝贝 2023-04-18 原文


web3d


一、前言

概述:基于WebGL的三维引擎,目前是国内资料最多、使用最广泛的三维引擎,可以制作一些3D可视化项目

目前随着元宇宙概念的爆火,THREE技术已经深入到了物联网、VR、游戏、数据可视化等多个平台。

最近一段时间主要对之前学习three.js的总结和记录,记录只对自己觉得重要的部分记录,其他的可以参考别的平台资源。

目前作品的相关展示平台已上线:VR_Team作品展示平台,欢迎大家查看。

项目开源地址web3d-product,下面为一部分的网页的作品展示。

粒子特效

在线预览粒子平面墙

在线预览迷失太空

在线预览粒子海浪

在线预览银河系

二维漫画

在线预览漫画-蜘蛛侠

可视化

在线预览电脑主机拆解

后期处理

在线预览后期处理-虚幻花朵

二、项目使用流程

2.1 项目结构

功能说明

  • public文件夹:存放一些独立的案例界面(html格式),不需要依赖,可以直接运行在网页
    • assets存在依赖的资源
    • Basic:基础界面
    • Comic:二维漫画
    • Particles:粒子效果
    • Processing:后期chuli
    • Visualization:可视化
  • src
    • views:功能主界面

2.2 基本使用

//安装依赖
npm i
//运行项目
npm run dev

2.3 项目模板

该模板集成最简单的一个three.js环境,在此基础上可以自行搭建。

2.4 技术栈

前端:vue + three.js + js

三、基础动画

概述:本质就是在render()函数不断渲染的过程中调用动画,每一帧不断增加目标体的位置,以此循环

体验地址基础动画

3.1 THREE.Clock

// 时间动画
let clockAnimate=()=>{
const elapsedTime=clock.getElapsedTime()

cube.position.x = Math.cos(elapsedTime)
cube.position.y=Math.sin(elapsedTime)
}

3.2 GASP

https://github.com/greensock/GSAP

// gasp动画
let gsapAnimate=()=>{
	gsap.to(cube.position, { x: 5, duration: 3, delay:1})
}

四、照相机

8.1 正交相机

参数(属性)含义
left渲染空间的左边界
right渲染空间的右边界
top渲染空间的上边界
bottom渲染空间的下边界
nearnear属性表示的是从距离相机多远的位置开始渲染,一般情况会设置一个很小的值。 默认值0.1
farfar属性表示的是距离相机多远的位置截止渲染,如果设置的值偏小小,会有部分场景看不到。 默认值1000
let width = window.innerWidth;
let height = window.innerHeight;
const camera = new THREE.OrthographicCamera(width / - 2, width / 2, height / 2, height / - 2, 1, 1000);
scene.add(camera);
camera.position.set(100, 200, 100);

8.2 透视相机

参数含义默认值
fovfov表示视场,所谓视场就是能够看到的角度范围,人的眼睛大约能够看到180度的视场,视角大小设置要根据具体应用,一般游戏会设置60~90度45
aspectaspect表示渲染窗口的长宽比,如果一个网页上只有一个全屏的canvas画布且画布上只有一个窗口,那么aspect的值就是网页窗口客户区的宽高比window.innerWidth/window.innerHeight
nearnear属性表示的是从距离相机多远的位置开始渲染,一般情况会设置一个很小的值。0.1
farfar属性表示的是距离相机多远的位置截止渲染,如果设置的值偏小,会有部分场景看不到1000
let width = window.innerWidth;
let height = window.innerHeight;
const camera = new THREE.PerspectiveCamera(45, width / height, 1, 1000);
camera.position.set(150, 100, 300);
camera.lookAt(scene.position);

注意

  • 相机的fov视口是上下的,不是左右
  • 向左移动相机,我们的场景会向右移动

4.3 相机控制器

概述:通过鼠标控制相机的移动、旋转、缩放以此实现场景物体的控制

导包

import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'

应用

const controls = new OrbitControls(camera, renderer.domElement)

自旋转

controls.autoRotate = true

必须在render函数调用update实时更新才奏效

五、画布和全屏

像素比:将一个像素分成若干个n*n的像素,这样的话显示会更加清晰,一般来说像素比为2就够了

// 自适应屏幕
let onWindowsResize = () => {

    sizes.width = window.innerWidth
    sizes.height = window.innerHeight
    camera.aspect = sizes.width / sizes.height
    camera.updateProjectionMatrix()
    renderer.setSize(sizes.width, sizes.height)
    renderer.setPixelRatio(window.devicePixelRatio)
}

//双击全屏
let onWindowsScreen = () => {
    let isFullScreen = document.fullscreenElement
    if (!isFullScreen) {
    	renderer.domElement.requestFullscreen()
    } else {
    	document.exitFullscreen()
    }
}
let expandFunction = () => {
    window.addEventListener('resize', onWindowsResize)
    window.addEventListener('dblclick', onWindowsScreen)
}

六、几何体

概述:几何体由一个个顶点粒子构成,每个粒子包含了位置、uv贴图(3d=>2d坐标对应)等,三个顶点形成一个面,在three.js中。物体由一个个三角形面构成。

BufferGeometry:是面片、线或点几何体的有效表述。包括顶点位置,面片索引、法相量、颜色值、UV 坐标和自定义缓存属性值。使用 BufferGeometry 可以有效减少向 GPU 传输上述数据所需的开销。目前,three.js的物体都用上了Buffer,可以直接调用,一些顶点可以使用这个。

演示链接三角形

let createBufferTriangle = () => {
    const count = 50
    for (let i = 0; i < count; i++) {
        const pointsArray = new Float32Array(9)
        for (let j = 0; j < 9; j++) {
            pointsArray[j] = (Math.random() - 0.5) * 5
        }

        const geometry = new THREE.BufferGeometry()
        const material = new THREE.MeshBasicMaterial({
            color: new THREE.Color(
                Math.random(),
                Math.random(),
                Math.random(),
            ),
            opacity: Math.random(),
            transparent: true,
            // wireframe:true
        })
        geometry.setAttribute('position', new THREE.BufferAttribute(pointsArray, 3))
        // geometry.attributes.position = new THREE.BufferAttribute(pointsArray, 3)
        const triangle = new THREE.Mesh(geometry, material)
        scene.add(triangle)

    }
}  

七、Debug UI

dat.gui:https://github.com/dataarts/dat.gui 样式好看,动画有点不流畅

lil-gui:https://lil-gui.georgealways.com/ 动画流畅,官方案例用的多

八、纹理贴图

8.1 mipmapping

mip映射(mipmapping)是一种技术,它包括一次又一次地创建半个较小版本的纹理,直到得到1x1纹理。
所有这些纹理变化都会发送到GPU,GPU将自动选择最合适的纹理版本。所有的这些都已经由THREE.js和GPU处理,但是我们可以选择不同的过滤算法

8.2 放大滤镜

概述:原本的贴图大小大于要渲染的大小,如缩放物体,应用这个

8.3 缩小滤镜

概述:原本的贴图大小小于要渲染的大小,如缩放物体,应用这个

当纹理的minFilter属性使用NearestFilter时,我们可以不需要再mip映射了,可以通过以下代码为纹理停用mipmapping,使得GPU不再处理mip映射

//如果不在乎远处物品效果如失真之类的,纹理的minFilter属性使用 NearestFilter时可以停用mipmapping以获得更好性能,根据实际项目而定
colorTexture.generateMipmaps = false

参考链接:https://threejs.org/docs/index.html#api/zh/constants/Textures

参考链接:https://blog.csdn.net/weixin_43990650/article/details/121595334

8.3 UV贴图

参考链接:https://www.bilibili.com/read/cv15999592

掘金:https://juejin.cn/post/7101209181822124069

资源推荐

  • www.poliigon.com
  • https://3dtextures.me/
  • www.arroway-textures.ch/

纹理、材质、灯光

这部分可以参考网上的资料,这里不做过多讲解

https://blog.csdn.net/weixin_43990650/article/details/121595334

九、阴影

9.1 灯光阴影

四步走

灯光cast=>球体cast=>平面receive=>renderer设置

概述:灯光利用照相机渲染出shadow map贴到场景中,得到阴影

简单例子

//直射光
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5)
scene.add(directionalLight)

directionalLight.position.set(2, 2, -1)
directionalLight.castShadow = true
directionalLight.shadow.mapSize.width = 1024
directionalLight.shadow.mapSize.height = 1024
directionalLight.shadow.camera.near = 1
directionalLight.shadow.camera.far = 6
directionalLight.shadow.camera.top = 2
directionalLight.shadow.camera.right = 2
directionalLight.shadow.camera.bottom = - 2
directionalLight.shadow.camera.left = - 2

directionalLight.shadow.radius = 10

//只是实体化的工具,参数修改上面的
const directionalLightCameraHelper = new THREE.CameraHelper(directionalLight.shadow.camera)
scene.add(directionalLightCameraHelper)
directionalLightCameraHelper.visible = false

注意

  • 只有平行光、点光源和聚光灯支持阴影
  • 平行光是正交相机、其他的是透视相机,可以通过调节角度

9.2 烘焙阴影

概述:提前在blender渲染好阴影,这个不会变化,关闭渲染器阴影投射就行

9.3 阴影跟随

概述:在小球下面放置具有阴影贴图的平面,关闭渲染器阴影,跟随小球运动改变透明度

renderer.shadowMap.enabled = false

十、粒子效果

概述:粒子可以被用来创建星星、烟雾、雨滴、灰尘、火焰等等。我们可以使用合理的帧速率来创建数千个粒子。每个粒子都是由始终面向摄影机的平面(俩个三角形)组成的

参考链接:https://blog.csdn.net/weixin_43990650/article/details/121736698

101801718.png" alt=“6” style=“zoom:50%;” />

概述:提前在blender渲染好阴影,这个不会变化,关闭渲染器阴影投射就行

9.3 阴影跟随

概述:在小球下面放置具有阴影贴图的平面,关闭渲染器阴影,跟随小球运动改变透明度

renderer.shadowMap.enabled = false

十、粒子效果

概述:粒子可以被用来创建星星、烟雾、雨滴、灰尘、火焰等等。我们可以使用合理的帧速率来创建数千个粒子。每个粒子都是由始终面向摄影机的平面(俩个三角形)组成的

官网链接:数字孪生开发平台

本项目由`江苏海洋大学计算机工程学院VR_Team106完成,欢迎加入!!!

有关【THREE.JS】网页中的炫酷3D的更多相关文章

  1. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  2. ruby - 其他文件中的 Rake 任务 - 2

    我试图在一个项目中使用rake,如果我把所有东西都放到Rakefile中,它会很大并且很难读取/找到东西,所以我试着将每个命名空间放在lib/rake中它自己的文件中,我添加了这个到我的rake文件的顶部:Dir['#{File.dirname(__FILE__)}/lib/rake/*.rake'].map{|f|requiref}它加载文件没问题,但没有任务。我现在只有一个.rake文件作为测试,名为“servers.rake”,它看起来像这样:namespace:serverdotask:testdoputs"test"endend所以当我运行rakeserver:testid时

  3. ruby-on-rails - Ruby net/ldap 模块中的内存泄漏 - 2

    作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代

  4. ruby-on-rails - Rails 3 中的多个路由文件 - 2

    Rails2.3可以选择随时使用RouteSet#add_configuration_file添加更多路由。是否可以在Rails3项目中做同样的事情? 最佳答案 在config/application.rb中:config.paths.config.routes在Rails3.2(也可能是Rails3.1)中,使用:config.paths["config/routes"] 关于ruby-on-rails-Rails3中的多个路由文件,我们在StackOverflow上找到一个类似的问题

  5. ruby-on-rails - Rails - 一个 View 中的多个模型 - 2

    我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何

  6. ruby-on-rails - Rails 3.2.1 中 ActionMailer 中的未定义方法 'default_content_type=' - 2

    我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer

  7. ruby-on-rails - Rails 应用程序中的 Rails : How are you using application_controller. rb 是新手吗? - 2

    刚入门rails,开始慢慢理解。有人可以解释或给我一些关于在application_controller中编码的好处或时间和原因的想法吗?有哪些用例。您如何为Rails应用程序使用应用程序Controller?我不想在那里放太多代码,因为据我了解,每个请求都会调用此Controller。这是真的? 最佳答案 ApplicationController实际上是您应用程序中的每个其他Controller都将从中继承的类(尽管这不是强制性的)。我同意不要用太多代码弄乱它并保持干净整洁的态度,尽管在某些情况下ApplicationContr

  8. ruby-on-rails - form_for 中不在模型中的自定义字段 - 2

    我想向我的Controller传递一个参数,它是一个简单的复选框,但我不知道如何在模型的form_for中引入它,这是我的观点:{:id=>'go_finance'}do|f|%>Transferirde:para:Entrada:"input",:placeholder=>"Quantofoiganho?"%>Saída:"output",:placeholder=>"Quantofoigasto?"%>Nota:我想做一个额外的复选框,但我该怎么做,模型中没有一个对象,而是一个要检查的对象,以便在Controller中创建一个ifelse,如果没有检查,请帮助我,非常感谢,谢谢

  9. ruby - rspec 需要 .rspec 文件中的 spec_helper - 2

    我注意到像bundler这样的项目在每个specfile中执行requirespec_helper我还注意到rspec使用选项--require,它允许您在引导rspec时要求一个文件。您还可以将其添加到.rspec文件中,因此只要您运行不带参数的rspec就会添加它。使用上述方法有什么缺点可以解释为什么像bundler这样的项目选择在每个规范文件中都需要spec_helper吗? 最佳答案 我不在Bundler上工作,所以我不能直接谈论他们的做法。并非所有项目都checkin.rspec文件。原因是这个文件,通常按照当前的惯例,只

  10. ruby-on-rails - active_admin 目录中的常量警告重新声明 - 2

    我正在使用active_admin,我在Rails3应用程序的应用程序中有一个目录管理,其中包含模型和页面的声明。时不时地我也有一个类,当那个类有一个常量时,就像这样:classFooBAR="bar"end然后,我在每个必须在我的Rails应用程序中重新加载一些代码的请求中收到此警告:/Users/pupeno/helloworld/app/admin/billing.rb:12:warning:alreadyinitializedconstantBAR知道发生了什么以及如何避免这些警告吗? 最佳答案 在纯Ruby中:classA

随机推荐