yyg
2023/01/15阅读:84主题:全栈蓝
WebGL与ThreeJS
3D的基础概念
3D分为纹理和贴图和材质
- 纹理即纹路,每个物体表面上不同的样子,比如木头的木纹状
- 贴图是图 最简单的形式是ps之类的软件做出来的一张图,这些图在3D中用来贴到物体的表面,用来表现物体的纹理
- 材质主要用来表现物体对光的交互(反射,折射等)性质的,比如金属对光的反射和毛毯对光的反射性质完全不一样,那么对3D程序来说,这样的差别就通过材质这个属性来计算出不同的颜色
哪些3d
数据可视化
- echarts
- Highcharts
- D3.js
跨界AR和VR
- Reacr-VR/AR
- ar.js
- renderloop
游戏开发
- Cocos2d-JS
- Egret
- CreateJS
3D开发
Three.js
xeolgl
cesiumo
babylonjs
概念知识
显示器不认识数学家YY的数学模型 他只认识像素图
图形工程师就是把数学家YY的数学模型给转化成图片扔给显示器显示出来
要做好上面的事,学习WebGL = 学数学家Yy的数学模型 + 怎么把数学模型转化成图片
OpenGL/Dx 规定与封装了把数学模型转换成图片的过程
数学不好能做WebGL 但当不了图形开发工程师
WebGL就是OpenGL和Javascript生的傻儿子
「WebGL怎么把数学模型转换成图片?」
OpenGL中有固定渲染管线和可编程渲染管线
WebGL目前只有可编程渲染管线
可编程渲染管线:固定的MVP套路计算机会跑,但有时候其中某些套路不想让计算机自动处理,手动编写某个套路就是可编程渲染管线
着色器:就是帮助我们做可编程渲染管线的工具。WebGL常用的有顶点着色器,片元着色器
顶点着色器:顾名思义它能够处理订单坐标、大小等(矩阵计算后的结果),能够把数学坐标光栅化
- 片元中的每一个元素对应与缓冲区中的一个像素。光栅化其实是一种将几何图元变成二维图像的过程。该过程包含了两部分的工作,第一部分的工作:决定窗口坐标中的哪些整形栅格区域被基本图元占用;第二部分的工作:分配一个颜色值和一个深度值到各个区域。光栅化过程产生的是片元。
- 光栅化:把物体的数学描述以及物体相关的颜色信息转换为屏幕上用于对应位置的像素及用于填充色素的颜色。这个过程就是光栅化,这是一个将模拟信号转化为离散信号的过程。光栅化就是把顶点数据转换为片元的过程
片元着色器:能够接受光栅化数据并加以处理使其显示到屏幕上(光栅化数据包含了像素的位置,颜色等信息)
固定渲染管线:就是把一套把数学模型转成图片的套路 ---模型坐标转换(M)、视图坐标转换(V)、投影坐标转换(P)简称MVP套路
- 模型坐标转换:模型的基准点(原点)、模型的大小、模型旋转角度等
- 视图坐标转换:从哪个方向、角度观看这个模型
- 投影坐标转换:离得越近的呈现出来的图像就应该越大,越远越小
javascript程序 ==> 顶点着色器 ==> 片元着色器 ==> webGL程序
在构建3D物体时通过顶点组成三角形网格,但这些三角形网格都是矢量图形,最终在屏幕上显示时还是需要转换为像素图形,这种转化过程被称为栅格化,是计算机图形学的关键技术之一,
webGL程序和普通的javascript程序不一样,WebGL程序除了Javascript部分之外,还包含两个着色程序,分别是顶点着色器和片元着色器;在ThreeJs中描述一个3D物体,需要轮廓,材质,和纹理三个要素;那么可以简单理解为顶点着色器用来处理物体的轮廓程序,片色着色器是用来处理物体材质和纹理的程序
Three.js结构
threejs三大组件
- Camera 相机
- Renderers(渲染器) ----Shaders
- Scenes(屏幕)
图像的表示
Lights(光线)
- Shadows
Geometries (几何图形)
Materials(材质)
Objects(图形对象)
Loaders(加载器)
- Managers
Textures(纹理)
框架原理
Math数学库
- Interpolants (插值)
Extras(附件)
- core
- Curves
- Helpers
- Objects
Constant(常量)
Core (核心)
- BufferAttribute
动画和声音
Animation(动画)
- Tracks
Audio(声音)
Three.js必备三个条件(scene、camera、renderer渲染)
场景Scene是一个物体的容器(通俗理解装东西的),开发者可以将需要的角色放入到场景中,例如苹果,葡萄。同时角色自身也管理着其在场景中的位置
相机camera的作用就是面对场景,在场景中取一个合适的景,把它拍下来。(可以想象成人的眼睛)
渲染器renderer的作用就是将相机拍摄下来的图片,放到浏览器中去显示
坐标系的位置和指向
坐标系的原点在画布中心 (canvas.width/2,canvas.height/2)
通过Three.js提供的THREE.AxisHelper()辅助方法将坐标系可视化
THREE.PerspectiveCamera(fov,aspect,near,far)
- For :视场,即摄像机能看到的视野。比如,人类有接近180度的视场,而有些鸟类有接近360度的视场。但是由于计算机不能完全显示我们能够看到的场景,所以一般会选择一块较小的区域。对于游戏而言,视场的大小通常为60-90度。 推荐默认值为:50
- Aspect:指定渲染结果的横向尺寸和纵向尺寸的比值。在我们的示例中,由于使用窗口作为输出界面,所以使用的是窗口的长度比。推荐默认值:window.innerWdth/window.innerHeight
- Near: 指定从距离摄像机多近的距离开始渲染。推荐默认值: 0.1
- Far:指定摄像机从它所处的位置开始能看到多远。若过小,那么场景中的远处不会被渲染;若过大,可能会影响性能,推荐默认值:1000
Mesh
网格就是一系列的多边形组成的,三角形或者四边形,网格一般由顶点来描绘,我们看见的三维开发的模型就是由一些列的点组成的
Mesh好比一个包装工
将可视化的材质 粘合在一个数学世界里的几何体上,形成一个可添加到场景中的对象
创建的材质和几何体可以多次使用
还有Points(点集)、Line(线/虚线)等
BoxGeometry 长方体 | CircleGeometr 圆形 | ConeGeometry圆锥体 | CylinderGeometry 圆柱体 |
---|---|---|---|
DodecahedronGeometry(十二面体) | IcosahedronGeometry(二十面体) | LatheGeometry(让任意曲线绕y轴旋转生成一个形状,如花瓶) | OctahedronGeometry(八面体) |
ParametricGeometry(根据参数生成形状) | PolyhedronGeometry(多面体) | RingGeometry(环形) | ShapeGemetry(二锥形) |
SphereGeometry(球体) | TetrahedronGeometry(四面体) | TorusGeometry(圆环形) | TorusKnotGeometry(换面钮结体) |
TubeGeometry(管道) | \ | \ | \ |
Material材质
- MeshBaseMaterial(网格基础材质)是一种非常简单的材质,这种材质不会考虑光照的影响。使用这种材质网格会渲染成简单的平面多边形,并且可以显示几何体的线框
- MeshDepthMaterial(网格深度材质)使用该材质的物体的外观不是由某个材质属性决定的,而是有物体到相机的距离决定的,离相机越近越亮,离相机越远越暗。该材质的属性很少,没有设置物体颜色的属性, 如果想改变物体的颜色,就需要创建多材质的物体
- MeshNormalMaterial(网格法向材质)通过法向量来隐射RGB颜色。每个法向量不同的面赋予不同的颜色
- MeshFaceMaterial (网格面材质)可以为几何体每一个面指定不同的材质,比如一个立方体有6个面 你可以为每一个面指定一个材质
- MeshLambertMaterial(网格朗伯材质)用于创建看上去暗淡的、不光亮的表面,可以对光源产生阴影的效果
- MeshPhongMaterial(网格Phong式材质)用于创建光亮表面的材质。可以产生阴影的效果
- ShaderMaterial(着色器材质)该材质是最复杂的一种材质 可以使用自己定制的着色器
Light光源
- 环境光源没有位置概念,会将颜色应用到场景的每一个物体上,主要作用是弱化阴影 给场景添加颜色
- 点光源类似于照明弹,朝所有的方向发光,因此不产生阴影
- 聚光灯光源类似于手电筒,形成锥形的光束、随着距离的增加而变弱,可以设定生成阴影
- 方向光光源类似于太阳,从很远的地方发出的平行光束 距离越远,衰减的越多
- 想要一个自然的室外效果 除了添加环境光弱化阴影,添加聚光灯为场景增加光线,还需要使用半球光光源将天空和空气泵以及地面的散射计算进去 使得更自然更真实
- 平面光光源定义了一个发光的发光体,需要使用webgl的延迟渲染机制
- 炫光效果 在有太阳的时候使用炫光光源 会使得场景更真实
粒子
粒子一直面向摄像机(无论你旋转摄像机还是设置粒子的rotation属性)
THREE.Sprite(meterial) 可以加载图片作为粒子的纹理
THREE.Points(geometry.material) 创建纯点作为粒子
射线
用于鼠标去获取在3D世界被鼠标选中的一些物体
mouse.x = (e.clientX/window.innerWidth)*2 -1
mouse.y = (e.clientY/window.innerHeight)*2 -1
推导过程:
设A点为点击点(x1,y1)x1 = e.clientX, y1 = e.clinetY
设A点在世界坐标中的坐标值为B(x2,y2)
由于A点的坐标值的原点是以屏幕左上角为(0,0)
我们可以计算可得以屏幕为原点的B值
X2 = x1-innerWidth/2
Y2 = innerHeight/2 - y1
又由于在世界坐标的范围是[-1,1],要得到正确的B值我们必须要将坐标标准化
X2 = (x1 - innerWidth/2)/(innerWidth/2) = (x1/innerWidth)*2-1
同理可得y2 = -(y1/innerHeight)*2 + 1
一般需要用到的插件
Dat.GUI提供了可视化调参的面板,对参数调整的操作提供了极大的便利
Stats.js 帧率,每帧的渲染时间、内存占用量、用户自定义
适合开发VR的开发工具
- VR盒子 大部分的VR盒子都是通过光学透镜把手机屏幕的画面转变为VR画质,使得用户享有沉浸式的体验,这一产品的代表作为谷歌Cardboard 这个需要500元,建议买国产
- VR一体机 是指哪些具备了独立处理器的VR设备 他们不再通过手机或电脑的处理器运行,具有独立输入输出的能力,自成一体,故称一体机,推荐一款 柔宇
- VR头显 是目前最顶级的VR设备 在这个领域里有HTC Vive和Oculus Rift配合一部高配置的电脑主机,但是你想要有顶级的VR体验 花个六七千是必须得
- 使用过的: 暴风VR 零镜小白VR
Three.js智慧工厂3D项目
「借助一些库实现快速开发」
缓存离屏渲染(两层canvas)
Lower-canvas和upper-canvas(底层绘制上层处理用户的相应)
射线算法优化相交的点
处理Retina屏,canvas.width,canvas.height放大至dpi倍
x
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
*{
margin:0;
padding:0;
}
body{
background: #000;
}
</style>
</head>
<body>
<script src="./node_modules/three/build/three.js"></script>
<script src="./node_modules/three/examples/jsm/loaders/GLTFLoader.js"></script>
<script src="./node_modules/three/examples/jsm/controls/OrbitControls.js"></script>
<script src="./node_modules/three/examples/jsm/loader/DRACOLoader.js"></script>
<script src="./index.js"></script>
</body>
</html>
Index.js
xxxxxxxxxx
/*
* @Author: yangyuguang
* @Date: 2023-01-15 11:10:55
* @LastEditors: yangyuguang
* @LastEditTime: 2023-01-15 15:48:54
* @FilePath: /tt/index.js
*/
let container,camera,scene,light,rendered,controls;
init()
function init(){
container = document.createElement('div')
document.body.appendChild(container)
// 创建眼睛
camera = new THREE.PerspectiveCamera(45,window.innerWidth/window.innerHeight,0.25,1000)
camera.position.set(0,80,200)
controls = new THREE.OrbitControls(camera,container)
controls.target.set(0,-0.2,-0.2)
controls.update()
scene = new THREE.Scene()//容器
light = new THREE.HemisphereLight(0xbbbbfff,0x444422)//光线
scene.add(light)
light.position.set(0,1,0)//光线角度
const loader = new THREE.GLTFLoader().setPath('model/')
const _DRACOLoader = new THREE.DRACOLoader()
_DRACOLoader.setDecoderPath("./javascript")
loader.setDRACOLoader(_DRACOLoader)
loader.load('model.gltf',function(gltf){
scene.add(gltf.scene)
},undefined,function(e){
gltf.scene.scale.set(0.03,0.03,0.03)
gltf.scene.position.set(-80,0,0)
scene.add(gltf.scene)
})
rendered = new THREE.WebGL1Renderer({
antialias:true
})
rendered.setPixelRatio(window.devicePixelRatio)
rendered.setSize(window.innerWidth,window.innerHeight)
container.appendChild(rendered.domElement)
// 打一些辅助线
const axesHelper = new THREE.AxesHelper(60)
scene.add(axesHelper)
const hemisphereLight = new THREE.HemisphereLight(light,60)
scene.add(hemisphereLight)
const helper = new THREE.CameraHelper(camera)
scene.add(helper)
}
function animate(){
requestAnimationFrame(animate)
rendered.render(scene,camera)
}
animate()
HTML方式
<!--
* @Author: yangyuguang
* @Date: 2022-12-04 17:22:11
* @LastEditors: yangyuguang
* @LastEditTime: 2023-01-11 11:19:31
* @FilePath: /排行榜初始代码/test2.html
-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>加载OBJ和MTL文件</title>
<style>
html,
body{
width: 100%;
height: 100%;
overflow: hidden;
}
*{
margin:0;
padding:0;
}
</style>
<!-- 引入three.js三维引擎 -->
<script src="http://www.yanhuangxueyuan.com/versions/threejsR92/build/three.js"></script>
<script src="http://www.yanhuangxueyuan.com/threejs/examples/js/controls/OrbitControls.js"></script>
<script src="http://www.yanhuangxueyuan.com/threejs/examples/js/loaders/OBJLoader.js"></script>
<script src="http://www.yanhuangxueyuan.com/threejs/examples/js/loaders/MTLLoader.js"></script>
</head>
<body>
<script>
var scene = new THREE.Scene(),
camera,
renderer = new THREE.WebGLRenderer();
/* OBJ和材质文件mtl加载 */
var OBJLoader = new THREE.OBJLoader(); //obj加载器
var MTLLoader = new THREE.MTLLoader();//材质文件加载器
MTLLoader.load('./static/material.mtl',function(meterials){
console.log(meterials);
OBJLoader.setMaterials(meterials);
OBJLoader.load('./static/model.obj',function(obj){
/* 返回的组对象插入场景中 */
scene.add(obj)
obj.children[0].scale.set(5,5,5)//网络模型播放
})
})
createLight()
createCamera()
createRenderer()
/* 通过requestAnimationFrame()操作三维场景 */
var controls = new THREE.OrbitControls(camera,renderer.domElement); //创建控件对象
// 创建一个时钟对象Clock
var clock = new THREE.Clock()
function render(){
renderer.render(scene,camera);//执行渲染操作
requestAnimationFrame(render) //请求再次执行渲染函数render
}
render()
// 正投影相机设置
function createCamera(){
var width = window.innerWidth; //窗口设置
var height = window.innerHeight;//窗口高度
var k = width/height;//窗口宽高比
var s = 150;//三维场景显示范围控制系数,系数越大,显示的范围越大
// 创建相机对象
camera = new THREE.OrthographicCamera(-s*k,s*k,s,-s,1,1000)
camera.position.set(200,300,200) //设置相机位置
camera.lookAt(scene.position);//设置相机方向(指向的场景对象)
}
// 透视相机
function createPerspectiveCamera(){
var width = window.innerWidth;//窗口宽度
var height = window.innerHeight//窗口高度
/* 透视投影相机对象 */
camera= new THREE.PerspectiveCamera(60,width/height,1,1000)
camera.position.set(200,300,200)//设置相机位置
camera.lookAt(scene.position)//设置相机方向(指向的场景对象)
}
/* 创建渲染器对象 */
function createRenderer(){
renderer.setSize(window.innerWidth,window.innerHeight)//设置渲染区域尺寸
renderer.setClearColor(0xb9d3ff,1)//设置背景颜色
document.body.appendChild(renderer.domElement)//body元素中插入canvas对象
}
/* 创建光源 */
function createLight(){
// 点光源
var point = new THREE.PointLight(0xffffff)
point.position.set(400,200,300)//点光源位置
scene.add(point);//点光源添加到场景中
// 环境光
var ambient = new THREE.AmbientLight(0x444444)
scene.add(ambient)
}
</script>
</body>
</html>
在vue中使用
「第一种方法使用three-obj-mtl-loader插件」
使用npm install three-obj-mtl-loader --save
在组件中引入:import { OBJLoader, MTLLoader } from 'three-obj-mtl-loader'
使用OBJLoader和MTLLoader加载文件:
import {OBJLoader,MTLLoader} from "three-obj-mtl-loader"
// 加载obj和mtl模型
let _this = this
let mtlLoader = new MTLLoader()
mtlLoader.load('./static/material.mtl',function(materials){
materials.preload();
let objLoader = new OBJLoader()
objLoader.load('./static/model.obj',function(obj){
obj.scale.x = obj.scale.y = obj.scale.z = 100
obj.rotation.y = 500
let mesh =obj
mesh.position.y = -50
_this.scene.add(mesh)
})
})
「第二种方法使用vue-3d-model组件」
npm install vue-3d-model --save
引入组件:
import { ModelObj } from 'vue-3d-model'
components: {
• ModelObj
},
data() {
return {
publicPath: process.env.BASE_URL
}
},
使用组件:
<model-obj
id="place"
:src="`${publicPath}model/model.obj`"
:mtl="`${publicPath}model/material.mtl`"
backgroundColor="rgb(0,0,0)"
:scale="{ x: 0.8, y: 0.8, z: 0.8 }"
>
</model-obj>
一般使用这个会存在找不到obj模块的情况,这是因为经过了webpack的处理,需要把.obj文件放到vue处理静态文件的文件夹中,vue-cli3是放在static文件夹下,但是vue-cli3及之后就需要放到public文件夹下,并且在组件中通过process.env.BASE_URL+public文件夹下的.obj文件的路径来引用
作者介绍