Cesium自定义材质Material以及一些思考

Cesium已有的材质,无法实现一些特定的功能。所以需要一些自定义的材质来实现。从git上找到了一个项目,里面有一两个实现自定义材质的方法。在此记录一下并学习。

这是一个扩散效果的圆形。代码如下:

function EllipsoidFadeMaterialProperty(color, duration) {
    this._definitionChanged = new Cesium.Event();
    this._color = undefined;
    this._colorSubscription = undefined;
    this.color = color;
    this.duration = duration;
    this._time = (new Date()).getTime();
}
Object.defineProperties(EllipsoidFadeMaterialProperty.prototype, {
    isConstant: {
        get: function () {
            return false;
        }
    },
    definitionChanged: {
        get: function () {
            return this._definitionChanged;
        }
    },
    color: Cesium.createPropertyDescriptor('color')
});
EllipsoidFadeMaterialProperty.prototype.getType = function (time) {
    return 'EllipsoidFade';
}
EllipsoidFadeMaterialProperty.prototype.getValue = function (time, result) {
    if (!Cesium.defined(result)) {
        result = {};
    }
    result.color = Cesium.Property.getValueOrClonedDefault(this._color, time, Cesium.Color.WHITE, result.color);

    result.time = (((new Date()).getTime() - this._time) % this.duration) / this.duration;
    return result;
}
EllipsoidFadeMaterialProperty.prototype.equals = function (other) {
    return this === other ||
        (other instanceof EllipsoidFadeMaterialProperty &&
            Property.equals(this._color, other._color))
}
Cesium.EllipsoidFadeMaterialProperty = EllipsoidFadeMaterialProperty;
Cesium.Material.EllipsoidFadeType = 'EllipsoidFade';
Cesium.Material.EllipsoidFadeSource =
    "czm_material czm_getMaterial(czm_materialInput materialInput)\n" +
    "{\n" +
    "czm_material material = czm_getDefaultMaterial(materialInput);\n" +
    "material.diffuse = 1.5 * color.rgb;\n" +
    "vec2 st = materialInput.st;\n" +
    "float dis = distance(st, vec2(0.5, 0.5));\n" +
    "float per = fract(time);\n" +
    "if(dis > per * 0.5){\n" +
    "material.alpha = 0.0;\n" +
    "discard;\n" +
    "}else {\n" +
    "material.alpha = color.a  * dis / per / 1.0;\n" +
    "}\n" +
    "return material;\n" +
    "}";
Cesium.Material._materialCache.addMaterial(Cesium.Material.EllipsoidFadeType, {
    fabric: {
        type: Cesium.Material.EllipsoidFadeType,
        uniforms: {
            color: new Cesium.Color(1.0, 0.0, 0.0, 1),
            time: 0
        },
        source: Cesium.Material.EllipsoidFadeSource
    },
    translucent: function (material) {
        return true;
    }
});

viewer.entities.add({
    name: 'EllipsoidFade',
    position: Cesium.Cartesian3.fromDegrees(104.0, 30.0, 100.0),
    ellipse: {
        height: 0,
        semiMinorAxis: 30000.0,
        semiMajorAxis: 30000.0,
        material: new Cesium.EllipsoidFadeMaterialProperty(Cesium.Color.RED, 3000)
    }
});

viewer.zoomTo(viewer.entities);

 我是从这个github里面找到的,git仓库链接:

GitHub - younggis/Cesium-Examples: Cesium示例,包括3DTiles、雷达扫描、动态扩散点、渐变立体墙、渐变建筑物、视场角大小、日照分析、空间三角形、可视域分析、动画、站心坐标转换、地形开挖、方量计算、FlowLine等

以下是对自定义材质的思考,思考的方向为两个:一、这个代码为什么要这么写?二、哪些才是重点内容?

一、代码结构为什么是这样的? 

Cesium官方有它自己自定义的材质,和上面代码类似的材质,我想到的是颜色材质:ColorMaterialProperty。

纯颜色的材质和这种有动态扩散效果的自定义材质区别的地方在于,扩散效果它是动态的,在某一时刻某一区域存在着透明的颜色。颜色的设置基本都是一样的。

找到了ColorMaterialProperty的源码进行了对比,这是源码:

function ColorMaterialProperty(color) {
  this._definitionChanged = new Event();
  this._color = undefined;
  this._colorSubscription = undefined;

  this.color = color;
}

Object.defineProperties(ColorMaterialProperty.prototype, {
  /**
   * Gets a value indicating if this property is constant.  A property is considered
   * constant if getValue always returns the same result for the current definition.
   * @memberof ColorMaterialProperty.prototype
   *
   * @type {Boolean}
   * @readonly
   */
  isConstant: {
    get: function () {
      return Property.isConstant(this._color);
    },
  },

  /**
   * Gets the event that is raised whenever the definition of this property changes.
   * The definition is considered to have changed if a call to getValue would return
   * a different result for the same time.
   * @memberof ColorMaterialProperty.prototype
   *
   * @type {Event}
   * @readonly
   */
  definitionChanged: {
    get: function () {
      return this._definitionChanged;
    },
  },

  /**
   * Gets or sets the {@link Color} {@link Property}.
   * @memberof ColorMaterialProperty.prototype
   * @type {Property|undefined}
   * @default Color.WHITE
   */
  color: createPropertyDescriptor("color"),
});

/**
 * Gets the {@link Material} type at the provided time.
 *
 * @param {JulianDate} time The time for which to retrieve the type.
 * @returns {String} The type of material.
 */
ColorMaterialProperty.prototype.getType = function (time) {
  return "Color";
};

/**
 * Gets the value of the property at the provided time.
 *
 * @param {JulianDate} time The time for which to retrieve the value.
 * @param {Object} [result] The object to store the value into, if omitted, a new instance is created and returned.
 * @returns {Object} The modified result parameter or a new instance if the result parameter was not supplied.
 */
ColorMaterialProperty.prototype.getValue = function (time, result) {
  if (!defined(result)) {
    result = {};
  }
  result.color = Property.getValueOrClonedDefault(
    this._color,
    time,
    Color.WHITE,
    result.color
  );
  return result;
};

/**
 * Compares this property to the provided property and returns
 * true if they are equal, false otherwise.
 *
 * @param {Property} [other] The other property.
 * @returns {Boolean} true if left and right are equal, false otherwise.
 */
ColorMaterialProperty.prototype.equals = function (other) {
  return (
    this === other || //
    (other instanceof ColorMaterialProperty && //
      Property.equals(this._color, other._color))
  );
};
export default ColorMaterialProperty;

在getValue里面加了些代码以及多接收了一个time参数,其它的地方基本上是相似的。那么,关键的代码应该就是这部分之外的。

为了理解后面几段代码,去看了下Material的源码,在1229行找到了下面这段。

/**
 * Gets the name of the color material.
 * @type {String}
 * @readonly
 */
Material.ColorType = "Color";
Material._materialCache.addMaterial(Material.ColorType, {
  fabric: {
    type: Material.ColorType,
    uniforms: {
      color: new Color(1.0, 0.0, 0.0, 0.5),
    },
    components: {
      diffuse: "color.rgb",
      alpha: "color.a",
    },
  },
  translucent: function (material) {
    return material.uniforms.color.alpha < 1.0;
  },
});

下面这个是1207行Material._materialCache对象:

Material._materialCache = {
  _materials: {},
  addMaterial: function (type, materialTemplate) {
    this._materials[type] = materialTemplate;
  },
  getMaterial: function (type) {
    return this._materials[type];
  },
};

那么重要的地方,就是fabric对象里面了。那段glsl代码,就是实现扩散效果的重要代码。

二、哪些是重要的内容?

fabric是什么东西?在Cesium的文档中给了一个链接:

https://github.com/CesiumGS/cesium/wiki/Fabricicon-default.png?t=M3K6https://github.com/CesiumGS/cesium/wiki/Fabric 里面介绍了cesium内置的一些材质,czm_materialInput和一些举例

Cesium自定义材质Material以及一些思考_第1张图片

对于czm_material和cam_materialInput也有解释:

Cesium自定义材质Material以及一些思考_第2张图片 

再会过来看那段source的代码,里面主要的逻辑在于:

Cesium.Material.EllipsoidFadeSource =
    "czm_material czm_getMaterial(czm_materialInput materialInput)\n" +
    "{\n" +
    "czm_material material = czm_getDefaultMaterial(materialInput);\n" +
    "material.diffuse = 1.5 * color.rgb;\n" +
    "vec2 st = materialInput.st;\n" +
    "float dis = distance(st, vec2(0.5, 0.5));\n" +
    "float per = fract(time);\n" +
    "if(dis > per * 0.5){\n" +
    "material.alpha = 0.0;\n" +
    "discard;\n" +
    "}else {\n" +
    "material.alpha = color.a  * dis / per / 1.0;\n" +
    "}\n" +
    "return material;\n" +
    "}";

 material.diffuse  入射光。color这个变量,来源于之前fabric里面uniforms的值。因为我更改其他地方的color命名没有效果,更改fabric里面的才有效果。

material.diffuse = 1.5*color.rgb; 控制的效果是这个颜色的亮度。把1.5改成0.5它会明显变得更暗。如果把1.5改成3或者6,明显可以看到颜色更亮了。而且有些地方的透明效果不太行了。

distance在opengl中可以求两个点point的距离。materialInpu.st是整个画布的大小,dis主要是扩散中心,从哪个位置开始扩散的。如果改成distance(st,vec2(0,0.5),那么它扩散的中心就变成了下方中点。

fract方法,在opengl中是求一个数的小数点部分。这个time应该是动态变化的,毕竟时间一直在走,可能和cesium不一样,它的时间数不是整数,而是小数。

接下来就是一个if判断了,然后就是设置透明度alpha为0。discard关键词在opengl中是片段截取的意思,相当于这一块不要了。else里面则是将透明度进行了一个计算。

你可能感兴趣的:(Cesium,JavaScript,GIS,前端)