背景
当年使用Arcgis for js的时候,想渲染水面,一直不成功,什么结合three.js,奈何技术还不行,搞不定水面渲染。但是!今天终于实现了,可以实现高端水面渲染了。
版本说明
cesium 1.97
<script src="https://cesium.com/downloads/cesiumjs/releases/1.97/Build/Cesium/Cesium.js"></script>
<link href="https://cesium.com/downloads/cesiumjs/releases/1.97/Build/Cesium/Widgets/widgets.css" rel="stylesheet">
数据准备
一张png图片,最好是黑白渐变色,这样RGB通道值都是一样的,方便渲染水深。没有png图片就用ps画一张,并且裁剪出不规则的形状。
图1 水深栅格图
解析cesium官方Water源码
使用Cesium.Material创建water,然后debug加断点,在源码框看Cesium变量,在里边翻找source,就看到源码的字符串了。如下图:
图2 Water材质源码
篡改源码,变成自己的材质器
fabric: {
uniforms: {
depthMap: './images/depth_6.png',//水深截图
baseWaterColor: new Cesium.Color(64 / 255.0, 157 / 255.0, 253 / 255.0, 0.5),
blendColor: new Cesium.Color(0.0, 1.0, 0.7, 0.5),
// specularMap: './images/water.jpg', // Change to your specular map
normalMap: './images/waterNormals.jpg', // Change to your normal map
frequency: 800.0,
animationSpeed: 0.01,
amplitude: 3,
specularIntensity: 10,
fadeFactor: 1,
specularMap: "czm_defaultImage"
},
source: `
uniform sampler2D specularMap;
uniform sampler2D normalMap;
uniform sampler2D depthMap;
uniform vec4 baseWaterColor;
uniform vec4 blendColor;
uniform float frequency;
uniform float animationSpeed;
uniform float amplitude;
uniform float specularIntensity;
uniform float fadeFactor;
czm_material czm_getMaterial(czm_materialInput materialInput)
{
czm_material material = czm_getDefaultMaterial(materialInput);
float time = czm_frameNumber * animationSpeed;
float fade = max(1.0, (length(materialInput.positionToEyeEC) / 10000000000.0) * frequency * fadeFactor);
float specularMapValue = texture2D(specularMap, materialInput.st).r;
vec4 noise = czm_getWaterNoise(normalMap, materialInput.st * frequency, time, 0.0);
vec3 normalTangentSpace = noise.xyz * vec3(1.0, 1.0, (1.0 / amplitude));
normalTangentSpace.xy /= fade;
normalTangentSpace = mix(vec3(0.0, 0.0, 50.0), normalTangentSpace, specularMapValue);
normalTangentSpace = normalize(normalTangentSpace);
float tsPerturbationRatio = clamp(dot(normalTangentSpace, vec3(0.0, 0.0, 1.0)), 0.0, 1.0);
material.alpha = mix(blendColor.a, baseWaterColor.a, specularMapValue) * specularMapValue;
material.diffuse = mix(blendColor.rgb, baseWaterColor.rgb, specularMapValue);
material.diffuse += (0.1 * tsPerturbationRatio);
material.diffuse = material.diffuse;
material.normal = normalize(materialInput.tangentToEyeMatrix * normalTangentSpace);
material.specular = specularIntensity;
material.shininess = 10.0;
// handle alpha to hidden other area
// made by dzm
float depthMapShow = texture2D(depthMap, materialInput.st).a;
if(depthMapShow < 0.5){
material.alpha = depthMapShow;
}
// set different color by depth
// you can edify code by yourself
float dep = texture2D(depthMap, materialInput.st).r;
if(dep > 0.3){
material.diffuse -= vec3(0.2);
}
if(dep > 0.5){
material.diffuse -= vec3(0.2);
}
if(dep > 0.7){
material.diffuse -= vec3(0.2);
}
if(dep > 0.9){
material.diffuse -= vec3(0.2);
}
return material;
}
`
},
效果图
图3 效果图1
图4 效果图2
中间踩坑
说到坑,那就太多了。好多个,慢慢爬出来的,坑如下:
- Cesium结合Three.js 特地去了github上找资源,找到了两个整合版本,还有Cesium官网的版本,渲染平常的3D物体是可以,但是渲染水面,就不行了。不显示的原因很多,猜测是WebGL的兼容问题、坐标系问题(找不见)、比例问题(太小,看不见渲染到哪里去了)
- Bilibili上找到一个水流模拟的案例,Cesium使用SPH方式的流体模拟测试_哔哩哔哩_bilibili 智智哥哥啊的案例很厉害,大佬。还看到了他的博客,讲思路,用sph实现。奈何,支持储备不够,搞不了人家那种。
- GPT聊天,找方案,我有水深的三角数据了,怎么才能Cesium渲染呢?给了一个个的方案,都不行。
- 中间了解到GLSL,使用两个着色器(点、片面),然后学了几天。The Book of Shaders 然后就会发现,GLSL的大佬是真的多呀,用GLSL做俄罗斯方块,复刻了!
- 复合材质渲染,没出效果,水都出不来,原因不详。
- 渲染栅格图,把源数据高程tif,浏览器不支持,搞成png,没经纬度了。问GPT咋办,一点点的试验,成了。所以:九品之人,AI替不了呀,看人家智智哥哥做的Cesium渲染,服!
相关知识储备
Cesium开发、GLSL、GIS基础、js调试