• Cesium中自定义材质material


    学习参考文章:https://github.com/AnalyticalGraphicsInc/cesium/wiki/Fabrichttps://www.cnblogs.com/onsummer/p/14059215.html
    好记性不如烂笔头,记录一下cesium自定义材质的知识。自定义材质不是必须的,但想要呈现绚丽的效果,还得自定义材质来实现。

    内置材质

    类型

    材质通过漫反射、镜面反射、法线、发射和 alpha 分量的组合来定义表面外观。 这些值是使用名为 Fabric 的 JSON 模式指定的,该模式在幕后被解析并组装成 glsl 着色器代码。内置的材质可以直接拿来使用,最常用的就是Color和Image。部分材质及它的uniforms如下:

    材料类型描述uniforms
    Color单纯的颜色color
    Imagejpg 或 png 的贴图图片image、repeat
    Checkerboard国际象棋格子lightColor、darkColor、repeat
    Stripe竖条纹旗帜horizontal、evenColor、oddColor、offset、repeat
    Dot行列点阵lightColor、darkColor、repeat
    Grid线状网格color、cellAlpha、lineCount、lineThickness、lineOffset
    DiffuseMap漫反射贴图image、channels、repeat
    SpecularMap单通道贴图image、channel、repeat
    AlphaMap单通道的不透明度贴图image、channel、repeat
    NormalMap三通道贴图image、channels、repeat、strength
    BumpMap单通道的凹凸贴图image、channels、repeat、strength
    EmissionMap三通道的自发光贴图image、channel、repeat
    PolylineArrow箭头线,终点在折线末端color
    PolylineGlow发光线color、glowPower、taperPower
    PolylineOutline描边线color、outlineColor、outlineWidth
    Water水面贴图,看起来有水波动效baseWaterColor、blendColor、specularMap、normalMap、frequency、animationSpeed、amplitude、specularIntensity
    RimLighting边缘会比较亮color、rimColor、width

    使用

    通过几何对象的 material 属性可以创建材质,这个属性是 Cesium.Material对象,使用内置材质设置material 非常简单。

    // color
    polygon.appearance.material = Cesium.Material.fromType('color');
    
    // image
    polygon.appearance.material = Cesium.Material.fromType('Image')
    polygon.appearance.material.uniforms.image = 'image.png'
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    上面就创建了自定义的颜色和贴图的材质。Cesium.Material.fromType() 方法是一个简写,完整的写法是如下,Fabric 是 Cesium 中用于描述材质的一种 JSON 规定,里面type属性是材质的类型,也就是材质标识,uniforms是对应材质的一些变量,可以设置材质的颜色、图片等,不同材质的uniforms不一样。

    // color
    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
    
    // image
    polygon.appearance.material = new Cesium.Material({
      fabric: {
        type: 'Image',
        uniforms: {
          image: 'image.png'
        }
      }
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    自定义材质

    使用 fabric 对象 + GLSL 代码和其他素材,就可以创建自定义材质。

    let fabric = {
      // ...
    }
    
    polygon.appearance.material = new Cesium.Material({
      fabric: fabric
    })
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    let fabric = {
       type : 'MyNewMaterial',
       // ...其他 fabric JSON 的属性
    }
    polygon.appearance.material = new Cesium.Material({
      fabric : fabric
    });
    // ... 然后在另一处需要这个 fabric
    anotherPolygon..appearance.material = Material.fromType('MyNewMaterial');
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    组件

    白色的漫反射材质或许是最常用的:

    let fabric = {
      components: {
        diffuse: 'vec3(1.0)'
      }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    稍微复杂一些,加一点镜面反射,使得正射视角看反光最强,侧面变弱:

    let fabric = {
      components : {
        diffuse : 'vec3(0.5)',
        specular : '0.1'
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    Fabric 是 Cesium 中用于描述材质的一种 JSON 规定,components 属性包含了 fabric 所定义的材质的各种子因素。每个子因素均使用简短的 glsl 代码字符串表示,因此上面写的 vec(0.5) 就表示 rgb 均为 0.5 的一种颜色。这个简单的 glsl 代码可以使用所有 glsl 内置的函数,例如 mix、cos、texture2D 等。components 可以定义 6 个属性:

    名称默认值描述
    diffuse‘vec3(0.0)’漫反射颜色,即物体的基本颜色
    specular‘0.0’镜面反射,定义的是单方向反射光强度
    shininess‘1.0’镜面反射的清晰度,这个值越大会出现更小的高光光斑
    normal法线,默认无法线
    emission‘vec3(0.0)’自发光,默认不发光
    alpha‘1.0’不透明度,0.0 是完全透明,1.0 是不透明。

    源代码

    components 还有一个更强大而灵活的选择是 glsl 源代码,通过 glsl 的方式修改材质。这个途径将设置的 glsl 代码传递到 czm_getMaterial 函数,这个函数执行后返回材质的 components:

    struct czm_materialInput
    {
      float s;
      vec2 st;
      vec3 str;
      mat3 tangentToEyeMatrix;
      vec3 positionToEyeEC;
      vec3 normalEC;
    };
    
    struct czm_material
    {
      vec3 diffuse;
      float specular;
      float shininess;
      vec3 normal;
      vec3 emission;
      float alpha;
    };
    
    czm_material czm_getMaterial(czm_materialInput materialInput);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    默认情况下,材质的默认值会被返回:

    czm_material czm_getMaterial(czm_materialInput materialInput)
    {
        return czm_getDefaultMaterial(materialInput);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5

    这个时候的 fabric 对象是:

    let fabric = {
      components: {
        source: `czm_material czm_getMaterial(czm_materialInput materialInput) { return czm_getDefaultMaterial(materialInput); }`
      }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    上面修改了漫反射和镜面反射的例子可以通过 glsl 改写为:

    let fabric = {
      source: `czm_material czm_getMaterial(czm_materialInput materialInput)
               {
              	  czm_material m = czm_getDefaultMaterial(materialInput);
              	  m.diffuse = vec3(0.5);
               	  m.specular = 0.5;
                  return m;
               }`
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    使用 glsl 代替 components 虽然看起来代码比较冗长,但是提供了灵活性。
    如果不是有特别的需求,使用 components 属性指定材质的各种因子就可以了。但是,不管是哪一种,在这些 glsl 代码中,都是可以直接使用 glsl 的内置函数和 Cesium 预定义的 函数、结构体、常量的。

    输入变量

    materialInput 变量在 source 和 components 中均可以使用,在 glsl 代码的定义中,这个变量是 czm_materialInput 结构体有如下字段:

    名称类型描述
    sfloat一维纹理坐标
    stvec2二维纹理坐标
    strvec3三维纹理坐标,三维纹理的二维部分不一定就是二维纹理坐标,切记。例如,在一个椭球几何中,s可能就是从下到上,st可能是经纬度,str三维纹理就是包围盒的三轴方向。
    tangentToEyeMatrixmat3用于法线贴图、凹凸贴图的转换矩阵,转换切线空间坐标到视图坐标
    positionToEyeECvec3从 fragment 到 视图空间坐标的向量(不知道这个 fragment 说的是什么),用于反射和折射等。向量的长度是 fragment 到视图(相机)的距离。
    normalECvec3fragment 在视图坐标中的法线(已归一化),作用于凹凸贴图、反射、折射等例如可以这么设置来可视化纹理坐标:
    let fabric = {
      components: {
        diffuse: 'vec3(materialInput.st, 0.0)'
      }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    一样的,可以把 diffuse 组件设置为 materialInput.normalEC 来可视化法线。

    除了 materialInput 这个传入的参数,还可以访问 Cesium 提供的 uniform 变量。

    例如,可以通过一个 color uniform 去设置 diffuse 组件和 alpha 组件,来创建自己的 Color 材质:

    let fabric = {
      type: 'MyColor',
      uniforms: {
        color: new Color(1.0, 0.0, 0.0, 1.0)
      },
      components: {
        diffuse: 'color.rgb',
        alpha: 'color.a'
      }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    在 fabric 中,glsl 中的 uniform 变量、new Cesium.Material() 和 Cesium.Material.fromType() 返回的 js 对象中的 uniform 变量与 uniforms 属性的子属性(例如这里的 uniforms.color)具有相同的名称。
    子属性的值(对于标量来说)或子属性(对于向量来说)即 uniform 的值。

    下例,通过 image uniform 来实现自定义的 DiffuseMap 材质:

    let fabric = {
      type: 'OurDiffuseMap',
      uniforms: {
        image: 'czm_defaultImage'
      },
      components: {
        diffuse: 'texture2D(image, materialInput.st).rgb'
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    czm_defaultImage 是 1x1 分辨率的图片,根据上面的说法,这可以从 dataurl 或图片文件中获取,例如:

    polygon.appearance.material = Material.fromType('OurDiffuseMap');
    polygon.appearance.material.uniforms.image = 'diffuse.png';
    
    
    • 1
    • 2
    • 3

    还有一个多维数据集的变量:czm_defaultCubeMap。

    支持 glsl 的 uniform 类型,例如 float、vec3、mat4 等。

    对于 uniform 数组还不支持,不过在规划中了。

    在 fabric 这个对象中,uniforms 下的所有属性均被 glsl 认作是 uniform 变量,可以直接当变量使用,例如上例中的 texture2D(image, materialInput.st) 中的 image 参数。

    在这里,规定了 image 这个 uniform 的类型是 Cesium 内置的 czm_defaultImage 结构体类型。

    fabric 对象

    fabric 是一个有官方规定如何写的 js 对象,它拥有 5 个属性:

    • type:用于声明 fabric 对象最终会生成什么材质,如果是官方内置的,直接用官方内置的,否则则创建自定义的材质并缓存。
    • materials:允许再塞进去子一级的 fabric,构成复合材质。
    • source:是 glsl 源代码,它主要是对 czm_getMaterial 这个 Cesium 内置的 glsl 函数的实现,返回值是 czm_material
    • components:是几个基本材质因子的 glsl 代码快捷入口,是 source 的一种简略实现。
    • uniforms:一些全局变量,可以在 glsl 代码中用到这个 uniform 定义的所有变量了。

    实例

    效果

    发光特效
    在这里插入图片描述

    代码

    材质代码:

    const Cesium = require('cesium/Cesium')
    function DynamicWallMaterialProperty (options) {
      this._definitionChanged = new Cesium.Event()
      this._color = undefined
      this._colorSubscription = undefined
      this.color = options.color
      this.duration = options.duration
      this.trailImage = options.trailImage
      this._time = (new Date()).getTime()
      this.viewer = options.viewer
    }
    Object.defineProperties(DynamicWallMaterialProperty.prototype, {
      isConstant: {
        get: function () {
          return false
        }
      },
      definitionChanged: {
        get: function () {
          return this._definitionChanged
        }
      },
      color: Cesium.createPropertyDescriptor('color')
    })
    DynamicWallMaterialProperty.prototype.getType = function (time) {
      return 'DynamicWall'
    }
    DynamicWallMaterialProperty.prototype.getValue = function (time, result) {
      if (!Cesium.defined(result)) {
        result = {}
      }
      result.color = Cesium.Property.getValueOrClonedDefault(this._color, time, Cesium.Color.WHITE, result.color)
      if (this.trailImage) {
        result.image = this.trailImage
      } else {
        result.image = Cesium.Material.DynamicWallImage
      }
    
      if (this.duration) {
        result.time = (((new Date()).getTime() - this._time) % this.duration) / this.duration
      }
      this.viewer.scene.requestRender()
      return result
    }
    DynamicWallMaterialProperty.prototype.equals = function (other) {
      return this === other ||
            (other instanceof DynamicWallMaterialProperty &&
                Cesium.Property.equals(this._color, other._color))
    }
    Cesium.DynamicWallMaterialProperty = DynamicWallMaterialProperty
    Cesium.Material.DynamicWallType = 'DynamicWall'
    Cesium.Material.DynamicWallImage = require('./colors.png')
    Cesium.Material.DynamicWallSource = `
                                                czm_material czm_getMaterial(czm_materialInput materialInput)
                                                {
                                                    czm_material material = czm_getDefaultMaterial(materialInput);
                                                    vec2 st = materialInput.st;
                                                    vec4 colorImage = texture2D(image, vec2(fract(st.t - time), st.t));
                                                    vec4 fragColor;
                                                    fragColor.rgb = color.rgb / 1.0;
                                                    fragColor = czm_gammaCorrect(fragColor);
                                                    material.alpha = colorImage.a * color.a;
                                                    material.diffuse = color.rgb;
                                                    material.emission = fragColor.rgb;
                                                    return material;
                                                }
                                                `
    Cesium.Material._materialCache.addMaterial(Cesium.Material.DynamicWallType, {
      fabric: {
        type: Cesium.Material.DynamicWallType,
        uniforms: {
          color: new Cesium.Color(1.0, 1.0, 1.0, 1),
          image: Cesium.Material.DynamicWallImage,
          time: 0
        },
        source: Cesium.Material.DynamicWallSource
      },
      translucent: function (material) {
        return true
      }
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81

    分析

    上面是自定义材质的代码,大部分自定义材质代码的模板也是如此,不同的就是glsl里的代码和uniforms变量。uniforms变量里定义的属性,可以直接在Cesium.Material.DynamicWallSource中的glsl代码里使用。简单的光效不学glsl语言也可以模仿网上案例修改一下参数,复杂的光效就得对glsl有一定的了解。

  • 相关阅读:
    【JavaSE专栏53】Java集合类HashMap详解
    串口控制舵机转动
    C语言这么没用??
    Snort中pcre和正则表达式的使用
    1700*C. Mixing Water(数学 | 二分)
    k8s 容器化技术 1
    Spring Cloud 网关的配置示例
    【白帽子讲Web安全】第二章 浏览器安全
    光传输系统中宽带光纤放大技术的频谱拓展方案
    自动化和数字化在 ERP 系统中意味着什么?
  • 原文地址:https://blog.csdn.net/weixin_38676065/article/details/126123975