https://blog.csdn.net/zhishiqu/article/details/79077883
這是威爾遜Muktar關於整合Three.js與銫的客人帖子。Three.js是一個輕量級的跨瀏覽器JavaScript庫,用於在瀏覽器中創建和顯示動畫3D計算機圖形。將Cesium的行星級渲染和GIS功能與Three.js廣泛而易用的通用3D API相結合,為新的WebGL體驗開啟了許多可能性。你可以在這里查看這個演示的實時版本和代碼本身。 - 加里
3D JavaScript庫現在已經完全成熟並且廣為人知,使得開發人員可以避免在瀏覽器中使用3D的麻煩。開發人員可以輕松創建相機,對象,燈光,材質和圖形,並選擇渲染器,使用HTML 5的畫布,WebGL或SVG繪制場景。
因為Cesium和Three.js都是用於3D可視化的,並且是從頭開始用JavaScript構建的,所以它們有相似之處,可以將這些驚人的庫集成在一起。我對這兩個框架進行整合的方法比看起來簡單:我將這兩個框架分離到了不同的視圖中,並參考了HTML Canvas元素,並將它們的控制器組合在同一個坐標系中。由於兩者都是開源的,我可以分享這個演示,這將涵蓋一些基礎知識。
左:銫現場。中心:Three.js場景。右:組合的場景。
銫是一個為了創建數字地球而開發的三維圖書館,其渲染對於真實的地球來說是非常精確的。借助3D Tiles,開發人員可以將幾乎所有內容都重新渲染到瀏覽器中的數字畫布上。
指導銫的基本渲染原理與Three.js沒有太大區別。Three.js是用於渲染3D對象的強大3D庫。通過在兩個場景中復制銫的球面坐標系和匹配的數字地球,很容易將兩個單獨的渲染引擎層整合到一個主場景中。我將給出一個關於其整合方法的簡單說明,如下所示:
- 初始化Cesium渲染器,
- 初始化Three.js渲染器,
- 初始化這兩個庫的3D對象,和
- 循環渲染器。
主功能
該html需要三個容器和銫:
-
<body>
-
<div id="cesiumContainer"></div>
-
<div id="ThreeContainer"></div>
-
</body>
-
<script> main(); </script>
這是主要功能:
-
function main(){
-
// boundaries in WGS84 to help with syncing the renderers
-
var minWGS84 = [115.23,39.55];
-
var maxWGS84 = [116.23,41.55];
-
var cesiumContainer = document.getElementById("cesiumContainer");
-
var ThreeContainer = document.getElementById("ThreeContainer");
-
-
var _3Dobjects = []; //Could be any Three.js object mesh
-
var three = {
-
renderer: null,
-
camera: null,
-
scene: null
-
};
-
-
var cesium = {
-
viewer: null
-
};
-
-
initCesium(); // Initialize Cesium renderer
-
initThree(); // Initialize Three.js renderer
-
init3DObject(); // Initialize Three.js object mesh with Cesium Cartesian coordinate system
-
loop(); // Looping renderer
-
}
初始化銫渲染器
首先,我們可以通過添加自定義圖像或默認提供的其他部分來自定義銫查看器。通過禁用Cesium的默認渲染循環,我們可以將其動畫幀與Three.js同步。
-
function initCesium(){
-
cesium.viewer = new Cesium.Viewer(cesiumContainer,{
-
useDefaultRenderLoop: false,
-
selectionIndicator : false,
-
homeButton:false,
-
sceneModePicker:false,
-
navigationHelpButton:false,
-
infoBox : false,
-
navigationHelpButton:false,
-
navigationInstructionsInitiallyVisible:false,
-
animation : false,
-
timeline : false,
-
fullscreenButton : false,
-
allowTextureFilterAnisotropic:false,
-
contextOptions:{
-
webgl: {
-
alpha: false,
-
antialias: true,
-
preserveDrawingBuffer : true,
-
failIfMajorPerformanceCaveat: false,
-
depth:true,
-
stencil:false,
-
anialias:false
-
},
-
},
-
targetFrameRate:60,
-
resolutionScale:0.1,
-
orderIndependentTranslucency : true,
-
creditContainer : "hidecredit",
-
imageryProvider : new Cesium.createTileMapServiceImageryProvider({
-
url: 'Assets/imagery/NaturalEarthII/',
-
maximumLevel : 5
-
}),
-
baseLayerPicker : false,
-
geocoder : false,
-
automaticallyTrackDataSourceClocks: false,
-
dataSources: null,
-
clock: null,
-
terrainShadows: Cesium.ShadowMode.DISABLED
-
});
-
-
var center = Cesium.Cartesian3.fromDegrees(
-
(minWGS84[0] + maxWGS84[0]) / 2,
-
((minWGS84[1] + maxWGS84[1]) / 2)-1,
-
200000
-
);
-
cesium.viewer.camera.flyTo({
-
destination : center,
-
orientation : {
-
heading : Cesium.Math.toRadians(0),
-
pitch : Cesium.Math.toRadians(-60),
-
roll : Cesium.Math.toRadians(0)
-
},
-
duration: 3
-
});
-
}
初始化Three.js渲染器
接下來我們簡單地初始化Three.js強制階段,包括場景,相機,渲染器和DOM元素。
-
function initThree(){
-
var fov = 45;
-
var width = window.innerWidth;
-
var height = window.innerHeight;
-
var aspect = width / height;
-
var near = 1;
-
var far = 10*1000*1000; // needs to be far to support Cesium's world-scale rendering
-
-
three.scene = new THREE.Scene();
-
three.camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
-
three.renderer = new THREE.WebGLRenderer({alpha: true});
-
ThreeContainer.appendChild(three.renderer.domElement);
-
}
在兩個庫中初始化3D對象
使用實體對象可以簡單地將Cesium對象添加到其查看器中; 例如,可以使用3D圖形類來渲染在Three.js中創建的3D繪圖對象網格,或者使用Three.js創建的任何其他3D對象。所有這些都保存在一個_3DObjects
進一步處理,其中包含用於同步相機的額外信息。這里我們將渲染一個[Lathe geometry]和一個[dodecahedron]。請注意,Three.js呈現z-up,而Cesium呈現y-up。
-
function init3DObject(){
-
//Cesium entity
-
var entity = {
-
name : 'Polygon',
-
polygon : {
-
hierarchy : Cesium.Cartesian3.fromDegreesArray([
-
minWGS84[0], minWGS84[1],
-
maxWGS84[0], minWGS84[1],
-
maxWGS84[0], maxWGS84[1],
-
minWGS84[0], maxWGS84[1],
-
]),
-
material : Cesium.Color.RED.withAlpha(0.2)
-
}
-
};
-
var Polygon = cesium.viewer.entities.add(entity);
-
-
// Lathe geometry
-
var doubleSideMaterial = new THREE.MeshNormalMaterial({
-
side: THREE.DoubleSide
-
});
-
var segments = 10;
-
var points = [];
-
for ( var i = 0; i < segments; i ++ ) {
-
points.push( new THREE.Vector2( Math.sin( i * 0.2 ) * segments + 5, ( i - 5 ) * 2 ) );
-
}
-
var geometry = new THREE.LatheGeometry( points );
-
var latheMesh = new THREE.Mesh( geometry, doubleSideMaterial ) ;
-
latheMesh.scale.set(1500,1500,1500); //scale object to be visible at planet scale
-
latheMesh.position.z += 15000.0; // translate "up" in Three.js space so the "bottom" of the mesh is the handle
-
latheMesh.rotation.x = Math.PI / 2; // rotate mesh for Cesium's Y-up system
-
var latheMeshYup = new THREE.Group();
-
latheMeshYup.add(latheMesh)
-
three.scene.add(latheMeshYup); // don’t forget to add it to the Three.js scene manually
-
-
//Assign Three.js object mesh to our object array
-
var _3DOB = new _3DObject();
-
_3DOB.threeMesh = latheMeshYup;
-
_3DOB.minWGS84 = minWGS84;
-
_3DOB.maxWGS84 = maxWGS84;
-
_3Dobjects.push(_3DOB);
-
-
// dodecahedron
-
geometry = new THREE.DodecahedronGeometry();
-
var dodecahedronMesh = new THREE.Mesh(geometry, new THREE.MeshNormalMaterial()) ;
-
dodecahedronMesh.scale.set(5000,5000,5000); //scale object to be visible at planet scale
-
dodecahedronMesh.position.z += 15000.0; // translate "up" in Three.js space so the "bottom" of the mesh is the handle
-
dodecahedronMesh.rotation.x = Math.PI / 2; // rotate mesh for Cesium's Y-up system
-
var dodecahedronMeshYup = new THREE.Group();
-
dodecahedronMeshYup.add(dodecahedronMesh)
-
three.scene.add(dodecahedronMeshYup); // don’t forget to add it to the Three.js scene manually
-
-
//Assign Three.js object mesh to our object array
-
_3DOB = new _3DObject();
-
_3DOB.threeMesh = dodecahedronMeshYup;
-
_3DOB.minWGS84 = minWGS84;
-
_3DOB.maxWGS84 = maxWGS84;
-
_3Dobjects.push(_3DOB);
-
}
-
function _3DObject(){
-
this.graphMesh = null; //Three.js 3DObject.mesh
-
this.minWGS84 = null; //location bounding box
-
this.maxWGS84 = null;
-
}
循環渲染器
-
function loop(){
-
requestAnimationFrame(loop);
-
renderCesium();
-
renderThreeObj();
-
}
-
function renderCesium(){
-
cesium.viewer.render();
-
}
我們將克隆Three.js攝像頭以匹配Cesium攝像頭,因此不需要為Three.js分配鼠標控制器,但是由於Three.js DOM元素在Cesium之上,我們仍然需要將其刪除。我們通過向pointer-events:none
Three.js渲染器添加CSS屬性來刪除它。現在一切都會根據銫的相機投影來渲染。
還有一個坐標轉換要做,使對象在地球上正確顯示。這包括將大地緯度/經度位置轉換為笛卡兒XYZ,並使用WGS84區域從左下角到左上角的方向作為向上矢量,使物體指向地球中心。這也可以通過使用本地笛卡爾東北向或東北向下來計算。
-
function renderThreeObj(){
-
// register Three.js scene with Cesium
-
three.camera.fov = Cesium.Math.toDegrees(cesium.viewer.camera.frustum.fovy) // ThreeJS FOV is vertical
-
three.camera.updateProjectionMatrix();
-
-
var cartToVec = function(cart){
-
return new THREE.Vector3(cart.x, cart.y, cart.z);
-
};
-
-
// Configure Three.js meshes to stand against globe center position up direction
-
for(id in _3Dobjects){
-
minWGS84 = _3Dobjects[id].minWGS84;
-
maxWGS84 = _3Dobjects[id].maxWGS84;
-
// convert lat/long center position to Cartesian3
-
var center = Cesium.Cartesian3.fromDegrees((minWGS84[0] + maxWGS84[0]) / 2, (minWGS84[1] + maxWGS84[1]) / 2);
-
-
// get forward direction for orienting model
-
var centerHigh = Cesium.Cartesian3.fromDegrees((minWGS84[0] + maxWGS84[0]) / 2, (minWGS84[1] + maxWGS84[1]) / 2,1);
-
-
// use direction from bottom left to top left as up-vector
-
var bottomLeft = cartToVec(Cesium.Cartesian3.fromDegrees(minWGS84[0], minWGS84[1]));
-
var topLeft = cartToVec(Cesium.Cartesian3.fromDegrees(minWGS84[0], maxWGS84[1]));
-
var latDir = new THREE.Vector3().subVectors(bottomLeft,topLeft ).normalize();
-
-
// configure entity position and orientation
-
_3Dobjects[id].graphMesh.position.copy(center);
-
_3Dobjects[id].graphMesh.lookAt(centerHigh);
-
_3Dobjects[id].graphMesh.up.copy(latDir);
-
}
-
-
// Clone Cesium Camera projection position so the
-
// Three.js Object will appear to be at the same place as above the Cesium Globe
-
three.camera.matrixAutoUpdate = false;
-
var cvm = cesium.viewer.camera.viewMatrix;
-
var civm = cesium.viewer.camera.inverseViewMatrix;
-
three.camera.matrixWorld.set(
-
civm[0], civm[4], civm[8 ], civm[12],
-
civm[1], civm[5], civm[9 ], civm[13],
-
civm[2], civm[6], civm[10], civm[14],
-
civm[3], civm[7], civm[11], civm[15]
-
);
-
three.camera.matrixWorldInverse.set(
-
cvm[0], cvm[4], cvm[8 ], cvm[12],
-
cvm[1], cvm[5], cvm[9 ], cvm[13],
-
cvm[2], cvm[6], cvm[10], cvm[14],
-
cvm[3], cvm[7], cvm[11], cvm[15]
-
);
-
three.camera.lookAt(new THREE.Vector3(0,0,0));
-
-
var width = ThreeContainer.clientWidth;
-
var height = ThreeContainer.clientHeight;
-
var aspect = width / height;
-
three.camera.aspect = aspect;
-
three.camera.updateProjectionMatrix();
-
-
three.renderer.setSize(width, height);
-
three.renderer.render(three.scene, three.camera);
-
}