简介
本文章内容包括白膜建筑osm数据集的获取,arcmap对osm数据清洗并导出为shp格式数据,shp格式文件转3Dtiles, cesium加载3Dtiles白膜建筑并使用着色器实现建筑摇晃效果。
相关参考链接:
获取某地模型并用Cesium加载(一)
Cesium 3Dtiles模型压平
Cesium 3Dtilesets 使用customShader的解读以及泛光效果示例
ArcGIS Pro 10.8.2破解+安装+软件分享+测试说明
ArcMap安装OpenStreetMap编辑工具插件ArcGIS Editor for OSM
osm数据下载
osm数据清洗并导出为shp文件
此步骤需要安装arcgis,安装教程请查看简介的相关参考链接。
Load OSM File工具需要安装OpenStreetMap插件,安装教程请查看简介的相关参考链接。
注意:如果使用此工具并没有将osm文件导入成功,请按照如下步骤检查OSM插件安装的路径是否正确,如果不对则重新配置并应用。
osm文件导入成功后如图所示:
为图层属性表添加一个高度字段height
将图层数据导出为shp格式
使用CesiumLab将shp文件转3Dtiles文件
Cesium加载3Dtiles数据
const primitive = viewer.scene.primitives.add(new Cesium.PrimitiveCollection());
new Cesium.Cesium3DTileset({
url: "/cdu2/tileset.json",//3dtiles文件路径
}).readyPromise
.then((data) => {
tilesetPrimitive = data //tilesetPrimitive作为公共变量
//loadTilesShader(tilesetPrimitive)//下文会给出此方法
primitive.add(tilesetPrimitive)
viewer.zoomTo(
tilesetPrimitive,
new Cesium.HeadingPitchRange(
0.0,
-0.5,
tilesetPrimitive.boundingSphere.radius * 2.0
)
);
})
.catch(function (error: Error) {
console.log(error);
});
加载出来的白膜建筑如图所示
白膜建筑的摇晃
接下来就是编写着色器让白膜建筑呈现摇晃的效果,思路就是改变顶点着色器中顶点的位置,采用sin函数或者cos函数使顶点坐标在某个方向上来回移动即可。
先来实现白膜建筑整体的摇晃效果,整体的摇晃不需要考虑顶点坐标的具体值是多少,只需要在原来顶点着色器代码的基础上加上一个顶点坐标来回偏移的方法即可,代码如下:
const loadTilesShader = (tileset) => {
//修改白膜建筑的颜色
tileset.style = new Cesium.Cesium3DTileStyle({
color: {
conditions: [
['true', 'rgba(0, 127, 255 ,1)']
]
}
});
tileset.tileLoad.addEventListener(function (tile) {
//localTile = tile;//将tile保存到一个公共变量,后续还会使用
const content = tile.content;
const model = content._model;
if (model && model._sourcePrograms && model._rendererResources) {
Object.keys(model._sourcePrograms).forEach(key => {
const program = model._sourcePrograms[key]
//console.log(model._rendererResources.sourceShaders);
//debugger
//在这里打断点可以查看原本的着色器代码如下
/*precision highp float;
uniform mat4 u_modelViewMatrix;
uniform mat4 u_projectionMatrix;
uniform mat3 u_normalMatrix;
attribute vec3 a_position;
varying vec3 v_positionEC;
attribute vec3 a_normal;
varying vec3 v_normal;
attribute float a_batchId;
void main(void)
{
vec3 weightedPosition = a_position;
vec3 weightedNormal = a_normal;
vec4 position = vec4(weightedPosition, 1.0);
position = u_modelViewMatrix * position;
v_positionEC = position.xyz;
gl_Position = u_projectionMatrix * position;
v_normal = u_normalMatrix * weightedNormal;
}*/
//在原有的着色器代码上添加修改顶点坐标的代码
model._rendererResources.sourceShaders[program.vertexShader] =
`
precision highp float;\n
uniform mat4 u_modelViewMatrix;\n
uniform mat4 u_projectionMatrix;\n
uniform mat3 u_normalMatrix;\n
attribute vec3 a_position;\n
varying vec3 v_positionEC;\n
attribute vec3 a_normal;\n
varying vec3 v_normal;\n
attribute float a_batchId;\n
void main(void) \n
{\n
vec3 weightedPosition = a_position;\n
vec3 weightedNormal = a_normal;\n
float time = czm_frameNumber / 5.0;\n
if ( a_position.y > 0.0 ){ //判断顶点高度,当建筑物高度大于这个值则施加摇晃效果
weightedPosition.x += sin(time) * a_position.y / 8.0; //在这里可控制摇晃方向和距离,高度越高,摇晃距离越大
}
vec4 position = u_modelViewMatrix * vec4(weightedPosition, 1.0);\n
v_positionEC = position.xyz;\n
gl_Position = u_projectionMatrix * position;\n
v_normal = u_normalMatrix * weightedNormal;\n
}\n
`
})
model._shouldRegenerateShaders = true
}
});
}
整个白膜建筑摇晃效果如下:
框选区域内白膜建筑的摇晃
接下来实现在框选区域范围内的摇晃效果,这个实现就相对比较麻烦了,需要将Cesium中框选的多边形顶点坐标与着色器中的顶点坐标转换到同一种坐标系下进行判断着色器顶点坐标是否在框选的多边形内部,emm…,这一步就相当复杂了,自己瞎搞了半天也没找到到底要怎么转换到同一个坐标系下,此时突然想到了框选区域对模型压平的案例,本质上应该也是修改顶点着色器,那么其中应该会有坐标转换的代码,终于还是找到了😄😄😄Cesium 3dtiles模型压平。
最终框选区域摇晃的代码如下:
//在多边形区域构造完成后即可调用updateShader方法更新白膜建筑着色器代码
const updateShader = () => {
const tileset = tilesetPrimitive;
const center = tileset.boundingSphere.center.clone();
const matrix = Cesium.Transforms.eastNorthUpToFixedFrame(center.clone());
const localMatrix = Cesium.Matrix4.inverse(matrix, new Cesium.Matrix4());
const points = cartesiansToLocal(activeShapePoints, localMatrix);
//activeShapePoints保存的是构成多边形的顶点的笛卡尔坐标数组
let instr = ``;
points.forEach((item,index) => {
instr += `mypoints[${index}] = vec2(${item[0]},${item[1]});`
})
let localMatrixStr = `mat4(`;
for(let i = 0; i < 16; i=i+4){
localMatrixStr += `vec4(${localMatrix[i]},${localMatrix[i+1]},${localMatrix[i+2]},${localMatrix[i+3]})`
if ((i+4)<16) {
localMatrixStr += ','
}
}
localMatrixStr += ')';
const tile = localTile;
const content = tile.content;
const model = content._model;
if (model && model._sourcePrograms && model._rendererResources) {
Object.keys(model._sourcePrograms).forEach(key => {
const program = model._sourcePrograms[key]
model._rendererResources.sourceShaders[program.vertexShader] =
`
precision highp float;\n
uniform mat4 u_modelViewMatrix;\n
uniform mat4 u_projectionMatrix;\n
uniform mat3 u_normalMatrix;\n
attribute vec3 a_position;\n
varying vec3 v_positionEC;\n
attribute vec3 a_normal;\n
varying vec3 v_normal;\n
attribute float a_batchId;\n
varying mat4 u_tileset_worldToLocalMatrix;
varying vec2 mypoints[${points.length}];
bool isPointInPolygon(vec2 point){
int nCross = 0; // 交点数
const int n = ${points.length};
for(int i = 0; i < n; i++){
vec2 p1 = mypoints[i];
vec2 p2 = mypoints[int(mod(float(i+1),float(n)))];
if(p1[1] == p2[1]){
continue;
}
if(point[1] < min(p1[1], p2[1])){
continue;
}
if(point[1] >= max(p1[1], p2[1])){
continue;
}
float x = p1[0] + ((point[1] - p1[1]) * (p2[0] - p1[0])) / (p2[1] - p1[1]);
if(x > point[0]){
nCross++;
}
}
return int(mod(float(nCross), float(2))) == 1;
}
void main(void) \n
{\n
${instr}
u_tileset_worldToLocalMatrix = ${localMatrixStr};
vec3 weightedPosition = a_position;\n
vec3 weightedNormal = a_normal;\n
float time = czm_frameNumber / 5.0;\n
vec4 model_local_position = vec4(a_position.xyz, 1.0);
vec4 tileset_local_position = u_tileset_worldToLocalMatrix * czm_model * model_local_position;
vec2 position2D = vec2(tileset_local_position.x,tileset_local_position.y);
if ( isPointInPolygon(position2D) ){ //判断顶点是否在范围内
weightedPosition.x += sin(time) * a_position.y / 8.0;//在这里可控制摇晃方向和距离,高度越高,摇晃距离越大
}
vec4 position = u_modelViewMatrix * vec4(weightedPosition, 1.0);\n
v_positionEC = position.xyz;\n
gl_Position = u_projectionMatrix * position;\n
v_normal = u_normalMatrix * weightedNormal;\n
}\n
`
})
model._shouldRegenerateShaders = true
}
}
// 世界坐标转局部坐标
const cartesiansToLocal = (positions, localMatrix) => {
const arr = [];
for (let i = 0; i < positions.length; i++) {
const position = positions[i];
const localp = Cesium.Matrix4.multiplyByPoint(
localMatrix,
position.clone(),
new Cesium.Cartesian3()
)
arr.push([localp.x, localp.y]);
}
return arr
}