首发:鲸讯微信小程序
作者:鲸林向海工作室
原帖:blog.csdn.net/zhishiqu/ar…
跟着原贴做了一下three
和cesium
的结合,代码稍微改动了点,记录一下。完整代码附在最后。
1、思路
1.要把cesiumjs
和threejs
的场景同时显示,所以需要创建两个容器,分别用于显示cesium
的场景和three
的场景,两个场景要重合;
2.将cesium
和three
的渲染频率调成一致;
3.将cesium
和three
的相机位置角度等调成一致。
4.将three
里的图形位置调整为在地球表面
2、具体实现
2.1初始化cesium
cesium
初始化时,要将它的自动渲染关掉(即useDefaultRenderLoop
属性调整为false
)
cesium.viewer = new Cesium.Viewer("cesiumContainer", {
useDefaultRenderLoop: false, //关闭自动渲染
selectionIndicator: false,
homeButton: false,
sceneModePicker: false,
navigationHelpButton: false,
animate: false,
timeline: false,
fullscreenButton: false,
navigationInstructionsInitiallyVisible: 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:"CreditDisplay",
// imageeryProvider: googlemap, //谷歌地图
baseLayerPicker: true,
geocoder: false,
automaticallyTrackDataSourceClocks: false,
dataSources: null,
clock: null,
terrainShadows: Cesium.ShadowMode.DISABLED
});
2.2初始化three.js
let fov = 45;
let width = window.innerWidth;
let height = window.innerHeight;
let aspect = width / height;
let near = 1;
let 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 });
// let axis=new THREE.AxesHelper(1000*1000*1000);
// three.scene.add(axis);
ThreeContainer.appendChild(three.renderer.domElement);
2.3调整three和cesium的渲染频率
手动开启cesium
和three
的渲染,并放进一个渲染频率里。
function loop() {
requestAnimationFrame(loop);
renderCesium(); //渲染cesium
renderThree(); //渲染three
}
function renderCesium() {
cesium.viewer.render();
}
function renderThree() {
var width = ThreeContainer.clientWidth;
var height = ThreeContainer.clientHeight;
three.renderer.setSize(width, height);
three.renderer.render(three.scene, three.camera);
}
2.4调整相机一致
这里使用的cesium
的相机为主相机,使three
的相机与cesium
保持一致即可。
由于相机的位置一直在变换,所以讲相机位置调整放进每一帧的渲染中。
function renderCamera(){
// register Three.js scene with Cesium
three.camera.fov = Cesium.Math.toDegrees(cesium.viewer.camera.frustum.fovy) // ThreeJS FOV is vertical
three.camera.updateProjectionMatrix();
// 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();
}
2.5加入要展示的图形
这里加入一个cesium
的图形polygon
,再加入一个three
的球体,以及一个three
的12面体。
cesium
是y
朝上,three
是z
朝上,所以要进行坐标的变换,使three
也变成y
朝上。其次,three
的图形要想放置在地球表面,需要z轴指向天空,垂直于地球表面,我们需要调整three
图形的方向。方便起见,我们创建一个组group
,将所有加入的three
的图形都放进这个group
中,然后对group
进行位置变换。
需要注意的是,three
中添加的球体需要加入光源才能看到,否则只能看到一个黑色的球体,我们把光源一同放入group
中,确保光源的位置足以照射到球体。
function _3DObject() {
this.threeMesh = null;
this.minWGS84 = null;
this.maxWGS84 = null;
}
function init3DObject() {
let 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.1)
}
}
let Polypon = cesium.viewer.entities.add(entity);
let doubleSideMaterial = new THREE.MeshNormalMaterial({
side: THREE.DoubleSide
});
geometry = new THREE.SphereGeometry(1, 32, 32);
let sphere = new THREE.Mesh(geometry, new THREE.MeshPhongMaterial({ color: 0xffffff, side: THREE.DoubleSide })); //12面体
// sphere.scale.set(5000,5000,5000);
// sphere.position.z+=15000;
// translate "up" in Three.js space so the "bottom" of the mesh is the handle
sphere.scale.set(5000, 5000, 5000);
sphere.uuid = "sphere";
var sphereYup = new THREE.Group();
sphereYup.add(sphere)
three.scene.add(sphereYup); // don’t forget to add it to the Three.js scene manually
sphereYup.position.set(ce.x, ce.y, ce.z);
_3DOB = new _3DObject();
_3DOB.threeMesh = sphereYup;
_3DOB.minWGS84 = minWGS84;
_3DOB.maxWGS84 = maxWGS84;
_3Dobjects.push(_3DOB);
geometry=new THREE.DodecahedronGeometry();
let dodecahedronMesh=new THREE.Mesh(geometry,new THREE.MeshNormalMaterial()); //12面体
dodecahedronMesh.scale.set(5000,5000,5000);
dodecahedronMesh.position.z+=15000;
// 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
dodecahedronMesh.uuid="12面体";
var dodecahedronMeshYup = new THREE.Group();
dodecahedronMeshYup.add(dodecahedronMesh)
three.scene.add(dodecahedronMeshYup); // don’t forget to add it to the Three.js scene manually
dodecahedronMeshYup.position.set(ce.x,ce.y,ce.z);
// Assign Three.js object mesh to our object array
_3DOB = new _3DObject();
_3DOB.threeMesh = dodecahedronMeshYup;
_3DOB.minWGS84 = minWGS84;
_3DOB.maxWGS84 = maxWGS84;
_3Dobjects.push(_3DOB);
//添加灯光
//添加点光源
var spotLight = new THREE.SpotLight(0xffffff);
spotLight.position.set(0,0,50000);
spotLight.castShadow = true; //设置光源投射阴影
spotLight.intensity = 1;
sphereYup.add(spotLight)
//添加环境光
var hemiLight = new THREE.HemisphereLight(0xff0000, 0xff0000, 1);
sphereYup.add(hemiLight);
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].threeMesh.position.copy(center);
_3Dobjects[id].threeMesh.lookAt(centerHigh);
_3Dobjects[id].threeMesh.up.copy(latDir);
}
}
3、效果如下图
4、遇到的坑
1.要注意threejs
的版本。文中所用为r87
版的threejs
,尝试换成r92
,r103
,都找不到模型,这个问题后续再探究一下是为什么。
2.关于three
的模型在cesium
中的位置变幻:
文中设置了一个最大坐标和最小坐标,位置如图:
位置变换前three的坐标系如图(红色为x轴,绿色为y轴,蓝色为z轴):
进行位置变化后:
4.关于自己的模型变黑:加载帖子中的两个自建模型,颜色是很明显的彩色,如图所示:
加载其他自建模型或其他导入的模型,则是如图:
原因是threejs渲染器中并没有加灯光进去,加上灯光就好了。
5、完整代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>three and cesium</title>
<script src="./js/Cesium/Cesium.js"></script>
<script src="./js/threer69.js"></script>
</head>
<body>
<div id="cesiumContainer"></div>
<div id="ThreeContainer"></div>
<style>
@import url(./js/Cesium/Widgets/widgets.css);
body {
height: 100%;
width: 100%;
margin: 0;
overflow: hidden;
padding: 0;
background: #000;
}
#cesiumContainer {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
margin: 0;
overflow: hidden;
padding: 0;
font-family: sans-serif;
}
#ThreeContainer {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
margin: 0;
overflow: hidden;
padding: 0;
font-family: sans-serif;
pointer-events: none;
}
.fullWindow {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
margin: 0;
overflow: hidden;
padding: 0;
font-family: sans-serif;
}
.loadingIndicator {
display: block;
position: absolute;
top: 50%;
left: 50%;
margin-top: -33px;
margin-left: -33px;
width: 66px;
height: 66px;
background-position: center;
background-repeat: no-repeat;
background-image: url(Images/ajax-loader.gif);
}
</style>
<script>
//cesium
let minWGS84 = [115.23, 39.55];
let maxWGS84 = [116.23, 41.55];
let cesiumContainer = document.getElementById("cesiumContainer");
let ThreeContainer = document.getElementById("ThreeContainer");
let _3Dobjects = [];
let three = {
renderer: null,
camera: null,
scene: null
};
let cesium = {
viewer: null
}
main();
function main() {
initCesium();
initThree();
init3DObject();
loop();
}
function initCesium() {
let esri = new Cesium.ArcGisMapServerImageryProvider({
url: 'https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer'
});
var googlemap = new Cesium.UrlTemplateImageryProvider(
{
url: 'http://mt1.google.cn/vt/lyrs=s&hl=zh-CN&x={x}&{x}&y={y}&z={z}&s=Gali'
})
cesium.viewer = new Cesium.Viewer("cesiumContainer", {
useDefaultRenderLoop: false,
selectionIndicator: false,
homeButton: false,
sceneModePicker: false,
navigationHelpButton: false,
animate: false,
timeline: false,
fullscreenButton: false,
navigationInstructionsInitiallyVisible: 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:"CreditDisplay",
// imageeryProvider: googlemap, //谷歌地图
baseLayerPicker: true,
geocoder: false,
automaticallyTrackDataSourceClocks: false,
dataSources: null,
clock: null,
terrainShadows: Cesium.ShadowMode.DISABLED
});
let center = Cesium.Cartesian3.fromDegrees(
(minWGS84[0] + maxWGS84[0]) / 2,
((minWGS84[1] + maxWGS84[1]) / 2) - 1,
200000
);
ce = center;
cesium.viewer.camera.flyTo({
destination: center,
orientation: {
heading: Cesium.Math.toRadians(0),
pitch: Cesium.Math.toRadians(-60),
roll: Cesium.Math.toRadians(0)
},
duration: 3
});
}
function initThree() {
let fov = 45;
let width = window.innerWidth;
let height = window.innerHeight;
let aspect = width / height;
let near = 1;
let 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 });
// let axis=new THREE.AxesHelper(1000*1000*1000);
// three.scene.add(axis);
ThreeContainer.appendChild(three.renderer.domElement);
}
function init3DObject() {
let 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.1)
}
}
let Polypon = cesium.viewer.entities.add(entity);
let doubleSideMaterial = new THREE.MeshNormalMaterial({
side: THREE.DoubleSide
});
geometry = new THREE.SphereGeometry(1, 32, 32);
let sphere = new THREE.Mesh(geometry, new THREE.MeshPhongMaterial({ color: 0xffffff, side: THREE.DoubleSide })); //12面体
// sphere.scale.set(5000,5000,5000);
// sphere.position.z+=15000;
// translate "up" in Three.js space so the "bottom" of the mesh is the handle
sphere.scale.set(5000, 5000, 5000);
sphere.uuid = "sphere";
var sphereYup = new THREE.Group();
sphereYup.add(sphere)
three.scene.add(sphereYup); // don’t forget to add it to the Three.js scene manually
sphereYup.position.set(ce.x, ce.y, ce.z);
_3DOB = new _3DObject();
_3DOB.threeMesh = sphereYup;
_3DOB.minWGS84 = minWGS84;
_3DOB.maxWGS84 = maxWGS84;
_3Dobjects.push(_3DOB);
geometry = new THREE.DodecahedronGeometry();
let dodecahedronMesh = new THREE.Mesh(geometry, new THREE.MeshNormalMaterial()); //12面体
dodecahedronMesh.scale.set(5000, 5000, 5000);
dodecahedronMesh.position.z += 15000;
// 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
dodecahedronMesh.uuid = "12面体";
var dodecahedronMeshYup = new THREE.Group();
dodecahedronMeshYup.add(dodecahedronMesh)
three.scene.add(dodecahedronMeshYup); // don’t forget to add it to the Three.js scene manually
dodecahedronMeshYup.position.set(ce.x, ce.y, ce.z);
// Assign Three.js object mesh to our object array
_3DOB = new _3DObject();
_3DOB.threeMesh = dodecahedronMeshYup;
_3DOB.minWGS84 = minWGS84;
_3DOB.maxWGS84 = maxWGS84;
_3Dobjects.push(_3DOB);
//添加灯光
//添加点光源
var spotLight = new THREE.SpotLight(0xffffff);
spotLight.position.set(0, 0, 50000);
spotLight.castShadow = true; //设置光源投射阴影
spotLight.intensity = 1;
sphereYup.add(spotLight)
//添加环境光
var hemiLight = new THREE.HemisphereLight(0xff0000, 0xff0000, 1);
sphereYup.add(hemiLight);
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].threeMesh.position.copy(center);
_3Dobjects[id].threeMesh.lookAt(centerHigh);
_3Dobjects[id].threeMesh.up.copy(latDir);
}
}
function _3DObject() {
this.threeMesh = null;
this.minWGS84 = null;
this.maxWGS84 = null;
}
function loop() {
requestAnimationFrame(loop);
renderCesium();
renderThreeObj();
renderCamera();
}
function renderCesium() {
cesium.viewer.render();
}
function renderThreeObj() {
var width = ThreeContainer.clientWidth;
var height = ThreeContainer.clientHeight;
three.renderer.setSize(width, height);
three.renderer.render(three.scene, three.camera);
}
function renderCamera() {
// register Three.js scene with Cesium
three.camera.fov = Cesium.Math.toDegrees(cesium.viewer.camera.frustum.fovy) // ThreeJS FOV is vertical
three.camera.updateProjectionMatrix();
// 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();
}
</script>
</body>
</html>