前言
前段時間想要做一個web端的圖形化積木式編程(類似少兒編程)的案例,網上沖浪了一圈又一圈,終于技術選型好,然后代碼一頓敲,終于出來了一個雛形。
TIPS:該案例設計主要參考iRobot Coding,只用做學習用途,侵刪。
最終實現效果
本文實現效果
-
加載模型到場景中
替換上文中的藍色正方形為babylon模型文件
完整代碼
- 加載模型實現
<template>
<div style="height: 100%;width: 100%;">
<div>
<canvas id="renderCanvas"></canvas>
</div>
</div>
</template>
<script>
import * as BABYLON from 'babylonjs';
import * as BABYLON_MATERAIAL from "babylonjs-materials"
async function loadScene() {
//第一篇場景初始化,可看上一篇文章
var scene = initScene()
//本文內容,加載網絡模型
await initRobot(scene)
//開啟debug窗口
// scene.debugLayer.show()
}
async function initRobot(scene) {
console.log('initRobot')
//模型url路徑
const url = "http://localhost:8887/"
//模型名稱
const modelName = "sportcar.babylon"
var result = await BABYLON.SceneLoader.ImportMeshAsync(null, url, modelName, scene);
var meshes = result.meshes
console.log("meshes", meshes)
const scale = 10//縮放比例
for (var mesh of meshes) {
mesh.scaling = new BABYLON.Vector3(scale, scale, scale)
}
}
function initScene() {
//獲取到renderCanvas這個元素
var canvas = document.getElementById("renderCanvas");
//初始化引擎
var engine = new BABYLON.Engine(canvas, true);
//初始化場景
var scene = new BABYLON.Scene(engine);
//注冊一個渲染循環來重復渲染場景
engine.runRenderLoop(function () {
scene.render();
});
//瀏覽器窗口變化時監聽
window.addEventListener("resize", function () {
engine.resize();
});
//相機初始化
var camera = new BABYLON.ArcRotateCamera("Camera", 0, 0, 5, new BABYLON.Vector3(0, 0, 10), scene);
camera.setPosition(new BABYLON.Vector3(20, 200, 400));
//相機角度限制
camera.upperBetaLimit = 1.5;//最大z軸旋轉角度差不多45度俯瞰
camera.lowerRadiusLimit = 50;//最小縮小比例
camera.upperRadiusLimit = 1500;//最大放大比例
//變焦速度
camera.wheelPrecision = 1; //電腦滾輪速度 越小靈敏度越高
camera.pinchPrecision = 20; //手機放大縮小速度 越小靈敏度越高
scene.activeCamera.panningSensibility = 100;//右鍵平移靈敏度
// 將相機和畫布關聯
camera.attachControl(canvas, true);
//燈光初始化
var light = new BABYLON.HemisphericLight("light1", new BABYLON.Vector3(0, 10, 0), scene);
//設置高光顏色
light.specular = new BABYLON.Color3(0, 0, 0);
//設置燈光強度
light.intensity = 1
// 綠地初始化
var materialPlane = new BABYLON.StandardMaterial("texturePlane", scene);
materialPlane.diffuseColor = new BABYLON.Color3(152 / 255.0, 209 / 255.0, 115 / 255.0)
materialPlane.backFaceCulling = false;
materialPlane.freeze()
var plane = BABYLON.MeshBuilder.CreateDisc("ground", {radius: 3000}, scene);
plane.rotation.x = Math.PI / 2;
plane.material = materialPlane;
plane.position.y = -0.1;
plane.freezeWorldMatrix()
//網格地板初始化
const groundSide = 144;
var ground = BABYLON.Mesh.CreateGround("ground", groundSide, groundSide, 1, scene, true);
var groundMaterial = new BABYLON_MATERAIAL.GridMaterial("grid", scene);
groundMaterial.mainColor = BABYLON.Color3.White();//底板顏色
groundMaterial.alpha = 1;//透明度
const gridLineGray = 0.95;
groundMaterial.lineColor = new BABYLON.Color3(gridLineGray, gridLineGray, gridLineGray);
groundMaterial.backFaceCulling = true; // 可看到背面
//大網格間距
groundMaterial.majorUnitFrequency = 16;
//小網格間距
groundMaterial.minorUnitVisibility = 0;
const gridOffset = 8; // 網格偏移量
groundMaterial.gridOffset = new BABYLON.Vector3(gridOffset, 0, gridOffset);
groundMaterial.freeze(); // 凍結材質,優化渲染速度
ground.material = groundMaterial
ground.freezeWorldMatrix()
//天空盒初始化
var skyMaterial = new BABYLON_MATERAIAL.SkyMaterial("skyMaterial", scene);
skyMaterial.inclination = 0
skyMaterial.backFaceCulling = false;
var skybox = BABYLON.Mesh.CreateBox("skyBox", 5000.0, scene);
skybox.material = skyMaterial;
return scene
}
export default {
name: "test",
data() {
return {}
},
async mounted() {
//加載場景
await loadScene()
},
}
</script>
<style scoped>
#renderCanvas {
width: 680px;
height: 680px;
touch-action: none;
z-index: 10000;
border-radius: 10px;
}
</style>
babylonjs模型格式轉換與導入
0、在開源模型網上下載一個模型/自己制作一個
- emmmm,沒有云服務器的同學可以直接跳到第2步的方案二啦!
- 文件格式 .glTF, .glb, .obj, .babylon(實際上只想要obj/babylon格式??♂?)
- ??上述模型都可在下方網址在線預覽模型??
- ??3d開源素材哪里找??
-
筆者從這里下載了一個心愛的老爺車(有obj文件就選obj文件,其他格式的話,需要在blender等3d引擎中導出obj)??
image.png
為什么非要obj格式文件?
為了形成規范,筆者所做的系統中模型能夠正常運行的基礎是nodes如下圖格式(每一個mesh網格都沒有父節點,不存在嵌套關系)。obj文件在線預覽就是這種規范。當然,其他導入的模型能符合這個規范也行。-
正確nodes結構(sandbox.babylonjs.com中打開obj文件)
例子1
例子2 -
錯誤nodes結構((sandbox.babylonjs.com中打開glb文件)
帶__root__父節點的nodes
1、轉換為.babylon文件
obj文件導入在線預覽網頁后,筆者選擇了統一把模型轉化為.babylon模型文件
-
理論上,將obj文件拖入上方打開在線模型預覽窗口,應該出現下面模型預覽界面。
理論上,導入obj文件后,會自動關聯mtl文件,然后mtl文件自動關聯所有圖片
-
實際上
導入obj文件時,無法導入文件關聯的mtl文件
導入mtl文件時,無法導入文件關聯的圖片文件 這是因為瀏覽器內核為了安全性,不能代碼加載本地資源,所以只能手動導入了。
-
具體步驟
1.0、準備好obj、mtl、mtl關聯的所有png圖片(不太清楚的可以用記事本打開mtl文件看一看)
1.1、打開【https://sandbox.babylonjs.com】,選擇或拖動obj文件進去。
1.2、拖動關聯的mtl文件進去。
1.3、打開調試工具欄,左邊工具欄點擊展開Textures,點擊里面的每一個png,然后點擊右邊的【load Texture from file】選擇對應圖片。
導入mtl文件關聯的材質圖片指引
1.4、按照提示紅框提示即可導出.babylon格式模型文件。導出的babylon格式,其模型結構和obj的模型結構是一致的,只不過.babylon格式模型相當于將obj、mtl、所有png都整合在一起形成一個文件了。有興趣的小伙伴可以看看左側Scene菜單,在代碼,可以遍歷該模型對象找到某個node節點,然后對這個節點進行位置變換從而到達動畫效果,或者對某個節點進行材質變換,顏色變化。
2、將模型文件放在服務器上
方案一(最新發現的方案)
- Web Server for Chrome(一個谷歌瀏覽器插件),可以將本地文件搭建為一個本地HTTP服務器,插件地址??
https://chrome.google.com/webstore/detail/web-server-for-chrome/ofhbbkphhbklhfoeikjpcbhemlocgigb
方案二
- 由于安全原因??,無法加載本地模型[file://],只能把它放在服務器上[http(s)://]
- 如果出現了Access-Control-Allow-Origin跨域問題,可以看看這里,改一下配置文件
方案三
沒有服務器的同學,也可以使用官方提供的模型進行學習啦
https://doc.babylonjs.com/toolsAndResources/assetLibraries/availableMeshes
3、加載模型
雖然筆者使用的是obj模型結構(所有mesh都是同級沒有父節點的,不知道能不能這樣一刀切??♂?,我想表達是沒有父節點),但是這里也會有glb模型結構(帶有root父節點的mesh加載)的模型加載。
3.1、async同步形式(obj模型結構)
async function initRobot(scene) {
console.log('initRobot')
//模型url路徑
const url = "http://localhost:8887/"
//模型名稱
const modelName = "sportcar.babylon"
var result = await BABYLON.SceneLoader.ImportMeshAsync(null, url, modelName, scene);
var meshes = result.meshes
console.log("meshes", meshes)
const scale = 10//縮放比例
for (var mesh of meshes) {
mesh.scaling = new BABYLON.Vector3(scale, scale, scale)
}
}
3.2、回調的形式(obj模型結構)
function initRobot(scene) {
console.log('initRobot')
//模型url路徑,上傳了自己模型到服務器的同學可以將url和modelName改為對應路徑即可
const url = "http://localhost:8887/"
//模型名稱
const modelName = "sportcar.babylon"
BABYLON.SceneLoader.ImportMesh("", url, modelName, scene, function (meshes) {
console.log(meshes)
const scale = 10//縮放比例
for (var mesh of meshes) {
mesh.scaling = new BABYLON.Vector3(scale, scale, scale)
}
});
}
3.3、async同步形式(glb模型結構)
async function initRobot(scene) {
console.log('initRobot')
//模型url路徑,上傳了自己模型到服務器的同學可以將url和modelName改為對應路徑即可
const url = "https://models.babylonjs.com/"
//模型名稱
const modelName = "toast_acrobatics.glb"
var result = await BABYLON.SceneLoader.ImportMeshAsync(null, url, modelName, scene);
var meshes = result.meshes
console.log(meshes)
var scale = 100//縮放比例
//只對__root__節點進行操作
meshes[0].scaling = new BABYLON.Vector3(scale, scale, scale)
meshes[0].position._x += 24
// scene.createDefaultEnvironment()
}
3.4、回調的形式(glb模型結構)
function initRobot(scene) {
console.log('initRobot')
//模型url路徑,上傳了自己模型到服務器的同學可以將url和modelName改為對應路徑即可
const url = "https://models.babylonjs.com/"
//模型名稱
const modelName = "toast_acrobatics.glb"
BABYLON.SceneLoader.ImportMesh("", url, modelName, scene, function (meshes) {
scene.createDefaultEnvironment()
console.log(meshes)
var scale = 100//縮放比例
//只對__root__節點進行操作
meshes[0].scaling = new BABYLON.Vector3(scale, scale, scale)
meshes[0].position._x += 24
// scene.createDefaultEnvironment()
});
}
- 加載很慢的話,可以f12開發者工具network看看是不是模型還在下載,模型很大,你要忍一下??
- 可以發現meshes打印出來數據列表(列表中每個t對象的name)和本文第1點中在線查看模型左側Scene菜單中的nodes列表中節點名稱是一致的
問題:導入模型后一片黑
- 解決方案:
scene.createDefaultEnvironment()
4、(額外)啟用調試窗口
scene.debugLayer.show()
- 是不是看到了熟悉的窗口
后續計劃
Babylon.js
- 點擊移動物體
- 自定義啟動界面
- 物體重力效果
- babylonjs-gui 按鈕實現
- babylonjs+ammojs 碰撞體實現
- 將3d界面放入可拖動窗口中
Blockly
- 入門使用blockly
- 自定義block塊
- blockly第三方組件使用
- 接入js-interpreter,步驟運行block塊
- ......(想到啥寫啥)