首页 > 建站教程 > 地图,GIS教程 >  Cesium自定义材质正文

Cesium自定义材质

材质是用于描述多边形、折线、椭球等对象的外观特征,材质可以是几何对象表面的任一一种着色,可以是贴在其表面的一张图片、也可以是一个纹理或图案。cesium中也提供了一些材质,比如颜色、图片,棋盘、虚线,箭头线等。但这些基本是不会满足我们实际开发中的需求,需要自定义材质。使用Fabric和GLSL可以写脚本新建材质,也可以从现在的材质中派生。


1. Material

Material是用于修改几何对象材质的一个类,在添加几何图形的时候,将设置material属于,用于设置几何对象的材质。如给绘制的线条设置一个颜色,可以使用颜色材质,如下所示:

let line = new Cesium.Entity({
    polyline:new Cesium.PolylineGraphics({
        positions : positions,
        material:Cesium.Color.RED.withAlpha(0.5),
        width:5,
    })
});


Cesium为我们提供了23种现成的Material类型,可通过Material.fromType方法和Fabric两种方式去获取并设置几何对象材质。如下是通过Material类的两种方式实现着色的示例:

polygon.material = Cesium.Material.fromType('Color');
polygon.material.uniforms.color = new Cesium.Color(1.0, 1.0, 0.0, 1.0);
//Fabric
polygon.material = new Cesium.Material({
    fabric : {
        type : 'Color',
        uniforms : {
            color : new Cesium.Color(1.0, 1.0, 0.0, 1.0)
        }
    }
});


2. Fabric

Fabric是Cesium中用于描述材质的一种JSON规定,使用Fabric和GLSL可以方便的定义材质。定义一个简单的Fabric对象,需要配置type,uniforms,两个属性,如果需要自定义着色器,需要添加source属性。如下为创建一个简单的Fabric对象:

polygon.appearance.material = new Cesium.Material({
  fabric: {
    type: 'Color',
    uniforms: {
      color: new Cesium.Color(1.0, 0.0, 0.0, 0.5)
    }
  }
})
// 修改颜色
polygon.appearance.material.uniforms.color = Cesium.Color.WHITE


type:用于定义材质的类型,使用的时候可以直接通过Cesium.Material.fromType('type');来指定定义好的材质。设置该参数,可以复用材质,传入一个不存在的 type类型之后,这个材质将被缓存下来。下次调用 new Cesium.Material或者 Material.fromType 就会引用缓存里的材质,不需要再传入Fabric对象。

uniforms:用于定义变量,每个 Material 均可以有 0 ~ N 个 uniform,这个参数在建立时指定,也能够在渲染后修改。其中有一个repeat属性,用于控制图片在水平和垂直方向重复的次数。

components: 该属性包含了 定义了材质外观的子属性。每个子属性是一个GLSL的代码段。该属性值包含了以下几个子属性:


    1. diffuse:材质的散射光通道,使用vec3定义了光在所有方向的散射值 。

    2. specular:材质的高光属性,定个定义了材质的反射强度。

    3. shininess:高反射的锐度,值 越大创建一个更小的高亮光斑。

    4. normal:材质的法向属性,使用vec3定义了在视点空间的表面法向量,一般在法向贴图上使用,默认是表面法向量。

    5. emission:材质的自发光属性,使用vec3定义了所有方向上灯光发出的颜色。

    6. alpha:材质的透明度,使用一个float值 定义。


2.1创建新的材质

创建一个新的材质,只需要设置Fabric,再加上一点点GLSL或者其他材质就可以了。通过source可以指定glsl的代码,如下是返回每个分量的默认值:

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


Fabric这么定义:

{
  source : 'czm_material czm_getMaterial(czm_materialInput materialInput) { return czm_getDefaultMaterial(materialInput); }'
}


2.2 材质输入

在czm_getMaterial函数中有一个czm_materialInput类型的属性,用于设置材质的输入,materialInput 变量在source和components属性中都可以配置,主要包括以下几个属性:

    1.s:一维纹理坐标。

    2.st:二维纹理坐标。

    3.str:三维纹理坐标。

    4. tangentToEyeMatrix:从片元的切线空间转到视点空间的转换矩阵,在法向贴图和凹凸贴图时使用。

    5. positionToEyeEC:从片元到视点之间的向量,为了反射和折射计算。向量的模表示了从片元到视点的距离。

    6. normalEC:片元在视点空间的单位化后的法向量,在凹凸贴图、反射、折射的时候使用。


2.3 合并材质

Fabric 有个materials属性,它的每个子属性也是Fabric材质。他们的材质可以可以在components或者source中引用。比如一个塑料材质可以通过 DiffuseMap和SpecularMap两个材质的合并来模拟。

{
  type : 'OurMappedPlastic',
  materials : {
    diffuseMaterial : {
      type : 'DiffuseMap'
    },
    specularMaterial : {
      type : 'SpecularMap'
    }
  },
  components : {
      diffuse : 'diffuseMaterial.diffuse',
      specular : 'specularMaterial.specular'
  }
};


3 自定义Material类

在实际开发中,为了使用方便,往往将一些常用的材质一个一个的封装,需要将其定义成Material类,以方便代码的复用。如下为封装的一个扩散回的代码:

import  * as Cesium from '@/Cesium/Source/Cesium';
import createPropertyDescriptor from "@/Cesium/Source/DataSources/createPropertyDescriptor.js";
import Property from "@/Cesium/Source/DataSources/Property.js";
const source =  "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" +
    "}";
function AnimationPointMaterialProperty(color,duration) {
    Cesium.PolylineTrailLinkMaterialProperty = AnimationPointMaterialProperty;
    Cesium.Material.AnimationPointMaterialType = 'AnimationPoint';
    Cesium.Material.AnimationPointMaterialSource = source;
    Cesium.Material._materialCache.addMaterial(Cesium.Material.AnimationPointMaterialType, {
        fabric: {
            type: Cesium.Material.AnimationPointMaterialType,
            uniforms: {
                color: new Cesium.Color(1.0, 0.0, 0.0, 1),
                time: 0
            },
            source: Cesium.Material.AnimationPointMaterialSource
        },
        translucent: function (material) {
            return true;
        }
    })
    this._definitionChanged = new Cesium.Event();
    this._color = undefined;
    this._colorSubscription = undefined;
    this.color = color;
    this.duration = duration;
    this._time = (new Date()).getTime();
}
Object.defineProperties(AnimationPointMaterialProperty.prototype, {
    isConstant: {
        get: function () {
            return Property.isConstant(this._color);
        },
    },
    definitionChanged: {
        get: function () {
            return this._definitionChanged;
        },
    },
    color: createPropertyDescriptor("color"),
});
AnimationPointMaterialProperty.prototype.getType = function () {
    return "AnimationPoint";
};
AnimationPointMaterialProperty.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;
};
AnimationPointMaterialProperty.prototype.equals = function (other) {
    return (
        this === other ||
        (other instanceof AnimationPointMaterialProperty &&
            Property.equals(this._color, other._color))
    );
};
export default AnimationPointMaterialProperty;


3.1 公共方法

在定义自己的AnimationPointMaterialProperty时,需要设置几个公共的方法,分别是:getValue,isConstant,definitionChanged,equals。

    1.getValue:用来获取某个时间点的特定属性值,包括两个参数:type和result,分别是用于传递时间点和存储属性值。

    2.isConstant:用来判断该属性是否会随时间变化,是一个bool类型。Cesium会通过这个变量来决定是否需要在场景更新的每一帧中都获取该属性的数值,从而来更新三维场景中的物体。如果isConstant为true,则只会获取一次数值,除非definitionChanged事件被触发。

    3.definitionChanged:是一个事件,可以通过该事件,来监听该Property自身所发生的变化,比如数值发生修改。

    4.equals:用来检测属性值是否相等。

具体的实例可以在。cgis中预览,后期将添加更多的自定义材质。