Cesium进阶学习二、Appearance

一、简介(Appearance的分类)
Appearance是Primitive的重要组成部分,在Primitive章节中,我们已经简单地使用了Appearance来设置Primitive几何图形的外观。Cesium中有以下几种Appearance,不同的Appearance适应于不同的场景,在使用时需要注意,用错了Appearance可能导致看不见渲染结果。
在这里插入图片描述

1、MaterialAppearance:适用于所有Geometry类型的材质外观,支持传入material,关于material见下面第二部分
在这里插入图片描述

2、EllipsoidSurfaceAppearance:EllipsoidSurfaceAppearance是MaterialAppearance的一个版本,主要应用于那些于地球表面平行的几何类型,比如PolygonGeometry、RectangleGeometry,不适用于与地表垂直的几何类型,比如WallGeometry。EllipsoidSurfaceAppearance可以在计算大量顶点属性的时候节省内存。
在这里插入图片描述

3、PerInstanceColorAppearance:实例化几何的材质外观,我们知道Primitive通过实例化几何可以同时显示大量的几何实体,但是一个Primitive只能有一个Appearance,那当我们在使用实例化几何显示大量几何实体的时候,如果我们想要每个几何具有不同的外观如何实现呢,答案是通过PerInstanceColorAppearance这个类。
在这里插入图片描述

4、PolylineMaterialAppearance:专门用于线的材质外观,支持传入material。
在这里插入图片描述

5、PolylineColorAppearance:专门用于线顶点颜色的外观,PolylineGometry支持根据顶点颜色进行着色,需要结合该类。
在这里插入图片描述

二、Material材质:
Material 我们一般称其为材质,用于表达物体表面外观。在现实世界中,每一种物体对光都会产生不同的反应,这就会使不同物体的表面,看起来不一样的(比如海水和沙漠),所以世界才会如此多姿多彩。Cesium的Material类为我们封装了大量的材质类型,不同的几何应用不同的材质,可以使场景的视觉效果更加的丰富。目前共有20多种内置的材质类型。
在这里插入图片描述

1、每一种材质,都需要不同的输入参数,这就像炒菜一样,不同的菜需要不同的原材料。 关于Material的更多信息可以查看 Material。使用Material类,我们有以下两种方式:
a、new Cesium.Material()

let material = new Cesium.Material({
  fabric: {
      type: "Color",
      uniforms: {
          color: Cesium.Color.BLUE
      }
  }
});

传入参数为一个fabric,fabric 是对材质描述的一个json对象

b、Cesium.Material.fromType()

let material = Cesium.Material.fromType("Color", {
    color: Cesium.Color.BLUE
});

2、Material 内部的的主要构成
a、uniforms:统一值,用于向着色器内传递数据
b、shaderSource: 材质的着色器代码(一般情况下,我们不会修改默认的着色器代码,但是需要知道其执行流程),默认的shaderSource如下:

czm_material czm_getMaterial(czm_materialInput materialInput)
{
  czm_material material = czm_getDefaultMaterial(materialInput);
  return material;
}

我们以Color材质的着色器为例

uniform vec4 color_0;
czm_material czm_getMaterial(czm_materialInput materialInput)
{
	 czm_material material = czm_getDefaultMaterial(materialInput);
	 material.diffuse = czm_gammaCorrect(color_0.rgb);
	 material.alpha = color_0.a;
	 return material;
}

首先会声明变量,用于接收我们通过uniforms传递的数据。注意,这些变量会自动添加一个序号,在appearance中使用时也要添加这个序号。其次定义了一个czm_getMaterial方法(这个方法会在Appearance的片元着色器中被调用),方法的参数materialInput是一个czm_materialInput类型,方法内部通过materialInput作为输入参数又调用了Cesium内置的一些方法进行计算,最后将计算结果存储到czm_material类型的变量material中作为返回值。
czm_materialInput结构体,在源码Shaders/Builtin/Structs/materialInput.js中定义,用于存储输入的纹理坐标,法线等数据。

struct czm_materialInput
{
	float s;
	vec2 st;
	vec3 str;
	mat3 tangentToEyeMatrix;
	vec3 positionToEyeEC;
	vec3 normalEC;
};

czm_material结构体,在源码Shaders/Builtin/Structs/material.js中定义,用于存储Material着色器计算的结果。

struct czm_material{
	vec3 diffuse;
	float specular;
	float shininess;
	vec3 normal;
	vec3 emission;
	float alpha;
}

三、Appearance的构成

以 MaterialAppearance 为例,因为 MaterialAppearance 适用范围最大,最具代表性,后续章节除了特别说明,否则Appearance都是表示MaterialAppearance。MaterialAppearance 构造函数如下:

在这里插入图片描述

重要属性介绍:
a、flat 平面着色,不考虑光照,当设置为true,着色器就不使用光照计算。图一flat = true;图二flat = false
在这里插入图片描述
在这里插入图片描述

b、material 使用Cesium内置的材质进行着色计算,在上面Material中介绍过。
c、vertexShaderSource 顶点着色器
d、fragmentShaderSource 片元着色器

四、Appearance顶点着色器

1、Appearance默认的vertexShaderSource如下:

 in vec3 position3DHigh;
 in vec3 position3DLow;
 in vec3 normal;
 in vec2 st;
 in float batchId;
 out vec3 v_positionEC;
 out vec3 v_normalEC;
 out 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;
 }

首先看到的是一个position3DHigh和一个position3DLow输入变量,这是Cesium为了保证从CPU传递到GUP的数据精度,将模型顶点拆分为position3DHeight和position3DLow两个变量。

in vec3 position3DHigh;
in vec3 position3DLow;

然后是输入的法线和纹理坐标

 in vec3 normal;
 in vec2 st;

最后是输出参数(输出到片元着色器)

out vec3 v_positionEC;//输出顶点 in eye
out vec3 v_normalEC;//输出法线 in eye
out vec2 v_st; //输出纹理坐标

在顶点着色器代码中,我们可以通过position3DHigh、position3DHigh这两个变量相加获取几何体的模型坐标

in vec3 positionModel=position3DHigh+position3DLow;

可以通过以下代码验证求取的模型坐标是否正确

let geometry = Cesium.BoxGeometry.fromDimensions({
    dimensions: new Cesium.Cartesian3(1.0, 1.0, 1.0),
});
let position = Cesium.Cartesian3.fromDegrees(124.21936679679918, 5.85136872098397, 10);
let instance = new Cesium.GeometryInstance({
    geometry: geometry,
    modelMatrix: Cesium.Transforms.eastNorthUpToFixedFrame(position),
});
let appearance = new Cesium.MaterialAppearance({
    fragmentShaderSource: `
     in vec3 v_color;
     void main()
     {
       out_FragColor = vec4(v_color,0.5);
     }
    ` ,
    vertexShaderSource: `
     in vec3 position3DHigh;
     in vec3 position3DLow;
     in vec3 normal;
     in vec2 st;
     in float batchId; 
     out vec3 v_positionEC;
     out vec3 v_normalEC;
     out vec2 v_st; 
     out vec3 v_color;
     void main()
     {   
        vec3 positionModel=position3DHigh + position3DLow;
        if(positionModel.z>0.){
            v_color=vec3(1.0,0.,0.);
        }else{
            v_color=vec3(0.0,1.,0.);
        }
        gl_Position=czm_modelViewProjection*vec4(positionModel,1.);
     }`,
});
let primitive = viewer.scene.primitives.add(new Cesium.Primitive({
    geometryInstances: instance,
    appearance: appearance,
}));

我们创建一个BoxGeometry,长宽高都设置为1,因为BoxGeometry的原点是位于其中心点,所以我们知道此时盒子的z值范围应该为-0.5到0.5,我们设置z值大于0.为红色,小于0.为绿色,此时不出意外我们看到的结果应该是一个上红下绿的几何体。运行代码查看结果,正如我们预想的一样,只不过颜色被插值了,所以看起来是一个渐变的颜色,这是正常的。当构造viewer对象时如果不设置 scene3DOnly: true那么通过position3DHigh + position3DLow获取到的不是几何体的东北上局部坐标。
在这里插入图片描述

五、Appearance片元着色器

1、Appearance默认的fragmentShaderSource如下:

in vec3 v_positionEC;
in vec3 v_normalEC;
in 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);

#ifdef FLAT
    out_FragColor = vec4(material.diffuse + material.emission, material.alpha);
#else
    out_FragColor = czm_phong(normalize(positionToEyeEC), material, czm_lightDirectionEC);
#endif
}

首先是定义变量接收从顶点着色器中传入的数据。然后定义了着色器的入口函数main,在方法中首先通过输入数据组装了一个czm_materialInput类型的结构体materialInput。然后调用czm_getMaterial方法并将materialInput作为参数计算材质的着色结果czm_getMaterial方法是在Material中定义的,我们在Material一节中介绍过)。最后根据是否使用光照计算最后的着色结果并设置给out_FragColor,out_FragColor就是最终的片元颜色了。如果不使用光照直接将物体表面的颜色作为输出结果,否则根据光照采用冯式着色法计算结果,冯式着色法是图形学中常用的一种着色计算模型,关于冯式着色法更多信息可阅读相关书籍。

Appearance最后会将Material的shaderSource和fragmentShaderSource进行合并,我们可以通过Appearance的getFragmentShaderSource方法获取完整的代码。如果我们不需要使用Material材质,那我们在fragmentShaderSource中完全不用调用czm_getMaterial方法。

#define FACE_FORWARD
uniform vec4 color_0;
czm_material czm_getMaterial(czm_materialInput materialInput)
{
czm_material material = czm_getDefaultMaterial(materialInput);
material.diffuse = czm_gammaCorrect(color_0.rgb); 
material.alpha = color_0.a; 
return material;
}

in vec3 v_positionEC;
in vec3 v_normalEC;
in 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);

#ifdef FLAT
    out_FragColor = vec4(material.diffuse + material.emission, material.alpha);
#else
    out_FragColor = czm_phong(normalize(positionToEyeEC), material, czm_lightDirectionEC);
#endif
}

六、向Appearance着色器中传递数据

我们在项目需要传递参数,有两种方式可以向Appearance的着色器中传递数据,即使您不需要Material的着色计算结果,想要完全在fragmentShaderSource中完全自定义自己的着色代码,您也可以通过Material的uniforms传递数据。

1、第一种方式是通过Material的uniforms属性:传递一个颜色值为例我们命名为u_color, 首先创建一个基础的Appearance并将u_color设置到uniforms属性中。

let appearance = new Cesium.MaterialAppearance({
       material: new Cesium.Material({
           fabric: {
               uniforms: {
                   u_color: Cesium.Color.BLUE
               }
           }
       })
   })

在片元着色器中可以直接使用,但是必须添加一个索引值,比如现在只有一个uniforms参数,那么索引值为_0,如下:

 fragmentShaderSource: `
  in vec3 v_positionEC;
  in vec3 v_normalEC;
  in vec2 v_st;
  void main()
  {
      out_FragColor =u_color_0;
  }`

在片元着色器中不需要声明变量接收,因为最后Appearance的片着色器和Material的着色器会合并,这在上一节中我们说过。而在顶点着色器中必须声明接收数据的变量,否则会出现未定义的错误。声明用于接收的变量名称也必须是带索引的,代码如下:

vertexShaderSource: `
  in vec3 position3DHigh;
  in vec3 position3DLow;
  in vec3 normal;
  in vec2 st;
  in float batchId;
  out vec3 v_positionEC;
  out vec3 v_normalEC;
  out vec2 v_st;

  uniform vec4 u_color_0;
  void main()
  {
      vec4 color=u_color_0;
      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;
  }
  `

2、第二种方式是为Appearance对象声明一个uniforms属性,然后将参数设置到uniforms中,这种方式在Cesium相关的文档中没有说明。

let appearance = new Cesium.MaterialAppearance({
    fragmentShaderSource: `
    in vec3 v_positionEC;
    in vec3 v_normalEC;
    in vec2 v_st;
    uniform vec4 u_color;
    void main()
    {
        out_FragColor =u_color;
        return;
        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);

    #ifdef FLAT
        out_FragColor = vec4(material.diffuse + material.emission, material.alpha);
    #else
        out_FragColor = czm_phong(normalize(positionToEyeEC), material, czm_lightDirectionEC);
    #endif
    }`,
    vertexShaderSource: `
        in vec3 position3DHigh;
        in vec3 position3DLow;
        in vec3 normal;
        in vec2 st;
        in float batchId;
        out vec3 v_positionEC;
        out vec3 v_normalEC;
        out vec2 v_st;

        uniform vec4 u_color;

        void main()
        {
            vec4 color=u_color;

            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;
        }
        `

})
appearance.uniforms = {
    u_color: Cesium.Color.BLUE
}

注意:
必须在创建完Appearance对象后再设置uniforms属性,不能在构造函数中设置
顶点着色器和片元着色器中如果要使用,必须声明用于接收的参数,参数不需要添加索引值

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Cesium进阶学习

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值