Fabric 是 Cesium 中用于描述材质的一种 JSON 规定。数组
材质表现了多边形、折线、椭圆等形状的外观。缓存
使用 Fabric 和 GLSL,能够彻底自定义材质。app
经过几何对象的 material
属性能够建立材质,这个属性是 Cesium.Material
对象。函数
能够这么用:this
// 假设 polygon 是一个 primitive polygon.appearance.material = Cesium.Material.fromType('color');
这就建立了一个只有颜色的材质,包括透明度的颜色。Cesium.Material.fromType()
方法是一个简写,完整的写法是:编码
polygon.appearance.material = new Cesium.Material({ fabric: { type: 'Color' // 大写 } })
每个 Material 均可以有 0 ~ N 个 uniform,这个参数在建立时指定,也能够在渲染后修改。例如,color 类型的 Material 就有格式为 rgba 的颜色 uniform:url
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
Cesium 有几个内置的材质。列举两个比较经常使用的:3d
材质类型 | 截图 | 描述 |
---|---|---|
type: 'Color' |
![]() |
一个简单的颜色,包括透明通道 |
type: 'Image' |
![]() |
jpg 或 png 贴图类型的材质 |
全部的内置材质能够简单地使用 Cesium.Material.fromType()
方法建立:code
polygon.appearance.material = Cesium.Material.fromType('Image') polygon.appearance.material.uniforms.image = 'image.png'
或者用全写法:component
polygon.appearance.material = new Cesium.Material({ fabric: { type: 'Image', uniforms: { image: 'image.png' } } })
从这儿开始,介绍因这个 fabric 对象中的 type
不一样的十几种内置纹理,2.1~2.5
只需指定几个参数,就能够生成一些有规律的纹理贴图,不须要依赖外部贴图文件。它们至关于漫反射+透明度的组合。
类型 | 截图 | 描述 |
---|---|---|
type: 'Checkerboard' |
![]() |
国际象棋格子 |
type: 'Stripe' |
![]() |
竖条纹旗帜 |
type: 'Dot' |
![]() |
行列点阵 |
type: 'Grid' |
![]() |
线状网格,显示一些网状结构的图形 |
基础材料表达的是各个材质因子表示的材料特征,例如镜面反射强度、自发光。一般,组合在一个 fabric 对象中建立复杂的材质。
注:若是不懂这些东西,能够请教技术美工。
类型 | 截图 | 描述 |
---|---|---|
type: 'DiffuseMap' |
![]() |
漫反射贴图,即最多见的贴图,一般是 rgb 三个颜色 |
type: 'SpecularMap' |
![]() |
单通道贴图,表示的是入射光强度贴图 |
type: 'AlphaMap' |
![]() |
单通道的不透明度贴图 |
type: 'NormalMap' |
![]() |
三通道贴图,表示的是法线贴图 |
type: 'BumpMap' |
![]() |
单通道的凹凸贴图 |
type: 'EmissionMap' |
![]() |
三通道的自发光贴图 |
折线材质只做用于折线图形。
类型 | 截图 | 描述 |
---|---|---|
type: 'PolylineArrow' |
![]() |
箭头线,终点在折线末端 |
type: 'PolylineGlow' |
![]() |
发光线 |
type: 'PolylineOutline' |
![]() |
描边线 |
还有一些材质不属于上面的分类,例如:
类型 | 截图 | 描述 |
---|---|---|
type: 'Water' |
![]() |
水面贴图,看起来有水波动效 |
type: 'RimLighting' |
![]() |
边缘会比较亮 |
许多材质是有 image
的,多是一个 base64 编码的字符串或文件路径:
polygon.appearance.material.uniforms.image = 'image.png'; polygon.appearance.material.uniforms.image = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAC/SURBVDhPrZPRDYQgEEQpjVKuFEvhw0IoxU6QgQwMK+vdx5FsooT3GHdjCM4qZnnnHvvkYoxFi/uvIhwiRCClXFC6v5UQ1uQAsbrkHCLsbaPjFgIzQQc1yUOwu33ePGE3BQUaee2BpjhbP5YUmkAlbNzsAURfBDqJnMIyyv4JjsCCgCnIR32uZUfcJuGBOwEk6bOKhoAADh31EIq3MgFg1mgkE1BA2AoUZoo2iZ3gyqGgmMDC/xWwkfb3/eUd7A1v3kxjNW9taQAAAABJRU5ErkJggg==';
有的材质要求贴图有三个颜色份量,而其余材料(如高光和透明贴图)的贴图只须要一个颜色份量。
能够指定贴图的通道。例如,镜面反射的材质中,镜面反射默认取贴图的自 r 颜色通道,可是能够修改它为 a 通道:
polygon.appearance.material = new Cesium.Material({ fabric : { type : 'SpecularMap', uniforms : { image : 'specular.png', channel : 'a' } } });
这意味着,容许把各类贴图集中到一个贴图文件种,而后使用不一样的通道便可,减小加载请求次数。
有些材质的贴图纹理能够重复屡次绘制,例如水平或垂直上的重复:
polygon.appearance.material = new Cesium.Material({ fabric: { type: 'DiffuseMap', uniforms: { image: 'diffuse.png', repeat: { x: 10, y: 2 } } } })
使用 fabric 对象 + GLSL 代码和其余素材,就能够建立材质。
若是一个材质不想被复用,那么就不要指定它的 type
属性。
let fabric = { // ... } polygon.appearance.material = new Cesium.Material({ fabric: fabric })
当 fabric 对象中的 type
属性以前是没有指定过的,那么在第一次调用 new Cesium.Material()
时,这个新的 fabric 材质将被缓存,随后再次 new Material 或 Material.fromType() 时将从缓存中取用。
let fabric = { type : 'MyNewMaterial', // ...其余 fabric JSON 的属性 } polygon.appearance.material = new Cesium.Material({ fabric : fabric }); // ... 而后在另外一处须要这个 fabric anotherPolygon..appearance.material = Material.fromType('MyNewMaterial');
白色的漫反射材质或许是最经常使用的:
let fabric = { components: { diffuse: 'vec3(1.0)' } }
稍微复杂一些,加一点镜面反射,使得正射视角看反光最强,侧面变弱:
let fabric = { components : { diffuse : 'vec3(0.5)', specular : '0.1' } }
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);
默认状况下,材质的默认值会被返回:
czm_material czm_getMaterial(czm_materialInput materialInput) { return czm_getDefaultMaterial(materialInput); }
这个时候的 fabric 对象是:
let fabric = { components: { source: `czm_material czm_getMaterial(czm_materialInput materialInput) { return czm_getDefaultMaterial(materialInput); }` } }
上面修改了漫反射和镜面反射的例子能够经过 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; }` }
使用 glsl 代替 components 虽然看起来代码比较冗长,可是提供了灵活性。
若是不是有特别的需求,使用 components 属性指定材质的各类因子就能够了。可是,不论是哪种,在这些 glsl 代码中,都是能够直接使用 glsl 的内置函数和 Cesium 预约义的 函数、结构体、常量的。
materialInput
变量在 source 和 components 中都可以使用,在 glsl 代码的定义中,这个变量是 czm_materialInput
结构体有以下字段:
名称 | 类型 | 描述 |
---|---|---|
s |
float |
一维纹理坐标 |
st |
vec2 |
二维纹理坐标 |
str |
vec3 |
三维纹理坐标,三维纹理的二维部分不必定就是二维纹理坐标,切记。例如,在一个椭球几何中,s可能就是从下到上,st多是经纬度,str三维纹理就是包围盒的三轴方向。 |
tangentToEyeMatrix |
mat3 |
用于法线贴图、凹凸贴图的转换矩阵,转换切线空间坐标到视图坐标 |
positionToEyeEC |
vec3 |
从 fragment 到 视图空间坐标的向量(不知道这个 fragment 说的是什么),用于反射和折射等。向量的长度是 fragment 到视图(相机)的距离。 |
normalEC |
vec3 |
fragment 在视图坐标中的法线(已归一化),做用于凹凸贴图、反射、折射等 |
例如能够这么设置来可视化纹理坐标:
let fabric = { components: { diffuse: 'vec3(materialInput.st, 0.0)' } }
同样的,能够把 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' } }
在 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' } }
czm_defaultImage
是 1x1 分辨率的图片,根据上面的说法,这能够从 dataurl 或图片文件中获取,例如:
polygon.appearance.material = Material.fromType('OurDiffuseMap'); polygon.appearance.material.uniforms.image = 'diffuse.png';
还有一个多维数据集的变量:czm_defaultCubeMap
。
支持 glsl 的 uniform 类型,例如 float、vec三、mat4 等。
对于 uniform 数组还不支持,不过在规划中了。
译者注
这段是真够烧脑子的,不知所云。
梳理一下,在 fabric 这个对象中,uniforms 下的全部属性均被 glsl 认做是 uniform 变量,能够直接当变量使用,例如上例中的
texture2D(image, materialInput.st)
中的 image 参数。在这里,规定了 image 这个 uniform 的类型是 Cesium 内置的
czm_defaultImage
结构体类型。
到如今为止,可使用内置的材质或者经过指定 fabric 中的 components 属性(或直接使用 glsl 源代码)来建立材质对象。
还能够经过现有材质来建立复合类型的材质。
fabric 对象有一个 materials
属性,它的每个子对象都可以是 fabric 对象,最终即一个材质对象。在 materials
中设置的子一级 fabric,能够在最顶级的 fabric 的 components、source 中引用。例如,如今将 DiffuseMap 材质 和 SpecularMap 材质建立一个复合材质:
let rootFabric = { type: 'OurMappedPlastic', materials: { diffuseMaterial: { type: 'DiffuseMap' // 基本类型中的一种 }, specularMaterial: { type: 'SpecularMap' } }, components: { diffuse: 'diffuseMaterial.diffuse', specular: 'specularMaterial.specular' } }
这个 rootFabric 材质拥有两个组件:diffuse(漫反射)和 specular(镜面反射强度),而这两个组件的值是来自 materials 中的两个子 fabric。显然,在 materials
定义的两个子属性的名称,将会在 components 的 glsl 代码中被做为变量使用,直接点出 diffuse 和 specular 字段。
而后就能够像其余材质同样使用它了:
let m = Cesium.Material.fromType('OurMappedPlastic') polygon.appearance.material = m; m.materials.diffuseMaterial.uniforms.image = 'diffuseMap.png'; m.materials.specularMaterial.uniforms.image = 'specularMap.png';
用了这么多 fabric 对象,其实这个 fabric 对象是有一些规定的。
在 Cesium 的官方打包包中,找到 Documentation/Schemas/Fabric
,就是 fabric 对象的规定。诸如 type
、materials
、uniforms
、components
、source
这几个属性均能找到详细的定义。
从渲染的角度看,一种材质实际上是一个 glsl 函数:czm_getMaterial
。片元着色器须要构造一个 czm_MaterialInput
结构体变量,调用 czm_getMaterial
函数,而后生成 czm_material
结构变量,传递给照明函数来计算片元的颜色。
在 js 中,fabric 对象应该有一个 material
属性。当此属性发生变更时,图元的 update 函数触发,而后将 fabric 材质最终的 glsl 代码与默认的片元着色器代码合并在一块儿,而后再将 uniform 合并:
const fsSource = this.material.shaderSource + ourFragmentShaderSource; this._drawUniforms = combine([this._uniforms, this.material._uniforms]);
这篇文章较为详细地介绍了 Appearance 对象中的 fabric 属性构成。
fabric 是一个有官方规定如何写的 js 对象,它拥有 5 个属性:
其中,type 用于声明 fabric 对象最终会生成什么材质,若是是官方内置的,直接用官方内置的(2.1~2.4),不然则建立自定义的材质并缓存。
materials 容许再塞进去子一级的 fabric,构成复合材质。
uniforms 是一些全局变量,例如你能够在这里写一个 myUniformVariable
,而后你就能够在 components 或者 source 的 glsl 代码中用到这个 uniform 变量了。
source 是 glsl 源代码,它主要是对 czm_getMaterial
这个 Cesium 内置的 glsl 函数的实现,返回值是 czm_material
components 是几个基本材质因子的 glsl 代码快捷入口,是 source 的一种简略实现。
建立好 fabric 材质对象后,随之就能够建立 Appearance 对象,与几何实例一块儿建立 Primitive 了。