对于一个 MaterialAppearance 对象来说,它的顶点着色器代码默认为: attribute vec3 position3DHigh; attribute vec3 position3DLow; attribute vec3 normal; attribute vec2 st; attribute float batchId; varying vec3 v_positionEC; varying vec3 v_normalEC; varying vec2 v_st; void main() { vec4 p = czm_computePosition(); v_positionEC = (czm_modelViewRelativeToEye * p).xyz; // position in eye coordinates v_normalEC = czm_normal * normal; // normal in eye coordinates v_st = st; gl_Position = czm_modelViewProjectionRelativeToEye * p; } 0. 预备知识Cesium 拥有一个小规模的内置 glsl 库,预置了非常多 1. czm_computePosition()很不幸的是,这个函数并不是完整地内置在 幸运的是,通过全文检索,我找到了它的生成代码,它位于
Primitive._modifyShaderPosition = function ( primitive, vertexShaderSource, scene3DOnly ) { // ... }; Cesium 会用正则去匹配你写的 默认情况下,这个 XXX 就等于 attribute vec3 position3DHigh; attribute vec3 position3DLow; vec4 czm_computePosition() { return czm_translateRelativeToEye(position3DHigh, position3DLow); } 到这一步,看到实际上作用的函数是 2. czm_translateRelativeToEye(vec3, vec3)vec4 czm_translateRelativeToEye(vec3 high, vec3 low) { vec3 highDifference = high - czm_encodedCameraPositionMCHigh; vec3 lowDifference = low - czm_encodedCameraPositionMCLow; return vec4(highDifference + lowDifference, 1.0); } 它的作用是,将世界坐标减去相机中心坐标,返回一个齐次坐标,即将世界坐标平移到相机坐标系下,而不旋转。 有的朋友可能会有两个疑问:①世界坐标在哪里?②相机坐标在哪里? 对于返回值 那么,世界坐标和相机坐标呢?代码中明明是 high、low、czm_encodedCameraPositionMCHigh、czm_encodedCameraPositionMCLow 啊? 这要从一个不在 API 中的类: 补充 EncodedCartesian3:编码后的笛卡尔坐标众所周知,64位浮点数(双精度)和 32位浮点数的精度是不一样的,也正是因为 Cesium 的设计初衷:世界级高精度三维地图引擎,导致了空间中的坐标值数值比较大。不要拘泥于地表,Cesium 的范围大至太阳系。 所以,直接将
数学大佬就想出了这个编码后的笛卡尔坐标对象,将原来的笛卡尔坐标拆成两个,一大一小,大的叫 可以这么创建编码后的笛卡尔坐标: const eCartesian3 = Cesium.EncodedCartesian3.fromCartesian(new Cesium.Cartesian3(-2644963.9889313546, 5763731.142118295, 2199400.7089496767)) 还原也很简单: const origin = Cesium.Cartesian3.add(eCartesian3.high, eCartesian3.low) 具体算法很简单,读者有兴趣可以自己去看源码。 现在,我们继续原来的讨论。 把 glsl 的代码稍微整理一下,不难看出其实是这样算的: return vec4(high + low - (czm_encodedCameraPositionMCHigh + czm_encodedCameraPositionMCLow), 1.0); 而前面的 后面的 拿 世界坐标 - 相机中心坐标,这是什么?平移啊! 这就解释了 3. czm_modelViewProjectionRelativeToEye这是一个动态的 czm_modelViewProjectionRelativeToEye: new AutomaticUniform({ size: 1, datatype: WebGLConstants.FLOAT_MAT4, getValue: function (uniformState) { return uniformState.modelViewProjectionRelativeToEye; }, }), 它是个4x4的矩阵,作用是将相机坐标转换至裁剪坐标。 所以不难得出顶点着色器的最后一行代码含义: gl_Position = czm_modelViewProjectionRelativeToEye * p; 它就是把 p 这个由 4. AutomaticUniforms.js上面提及两次这个文件,这个文件的设置了一堆 glsl 5. 顶点着色器修改有了上述基础,想修改 Primitive 的顶点就十分容易了,最简单的思路是,根据 而 “地形压平”、“3DTiles 瓦片压平” 的思路就是基于此,但是,找到地形的 Primitive、3DTiles 的 Primitive,还有很长一段路要走,譬如 Cesium 对数据的跟新机制、渲染循环机制、视锥体与绘制命令的机制的熟读等等知识。 |
|