纹理贴到canvas上,然后通过点击canvas上的点找纹理对应的位置然后反算世界坐标系
注意:cesium相机实时渲染纹理 默认会进行y轴翻转
1.拿到的fbo 经过readPixels后,会有一堆Uint8Array 数据 需要给转换成rgba;
let r = pixels[i];let g = pixels[i + 1];let b = pixels[i + 2];let a = pixels[i + 3];
2.拿到这些值后需要根据canvas的宽度计算出对应的x,y值,简称canvas下的屏幕坐标.,
let x = (i / 4) % width;let y = Math.floor(i / (4 * width));
3.由深度图和当前uv坐标得到当前像素的NDC坐标,
let ndc = new Cesium.Cartesian4(); //等价于 Cesium.Cartesian4.UNIT_Wndc.x = (x / width) * 2.0 - 1.0;ndc.y = (y / height) * 2.0 - 1.0;ndc.z = 2;//宽度值ndc.w = 1.0;
4.然后只要采用View-Projection(视图-裁剪)的逆矩阵就可以将NDC坐标变换到世界坐标
注意:矩阵乘积的逆等于矩阵的逆的相反顺序的乘积
模型视图投影矩阵=投影矩阵×视图矩阵×模型矩阵
视图投影矩阵的逆矩阵 = 视图矩阵的逆矩阵 * 投影矩阵的逆矩阵
let inverseViewProjection = Cesium.Matrix4.multiply(this.customCamera.inverseViewMatrix,//应该是生成纹理的当时的矩阵,不是scene 下的矩阵uniformState.inverseProjection,new Cesium.Matrix4());let worldCoords = Cesium.Matrix4.multiplyByVector(inverseViewProjection,ndc,new Cesium.Cartesian4());let w = 1.0 / worldCoords.w; //投影坐标的转ndc 需要除以w 分量Cesium.Cartesian3.multiplyByScalar(worldCoords, w, worldCoords);worldPositions.push(worldCoords);
到此为止世界坐标计算出来了,开始下一步.从canvas上拾取点然后换世界坐标
5.canvas 的坐标系和webgl的坐标系 不同,还有由于我的canvas 并不是全屏的
let rect = canvas.getBoundingClientRect();let x = event.clientX - rect.left * (canvas.width / rect.width);let y = event.clientY - rect.top * (canvas.height / rect.height);
这样把x,y 值传入计算世界坐标的时候就能换算出点击canvas对应的世界坐标.其中坐标的转换包括canvas到webgl的坐标转换,屏幕坐标到ndc坐标转换,ndc 到世界坐标的转换.