Cesium官方教程12--材质(Fabric)
原文地址:https://github.com/AnalyticalGraphicsInc/cesium/wiki/Fabric
介绍
Fabric 是Cesium中基于JSON格式来描述materials的机制。材质描述多边形、折线、椭球等对象的外观特征。
材质可以简单的是覆盖一张图片,或者是条纹或者棋盘图案。使用Fabric 和GLSL,可以从零开始写脚本新建材质,也可以从现有的材质中派生。比如潮湿碎裂的砖块可以使用程序生成的纹理、凹凸贴图和反射贴图来组合。
对象通过material
属性来支持材质效果。当前这些对象是多边形、折线、椭球等(这篇文章写的较早,其实现在已经很多几何体都支持材质了)。
polygon.material = Material.fromType('Color');
上面,Color
是一个内置材质,它表示了包含透明度在内的一个颜色值。Material.fromType
是简略写法,完整的Fabric的JSON应该是这样的:
polygon.material = new Cesium.Material({
fabric : {
type : 'Color'
}
});
每一个材质包含0或者更多个uniforms,uniform是一种输入参数变量,在创建材质时或者创建材质后修改。比如 , Color
有一个 color
uniform ,它包含red
, green
, blue
, 和alpha
四个部件。
polygon.material = new Cesium.Material({
fabric : {
type : 'Color',
uniforms : {
color : new Cesium.Color(1.0, 0.0, 0.0, 0.5)
}
}
});
// 把红色半透明修改为 白色不透明
polygon.material.uniforms.color = Cesium.Color.WHITE;
内置材质
Cesium有一些内置材质,应用最广泛的是这两个:
名称 | 效果图 | 描述 |
---|---|---|
Color |
![]() |
包含透明通道的颜色值 |
Image |
![]() |
jpg或者png格式的图片,可以带透明通道,用rgb 表示颜色,a 表示透明度 |
如同上面的 Color
一样,所有的内置材质都可以这么创建。比如:
polygon.material = Material.fromType('Image');
polygon.material.uniforms.image = 'image.png';
或者
polygon.material = new Cesium.Material({
fabric : {
type : 'Image',
uniforms : {
image : 'image.png'
}
}
});
程序生成的纹理 (Procedural Textures)
程序生成的纹理,他们不依赖于外部图片文件,是通过GPU编程计算的图案,他们可以表示颜色和透明。
名称 | 效果图 | 描述 |
---|---|---|
棋盘图Checkerboard |
![]() |
明暗交替组成的棋盘图。 |
条纹Stripe |
![]() |
水平或者垂直方向明暗交替的图案 |
斑点Dot |
![]() |
按行列排列的点组成 |
网格Grid |
![]() |
网格边线,描述3D体的时候有用 |
基本材质
Base materials represent fine-grain fundamental material characteristics, such as how much incoming light is reflected in a single direction, i.e., the specular intensity, or how much light is emitted, i.e., the emission. These materials can be used as is, but are more commonly combinedusing Fabric to create a more complex material.
名称 | 效果图 | 描述 |
---|---|---|
漫反射贴图DiffuseMap |
![]() |
一张图片定义了光在所有方向上的散射颜色,一般是个三维向量(vec3 ) |
高光反射贴图 SpecularMap |
![]() |
一张图片,定义了光在某一个方向上的反射颜色 ,一般是个标量(scalar ),通常用来模拟某个光亮的平面,比如陆地上的水面。 |
透明贴图AlphaMap |
![]() |
一张图片,定义了材质透明度 ,一般是个标量(scalar )。通常让一部分表面透明或者半透明,比如栅栏 |
法向贴图NormalMap |
![]() 个人感觉原文这张法向贴图不太对
|
一张图片,定义了在切线空间定义了表面的法向量,一般是个三维向量( vec3 )。法向贴图在不增加几何体复杂度的前提下,提升了表面渲染的细节 |
凹凸贴图BumpMap |
![]() |
一张图片,定义了表面的高度 ,一般是个标量(scalar )。就像法向贴图,也可以在不增加几何体复杂度的前提下,提升了表面渲染的细节 ,它通过相邻像素之间的差异来微调法向量 |
自发光贴图 EmissionMap |
![]() |
一张图片,定义了材质在所有方向上发光颜色,一般是个三维向量(vec3 )。比如走廊里的灯泡 |
折线材质
这只一种只能添加到折线几何体上的材质。
名称 | 效果图 | 描述 |
---|---|---|
带箭头折线PolylineArrow |
![]() |
在折线尾端增加一个箭头 |
泛光折线 PolylineGlow |
![]() |
折线泛光 |
带边界折线PolylineOutline |
![]() |
带边界折线 |
其他材质
还有一些不适合归到其他类的材质
名称 | 效果图 | 描述 |
---|---|---|
水面 Water |
![]() |
带波纹动画的水面 |
外轮廓高亮 RimLighting |
![]() |
高亮边缘或者轮廓 |
了解更多材质,可以去看下这个 Cesium Materials Plugin.
通用的Uniforms
很多材质都有一个image uniform,它是一个图片访问地址,或者数据URI。
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要求图片有一个通道。我们可以指定渲染的时候从哪些通道(或者什么顺序)从原始图片中读取数据,通过 channel
这个字符串uniform来设置。比如,默认Specular材质是从 r
读取高光反射参数。不过我们可以如下修改它:
polygon.material = new Cesium.Material({
fabric : {
type : 'SpecularMap',
uniforms : {
image : 'specular.png',
channel : 'a'
}
}
});
这就是说可以把多个材质的信息放到一个图片里,比如在同一个图片内,用rgb
通道存储diffuse值,用a
通道存储specular值。也就是说,我们的图片只需要载入一次。
通常材质里有一个repeat
uniform,它控制了图片在水平和垂直方向重复了多少次。这个在表面重复贴图的时候很方便:
polygon.material = new Cesium.Material({
fabric : {
type : 'DiffuseMap',
uniforms : {
image : 'diffuse.png',
repeat : {
x : 10,
y : 2
}
}
}
});
创建新的材质
使用Fabric,只需要一点点GLSL或者其他材质就可以了。
如果不打算复用材质,那么不要设置type
参数。
var fabric = {
// 没有类型
//fabric的剩余JSON值
};
polygon.material = new Cesium.Material({
fabric : fabric
});
当在new Cesium.Material
时,传入一个不存在的 type
类型之后,这个材质将被缓存下来。下次调用 new Cesium.Material
或者 Material.fromType
就会引用缓存里的,就如同我们内置的材质一样,那时候就不需要提供整个Fabric的定义,而仅仅传递 type
以及想更改的 uniforms
值。
var fabric = {
type : 'MyNewMaterial',
//剩余JSON值
};
polygon.material = new Cesium.Material({
fabric : fabric
});
//再次使用的时候,只需要这样
anotherPolygon.material = Material.fromType('MyNewMaterial');
Components
或许最简单有趣的材质就是纯白色散射光:
var fabric = {
components : {
diffuse : 'vec3(1.0)'
}
}
稍微复杂一点,增加一个高光元素,当视角正对反射光的时候更亮一些,当视角在边上的时候稍微亮一些。
{
components : {
diffuse : 'vec3(0.5)',
specular : '0.1'
}
}
components
属性包含了 定义了材质外观的子属性。每个子属性是一个GLSL的代码段,比如上面的vec3(0.5)
,它实际创建了一个三维向量,每个分量都设置为 0.5
。这里可以访问所有的GLSL函数,包括 mix,
cos,
texture2D`等等。现在有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 |
材质的透明度。 使用一个float值定义,0.0 表示全透明; 1.0 表示不透明。 |
综上所述,子属性或者components 定义了材质的特点。他们是材质的输出值,是光照系统的输入值。
代码
提供完整的GLSL代码是一种比前面 components
更灵活的方式。通过自定义czm_getMaterial
函数,返回材质的各个分量。代码如下:
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 这么定义:
{
source : 'czm_material czm_getMaterial(czm_materialInput materialInput) { return czm_getDefaultMaterial(materialInput); }'
}
下面的示例代码,只设置了diffuse
和 specular
分量的值:
czm_material czm_getMaterial(czm_materialInput materialInput)
{
czm_materialInput 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提供的GLSL函数(functions), 变量(uniforms), and 常量(constants)(链接已失效)。
材质输入
materialInput
变量在source
和 components
属性中都可以配置。它具有下面的字段,用来计算材质分量:
名称 | 默认值 | 说明 |
---|---|---|
s |
float |
一维纹理坐标 |
st |
vec2 |
二维纹理坐标 |
str |
vec3 |
三维纹理坐标。注意这些维度不同的纹理坐标不一定分量相同,也就是说不能保证 str.st == st 和st.s == s 。比如对于椭球体。一维纹理坐标s 是从下到上。二维纹理坐标st 是经度纬度。三维纹理坐标str 是沿着坐标轴的包围盒 |
tangentToEyeMatrix |
mat3 |
从片元的切线空间转到视点空间的转换矩阵,在法向贴图和凹凸贴图时使用。 |
positionToEyeEC |
vec3 |
从片元到视点之间的向量,为了反射和折射计算。向量的模表示了从片元到视点的距离。 |
normalEC |
vec3 |
片元在视点空间的单位化后的法向量,在凹凸贴图、反射、折射的时候使用。 |
把纹理坐标的st
值显示出来的简单方法:
{
components : {
diffuse : 'vec3(materialInput.st, 0.0)'
}
}
类似的,查看视点坐标下的法向量,只需要 把materialInput.normalEC
设置到 diffuse
分量上。
除此之外,在materialInput
里,可以访问uniforms变量,包括Cesium 提供的内置变量 uniforms 和 材质设置的uniforms变量。比如,我们可以设置自定义的Color
材质,依据一个color 变量来设置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中的uniform变量名 ,也是 new Material
和 Material.fromType
返回中JavaScript的对象属性名。子属性的值也是GLSL中uniform变量的值。(这块意思就是说uniform
下的属性和值在GLSL的GPU环境和js的内存环境中一致的)。
可以通过一个自定义的 image
变量来实现材质的DiffuseMap
:
{
type : 'OurDiffuseMap',
uniforms : {
image : 'czm_defaultImage'
},
components : {
diffuse : 'texture2D(image, materialInput.st).rgb'
}
}
上面代码里,'czm_defaultImage'
是一个1x1的图片。前面说过,这个值可以是一个图片URL地址或者 数据URI。比如用户可以使用我们自定义的OurDiffuseMap
材质,这么来设置纹理:
polygon.material = Material.fromType('OurDiffuseMap');
polygon.material.uniforms.image = 'diffuse.png';
也有一个内置的立体贴图:czm_defaultCubeMap
。GLSL 标准的uniform变量类型float
, vec3
, mat4
都是支持的。Uniform数组还不支持,但是已经在计划内 roadmap。
材质的合并
至此,我们可以使用内置的材质,可以通过设置材质的components
来自定义 ,或者实现完整的GLSL代码source
来自定义。我们还可以通过继承已有的材质来新建材质。
Fabric 有个materials
属性,它的每个子属性也是Fabric材质。他们的材质可以可以在 components
或者source
中引用。比如一个塑料材质可以通过 DiffuseMap
和SpecularMap
两个材质的合并来模拟。
{
type : 'OurMappedPlastic',
materials : {
diffuseMaterial : {
type : 'DiffuseMap'
},
specularMaterial : {
type : 'SpecularMap'
}
},
components : {
diffuse : 'diffuseMaterial.diffuse',
specular : 'specularMaterial.specular'
}
};
这个材质的diffuse
和specular
都是从其他材质中提取的。子属性的名字叫diffuseMaterial
和specularMaterial
(根据类型 DiffuseMap
和SpecularMap
创建的材质。不要搞混 类型 和 实例对象的名称,在 components
和source
属性中,子材质通过名称访问,因为他们都是一个czm_material
结构,所以可以访问.diffuse
和 .specular
分量。
基于这个Fabric材质,可以这么用我们的材质:
var m = Material.fromType('OurMappedPlastic');
polygon.material = m;
m.materials.diffuseMaterial.uniforms.image = 'diffuseMap.png';
m.materials.specularMaterial.uniforms.image = 'specularMap.png';
Fabric 格式
Fabric 是基于JSON 格式的格式定义。这格式定义里详细描述了Fabric的属性和子属性,包括 type
, materials
, uniforms
, components
, 和 source
等。那里面有一些JSON的格式示例,但是没有必要去看。
对于一些严格要求的Fabric文件,可以使用一些类似 JSV的工具去验证Fabric格式。
渲染流水线中的材质
Polygon, PolylineCollection, Ellipsoid, CustomSensorVolume等几何体 已经 和材质系统集成。大部分用户只需要简单的设置material
就可以了。可是,用户还是想实现自己的材质渲染代码。直接了当的去做就行了。
在渲染阶段,材质就是一段GLSL函数czm_getMaterial
和 一些uniform变量。片段着色器需要构造一个 czm_MaterialInput
结构,然后调用czm_getMaterial
方法,把获得的 czm_material
结果传递给光照处理函数去计算图元颜色。
在JavaScript代码里,这些对象应该有一个 material
属性。当这个属性变换的时候,update
函数应该把材质的GLSL代码转为对象的片段着色器代码,并且把对象和材质的uniform变量合并起来。
var fsSource = this.material.shaderSource + ourFragmentShaderSource;
this._drawUniforms = combine([this._uniforms, this.material._uniforms]);

Cesium官方教程12--材质(Fabric)的更多相关文章
- 关于Cesium 官方教程
最近一直在准备第一次QQ群的Cesium基础培训公开课,虽说使用Cesium也有段日子了,但是要说对Cesium了解有多深,还真不一定.原因是一直以来我都是用哪里学哪里.基于多年开发三维数字地球的底层 ...
- Cesium官方教程13--Cesium和Webpack
原文地址:https://cesiumjs.org/tutorials/cesium-and-webpack/ Cesium 和 Webpack Webpack是非常强大非常流行的JavaScript ...
- Cesium官方教程8-- 几何体和外观效果
原文地址:https://cesiumjs.org/tutorials/Geometry-and-Appearances/ 几何体和外观效果(Geometry and Appearances) 这篇教 ...
- Cesium官方教程11--建模人员必读
原文地址:https://cesium.com/blog/2014/12/15/gltf-tips-for-artists/ 这篇文章是Branden Coker, an artist from AG ...
- Cesium官方教程6--相机
相机(Camera) 相机控制了场景的观察视角.有很多相机操控方法,比如旋转.缩放.平移以及飞行定位.Cesium默认支持使用鼠标和触摸事件控制相机.Cesium也提供了一套可编程的相机控制API.这 ...
- Cesium官方教程10--高级粒子特效
原文地址:https://cesiumjs.org/tutorials/Particle-Systems-More-Effects-Tutorial/ 高级粒子系统特效 这篇教程学习更多的效果,包括天 ...
- Cesium官方教程9--粒子系统
原文地址:https://cesiumjs.org/tutorials/Particle-Systems-Tutorial/ 粒子系统介绍 这篇教程带你学习Cesium的粒子相关API,比如如何在你的 ...
- Cesium官方教程7--三维模型
原文地址:https://cesiumjs.org/tutorials/3D-Models-Tutorial/ 三维模型 (3D Models) 这篇教程给大家介绍,如何在Cesium中通过Primi ...
- Cesium官方教程4--影像图层
原文地址:https://cesiumjs.org/tutorials/Imagery-Layers-Tutorial/ 影像图层 Cesium支持多种服务来源的高精度影像(地图)数据的加载和渲染.图 ...
随机推荐
- redhat7和redhat6混合搭建文档
1.下载cm对应redhat7的版本.2.修改/opt/cloudera/cm-5.10.0/etc/cloudera-scm-agent/config.ini成为中心机器hostname/ip(ma ...
- Ansible的copy模块批量下发文件
copy模块的参数,ansible 主机组 -m copy -a '' src: 指定源文件或目录 dest: 指定目标服务器的文件或目录 backup: 是否要备份 owner: 拷贝到目标服务器后 ...
- 9_山寨系统调用 SystemCallEntry
思想: 再次在 GDT 中偷内存 搭建 系统调用需要的 逻辑框架与功能实现: 基本分解妄想: 构建系统调用的代码: 拷贝到 偷取的内存中: idt 向量 序号21位置: 8003ee00`0008f1 ...
- @Formula
@Formula 计算临时属性. 相当于可以关联查询字段,然后放在实体中当做属性使用. 任务:在User实体层,增加一个额外的属性,来获取Test表中的name字段. 1 表结构 User表 Tes ...
- 无法解析的外部符号 jpeg_std_error
1>dlib.lib(png_loader.obj) : error LNK2001: 无法解析的外部符号 png_set_sig_bytes 1>dlib.lib(png_loader. ...
- NOIP提高真题整理(2011-2018)-标签
加粗的后面应该会有相应的简单解析(如果没咕的话:)). 2011 day1 T1:铺地毯:逆着铺 T2:选择客栈:按颜色分类枚举+二分答案 T3:Mayan游戏:大模拟dfs+剪枝 day2 T1:计 ...
- JMeter设置响应数据的编码格式
1.修改配置文件jmeter.properties第974行,默认编码格式为ISO-8859-1,手动修改为UTF-8 2.增加元器件 在线程组右键,添加->后置处理器->BeanShel ...
- Java集合框架(List,Set,Map)
单列集合基本框架 List接口特点:1. 它是一个元素存取有序的集合.例如,存元素的顺序是11.22.33.那么集合中,元素的存储就是按照11.22.33的顺序完成的). 2. 它是一个带有索引的集合 ...
- python中与time模块和datetime模块相关操作
使用time模块计时程序运行时长 import time time_start = time.time() #程序主体部分 time_end = time.time() print('总耗时: ', ...
- ETL工具-Kattle:初识kattle
ETL是EXTRACT(抽取).TRANSFORM(转换).LOAD(加载)的简称,实现数据从多个异构数据源加载到数据库或其他目标地址,是数据仓库建设和维护中的重要一环也是工作量较大的一块.当前知道的 ...