随着时间的推移技术的进步,前端越来越杂了,但是也越来越精彩了。只是会用一点
ThreeJs
,对于WebGl
的原理并没了解过,这并不影响我们利用ThreeJs
去做出一个非常炫酷的项目。
新世界的大门打开啦!
gltf
模型,obj
模型也没问题,我会介绍如何转化与压缩 PS:为什么只有这俩,因为我写这个项目只用到了这俩,处理的经验也是针对这俩的,我项目中所用的模型是公司所有暂不能提供。ThreeJs
的基础 俗话说得好 万丈高楼平地起嘛 如果没有这方面基础的同学也不要急 推荐一本书《THREE.JS开发指南》
,有基础也有提高 很棒ThreeJs
处理模型并应用到项目中,可能有少许不足之处,还望各路大神指正教导geoJson
作为地理数据,去建模,建造出来的更精确,而且可以利用地理坐标和世界坐标去关联(猜想),利于项目开发,毕竟第一次,这个锅我背了Threejs
的文档是不全的,很多控制器
,loader
,后期处理
都没有文档,要自己多看看Threejs
的examples
,很多效果都可以基于Demo
去实现ThreeJs
的创建的对象,避免内存泄露,能dispose
的dispose
,多个children
的要遍历remove
掉 而且里面的 material
和geometry
也要删掉,最近刚知道一个取消占用的妙招,WEBGL_lose_context
<html lang="en">
<head>
<title>Threejs-city-model-showtitle>
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"
/>
<style>
body {
color: #fff;
margin: 0px;
overflow: hidden;
}
style>
head>
<body>
<script src="../build/three.min.js">script>
body>
html>
首先,我们要祭出ThreeJs
的最重要的几大组件——scene(场景)
、camera(相机)
、renderer(渲染器)
、light(灯光)
,以及渲染的目标——container
(就是DOM结构),老生常谈,不多说
打个比方,scene就是舞台,camera就是拍摄舞台的摄像机,它能决定观众看到什么,而一个舞台没有灯光的话它就是黑乎乎的,所以light就是舞台上的各种灯光,所以舞台上表演什么,就是舞台中有什么,所以要加入到scene中 scene.add(“演员们(模型)”)
var camera, scene, renderer;
var container;
var ambientLight, pointLight;
// 初始化
init()
// 循环渲染每一帧 一帧一帧的 就是你打游戏时的FPS
animate()
function init(){
// 初始化相机
// 这里使用的是透视相机来模拟人眼看到的效果 近大远小
camera = new THREE.PerspectiveCamera(
45,
window.innerWidth / window.innerHeight,
1,
2000
);
camera.position.z = 70;
camera.position.x = 50;
camera.position.y = 10;
// 初始化场景
scene = new THREE.Scene();
// 初始化灯光
// 环境光 能保持整体都是亮点
ambientLight = new THREE.AmbientLight(0x404040)
// 点光源 就像灯泡一样的效果 白色灯光 亮度0.6
pointLight = new THREE.PointLight(0xffffff, 0.6);
// 将灯光加入到场景中
scene.add(ambientLight)
// 将灯光加到摄像机中 点光源跟随摄像机移动
// 为什么这样做 因为这样可以让后期处理时的辉光效果更漂亮
camera.add(pointLight);
// 我们将摄像机加入到场景中
scene.add(camera);
// 初始化渲染器
renderer = new THREE.WebGLRenderer({
// 开启抗锯齿
antialias: true,
// 开启背景透明
alpha: true
});
// 把自动清除颜色缓存关闭 这个如果不关闭 后期处理这块会不能有效显示
// 书上的描述是 如果不这样做,每次调用效果组合器的render()函数时,之前渲染的场景会被清理掉。通过这种方法,我们只会在render循环开始时,把所有东西清理一遍。
renderer.autoClear = false;
// 背景透明 配合 alpha
renderer.setClearColor(0xffffff, 0);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
// 伽马值启动 更像人眼观察的场景
renderer.gammaInput = true;
renderer.gammaOutput = true;
// 渲染到DOM中去
container = document.createElement("div");
container.appendChild(renderer.domElement);
document.body.appendChild(container);
}
// 这样一来,基础场景创建就完成了,接下来我们来让它循环渲染起来
function animate() {
// 这个方法低版本浏览器兼容不好 可以从github上找些兼容库 如果要兼容低版本浏览器
requestAnimationFrame(animate);
// 渲染我们的场景 摄像机啪啪啪的拍和录
// 由于把renderer autoClear 关闭了 所以我们要在渲染函数中手动清除
renderer.clear();
renderer.render(scene, camera);
}
// ok 基础部分完成 接下来我们来加载模型
限于经验和技术等各种外力因素影响,项目最开始时编写demo使用的是Obj模型
和Mtl贴图文件(不太确定贴图文件的叫法是否准确)
,使用起来也很简单(ThreeJs
仓库里的webgl_loader_obj_mtl.html
拿来改下就行了)
<html lang="en">
<head>
<title>Threejs-city-model-showtitle>
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"
/>
<style>
body {
color: #fff;
margin: 0px;
overflow: hidden;
}
style>
head>
<body>
<script src="../build/three.min.js">script>
<script src="js/loaders/MTLLoader.js">script>
<script src="js/loaders/OBJLoader.js">script>
<script>
/* 省略创建场景部分的代码 */
// 加载的过程
var onProgress = function(xhr) {
if (xhr.lengthComputable) {
var percentComplete = (xhr.loaded / xhr.total) * 100;
console.log(Math.round(percentComplete, 2) + "% downloaded");
}
};
var onError = function() {
// 载入出错时候
};
// 加载Mtl贴图文件
new THREE.MTLLoader()
// 贴图文件的路径
.setPath("models/obj/male02/")
.load("male02_dds.mtl", function(materials) {
// 看代码意思是预加载
materials.preload();
// 加载OBJ模型
new THREE.OBJLoader()
// 设置OBJ模型的材质贴图
.setMaterials(materials)
.setPath("models/obj/male02/")
.load(
"male02.obj",
function(object) {
object.position.y = -95;
scene.add(object);
},
onProgress,
onError
);
});
script>
body>
html>
这一步一般会出现的问题有如下
network
是否报404
错误,如果报错,一般都是mtl
贴图文件(看起来像是雪碧图
那种)没给你,或者路径配置的不是相对路径
,如果贴图没错误,模型是黑色的,在mtl
文件中可以更改ka
或kd
的三个值(对应rgb
),或者打印出模型属性,在material.color
中更改点色值或别的属性。黑色的时候,看不到贴图。一般这样一通操作之后,就能看到了模型了ThreeJs
官方推荐gltf
格式的模型在浏览器中渲染,因为它是为浏览器而生的,性能好,体积小。我们项目中使用的模型文件,一开始是Obj
和Mtl
的,达到25MB大小,在vue
项目中渲染会阻塞浏览器46s,原生html
+js
的项目中好些,几秒时间就行了,我怀疑是我写法的问题,但是我测试仅仅是加载模型渲染到场景,并没有多余操作和数据绑定,还是一样,阻塞进程,一度导致我怀疑人生???黑人问号脸。那么如何将Obj模型
转换为gltf
模型,还能再优化吗?进入下一章节!对了对了,Obj
模型也是可以压缩的,而且ObjLoader2
加载会快一点真的很牛逼 模型加贴图从 25mb 减小到了1.8mb 上效果图
1.这是不加贴图和mtl
的obj
文件 已经达到了22.5MB!
obj2gltf-github
// 全局安装后
obj文件所在目录 输出目录
obj2gltf -i ./examples/models/obj/hanchuan/city.obj -o ./gltf/city.gltf --unlit --separate
--unlit
的作用是可以保留环境贴图的效果,环境贴图后面再介绍--separate
是将贴图文件提取出来,提出来浏览器可以缓存,如果你需要继续压缩gltf
文件,这里不加这个参数也行,因为压缩的时候也能提出来gltf-pipeline-github
gltf-pipeline -i ../../../gltf/city.gltf -o ../../../examples/models/obj/hanchuan/city_small1.gltf -d --separate
-d
是--draco.compressMeshes
的缩写,使用draco
算法压缩模型--separate
就是将贴图文件提取出来,不提可以不加这样,我们就完成了gltf
模型的转化和压缩,性能暴增!秒开!
在我们最终的模型中,obj模型297Mb,转gltf之后还有150Mb左右,最终经过压缩,还有7.3Mb!
抛弃了Obj
和Mtl
之后,我们的加载器也要做一下改变
<html lang="en">
<head>
<title>Threejs-city-model-showtitle>
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"
/>
<style>
body {
color: #fff;
margin: 0px;
overflow: hidden;
}
style>
head>
<body>
<script src="../build/three.min.js">script>
<script src="js/loaders/GLTFLoader.js">script>
<script src="js/loaders/DRACOLoader.js">script>
<script>
/* 省略创建场景部分的代码 */
// 加载的过程
var onProgress = function(xhr) {
if (xhr.lengthComputable) {
var percentComplete = (xhr.loaded / xhr.total) * 100;
console.log(Math.round(percentComplete, 2) + "% downloaded");
}
};
var onError = function() {
// 载入出错时候
};
var loader = new THREE.GLTFLoader();
// 这个是Threejs解析draco压缩之后的解析器
// 它从这里读取解析器JS
THREE.DRACOLoader.setDecoderPath("js/libs/draco/gltf/");
// 将Draco解析器和GltfLoader绑定在一起
loader.setDRACOLoader(new THREE.DRACOLoader());
loader.load(
"models/obj/hanchuan/city_small1.gltf",
function(gltf) {
// gltf.scene 拿到这个可以处理模型
scene.add(gltf.scene)
},
onProgress,
onError
);
script>
body>
html>
这时候的场景,应该是这样的,很丑吧哈哈哈,没关系没关系,我们可以为它美容,不过在此之前,我们先来试着转动这个模型,看看性能怎么样。
var controls
function init(){
// 省略创建场景部分
controls = new THREE.OrbitControls(camera, renderer.domElement);
}
它的常用参数在源码中可以找到,也可以百度/goggle一下中文翻译的,不做太多介绍,这是其中一段源码。
// Set to false to disable this control
this.enabled = true;
// "target" sets the location of focus, where the object orbits around
this.target = new THREE.Vector3();
// How far you can dolly in and out ( PerspectiveCamera only )
this.minDistance = 0;
this.maxDistance = Infinity;
// How far you can zoom in and out ( OrthographicCamera only )
this.minZoom = 0;
this.maxZoom = Infinity;
// How far you can orbit vertically, upper and lower limits.
// Range is 0 to Math.PI radians.
this.minPolarAngle = 0; // radians
this.maxPolarAngle = Math.PI; // radians
// How far you can orbit horizontally, upper and lower limits.
// If set, must be a sub-interval of the interval [ - Math.PI, Math.PI ].
this.minAzimuthAngle = - Infinity; // radians
this.maxAzimuthAngle = Infinity; // radians
// Set to true to enable damping (inertia)
// If damping is enabled, you must call controls.update() in your animation loop
this.enableDamping = false;
this.dampingFactor = 0.25;
// This option actually enables dollying in and out; left as "zoom" for backwards compatibility.
// Set to false to disable zooming
this.enableZoom = true;
this.zoomSpeed = 1.0;
// Set to false to disable rotating
this.enableRotate = true;
this.rotateSpeed = 1.0;
// Set to false to disable panning
this.enablePan = true;
this.panSpeed = 1.0;
this.screenSpacePanning = false; // if true, pan in screen-space
this.keyPanSpeed = 7.0; // pixels moved per arrow key push
// Set to true to automatically rotate around the target
// If auto-rotate is enabled, you must call controls.update() in your animation loop
this.autoRotate = false;
this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60
// Set to false to disable use of the keys
this.enableKeys = true;
// The four arrow keys
this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 };
// Mouse buttons
this.mouseButtons = { LEFT: THREE.MOUSE.LEFT, MIDDLE: THREE.MOUSE.MIDDLE, RIGHT: THREE.MOUSE.RIGHT };
// for reset
this.target0 = this.target.clone();
this.position0 = this.object.position.clone();
this.zoom0 = this.object.zoom;
//
// public methods
//
this.getPolarAngle = function () {
function animate(){}
中,设置camera.lookAt=scene.position
效果也很不错。ThreeJs
中内置了很多有趣的控制器,用法和效果都可以从ThreeJs
的examples
中找到,记得看看。玩过LOL
,大型单机游戏的同学都知道,如果帧率
不好,画面看起来就会卡顿,影响体验,这也为什么用requestAnimationFrame
去作为渲染调用的原因之一,它的性能比函数递归
和setInterval
实现渲染调用好很多。那么我们如何去检测我们的场景渲染的性能怎么样呢?就可以使用Stats
// 不要忘了引入进来
var stats;
function init(){
// 省略创建场景部分
stats = new Stats();
container.appendChild(stats.dom);
}
function animatie(){
stats.update();
// 省略renderer
}
process.env.NODE_ENV
控制开发环境再显示这个。若不为空,在渲染场景的时候将设置背景,且背景总是首先被渲染的。 可以设置一个用于的“clear”的Color(颜色)、一个覆盖canvas的Texture(纹理),或是一个CubeTexture。默认值为null。
TextureLoader
、CubeTexture
和SphereGeometry
都可以作为背景图,简单介绍下这三者。threejs全景图原理
,不做过多叙述function init(){
// 省略其余代码
// ....
// 添加一张静止的背景图
scene.background = new THREE.TextureLoader().load("你的背景图")
// ....
}
细心的同学会发现,河流和楼上会有星星点点的光,这是怎么实现的呢?答案就是环境贴图
。
环境贴图
简单的讲,环境贴图就像把物体的表面化作一面镜子,可以反射出你为它赋予的图片。
如何设置环境贴图呢?回到我们加载模型的部分。核心就是创建立方纹理
然后设置某个模型的material
的envMap
为这个立方纹理。 环境贴图的使用限制受纹理影响,有一部分纹理加不上环境贴图。
<html lang="en">
<head>
<title>Threejs-city-model-showtitle>
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"
/>
<style>
body {
color: #fff;
margin: 0px;
overflow: hidden;
}
style>
head>
<body>
<script src="../build/three.min.js">script>
<script src="js/loaders/GLTFLoader.js">script>
<script src="js/loaders/DRACOLoader.js">script>
<script>
/* 省略创建场景部分的代码 */
// 创建一个立方纹理
var envMap = new THREE.CubeTextureLoader()
.setPath("textures/")
.load(new Array(6).fill("start.jpg"));
var loader = new THREE.GLTFLoader();
// 这个是Threejs解析draco压缩之后的解析器
// 它从这里读取解析器JS
THREE.DRACOLoader.setDecoderPath("js/libs/draco/gltf/");
// 将Draco解析器和GltfLoader绑定在一起
loader.setDRACOLoader(new THREE.DRACOLoader());
loader.load(
"models/obj/hanchuan/city_small1.gltf",
function(gltf) {
// gltf.scene 拿到这个可以处理模型
gltf.scene.traverse(function(child) {
if (child.isMesh) {
/* 这些都是DEMO 具体看你模型调整 下节介绍通过鼠标点击确定模型所属对象 然后去调试模型 */
// 这些名称都可以通过打印看出 console.log(child)
// 比如我想给这些加上环境贴图 就可以这样写
/hai|city|liubianxing/i.test(child.name) &&
(child.material.envMap = envMap);
if (/city/i.test(child.name)) {
// 更改模型颜色
child.material.color = new THREE.Color(6, 6, 5);
// 更改模型环境贴图影响 0-1
child.material.reflectivity = 0.9;
}
// 更改模型位置
/lumian|hai/i.test(child.name) && (child.position.y = 0.5);
// ...
}
});
scene.add(gltf.scene)
},
onProgress,
onError
);
script>
body>
html>
光线投射用于进行鼠标拾取(在三维空间中计算出鼠标移过了什么物体)。
这一块的基础建议好好看看《THREE.JS开发指南》
这本书。如果需要多个pass
,要学会使用MaskPass
和clearPass
。这一块因为不熟悉,我在添加效果的时候花费了很大量的时间,尤其是Threejs
内置的pass
效果没有文档,甚至你都不知道内置了多少种效果…《THREE.JS开发指南》
这本书介绍的比较全面,用法也很详细。
EffectComposer
对象,然后在该对象上添加后期处理通道。render
循环中,使用EffectComposer
渲染场景、应用通道,并输出结果EffectComposer
效果组合器,每个通道会按照其加入EffectComposer
的顺序执行。RenderPass
该通道在指定的场景和相机的基础上渲染出一个新的场景。一般在第一个加入到Composer
中,它会渲染场景,但是不会将渲染结果输出到屏幕上。ShaderPass
使用该通道可以传入一个自定义的着色器,用来生成高级的、自定义的后期处理通道BloomPass
该通道会使明亮区域渗入较暗的区域,模拟相机照到过多亮光的情形CopyShader
它不会添加任何特殊效果,只是将最后一个通道的结果复制到屏幕上,BloomPass
无法直接添加到屏幕上,需要借助这个Shader
,其实使用bloompass.renderToScreen = true
是可以添加的,但是后续再加处理效果会无效,所以一定要借用这个Shader
<html lang="en">
<head>
<title>Threejs-city-model-showtitle>
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"
/>
<style>
body {
color: #fff;
margin: 0px;
overflow: hidden;
}
style>
head>
<body>
<script src="js/postprocessing/EffectComposer.js">script>
<script src="js/postprocessing/RenderPass.js">script>
<script src="js/postprocessing/ShaderPass.js">script>
<script src="js/shaders/CopyShader.js">script>
<script src="js/shaders/LuminosityHighPassShader.js">script>
<script src="js/postprocessing/UnrealBloomPass.js">script>
<script>
var clock;
/* 省略创建场景部分的代码 */
// 初始化renderPass
var renderScene = new THREE.RenderPass(scene, camera);
// 初始化bloomPass
var bloomPass = new THREE.UnrealBloomPass(
// 没研究过这些参数的意义 会提上日程
new THREE.Vector2(window.innerWidth, window.innerHeight),
1.5,
0.4,
0.85
);
// 一些参数 可以调整看效果
bloomPass.threshold = 0.36;
bloomPass.strength = 0.6;
bloomPass.radius = 0;
// effectCopy
var effectCopy = new THREE.ShaderPass(THREE.CopyShader);
// 让effectCopy渲染到屏幕上 没这句不会再屏幕上渲染
effectCopy.renderToScreen = true;
// 初始化 composer
var composer = new THREE.EffectComposer(renderer);
// 模版缓冲(stencil buffer) https://blog.csdn.net/silangquan/article/details/46608915
composer.renderTarget1.stencilBuffer = true;
composer.renderTarget2.stencilBuffer = true;
composer.setSize(window.innerWidth, window.innerHeight);
composer.addPass(renderScene);
composer.addPass(bloomPass);
composer.addPass(effectCopy);
// 修改animate
function animate() {
requestAnimationFrame(animate);
var delt = clock.getDelta();
stats.update();
renderer.clear();
// 删除renderer使用composerrender去渲染
// renderer.render(scene, camera);
// 没理解透这个delt的作用 ???
composer.render(delt);
}
script>
body>
html>
这样 辉光效果就出来了。还不够还不够,让我们加上FocusShaper
,让它看起来像聚焦在中心一样(突出中心)。
map
贴图来更改亮度,比如暗色的贴图,它反光就会很软我们要引入FocusShader
。
<html lang="en">
<head>
<title>Threejs-city-model-showtitle>
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"
/>
<style>
body {
color: #fff;
margin: 0px;
overflow: hidden;
}
style>
head>
<body>
<script src="js/postprocessing/EffectComposer.js">script>
<script src="js/postprocessing/RenderPass.js">script>
<script src="js/postprocessing/ShaderPass.js">script>
<script src="js/shaders/CopyShader.js">script>
<script src="js/shaders/LuminosityHighPassShader.js">script>
<script src="js/postprocessing/UnrealBloomPass.js">script>
<script src="js/shaders/FocusShader.js">script>
<script>
var clock;
/* 省略创建场景部分的代码 */
// 创建focusShader 相对于bloompass新加的
var focusShader = new THREE.ShaderPass(THREE.FocusShader);
focusShader.uniforms["screenWidth"].value = window.innerWidth;
focusShader.uniforms["screenHeight"].value = window.innerHeight;
focusShader.uniforms["sampleDistance"].value = 1.07;
// 初始化renderPass
var renderScene = new THREE.RenderPass(scene, camera);
// 初始化bloomPass
var bloomPass = new THREE.UnrealBloomPass(
// 没研究过这些参数的意义 会提上日程
new THREE.Vector2(window.innerWidth, window.innerHeight),
1.5,
0.4,
0.85
);
// 一些参数 可以调整看效果
bloomPass.threshold = 0.36;
bloomPass.strength = 0.6;
bloomPass.radius = 0;
// effectCopy
var effectCopy = new THREE.ShaderPass(THREE.CopyShader);
// 让effectCopy渲染到屏幕上 没这句不会再屏幕上渲染
effectCopy.renderToScreen = true;
// 初始化 composer
var composer = new THREE.EffectComposer(renderer);
// 模版缓冲(stencil buffer) https://blog.csdn.net/silangquan/article/details/46608915
composer.renderTarget1.stencilBuffer = true;
composer.renderTarget2.stencilBuffer = true;
composer.setSize(window.innerWidth, window.innerHeight);
composer.addPass(renderScene);
composer.addPass(bloomPass);
// 相对于bloompass新加的
composer.addPass(focusShader);
composer.addPass(effectCopy);
// 修改animate
function animate() {
requestAnimationFrame(animate);
var delt = clock.getDelta();
stats.update();
renderer.clear();
// 删除renderer使用composerrender去渲染
// renderer.render(scene, camera);
// 没理解透这个delt的作用 ???
composer.render(delt);
}
script>
body>
html>
模型的渲染和后期处理就到此就全部结束了。
精灵是一个总是面朝着摄像机的平面,通常含有使用一个半透明的纹理。
var textured = new THREE.TextureLoader().load("textures/warning.png");
var spriteMaterial = new THREE.SpriteMaterial({
// color: 0xffffff,
map: textured
});
var sprite = new THREE.Sprite(spriteMaterial);
sprite.position.set(
25.729931791092394,
10.179400757773436,
36.07142388020101
);
// console.log(sprite);
sprite.scale.x = 10;
sprite.scale.y = 5;
scene.add(sprite);
这张图火灾预警的图其实就是一张透明的png图片,精灵可以用canvas贴图
,你可以自己编写canvas
渲染在指定点上,也可以使用CSS3DRenderer
去实现。
通常的情况下Threejs
里的模型是要分组的。在处理交互起来,有分组会更加清晰明了,就像模块拆分一样。
var group = new THREE.Group();
ShapeGeometry
创建,使用可以设置透明的material
比较好。material
设置transparent:true
可以支持透明model.position.set(x,y,z)
line
、lineLoop
、CubicBezierCurve3
等Threejs
提供的画线方法管道
,然后增加一个路径一样的贴图
,设置wrap
为重复,在animate
中不断更改texture.offset
即可由于单页面中,Threejs
创建的任何材质,模型,贴图……只要含有dispose
方法的,你在页面组件即将销毁的周期中,都要调用下dispose
方法清除,不然可能内存泄漏。刚学会一个妙招,利用WEBGL_lose_context这个API 可以让当前的webgl环境失效,达到取消占用的目的。
beforeDestory(){
this.bloomPass.dispose();
this.envMap.dispose();
this.skymap.dispose();
this.dracoLoader.dispose();
this.spriteMaterial.dispose();
this.sphereGeometry.dispose();
this.meshBasicMaterial.dispose();
this.scene.dispose();
this.controls.dispose();
/*
const data = this.$data;
for (let i in data) {
if (data.hasOwnProperty(i)) {
if (data[i] && typeof data[i].dispose == "function") {
data[i].dispose();
}
}
}
*/
// this.renderer.domElement 就是你的threejs的canvas Dom
let gl = this.renderer.domElement.getContext("webgl");
gl && gl.getExtension("WEBGL_lose_context").loseContext();
}
var lineMaterial = new THREE.LineBasicMaterial({
// 线的颜色
color: "blue",
transparent: true,
opacity: 0.8,
depthFunc: THREE.AlwaysDepth
});
模型.add(
new THREE.LineSegments(模型geometry, lineMaterial)
);
// 之后把模型设置下透明度就成了
function lonlatToMercator(lon, lat, height) {
var z = height ? height : 0;
var x = (lon / 180.0) * 20037508.3427892;
var y = (Math.PI / 180.0) * lat;
var tmp = Math.PI / 4.0 + y / 2.0;
y = (20037508.3427892 * Math.log(Math.tan(tmp))) / Math.PI;
return { x: x, y: y, z: z };
}
// 找到地图的中心对应的经纬度坐标
var center = lonlatToMercator(113.82909, 30.6549, 1);
function lonlatToThree(lon, lat, height) {
var z = height ? height : 0;
var x = (lon / 180.0) * 20037508.3427892;
var y = (Math.PI / 180.0) * lat;
var tmp = Math.PI / 4.0 + y / 2.0;
y = (20037508.3427892 * Math.log(Math.tan(tmp))) / Math.PI;
var result = {
x: x - center.x,
y: y - center.y,
z: z - center.z
};
// x 越大越远
// 因为比地图大了 可以让地图整体放大或缩小 然后偏移到大概位置
return [result.x / 100 + 17, -result.y / 100 + 33];
// [-result.x / 100 - 14, -result.y / 100 - 35];
}
console.log(lonlatToThree(113.84411, 30.65231));
使用SSAA
、FXAA
、SMAA
等抗锯齿后处理。任选其一即可。
initFxaaPass() {
let fxaaPass = new ShaderPass(FXAAShader);
const pixelRatio = this.renderer.getPixelRatio();
fxaaPass.material.uniforms["resolution"].value.x =
1 / (this.width * pixelRatio);
fxaaPass.material.uniforms["resolution"].value.y =
1 / (this.height * pixelRatio);
fxaaPass.renderToScreen = true;
this.fxaaPass= fxaaPass;
},
initSmaaShader() {
const pixelRatio = this.renderer.getPixelRatio();
this.smaaPass = new SMAAPass(
this.width * pixelRatio,
this.height * pixelRatio
);
this.smaaShader.renderToScreen = true;
},
initSsaaShader() {
this.ssaaRenderPass = new SSAARenderPass(this.scene, this.camera);
this.ssaaRenderPass.unbiased = false;
this.ssaaRenderPass.sampleLevel = 2;
},
利用EffectComposer
应用某个效果
initEffectComposer() {
const composer = new EffectComposer(this.renderer);
composer.setSize(this.width, this.height);
composer.addPass(this.renderScene);
composer.addPass(this.ssaaRenderPass);
composer.addPass(this.bloomPass);
composer.addPass(this.focusShader);
composer.addPass(this.effectCopy);
this.composer = composer;
},
png
图片, 类似如下图import * as THREE from "three";
const scaleSpeed = 0.01;
export default {
data(){
return {
// ...
}
},
created(){
this.loadRangeMap()
},
beforeDestory(){
// ...
},
methods: {
initRingAnimate() {
Array.isArray(this.gatewayGroup.children) &&
this.gatewayGroup.children.forEach(v => {
Array.isArray(v.children) &&
v.children.forEach(item => {
if (item.userData.type === "ring") {
item.rotation.z = item.rotation.z + scaleSpeed;
}
});
});
},
loadRangeMap() {
this.rangeMap = this.textureLoader.load(require("../images/range.png"));
},
initOctahedronBufferGeometry() {
this.octahedronBufferGeometry = new THREE.OctahedronBufferGeometry();
},
initCylinderBufferGeometry() {
this.cylinderBufferGeometry = new THREE.CylinderBufferGeometry(
2,
2,
14,
12,
1,
true
);
},
initOctahedron(color) {
let geometry = this.octahedronBufferGeometry;
let material = new THREE.MeshBasicMaterial({
color,
transparent: true,
opacity: 0.3
});
let lineMaterial = new THREE.LineBasicMaterial({
color,
depthFunc: THREE.AlwaysDepth
});
let octahedron = new THREE.Mesh(geometry, material);
let line = new THREE.LineSegments(geometry, lineMaterial);
octahedron.add(line);
octahedron.position.z = -8;
return octahedron;
},
initRing(color) {
let geometry = this.cylinderBufferGeometry;
let material = new THREE.MeshBasicMaterial({
color,
map: this.rangeMap,
side: THREE.DoubleSide,
transparent: true,
depthWrite: false
});
let cylinder = new THREE.Mesh(geometry, material);
cylinder.rotation.x = (Math.PI / 180) * -90;
cylinder.position.z = -2;
return cylinder;
},
initGateway(data = { color: "#54C41D",x: 0, z: 0 }) {
let group = new THREE.Group();
let octahedron = this.initOctahedron(data.color);
let ring = this.initRing(data.color);
group.add(ring);
group.add(octahedron);
group.rotation.x = (Math.PI / 180) * 90;
group.position.y = 0.2;
group.position.x = data.x;
group.position.z = data.z;
this.gatewayGroup.add(group);
}
}
};
group.children
是个数组,每次删除的时候,数组都会变动,比如长度是5,你删了第一个,下次循环你要删除第二个,但是数组长度变了,第二次删除的时候其实删的是第三个了。children.map(v=>{group.remove(children[0])})
一直删除第一个for(let i = 0, l = children.length; i < l; i++){ group.remove(children[i]) }
将数组长度存储下来,就不会变啦!