Cesium之自定义材质

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

1. Material

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

php 复制代码
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类的两种方式实现着色的示例:

ini 复制代码
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

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

php 复制代码
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的代码,如下是返回每个分量的默认值:

scss 复制代码
czm_material czm_getMaterial(czm_materialInput materialInput)
{
    return czm_getDefaultMaterial(materialInput);
}

Fabric这么定义:

bash 复制代码
{
  source : 'czm_material czm_getMaterial(czm_materialInput materialInput) { return czm_getDefaultMaterial(materialInput); }'
}

2.2 材质输入

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

**1.s:**一维纹理坐标。

**2.st:**二维纹理坐标。

**3.str:**三维纹理坐标。

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

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

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

2.3 合并材质

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

css 复制代码
{
  type : 'OurMappedPlastic',
  materials : {
    diffuseMaterial : {
      type : 'DiffuseMap'
    },
    specularMaterial : {
      type : 'SpecularMap'
    }
  },
  components : {
      diffuse : 'diffuseMaterial.diffuse',
      specular : 'specularMaterial.specular'
  }
};

3自定义Material类

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

ini 复制代码
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: 用来获取某个时间点的特定属性值,包括两个参数:typeresult,分别是用于传递时间点和存储属性值。
  2. isConstant:用来判断该属性是否会随时间变化,是一个bool类型。Cesium会通过这个变量来决定是否需要在场景更新的每一帧中都获取该属性的数值,从而来更新三维场景中的物体。如果isConstanttrue,则只会获取一次数值,除非definitionChanged事件被触发。
  3. definitionChanged:是一个事件,可以通过该事件,来监听该Property自身所发生的变化,比如数值发生修改。
  4. equals:用来检测属性值是否相等。

也可参考

blog.csdn.net/u010133378/... blog.csdn.net/qq_40120946...

相关推荐
Bigger21 分钟前
从零搭建 AI 代码审查服务:一份前端也能看懂的 Python 学习笔记
前端·ci/cd·ai编程
lichenyang45337 分钟前
JSAPI、NAPI、Biz、Imp:ASCF Demo 如何真正调用系统能力和 C++ 能力
前端
lichenyang4531 小时前
IPC、JSVM、UIThread、libuv:ASCF 架构图里最容易混的几个词
前端
用户059540174461 小时前
Redis记忆存储故障恢复测试踩坑实录:手动测试让我漏掉了2个一致性Bug
前端·css
用户2136610035721 小时前
Vue2脚手架工程化与Axios集成
前端·vue.js
我不是外星人1 小时前
我把 Claude Code 搬到网页!自研高颜值 Web 交互工作台
前端·ai编程·claude
mixuecoding1 小时前
零成本搭建全球科技热点情报站:12 个平台,6 小时,0 元
前端
用户059540174461 小时前
用了3年Mock,才发现Redis记忆存储的测试一直漏掉了60%的边界场景
前端·css
石小石Orz1 小时前
AI具身交互:实现一个会说话的3D虚拟伴侣
前端·人工智能·后端
Muen2 小时前
iOS设计模式-外观Facade
前端