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/Fabrichttps://github.com/CesiumGS/cesium/wiki/Fabric 里面介绍了cesium内置的一些材质,czm_materialInput和一些举例
对于czm_material和cam_materialInput也有解释:
再会过来看那段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里面则是将透明度进行了一个计算。