1、简介
Fabric 是Cesium中定义的描述材质Material
的JSON 结构体。Material代表了一个物体的外观。
材质Material
可以是比较简单的,比如直接将一张图片赋予表面,或者使用条纹状、棋盘状的图案;也可以使用Fabric
和GLSL
,重新创建一个新的材质或者组合现有的材质。例如,我们可以通过程序生成的纹理(procedural brick)
、凹凸贴图(bump map)
和高光贴图(specular map)
来生成一个潮湿碎裂的砖块材质。
在Cesium中支持赋予材质的图元都有一个material
属性。
polygon.material = Material.fromType('Color');
上边这行代码中,Color是一个内置材质属性,代表了单一颜色,包括透明度(alpha)。Material.fromType
是一个快捷的方式,完整的Fabric JSON 应该是这样:
polygon.material = new Cesium.Material({
fabric : {
type : 'Color'
}
});
每一种材质(material)
都有uniforms
属性,uniforms
可以在创建材质(material)
时或之后设置。
polygon.material = new Cesium.Material({
fabric : {
type : 'Color',
uniforms : {
color : new Cesium.Color(1.0, 0.0, 0.0, 0.5)
}
}
});
// Change from translucent red to opaque white
polygon.material.uniforms.color = Cesium.Color.WHITE;
2、内置材质
Cesium有一些内置的材质,常用的两种为:
所有内置的材质都可以像我们创建颜色那样简单的创建:
polygon.material = Material.fromType('Image');
polygon.material.uniforms.image = 'image.png';
也可以这样创建:
polygon.material = new Cesium.Material({
fabric : {
type : 'Image',
uniforms : {
image : 'image.png'
}
}
});
2.1 程式化纹理
程式化纹理不直接依赖于图片文件,而是通过GPU计算而来,他们可以设置漫反射(diffuse)
和透明度(alpha)
。
2.2 基础材质
基础材质较为底层,可以设置基础的材质特性,比如某个方向有多少光线被反射(镜面强度)、或者光线发射量等等。这些材质特性通常组合起来使用,从而创建一个复杂的材质。
2.3 多段线(polyline)材质
多段线材质只可以用于线状的物体。
2.4 其他材质
3、uniform
很多的材质(material)
都有image uniform,定义了图片路径或数据路径:
polygon.material.uniforms.image = 'image.png';
polygon.material.uniforms.image = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAC/SURBVDhPrZPRDYQgEEQpjVKuFEvhw0IoxU6QgQwMK+vdx5FsooT3GHdjCM4qZnnnHvvkYoxFi/uvIhwiRCClXFC6v5UQ1uQAsbrkHCLsbaPjFgIzQQc1yUOwu33ePGE3BQUaee2BpjhbP5YUmkAlbNzsAURfBDqJnMIyyv4JjsCCgCnIR32uZUfcJuGBOwEk6bOKhoAADh31EIq3MgFg1mgkE1BA2AoUZoo2iZ3gyqGgmMDC/xWwkfb3/eUd7A1v3kxjNW9taQAAAABJRU5ErkJggg=='
一些属性,例如漫反射(diffuse)
或法线贴图(NormalMap)
的属性,要求图片的每个像素具有RGB三个通道;其他材质属性,例如高光(specular)
和透明度(alpha)
,要求具有一个通道。我们可以指定从纹理图片的那个通道中获取数据,通过channels
或者channel
来设置。
例如,默认情况下高光specular
属性读取r通道,我们可以改变读取的通道:
polygon.material = new Cesium.Material({
fabric : {
type : 'SpecularMap',
uniforms : {
image : 'specular.png',
channel : 'a'
}
}
});
允许多个材质从相同的图片中读取数据,例如一张相同的图片即存储了漫反射属性(diffuse)
所需的rgb通道,也存储了高光属性(specular)
所需的a通道。这样这张图片只会加载一次,节省了资源。
材质material也可以使用images的repeat属性,控制图片在横向和纵向的重复次数。经常被用于在物体表面的瓦片纹理。
polygon.material = new Cesium.Material({
fabric : {
type : 'DiffuseMap',
uniforms : {
image : 'diffuse.png',
repeat : {
x : 10,
y : 2
}
}
}
});
4、创建一个新的材质
创建一个新的材质,可以通过Fabric、一些GLSL代码,以及其他的材质组合起来。
如果新创建的材质只使用一次,可以不设置Type
属性:
var fabric = {
// no type
// ...rest of fabric JSON
};
polygon.material = new Cesium.Material({
fabric : fabric
});
当使用一个新的材质类型type
时,材质会在第一次绘制时缓存,之后的绘制(通过new Cesium.Material
或者Material.fromType)就可以直接使用缓存的材质,就像使用内置材质一样,不需要提供完整的Fabric
描述,只需要通过type
和定义需要使用的uniforms
即可。
var fabric = {
type : 'MyNewMaterial',
// ...rest of fabric JSON
};
polygon.material = new Cesium.Material({
fabric : fabric
});
// ... later calls just use the type.
anotherPolygon.material = Material.fromType('MyNewMaterial');
4.1 Components
可能最简单的使用材质的方式就是直接反射白色:
var fabric = {
components : {
diffuse : 'vec3(1.0)'
}
}
稍微复杂的例子是添加高光反射属性specular component
,这样材料的反射光线在直视的时候最强烈,而在斜视时就变弱。
{
components : {
diffuse : 'vec3(0.5)',
specular : '0.1'
}
}
components
属性包含了定义材料外观的子属性。每个子属性都是一个GLSL
代码片段,比如上面示例代码中的vec(0.5)
创建了一个3D向量,3D向量中每个分量都被设置为0.5,。
共有以下几个子属性:
总的来说,这些子属性定义了材质的特点,他们是材质material的输出值,同时也是光照系统的输入值。
4.2 Source
source
属性通过为czm_getMaterial
函数定义GLSL代码的方式,提供了一个更加灵活的定义材质的方式。
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);
使用source
最简单的方式是每一个属性都直接返回默认值。
czm_material czm_getMaterial(czm_materialInput materialInput)
{
return czm_getDefaultMaterial(materialInput);
}
Fabric
定义:
{
source : 'czm_material czm_getMaterial(czm_materialInput materialInput) { return czm_getDefaultMaterial(materialInput); }'
}
我们把之前的例子中通过source
来实现的:
// 通过FABRIC 定义
{
components : {
diffuse : 'vec3(0.5)',
specular : '0.1'
}
}
// 通过source
{
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;
}`
}
使用source
代替components
的方式代码量更加多,但也会更加灵活,包括可以为不同的组件公用相同的计算逻辑和函数。一个经验法则是在大多数情况下使用components
,除非需要用到czm_getMaterial
的灵活性。在底层实现上,components
子属性也是通过czm_getMaterial
来实现的。在这两种方式下,我们都可以用到GLSL
和Cesium
提供的内置的函数、变量、常量等。
4.3 Input
materialInput
变量在source
和components
属性中都是有效的,materialInput
具有以下参数字段:
|
| **tangentToEyeMatrix**
| mat3 | 片元切线空间到视点坐标系的转换矩阵,通常用于法线贴图和凹凸贴图中。 |
| **positionToEyeEC**
| vec3 | 在视点坐标系下从片元到视点的向量,用于反射、折射等等。值大小表示从片元到视点的距离。 |
| **normalEC**
| vec3 | 片元在视点坐标系下单位化后的发现了,用于凹凸贴图、反射、折射等。 |
一个使用二维纹理坐标st
的简单材质使用如下:
{
components : {
diffuse : 'vec3(materialInput.st, 0.0)'
}
}
同样的我们可以设置materialInput.normalEC
到diffuse
,以在视点坐标系下法线的可视化。
除此之外,对于materialInput
,材质还可以使用uniform
变量,不管是Cesium内置的uniforms
还是材质特定的uniforms
。
例如,我们可以实现我们自己的Color
材质,基于color uniform
设置diffuse
以及alpha
属性。
{
type : 'OurColor',
uniforms : {
color : new Color(1.0, 0.0, 0.0, 1.0)
},
components : {
diffuse : 'color.rgb',
alpha : 'color.a'
}
}
在Fabric
中,uniform
属性的子属性也是**GLSL**
中的uniforms
的名称,以及使用new Material
以及Material.fromType
中的返回的**JavaScript**
对象。子属性的值也是uniform的值(对于标量及向量)。
我们可以通过image uniform
实现我们自己的diffuseMap
属性
{
type : 'OurDiffuseMap',
uniforms : {
image : 'czm_defaultImage'
},
components : {
diffuse : 'texture2D(image, materialInput.st).rgb'
}
}
在上面代码中,'czm_defaultImage'
是一个1x1占位符图像,也可以使用图像URL或数据URL来指定纹理图像。例如,用户可以这样来创建一个OurDiffuseMap
:
polygon.material = Material.fromType('OurDiffuseMap');
polygon.material.uniforms.image = 'diffuse.png';
还有一个立方图占位符 czm_defaultCubeMap
。支持标准的GLSL
uniform 类型,float
、vec3
、mat4
等等。Uniform
数组还不支持。
4.4组合材质
到目前为止,我们可以使用内置材质,或者创建我们自己的材质。我们也可以从现有的材质中创建材质(递归),形成材质的层次结构。
材质Fabric
具有materials
属性,这个materials
属性的子属性的值都是一个材质Fabric
,也就是一种材质。这些materials
可以在components
和source
属性中引用。例如,一个代表塑料的材质可以通过设置diffuseMap
及specularMap
来实现。
{
type : 'OurMappedPlastic',
materials : {
diffuseMaterial : {
type : 'DiffuseMap'
},
specularMaterial : {
type : 'SpecularMap'
}
},
components : {
diffuse : 'diffuseMaterial.diffuse',
specular : 'specularMaterial.specular'
}
};
上边这段代码中,components
属性中有漫反射diffuse
以及镜面反射specular
属性,他们从材质的materials
属性中提取数值。命名为diffuseMaterial
及 specularMaterial
的子属性(通过类型DiffuseMap
及SpecularMap
创建,不要混淆了名称——实例——类型(也可以叫类))。在components
和source
属性中,子属性可以通过字段名来访问,就像他们是czm_material
结构体,因此在上面代码中通过.diffuse
和.specular
字段可以访问。
鉴于这一点,我们的材质material
可以像其他材质material
一样被使用。
var m = Material.fromType('OurMappedPlastic');
polygon.material = m;
m.materials.diffuseMaterial.uniforms.image = 'diffuseMap.png';
m.materials.specularMaterial.uniforms.image = 'specularMap.png';
5、Fabric 的结构
Fabric
的JSON结构描述在Cesium仓库中。所有Fabric
属性、子属性的细节,包括type
, materials
, uniforms
, components
, source
。有一些JSON。
除了更严格的Fabric文档外,该模式还可用于使用JSV等工具来验证Fabric。
6、渲染管线中的材质(使用GLSL自定义材质)
几何对象包括 Polygon, PolylineCollection, Ellipsoid, CustomSensorVolume,等,都与材质系统整合以支持材质。大多数用户只需简单设置他们所需的材质属性即可,但对于想用书写自定义渲染代码的用户,可能也需要与材质系统结合。
从渲染的角度,材质是GLSL
函数,czm_getMaterial
,以及uniforms
的组合。片元着色器需要构造一个czm_MaterialInput
,调用czm_getMaterial
函数,之后将计算的czm_material
结果传递给lighting
函数,以计算片元着色器颜色。
在JavaScript中,对象应该有一个公共的material
属性。当这个属性变化时,update
函数应该将材质中的GLSL
源预置到几何对象的片元着色器中,并且组合几何对象和材料的uniforms
。
var fsSource =
this.material.shaderSource +
ourFragmentShaderSource;
this._drawUniforms = combine([this._uniforms, this.material._uniforms]);