天天看點

Cesium 頂點着色器中求解模型坐标

1. 由世界坐标轉模型坐标

頂點着色器:

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()
{
  vec3 positionWC = position3DHigh + position3DLow; // 得到世界坐标
  // 官方得到世界坐标(齊次)是這麼做的,在三維模式下等價
	// vec4 positionWC = czm_computePosition();
  vec4 positionMC = czm_inverseModel * vec4(positionWC, 1); // 得到模型坐标

  // 以下為官方代碼,未改動,僅修改注釋
  vec4 p = czm_computePosition(); // 得到世界坐标

  v_positionEC = (czm_modelViewRelativeToEye * p).xyz; // 得到相機坐标
  v_normalEC = czm_normal * normal; // 得到相機坐标系下的法線向量
  v_st = st; // 傳遞uv

  gl_Position = czm_modelViewProjectionRelativeToEye * p; // 世界坐标到裁剪空間坐标
}
           

2. 由相機坐标轉模型坐标

頂點着色器

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; // 得到相機坐标
  v_normalEC = czm_normal * normal;
  v_st = st;
  
  /** 此處開始添加計算模型坐标的代碼 */
  vec4 positionMC = czm_inverseModelView * vec4(v_positionEC, 1.0); // 得到模型坐标
  /** 添加的代碼結束 */
  
  gl_Position = czm_modelViewProjectionRelativeToEye * p; // 世界坐标到裁剪空間坐标
}
           

3. 坐标陷阱:模型坐标系≠東北上坐标系

參考如下代碼:

var viewer = new Cesium.Viewer("cesiumContainer");
viewer.scene.globe.depthTestAgainstTerrain = true;
viewer.camera.setView({
    destination : new Cesium.Cartesian3(-2644963.9889313546, 5763731.142118295, 2199400.7089496767), //世界坐标系下的一個坐标點
    orientation : {//旋轉角度
        heading :6.075,
        pitch :-0.727,
        roll : 6.283
    }
});

const extrudedPolygon = new Cesium.PolygonGeometry({
  polygonHierarchy : new Cesium.PolygonHierarchy(
    Cesium.Cartesian3.fromDegreesArray([
      112.41726298378288, 23.290411251106182,
      113.67072522399741, 23.560312361463682,
      114.09370956893551, 22.590768298743153,
      112.83803246418894, 22.285610818885644
    ])
  ),
  extrudedHeight: 30000
});

const instance = new Cesium.GeometryInstance({
  geometry: extrudedPolygon,
  id: 'box with height'
});

const m = new Cesium.Material({
  fabric: {
    type: 'Color',
    uniforms: {
      color: new Cesium.Color(216 / 255.0, 170 / 255.0, 208 / 255.0).withAlpha(0.618),
    },
  }
});

const aper =  new Cesium.MaterialAppearance({
  fragmentShaderSource: 
  ` varying vec3 v_positionEC;
    varying vec3 v_normalEC;
    varying vec2 v_st;

    void main()
    {
        vec3 positionToEyeEC = -v_positionEC;

        vec3 normalEC = normalize(v_normalEC);
    #ifdef FACE_FORWARD
        normalEC = faceforward(normalEC, vec3(0.0, 0.0, 1.0), -normalEC);
    #endif

        czm_materialInput materialInput;
        materialInput.normalEC = normalEC;
        materialInput.positionToEyeEC = positionToEyeEC;
        materialInput.st = v_st;
        czm_material material = czm_getMaterial(materialInput);
				material.diffuse = vec3(0.24313725490196078, 0.7372549019607844, 0.9333333333333333);
			  material.emission = vec3(0.0, 0.66666666, 0.0);
        material.specular = 0.5;
        material.shininess = 0.8;

    #ifdef FLAT
        gl_FragColor = vec4(material.diffuse + material.emission, material.alpha);
    #else
        gl_FragColor = czm_phong(normalize(positionToEyeEC), material, czm_lightDirectionEC);
    #endif
    }
   `,
  vertexShaderSource: 
  `
  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;
      vec4 positionMC = czm_inverseModelView * vec4(v_positionEC, 1.0);
      vec4 positionMC_new = vec4(positionMC.xy, positionMC.z + czm_frameNumber * 100.0, 1.0); // z軸向上平移動畫
      vec4 resultPosition = czm_modelViewInfiniteProjection * positionMC_new; // 一步直接算到 gl_Position 所需的坐标
      gl_Position = resultPosition;

  }
	`,
});

var p = viewer.scene.primitives.add(new Cesium.Primitive({
  geometryInstances: instance,
  appearance: aper,
  releaseGeometryInstances: false,
  compressVertices: false,
}));
           

在頂點着色器處,我對模型坐标的z值進行了修改,達到z軸平移動畫的效果

Cesium 頂點着色器中求解模型坐标

可是動畫的效果并不是沿着地表的垂直向上的方向平移,換做是 x、y 平移也不是對應的正東方、正北方(如果平移量大,還要考慮曲率的問題)

是以,可以下結論:

頂點着色器中的模型坐标所用的局部坐标系,僅僅是原點在模型中心,但是三軸并不是沿着正東x、正北y、垂直朝上z這三軸的。

要格外注意這一點,這是我在使用 Primitive API 時發現的問題。

繼續閱讀