相机类型
1 .其实什么类型都可以,关键是操作逻辑,要变成拖拽移动视角,并且是滑轮放大和缩小
2 .先拿node来做一下试试,感觉有别的相机
第一版,以动态地形为基准,感觉有点粗糙,镜头会变到地下面,这个是平面的,感觉还要操作,应该是只操作x,z坐标
let startPosition
// 开始拖拽的位置
function getPosition(){
let pickInfo=scene.pick(scene.pointerX,scene.pointerY,function(mesh){
return true
})
if(pickInfo.hit){
return pickInfo.pickedPoint
}
}
function pointerDown() {
startPosition=getPosition()
}
function pointerUp(){
startPosition=null
}
function pointerMove(event){
if(!startPosition)return
let current=getPosition()
if(!current)return
let diff=current.subtract(startPosition)
node.position.addInPlace(diff)
startPosition=current
}
scene.onPointerObservable.add((pointerInfo)=>{
switch(pointerInfo.type){
case BABYLON.PointerEventTypes.POINTERDOWN:
pointerDown()
break
case BABYLON.PointerEventTypes.POINTERUP:
pointerUp()
break
case BABYLON.PointerEventTypes.POINTERMOVE:
pointerMove(pointerInfo)
break
}
})
第二版,以屏幕为定位
<!DOCTYPE html>
<!-- 添加小人,使用序列图 -->
<html>
<head>
<meta http-equiv="Content-Type" content="text/html" charset="utf-8"/>
<title>Babylon - Getting Started</title>
<!-- Link to the last version of BabylonJS -->
<script src="https://preview.babylonjs.com/babylon.js"></script>
<!-- Link to the last version of BabylonJS loaders to enable loading filetypes such as .gltf -->
<script src="https://preview.babylonjs.com/loaders/babylonjs.loaders.min.js"></script>
<!-- Link to pep.js to ensure pointer events work consistently in all browsers -->
<script src="https://code.jquery.com/pep/0.4.1/pep.js"></script>
<!-- <script src="https://cdnjs.cloudflare.com/ajax/libs/cannon.js/0.6.2/cannon.min.js"></script> -->
<!-- <script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.6.2/dat.gui.min.js"></script> -->
<!-- <script src="https://preview.babylonjs.com/gui/babylon.gui.min.js"></script> -->
<!-- <script src="https://cdn.rawgit.com/BabylonJS/Extensions/master/DynamicTerrain/dist/babylon.dynamicTerrain.min.js"></script> -->
<script src="./dy.js"></script>
<script src="./noise.js"></script>
</head>
<style>
html, body {
overflow: hidden;
width : 100%;
height : 100%;
margin : 0;
padding : 0;
}
#renderCanvas {
width : 100%;
height : 100%;
touch-action: none;
}
</style>
<body>
<canvas id="renderCanvas" touch-action="none"></canvas>
<script>
const canvas = document.getElementById("renderCanvas");
var engine = null;
// 这里还不能用let,不然就爆炸,获取不到engine
var scene = null;
var sceneToRender = null;
const createDefaultEngine = function() { return new BABYLON.Engine(canvas, true, { preserveDrawingBuffer: true, stencil: true, disableWebGL2Support: false}); };
let createScene=async function(){
// 关键函数都写在这个里面
var scene = new BABYLON.Scene(engine)
var camera = new BABYLON.UniversalCamera("camera1", new BABYLON.Vector3(0, 20, 0), scene);
// 第二个坐标是摄像机在场景中的位置,现在还需要一个就是摄像机的朝向
camera.rotation=new BABYLON.Vector3(0.7697057340138546,0.00772265625,0)
camera.position.y=60
// 高度最高现在定为60,最大值,按照这个最大值来调参数,地图大小也需要调,现在镜头外面还有没有显示的地
var light = new BABYLON.HemisphericLight("light1", new BABYLON.Vector3(0.0, 1.0, 0.0), scene);
light.intensity = 0.75;
light.specular = BABYLON.Color3.Black();
// 这俩值是不是决定了地图的实际大小
let terrainTexture=new BABYLON.Texture("https://www.babylonjs-playground.com/textures/ground.jpg",scene)
let terrainMat=new BABYLON.StandardMaterial('tm',scene)
terrainMat.diffuseTexture=terrainTexture
terrainMat.diffuseTexture.UScale=1
terrainMat.diffuseTexture.VScale=1
var spsMaterial = new BABYLON.StandardMaterial("spsm", scene);
var spsUrl = "https://jerome.bousquie.fr/BJS/images/uv_texture.jpg";
var spsTexture = new BABYLON.Texture(spsUrl, scene);
spsMaterial.diffuseTexture = spsTexture;
var mapSubX = 1000;
var mapSubZ = 1000;
var seed = 0.3;
var noiseScale = 0.03;
var elevationScale = 6.0;
noise.seed(seed);
var mapData = new Float32Array(mapSubX * mapSubZ * 3);
let SPmapData=[[],[],[]]
// 这俩其实不是很常用,毕竟真的引入mesh这俩应该都有的吧,除非mesh仅仅是颜色不一样代表不同的物体,来做优化的时候
var SPcolorData = [[], [], []];
var SPuvData = [[], [], []];
// x位移系数:在原来的基础上移动的距离,或者说实际屏幕和地图的位移比例吧
let xMove=0.033
// 第一次0.5的值感觉还是有点大
// y位移系数
let yMove=0.053
// 最后这个值感觉这里就差不多了,但是这个系数还是要和镜头高度有关系的
for (var l = 0; l < mapSubZ; l++) {
for (var w = 0; w < mapSubX; w++) {
var x = (w - mapSubX * 0.5) * 2.0;
var z = (l - mapSubZ * 0.5) * 2.0;
var y = noise.simplex2(x * noiseScale, z * noiseScale); // altitude
y *= (0.5 + y) * y * elevationScale;
mapData[3 *(l * mapSubX + w)] = x;
mapData[3 * (l * mapSubX + w) + 1] = y;
mapData[3 * (l * mapSubX + w) + 2] = z;
let index=l*mapSubX+w
if(Math.random()>0.85){
// 如果满足这个条件,就添加一个物体
let xp=x
let yp=y
let zp=z
let ry=Math.random()*3.6
let sx=0.5+Math.random()
let sy=0.5+Math.random()
let sz=0.5+Math.random()
let r = Math.abs(xp) / mapSubX * 1.2 + 0.5;
let g = Math.abs(zp) / mapSubZ * 1.2 + 0.5;
let b = Math.abs(yp) / elevationScale + 0.1;
// UVs
let u = 0.9 * Math.random();
let v = 0.9 * Math.random();
let type = index % 3;
SPmapData[index % 3].push(xp, yp, zp, 0, ry, 0, sx, sy, sz);
SPcolorData[type].push(r, g, b, 1.0);
SPuvData[type].push(u, v, u + 0.1, v + 0.1);
}
}
}
let model1=BABYLON.MeshBuilder.CreateBox('m1',{size:1},scene)
let model2=BABYLON.MeshBuilder.CreatePolyhedron('m2',{size:0.5},scene)
let model3=BABYLON.MeshBuilder.CreateSphere('m3',{segments:3},scene)
let sps=new BABYLON.SolidParticleSystem("SPS",scene)
let type1=sps.addShape(model1,10000)
let type2=sps.addShape(model2,10000)
// 为什么数量少的时候,镜头了将要到达的地方物体消失,正好相反了
// 这里的数量是当前地形中最多可见的形状的数量,100的意思就是地图中最多可见100个model2模型
let type3=sps.addShape(model3,10000)
sps.buildMesh()
model1.dispose()
model2.dispose()
model3.dispose()
sps.mesh.material = spsMaterial;
var terrainSub = 160;
// 这个才是镜头最大展示的数量
var params = {
mapData: mapData,
mapSubX: mapSubX,
mapSubZ: mapSubZ,
SPmapData: SPmapData,
sps: sps,
terrainSub: terrainSub,
subToleranceX:160,
subToleranceZ:160,
// 这个更新是指所有的静态元素更新,有的时候,可能不移动镜头都需要更新计算某些值,比如地块的变化。所以还需要主动触发地块更新
}
var terrain = new BABYLON.DynamicTerrain("t", params, scene);
terrain.isAlwaysVisible=true
var terrainMaterial = new BABYLON.StandardMaterial("tm", scene);
terrainMaterial.diffuseTexture = terrainTexture;
terrain.mesh.material = terrainMaterial;
// terrain.updateCameraLOD = function(terrainCamera) {
// // LOD value increases with camera altitude
// var camLOD = Math.abs((terrainCamera.globalPosition.y / 30.0)|0);
// // 这里加了动态调节,就不用别的地方加了,但是这里好像不能使用这个,因为我开大镜头,精度是不能丢失的,现在会丢失地面建筑物的精度,这个只能保证拉大镜头,地面永远都在镜头内
// return camLOD;
// };
terrain.update(true);
let startPosition
// 开始拖拽的位置
let isPointerDowm=false
function pointerDown(e) {
isPointerDowm=true
}
function pointerUp(){
isPointerDowm=false
}
function pointerMove(e){
if(!isPointerDowm)return
let mx=e.event.movementX*xMove
let my=e.event.movementY*yMove
//这是最基础的调整,还需要调整,乘一个系数
camera.position.addInPlace(new BABYLON.Vector3(-mx,0,my))
}
function pointerWhell(e){
const delta=e.event.wheelDelta
if(delta>0){
if(camera.position.y>=60)return
camera.position.y+=1
}else{
if(camera.position.y<=20)return
camera.position.y-=1
}
console.log('当前相机高度',camera.position.y)
}
scene.onPointerObservable.add((pointerInfo)=>{
switch(pointerInfo.type){
case BABYLON.PointerEventTypes.POINTERDOWN:
pointerDown(pointerInfo)
break
case BABYLON.PointerEventTypes.POINTERUP:
pointerUp(pointerInfo)
break
case BABYLON.PointerEventTypes.POINTERMOVE:
pointerMove(pointerInfo)
break
case BABYLON.PointerEventTypes.POINTERWHEEL:
pointerWhell(pointerInfo)
break;
}
})
let count=0
scene.registerBeforeRender(()=>{
count++
if(count>=200){
// console.log('speed',camera.speed)
// console.log('seppd',camera.position)
count=0
}
})
return scene
}
window.initFunction=async function(){
let asyncEngineCreation=async function(){
try{
return createDefaultEngine()
}catch(e){
return createDefaultEngine()
}
}
window.engine=await asyncEngineCreation()
if(!engine){
throw('engine should not be null')
}
window.scene=createScene()
}
initFunction().then(()=>{
scene.then((returnedScene)=>{
sceneToRender=returnedScene
})
let limit=60
let count=0
let fps=0
engine.runRenderLoop(function(){
count++
if(count===limit){
fps=Math.round(engine.getFps())
count=0
console.log('当前帧数是'+fps)
}
if(sceneToRender&&sceneToRender.activeCamera){
sceneToRender.render()
}
})
})
window.addEventListener('resize',function(){
engine.resize()
})
</script>
</body>
</html>
问题1
1 .视角大小,换一种相机之后,展示的大小有问题啊
2 .默认的freeCamera的操作逻辑如下
1 .键盘,左右方向键左右移动相机,上下方向键前后移动
2 .鼠标,以相机为原点绕旋转轴旋转相机
3 .触摸-左右滑动相机左右移动,上下滑动前后移动
3 .我们想要的效果
1 .控制缩放,摄像机可以拉近和拉远,滚轮实现
2 .移动
3 .限制相机移动范围
4 .后续
1 .相机的移动需要有缓动,不能很僵硬的移动
2 .不同相机高度,移动的距离是不一样的。镜头远,滑动移动的距离大,镜头近,滑动移动的距离小
3 .要把屏幕上的移动距离和实际的体块的大小完全一致,比如屏幕上移动了10cm,地图里面的地块也应该移动了这么多
4 .按照原来的基本逻辑,按一下键盘,会移动5的距离,但是拖拽的时候就做不了这个
5 .这里改相机的speed没用,加了一个系数实现了想要的效果
6 .镜头的朝向,这个要改镜头的旋转
7 .他的渲染策略是以中心为基础渲染,现在是第三方镜头,会不会存在一些已经渲染的但是不在镜头中的呢
5 .是否需要更改相机类型为followCamera
6 .裸体一旦沦为艺术,那便是最圣洁的,道德一旦沦为虚伪,那便是最下流的
SLG地图的更新
1 .地形,地块的更新,拖动相机才会发生变化。这种是拖动相机自动完成的,这里面存储了物体的信息,这里只有拖动才会触发。
2 .不拖动相机也会发生的变化,部队,建筑物,本来是想建筑物
1 .有一些建筑物的信息不拖动也需要触发,比如建筑的升级,这种变化就需要单独触发地形更新,但是为了动态地形里面的一个建筑物,就让全部的update,感觉也有点不合理,这里要看下怎么操作
2 .部队那些不放在这里处理,需要一个专门更新mesh的函数现在的terrain.update,应该是全部会计算的
LOD不能添加
terrain.updateCameraLOD = function(terrainCamera) {
// LOD value increases with camera altitude
var camLOD = Math.abs((terrainCamera.globalPosition.y / 30.0)|0);
// 这里加了动态调节,就不用别的地方加了,但是这里好像不能使用这个,因为我开大镜头,精度是不能丢失的,现在会丢失地面建筑物的精度,这个只能保证拉大镜头,地面永远都在镜头内
return camLOD;
};
1 .slg里面的lod只是做一些镜头拉小,野怪消失不见,建筑物或者地形的渲染精度是不会变得
性能问题
1 .首次渲染出来,拖拽的时候会有卡顿
2 .为什么第一次pointerDown会有问题,第一次不是仅仅是添加了一个鼠标被按下的标志位么
TODO
1 .添加阴影
2 .添加各种地形
3 .添加各种建筑
4 .添加各种动画
最难的,里面的坐标也有点难搞
1 .
实践
1 .这里面好像不能添加复杂的粒子,添加一个引入的mesh很卡,但是单独的固体粒子系统里面是没问题的,而且,动态地形还需要做的是我还要管理mesh的动画和操作,这样越来越复杂的话,感觉还是要分开,那这样动态地形的优势就完全没了.还是需要管理单独的粒子系统
单独的粒子系统可以加入引入mesh的情况,选中也是可以的,播放动画呢
const scene = new BABYLON.Scene(engine);
const camera = new BABYLON.ArcRotateCamera("ArcRotateCamera", -Math.PI / 2, Math.PI / 2.2, 50, new BABYLON.Vector3(0, 0, 0), scene);
camera.attachControl(canvas, true);
const light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(0, 1, 0), scene);
const SPS = new BABYLON.SolidParticleSystem("SPS", scene,{useModelMaterial:true});
BABYLON.SceneLoader.ImportMeshAsync("", "scenes/", "skull.babylon", scene).then((result)=>{
console.log(result)
SPS.addShape(result.meshes[0],2)
const mesh = SPS.buildMesh();
SPS.initParticles = () => {
for (let p = 0; p < SPS.nbParticles; p++) {
const particle = SPS.particles[p];
particle.position.x = BABYLON.Scalar.RandomRange(-50, 50);
particle.position.y = BABYLON.Scalar.RandomRange(-50, 50);
particle.position.z = BABYLON.Scalar.RandomRange(-50, 50);
}
};
// //Update SPS mesh
SPS.initParticles();
SPS.setParticles();
})
return scene;
单独的粒子播放各自的动画
1 .这个就更不要想了
2 .现在是可以展示粒子了,但是很卡,视野内显示有1000个不行了,下一步就是要看是粒子的局限,还是这个动态系统的问题
3 .直接拿固体粒子发现是可以的,3000以下是无压力的,所以说地形就应该干地形应该做的事情,或者粒子不是那么多,现在可能许多点都是都是很密的,把密度往下压一压试试
4 .要看粒子的里面用这种mesh能不能播自己的动画
5 .粒子是否真的能干这个东西,还是要使用最经典的克隆https://playground.babylonjs.com/#TWQZAU#1或者这个是什么原理 瘦实例来实现的,粒子是完全有用的,所以还是瘦实例是对的.这里应该只是用来摆山,城之类不动的,玩家主城
解决方法
1 .减少密度,仅仅只是永远不变的物体,加入粒子,比如一些固定的城,山之类的
2 .类似土地之类的大面积的,压根就不让他展示渲染,换另一种逻辑算了,操作不了。尤其是带动画的这种,感觉更加不行。粒子,只能用来实现一些简单的,操作,这种算是复杂的是不行的.
3 .https://doc.babylonjs.com/divingDeeper/particles/particle_system/animation 这里看来是可以实现动画粒子的,但是。只是纹理之类的,也不算是真的mesh动作的那种吧,这里只是可以用来实现复杂的粒子效果而存在。比如火,烟之类的
减小密度,看是否卡顿
for (var l = 0; l < mapSubZ; l++) {
for (var w = 0; w < mapSubX; w++) {
var x = (w - mapSubX * 0.5) * 2.0;
var z = (l - mapSubZ * 0.5) * 2.0;
var y = noise.simplex2(x * noiseScale, z * noiseScale); // altitude
y *= (0.5 + y) * y * elevationScale;
mapData[3 *(l * mapSubX + w)] = x;
mapData[3 * (l * mapSubX + w) + 1] = y;
mapData[3 * (l * mapSubX + w) + 2] = z;
let index=l*mapSubX+w
let random=Math.random()
if(random>0.99999){
// 如果满足这个条件,就添加一个物体
let xp=x
let yp=y
let zp=z
let ry=Math.random()*3.6
let sx=0.5+Math.random()
let sy=0.5+Math.random()
let sz=0.5+Math.random()
let type = index % 3;
SPmapData[index % 3].push(xp, yp, zp, 0, ry, 0, sx, sy, sz);
console.log(SPmapData,'现在的粒子数',random)
}
}
}
//这样看起来完全不卡顿啊
虚拟地图的摄像机
1 .想做类似于MOBA的地图视角,但是发现地图生成的时候就是按照摄像机在正中间,如果简单的来偏转,就需要重新渲染更多的无用的出来
[图片上传失败...(image-d10f5-1639585054269)]
2 .最后结论,虚拟地形上的元素,只适合非常小的,不变的东西,比如草,没了,别的东西都不适合,虽然较少粒子数也支持一些复杂的东西,但是也不支持mesh的动画啥的,投一些宝箱啥的,但是一旦东西多了。粒子是会创建每个实体的,不能用来节省资源。所以还是要用瘦实例的方式来操作
3 .上面是很多的粒子实体,但是实际上同屏最多展示100块地。试下100块这些会不会卡
4 .如何查看当前画面有多少粒子呢?
5 .100188 '现在的粒子数'三倍的这个粒子数,都是可以跑满60帧的
6 .复杂的这些同屏显示500个就会卡顿,但是正常的slg游戏,一个屏幕最多显示100块地,那就是说这种方法还是可以用的
<!DOCTYPE html>
<!-- 添加小人,使用序列图 -->
<html>
<head>
<meta http-equiv="Content-Type" content="text/html" charset="utf-8"/>
<title>Babylon - Getting Started</title>
<!-- Link to the last version of BabylonJS -->
<script src="https://preview.babylonjs.com/babylon.js"></script>
<!-- Link to the last version of BabylonJS loaders to enable loading filetypes such as .gltf -->
<script src="https://preview.babylonjs.com/loaders/babylonjs.loaders.min.js"></script>
<!-- Link to pep.js to ensure pointer events work consistently in all browsers -->
<script src="https://code.jquery.com/pep/0.4.1/pep.js"></script>
<!-- <script src="https://cdnjs.cloudflare.com/ajax/libs/cannon.js/0.6.2/cannon.min.js"></script> -->
<!-- <script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.6.2/dat.gui.min.js"></script> -->
<!-- <script src="https://preview.babylonjs.com/gui/babylon.gui.min.js"></script> -->
<!-- <script src="https://cdn.rawgit.com/BabylonJS/Extensions/master/DynamicTerrain/dist/babylon.dynamicTerrain.min.js"></script> -->
<script src="https://preview.babylonjs.com/proceduralTexturesLibrary/babylonjs.proceduralTextures.min.js"></script>
<script src="https://preview.babylonjs.com/materialsLibrary/babylonjs.materials.min.js"></script>
<script src="./dy.js"></script>
<script src="./noise.js"></script>
<!-- <script src="./tree.js"></script> -->
</head>
<style>
html, body {
overflow: hidden;
width : 100%;
height : 100%;
margin : 0;
padding : 0;
}
#renderCanvas {
width : 100%;
height : 100%;
touch-action: none;
}
</style>
<body>
<canvas id="renderCanvas" touch-action="none"></canvas>
<script>
const canvas = document.getElementById("renderCanvas");
var engine = null;
// 这里还不能用let,不然就爆炸,获取不到engine
var scene = null;
var sceneToRender = null;
const createDefaultEngine = function() { return new BABYLON.Engine(canvas, true, { preserveDrawingBuffer: true, stencil: true, disableWebGL2Support: false}); };
let createScene=async function(){
// 关键函数都写在这个里面
var scene = new BABYLON.Scene(engine)
var camera = new BABYLON.UniversalCamera("camera1", new BABYLON.Vector3(0, 20, 0), scene);
// 第二个坐标是摄像机在场景中的位置,现在还需要一个就是摄像机的朝向
camera.rotation=new BABYLON.Vector3(0.8697057340138546,0.00772265625,0)
camera.position.y=100
// 高度最高现在定为60,最大值,按照这个最大值来调参数,地图大小也需要调,现在镜头外面还有没有显示的地
let tree=await BABYLON.SceneLoader.ImportMeshAsync('','https://playground.babylonjs.com/scenes/Elf/','Elf.gltf',scene)
console.log('tree',tree)
var light = new BABYLON.HemisphericLight("light1", new BABYLON.Vector3(0.0, 1.0, 0.0), scene);
light.intensity = 0.75;
light.specular = BABYLON.Color3.Black();
// 这俩值是不是决定了地图的实际大小
let terrainTexture=new BABYLON.Texture("https://www.babylonjs-playground.com/textures/ground.jpg",scene)
let terrainMat=new BABYLON.StandardMaterial('tm',scene)
terrainMat.diffuseTexture=terrainTexture
terrainMat.diffuseTexture.UScale=1
terrainMat.diffuseTexture.VScale=1
var spsMaterial = new BABYLON.StandardMaterial("spsm", scene);
var spsUrl = "https://jerome.bousquie.fr/BJS/images/uv_texture.jpg";
var spsTexture = new BABYLON.Texture(spsUrl, scene);
spsMaterial.diffuseTexture = spsTexture;
var mapSubX = 1000;
var mapSubZ = 1000;
var seed = 0.3;
var noiseScale = 0.03;
var elevationScale = 6.0;
noise.seed(seed);
var mapData = new Float32Array(mapSubX * mapSubZ * 3);
let SPmapData=[[],[],[]]
// 这俩其实不是很常用,毕竟真的引入mesh这俩应该都有的吧,除非mesh仅仅是颜色不一样代表不同的物体,来做优化的时候
var SPcolorData = [[], [], []];
var SPuvData = [[], [], []];
// x位移系数:在原来的基础上移动的距离,或者说实际屏幕和地图的位移比例吧
let xMove=0.033
// 第一次0.5的值感觉还是有点大
// y位移系数
let yMove=0.053
// 最后这个值感觉这里就差不多了,但是这个系数还是要和镜头高度有关系的
for (var l = 0; l < mapSubZ; l++) {
for (var w = 0; w < mapSubX; w++) {
var x = (w - mapSubX * 0.5) * 2.0;
var z = (l - mapSubZ * 0.5) * 2.0;
var y = noise.simplex2(x * noiseScale, z * noiseScale); // altitude
y *= (0.5 + y) * y * elevationScale;
mapData[3 *(l * mapSubX + w)] = x;
mapData[3 * (l * mapSubX + w) + 1] = y;
mapData[3 * (l * mapSubX + w) + 2] = z;
let index=l*mapSubX+w
let random=Math.random()
if(Math.random()>0.9){
// 如果满足这个条件,就添加一个物体
let xp=x
let yp=y
let zp=z
let ry=Math.random()*3.6
let sx=0.5+Math.random()
let sy=0.5+Math.random()
let sz=0.5+Math.random()
let type = index % 3;
SPmapData[type].push(xp, yp, zp, 0, ry, 0, sx, sy, sz);
// console.log(SPmapData,'现在的粒子数',random)
}
}
}
console.log(SPmapData[0].length/3,'现在的粒子数')
let model2=BABYLON.MeshBuilder.CreatePolyhedron('m2',{size:0.5},scene)
let model3=BABYLON.MeshBuilder.CreateSphere('m3',{segments:3},scene)
let sps=new BABYLON.SolidParticleSystem("SPS",scene)
let type1=sps.addShape(tree.meshes[1],100)
// let type1=sps.addShape(model2,1000)
let type2=sps.addShape(model2,100)
// 为什么数量少的时候,镜头了将要到达的地方物体消失,正好相反了
// 这里的数量是当前地形中最多可见的形状的数量,100的意思就是地图中最多可见100个model2模型
let type3=sps.addShape(model3,100)
sps.buildMesh()
tree.meshes[1].dispose()
model2.dispose()
model3.dispose()
// sps.mesh.material = spsMaterial;
var terrainSub = 160;
// 这个才是镜头最大展示的数量
var params = {
mapData: mapData,
mapSubX: mapSubX,
mapSubZ: mapSubZ,
SPmapData: SPmapData,
sps: sps,
terrainSub: terrainSub,
subToleranceX:160,
subToleranceZ:160,
// 这个更新是指所有的静态元素更新,有的时候,可能不移动镜头都需要更新计算某些值,比如地块的变化。所以还需要主动触发地块更新
}
var terrain = new BABYLON.DynamicTerrain("t", params, scene);
terrain.isAlwaysVisible=true
var terrainMaterial = new BABYLON.StandardMaterial("tm", scene);
terrainMaterial.diffuseTexture = terrainTexture;
terrain.mesh.material = terrainMaterial;
// terrain.updateCameraLOD = function(terrainCamera) {
// // LOD value increases with camera altitude
// var camLOD = Math.abs((terrainCamera.globalPosition.y / 30.0)|0);
// // 这里加了动态调节,就不用别的地方加了,但是这里好像不能使用这个,因为我开大镜头,精度是不能丢失的,现在会丢失地面建筑物的精度,这个只能保证拉大镜头,地面永远都在镜头内
// return camLOD;
// };
terrain.update(true);
let startPosition
// 开始拖拽的位置
let isPointerDowm=false
function pointerDown(e) {
isPointerDowm=true
}
function pointerUp(){
isPointerDowm=false
}
function pointerMove(e){
if(!isPointerDowm)return
let mx=e.event.movementX*xMove
let my=e.event.movementY*yMove
//这是最基础的调整,还需要调整,乘一个系数
camera.position.addInPlace(new BABYLON.Vector3(-mx,0,my))
}
function pointerWhell(e){
const delta=e.event.wheelDelta
if(delta>0){
// if(camera.position.y>=100)return
// camera.position.y+=1
// 用这里来调试下摄像机吧.现在的摄像机老是感觉很奇怪
camera.rotation.y+=0.1
// console.log(camera.rotation.x)
}else{
// if(camera.position.y<=20)return
// camera.position.y-=1
camera.rotation.y-=0.1
}
// console.log('当前相机高度',camera.position.y)
console.log(camera.rotation)
}
scene.onPointerObservable.add((pointerInfo)=>{
switch(pointerInfo.type){
case BABYLON.PointerEventTypes.POINTERDOWN:
pointerDown(pointerInfo)
break
case BABYLON.PointerEventTypes.POINTERUP:
pointerUp(pointerInfo)
break
case BABYLON.PointerEventTypes.POINTERMOVE:
pointerMove(pointerInfo)
break
case BABYLON.PointerEventTypes.POINTERWHEEL:
pointerWhell(pointerInfo)
break;
}
})
let count=0
scene.registerBeforeRender(()=>{
count++
if(count>=200){
// console.log('speed',camera.speed)
// console.log('seppd',camera.position)
count=0
}
})
return scene
}
window.initFunction=async function(){
let asyncEngineCreation=async function(){
try{
return createDefaultEngine()
}catch(e){
return createDefaultEngine()
}
}
window.engine=await asyncEngineCreation()
if(!engine){
throw('engine should not be null')
}
window.scene=createScene()
}
initFunction().then(()=>{
scene.then((returnedScene)=>{
sceneToRender=returnedScene
})
let limit=60
let count=0
let fps=0
engine.runRenderLoop(function(){
count++
if(count===limit){
fps=Math.round(engine.getFps())
count=0
console.log('当前帧数是'+fps)
}
if(sceneToRender&&sceneToRender.activeCamera){
sceneToRender.render()
}
})
})
window.addEventListener('resize',function(){
engine.resize()
})
</script>
</body>
</html>
总结
1 .也就是说,最小的渲染粒度是100块地,现在可以保证这里是完全正确的
2 .总的地图数据我们可以一次全加载进来,甚至包括点的数据,但是同时最多显示的数据则是要严格要求的.这样就保证了不会卡顿.在虚拟地形里面,同时操作100块地都会卡顿,无法60帧
3 .粒子系统和这个分来来展示,单独这个显示是可以4040,就同一个展示来说,那么他们唯一的不同,就还没算,移动相机的时候需要新算的地块,因为就展示来说,5050都不会卡,关键是拖拽之后,还有新的数据需要显示.
1 .其实这里如果我们要做的就是每次移动完,拿到当前摄像机里面的所有的数据然后渲染出来.这样感觉的话,这种也是要崩的啊,其实有点像这种话操作,每次鼠标抬起的时候,就渲染新的数据,甚至move的时候也渲染
function pointerUp(e){
isPointerDowm=false
let xCount=20
let yCount=20
console.log(e)
// 循环添加树
for(let x=0;x<xCount;x++){
for(let y=0;y<yCount;y++){
let newTree=tree.meshes[0].clone(`tree-${x}-${y}`)
newTree.position.x=10*x+e.pickInfo.pickedPoint.x
newTree.position.z=10*y+e.pickInfo.pickedPoint.z
}
}
}
//果然不行,40*40着这种情况根本拖动不了.而且还仅仅是up的时候不算
//进过测试,up这种操作,仅仅支持20*20这种量级,这里还可以优化,一次拖拽,并不需要全部生成新的,仅仅是需要diff,这还有需要操作的东西.也就是说,每次move,都要算出remove,add的数据,然后做删除,增加,先增加,然后删除.这个好像还很麻烦,但是虚拟列表那里已经自带了.还是想下那个行不行,如果move的时候性能最后和虚拟布局的性能一样,那还不如用那个来做,不过,这个还是最简单的clone的操作,可能从实现方面已经是最拉胯的方式了
move的时候
最关键的操作
1 .最关键的现在感觉是地图的摄像机高度,角度,这些决定了真的应该渲染的元素的数量,比如这样的图,这个高度,这个角度,决定了地图内显示的真正数量的数据,
var terrainSub = 50;
// 这个才是镜头最大展示的数量,这个才是关键,这个是50的话,一点也不卡
var params = {
mapData: mapData,
mapSubX: mapSubX,
mapSubZ: mapSubZ,
SPmapData: SPmapData,
sps: sps,
terrainSub: terrainSub,
subToleranceX:terrainSub,
subToleranceZ:terrainSub,
// 这个更新是指所有的静态元素更新,有的时候,可能不移动镜头都需要更新计算某些值,比如地块的变化。所以还需要主动触发地块更新
}
3 .现在的镜头高度下,需要70才能完全盖住视野,所以最后渲染的就是70个宽度内的虚拟列表内的东西,一切都和摄像机高度有关系,但是65的时候发现帧数就已经不满60了,所以这个最后还是一个协调的东西,互相妥协,也就是高度,视野内渲染的数量,然后去决定到底使用那种方案
4 .
<!DOCTYPE html>
<!-- 添加小人,使用序列图 -->
<html>
<head>
<meta http-equiv="Content-Type" content="text/html" charset="utf-8"/>
<title>Babylon - Getting Started</title>
<!-- Link to the last version of BabylonJS -->
<script src="https://preview.babylonjs.com/babylon.js"></script>
<!-- Link to the last version of BabylonJS loaders to enable loading filetypes such as .gltf -->
<script src="https://preview.babylonjs.com/loaders/babylonjs.loaders.min.js"></script>
<!-- Link to pep.js to ensure pointer events work consistently in all browsers -->
<script src="https://code.jquery.com/pep/0.4.1/pep.js"></script>
<!-- <script src="https://cdnjs.cloudflare.com/ajax/libs/cannon.js/0.6.2/cannon.min.js"></script> -->
<!-- <script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.6.2/dat.gui.min.js"></script> -->
<!-- <script src="https://preview.babylonjs.com/gui/babylon.gui.min.js"></script> -->
<!-- <script src="https://cdn.rawgit.com/BabylonJS/Extensions/master/DynamicTerrain/dist/babylon.dynamicTerrain.min.js"></script> -->
<script src="https://preview.babylonjs.com/proceduralTexturesLibrary/babylonjs.proceduralTextures.min.js"></script>
<script src="https://preview.babylonjs.com/materialsLibrary/babylonjs.materials.min.js"></script>
<script src="./dy.js"></script>
<script src="./noise.js"></script>
<!-- <script src="./tree.js"></script> -->
</head>
<style>
html, body {
overflow: hidden;
width : 100%;
height : 100%;
margin : 0;
padding : 0;
}
#renderCanvas {
width : 100%;
height : 100%;
touch-action: none;
}
</style>
<body>
<canvas id="renderCanvas" touch-action="none"></canvas>
<script>
const canvas = document.getElementById("renderCanvas");
var engine = null;
// 这里还不能用let,不然就爆炸,获取不到engine
var scene = null;
var sceneToRender = null;
const createDefaultEngine = function() { return new BABYLON.Engine(canvas, true, { preserveDrawingBuffer: true, stencil: true, disableWebGL2Support: false}); };
let createScene=async function(){
// 关键函数都写在这个里面
var scene = new BABYLON.Scene(engine)
var camera = new BABYLON.UniversalCamera("camera1", new BABYLON.Vector3(0, 20, 0), scene);
// 第二个坐标是摄像机在场景中的位置,现在还需要一个就是摄像机的朝向
camera.rotation=new BABYLON.Vector3( 1.8697057340138552,0.00772265625,0)
camera.position.y=70
// 高度最高现在定为60,最大值,按照这个最大值来调参数,地图大小也需要调,现在镜头外面还有没有显示的地
// let tree=await BABYLON.SceneLoader.ImportMeshAsync('','https://playground.babylonjs.com/scenes/Elf/','Elf.gltf',scene)
let tree=await BABYLON.SceneLoader.ImportMeshAsync('',"http://192.168.1.102:8080/source/glb/","tree.babylon",scene)
console.log('tree',tree.meshes[0].rotation)
var light = new BABYLON.HemisphericLight("light1", new BABYLON.Vector3(0.0, 1.0, 0.0), scene);
light.intensity = 0.75;
light.specular = BABYLON.Color3.Black();
// 这俩值是不是决定了地图的实际大小
let terrainTexture=new BABYLON.Texture("https://www.babylonjs-playground.com/textures/ground.jpg",scene)
let terrainMat=new BABYLON.StandardMaterial('tm',scene)
terrainMat.diffuseTexture=terrainTexture
terrainMat.diffuseTexture.UScale=1
terrainMat.diffuseTexture.VScale=1
var spsMaterial = new BABYLON.StandardMaterial("spsm", scene);
var spsUrl = "https://jerome.bousquie.fr/BJS/images/uv_texture.jpg";
var spsTexture = new BABYLON.Texture(spsUrl, scene);
spsMaterial.diffuseTexture = spsTexture;
var mapSubX = 1000;
var mapSubZ = 1000;
var seed = 0.3;
var noiseScale = 0.03;
var elevationScale = 6.0;
noise.seed(seed);
var mapData = new Float32Array(mapSubX * mapSubZ * 3);
let SPmapData=[[],[],[]]
// 这俩其实不是很常用,毕竟真的引入mesh这俩应该都有的吧,除非mesh仅仅是颜色不一样代表不同的物体,来做优化的时候
var SPcolorData = [[], [], []];
var SPuvData = [[], [], []];
// x位移系数:在原来的基础上移动的距离,或者说实际屏幕和地图的位移比例吧
let xMove=0.033
// 第一次0.5的值感觉还是有点大
// y位移系数
let yMove=0.053
// 最后这个值感觉这里就差不多了,但是这个系数还是要和镜头高度有关系的
for (var l = 0; l < mapSubZ; l++) {
for (var w = 0; w < mapSubX; w++) {
var x = (w - mapSubX * 0.5) * 2.0;
var z = (l - mapSubZ * 0.5) * 2.0;
var y = noise.simplex2(x * noiseScale, z * noiseScale); // altitude
y *= (0.5 + y) * y * elevationScale;
mapData[3 *(l * mapSubX + w)] = x;
mapData[3 * (l * mapSubX + w) + 1] = y;
mapData[3 * (l * mapSubX + w) + 2] = z;
let index=l*mapSubX+w
let random=Math.random()
if(Math.random()>0.9){
// 如果满足这个条件,就添加一个物体
let xp=x
let yp=y
let zp=z
let ry=Math.random()*3.6
let type = index % 3;
SPmapData[type].push(xp, yp, zp, -1.57, 0, 0, 10,10,10);
// console.log(SPmapData,'现在的粒子数',random)
}
}
}
console.log(SPmapData[0].length/3,'现在的粒子数')
let model2=BABYLON.MeshBuilder.CreatePolyhedron('m2',{size:0.5},scene)
let model3=BABYLON.MeshBuilder.CreateSphere('m3',{segments:3},scene)
let sps=new BABYLON.SolidParticleSystem("SPS",scene,{useModelMaterial:true})
let type1=sps.addShape(tree.meshes[0],1000)
// let type1=sps.addShape(model2,1000)
let type2=sps.addShape(model2,1)
// 为什么数量少的时候,镜头了将要到达的地方物体消失,正好相反了
// 这里的数量是当前地形中最多可见的形状的数量,100的意思就是地图中最多可见100个model2模型
let type3=sps.addShape(model3,1)
sps.buildMesh()
// tree.meshes[0].dispose()
model2.dispose()
model3.dispose()
// sps.mesh.material = spsMaterial;
var terrainSub = 65;
// 这个才是镜头最大展示的数量
var params = {
mapData: mapData,
mapSubX: mapSubX,
mapSubZ: mapSubZ,
SPmapData: SPmapData,
sps: sps,
terrainSub: terrainSub,
subToleranceX:terrainSub,
subToleranceZ:terrainSub,
// 这个更新是指所有的静态元素更新,有的时候,可能不移动镜头都需要更新计算某些值,比如地块的变化。所以还需要主动触发地块更新
}
var terrain = new BABYLON.DynamicTerrain("t", params, scene);
terrain.isAlwaysVisible=true
var terrainMaterial = new BABYLON.StandardMaterial("tm", scene);
terrainMaterial.diffuseTexture = terrainTexture;
terrain.mesh.material = terrainMaterial;
// terrain.updateCameraLOD = function(terrainCamera) {
// // LOD value increases with camera altitude
// var camLOD = Math.abs((terrainCamera.globalPosition.y / 30.0)|0);
// // 这里加了动态调节,就不用别的地方加了,但是这里好像不能使用这个,因为我开大镜头,精度是不能丢失的,现在会丢失地面建筑物的精度,这个只能保证拉大镜头,地面永远都在镜头内
// return camLOD;
// };
terrain.update(true);
let startPosition
// 开始拖拽的位置
let isPointerDowm=false
function pointerDown(e) {
isPointerDowm=true
}
function pointerUp(){
isPointerDowm=false
}
function pointerMove(e){
if(!isPointerDowm)return
let mx=e.event.movementX*xMove
let my=e.event.movementY*yMove
//这是最基础的调整,还需要调整,乘一个系数
camera.position.addInPlace(new BABYLON.Vector3(-mx,0,my))
}
function pointerWhell(e){
const delta=e.event.wheelDelta
if(delta>0){
if(camera.position.y>=100)return
camera.position.y+=1
// 用这里来调试下摄像机吧.现在的摄像机老是感觉很奇怪
// camera.rotation.x+=0.1
// console.log(camera.rotation.x)
}else{
if(camera.position.y<=20)return
camera.position.y-=1
// camera.rotation.x-=0.1
}
// console.log('当前相机高度',camera.position.y)
console.log(camera.rotation)
}
scene.onPointerObservable.add((pointerInfo)=>{
switch(pointerInfo.type){
case BABYLON.PointerEventTypes.POINTERDOWN:
pointerDown(pointerInfo)
break
case BABYLON.PointerEventTypes.POINTERUP:
pointerUp(pointerInfo)
break
case BABYLON.PointerEventTypes.POINTERMOVE:
pointerMove(pointerInfo)
break
case BABYLON.PointerEventTypes.POINTERWHEEL:
pointerWhell(pointerInfo)
break;
}
})
let count=0
scene.registerBeforeRender(()=>{
count++
if(count>=200){
// console.log('speed',camera.speed)
// console.log('seppd',camera.position)
count=0
}
})
return scene
}
window.initFunction=async function(){
let asyncEngineCreation=async function(){
try{
return createDefaultEngine()
}catch(e){
return createDefaultEngine()
}
}
window.engine=await asyncEngineCreation()
if(!engine){
throw('engine should not be null')
}
window.scene=createScene()
}
initFunction().then(()=>{
scene.then((returnedScene)=>{
sceneToRender=returnedScene
})
let limit=60
let count=0
let fps=0
engine.runRenderLoop(function(){
count++
if(count===limit){
fps=Math.round(engine.getFps())
count=0
console.log('当前帧数是'+fps)
}
if(sceneToRender&&sceneToRender.activeCamera){
sceneToRender.render()
}
})
})
window.addEventListener('resize',function(){
engine.resize()
})
</script>
</body>
</html>