Loading

Cesium官方教程——Fabric

1、简介

Fabric 是Cesium中定义的描述材质Material的JSON 结构体。Material代表了一个物体的外观。

材质Material可以是比较简单的,比如直接将一张图片赋予表面,或者使用条纹状、棋盘状的图案;也可以使用FabricGLSL,重新创建一个新的材质或者组合现有的材质。例如,我们可以通过程序生成的纹理(procedural brick)、凹凸贴图(bump map)和高光贴图(specular map)来生成一个潮湿碎裂的砖块材质。
image.png
在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)时或之后设置。

注:uniform是一个webgl概念,用于与顶点无关的数据。

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有一些内置的材质,常用的两种为:

名称 缩略图 描述
Color image.png 颜色,包括透明度
Image image.png 图片,可以没有透明度(.jpg),也可以包含透明度(.png)格式;
漫反射、rgb、alpha通道组合。

所有内置的材质都可以像我们创建颜色那样简单的创建:

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)

名称 缩略图 描述
Checkerboard image.png Checkerboard with alternating light and dark colors.
Stripe image.png Alternating light and dark horizontal or vertical stripes
Dot image.png A pattern of dots organized by row and column.
Grid image.png A grid of lines, useful for displaying 3D volumes.

2.2 基础材质

基础材质较为底层,可以设置基础的材质特性,比如某个方向有多少光线被反射(镜面强度)、或者光线发射量等等。这些材质特性通常组合起来使用,从而创建一个复杂的材质。

Name 注释 Screenshot Description
漫反射贴图
DiffuseMap
可以表现出物体被光照射到而显出的颜色和强度 image.png 一张具有一个三维向量(vec3)的图片,定义了光在所有方向上的散射颜色。
高光贴图
SpecularMap
高光贴图是用来表现当光线照射到模型表面时,其表面属性的 image.png 一张具有标量分量的图片,定义了单一方向反射的入射光的强度。
通过用于使物体表面的一部分有光泽,比如:水和地面。
透明贴图
AlphaMap

image.png 一张具有标量分量的图片,定义了材质的透明度。通常用于使物体表面的一部分有透明度,比如:删栏。


法线贴图
NormalMap

image.png 一张具有三维向量的图片,定义了物体表面发现的切线坐标。通常用于增加物体表面的细节。
凹凸贴图
BumpMap
也叫高度贴图,丰富物体表面的细节。 image.png 一张具有标量分量的图片,类似法线贴图,凹凸贴图用于增加物体表面的细节无需通过实际修改物体表面。
自发光贴图
EmissionMap

image.png 一张具有三维向量(vec3)的图片,定义了材质在所有方向上发射的光线。例如:沿着一条公路的灯

2.3 多段线(polyline)材质

多段线材质只可以用于线状的物体。

Name 注释 Screenshot
带箭头线
PolylineArrow
image.png Places an arrow head at the end point of a line.
发光线
PolylineGlow
image.png Makes glowing lines.
带边框的线
PolylineOutline
image.png Line outline.

2.4 其他材质

名称 注释 简介
Water image.png Animating water with waves and ripples.
高亮轮廓
RimLighting
image.png 突出边缘轮廓

更多的内置材质,请查看 Cesium Materials Plugin.

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,。
共有以下几个子属性:

属性名 默认值 描述
漫反射
diffuse
'vec3(0.0)' 漫反射属性是一个三维向量vec3,这个向量定义了光在所有方向上的散射值。
高光
specular
0.0 高光属性,使用浮点数定义了单一方向上的反射强度。
光泽度
shininess
1.0 镜面反射的光泽度。越高的值创建一个更小、更集中的镜面高光。
法线
normal

材质的法线属性是一个三维向量vec3,定义了视点坐标系下的表面法向量。通常用于法向贴图。默认值是物体表面默认的发现。
自发光
emission
'vec3(0.0)' 自发光属性使用三维向量vec3定义,定义了在所有方向上灯光发出的颜色值。默认是vec3(0.0),也就是不发光。
alpha 1.0 阿法尔通道使用浮点数定义,0.0表示全透,1.0表示不透明。

总的来说,这些子属性定义了材质的特点,他们是材质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来实现的。在这两种方式下,我们都可以用到GLSLCesium提供的内置的函数、变量、常量等。

4.3 Input

materialInput变量在sourcecomponents属性中都是有效的,materialInput具有以下参数字段:

名称 类型 描述
**s** float 一维纹理坐标
**st** vec2 二维纹理坐标
**str** vec3 三维纹理坐标。> 注意:一维、二维、三维纹理坐标之间并不一定分量相同,不能保证str.st == st and st.s == s。例如,对于一个椭圆体,一维纹理坐标s可能是从底部到顶部,二维纹理坐标st是经纬度坐标,三维纹理坐标是沿着坐标轴的包围盒。

|
| **tangentToEyeMatrix** | mat3 | 片元切线空间到视点坐标系的转换矩阵,通常用于法线贴图和凹凸贴图中。 |
| **positionToEyeEC** | vec3 | 在视点坐标系下从片元到视点的向量,用于反射、折射等等。值大小表示从片元到视点的距离。 |
| **normalEC** | vec3 | 片元在视点坐标系下单位化后的发现了,用于凹凸贴图、反射、折射等。 |

一个使用二维纹理坐标st的简单材质使用如下:

{
  components : {
    diffuse : 'vec3(materialInput.st, 0.0)'
  }
}

同样的我们可以设置materialInput.normalECdiffuse,以在视点坐标系下法线的可视化。
除此之外,对于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。支持标准的GLSLuniform 类型,floatvec3mat4等等。Uniform数组还不支持。

4.4组合材质

到目前为止,我们可以使用内置材质,或者创建我们自己的材质。我们也可以从现有的材质中创建材质(递归),形成材质的层次结构。

材质Fabric具有materials属性,这个materials属性的子属性的值都是一个材质Fabric,也就是一种材质。这些materials可以在componentssource属性中引用。例如,一个代表塑料的材质可以通过设置diffuseMapspecularMap来实现。

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

上边这段代码中,components属性中有漫反射diffuse以及镜面反射specular属性,他们从材质的materials属性中提取数值。命名为diffuseMaterialspecularMaterial的子属性(通过类型DiffuseMapSpecularMap创建,不要混淆了名称——实例——类型(也可以叫类))。在componentssource属性中,子属性可以通过字段名来访问,就像他们是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 的结构

FabricJSON结构描述在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]);
posted @ 2023-03-21 09:48  Nicander  阅读(659)  评论(0编辑  收藏  举报