CesiumJS PrimitiveAPI 高级着色入门-外观材质
CesiumJS 是一个非常强大的开源 JavaScript 库,用于在 web 浏览器中创建 3D 地球仪和 2D 地图。它提供了丰富的API,允许开发者构建复杂的地理空间可视化应用。在 CesiumJS 中,PrimitiveAPI 允许你以较低级别的方式渲染几何体,如点、线和多边形等,这为自定义着色和渲染效果提供了可能。
这一节讲 Primitive API
配套的第二个大类,Appearance + Material API
,也叫外观材质 API,它允许开发者为自己的 Primitive 编写着色器。
3D模型在线预览提供多种低代码平台3D模型在线预览解决方案,实现了将多种3D模型格式无缝集成到低代码业务表单中。这意味着用户可以在不离开低代码平台的情况下,直接查看和操作3D模型,极大地提升了数据可视化的效果和用户交互体验。
使用材质
外观 API
CesiumJS 提供了如下几个具体的 Appearance
类:
MaterialAppearance
- 材质外观,通用型,适用于上一章中大部分 GeometryEllipsoidSurfaceAppearance
- 上一个的子类,允许用在椭球面上的一些几何,例如 Polygon、Rectangle 等几何类型,这个外观类使用算法来表达部分顶点属性以节约数据大小PerInstanceColorAppearance
- 如果每个 GeometryInstance 用的是单独的颜色,可以用这个外观类,在上一章的例子中就用到这个类PolylineMaterialAppearance
- 使用材质(下一小节)来给有宽度的折线着色PolylineColorAppearance
- 使用逐顶点或逐线段来给有宽度的折线着色
外观类有一个抽象父类 Appearance
(JavaScript 中没有抽象类,CesiumJS 也没有继承,大致意思,理解记可),上述 5 个均为它的实现类。
通常,为 Primitive 几何着色的主要职责在材质类,但是即使没有材质类,完全通过 GLSL
代码,设定外观类的顶点着色器和片元着色器(当然,要合规)也是可以完成渲染的。
下面就演示一下用 MaterialAppearance
与着色器代码实现立方体几何对象(BoxGeometry
)的着色案例:
然后你就能获得一个 blingbling 的立方块:
注意在创建 BoxGeometry
时,留了一行注释:
虽然法线影响光照,但是这里只是缺少了纹理坐标,盒子就没有 blingbling 的效果了:
具体的着色逻辑不深究,但是足够说明问题:这个 vertexFormat 会影响几何体的着色效果。
还有一个与外观有关的参数,那就是 new Primitive
时的构造参数 compressVertices
,这个值默认是 true
,即会根据几何体的 vertexFormat
参数来决定是否压缩 VertexBuffer。
如果设为:
即不压缩顶点缓冲,但是 vertexFormat
设置的格式缺少了其中某一个,比如这里就缺少了纹理坐标,那么就会出现顶点缓冲和顶点格式不匹配的情况,会出现报错:
通常,使用 MaterialAppearance
能搭配大多数几何类了,也可以自己使用 Geometry + GeometryAttribute
这两个最基础的类创建出自定义的 Geometry
,搭配使用。
材质 API
CesiumJS 有自己的材质规则,叫做 Fabric 材质,在下面小节会展开。
先看看直接实例化的参数。使用 new Material({})
创建一个材质对象,除了 fabric
参数外,还需要这几个参数(有些是可选的):
strict: boolean
,默认false
,即是否严格检查材质与 uniform、嵌套材质的匹配问题translucent: boolean | (m: Material) => boolean
,默认true
,为真则使用此材质的几何体允许有半透明minificationFilter: TextureMinificationFilter
,默认TextureMinificationFilter.LINEAR
,采样参数magnificationFilter: TextureMagnificationFilter
,默认TextureMagnificationFilter.LINEAR
,采样参数
而 fabric
参数,则是 Fabric 材质的全部内容,如果不使用内置材质类型要自己写材质的话,就需要认真研究这个 fabric
对象的参数规则了。
Fabric 材质初步 - 内置材质、材质缓存与 uniform
如几何、外观 API 一样,Material
类也给予了开发者一定的内置材质,略像简单工厂模式。只需要使用 Material.fromType()
就可以使用内置的十几种写好着色器的材质。
列举几种基础材质和几种常见材质:
- 常见材质
Material.fromType('Color')
- 纯颜色 - 常见材质
Material.fromType('Image')
- 普通贴图 - 基础材质
Material.fromType('DiffuseMap')
- 漫反射贴图 - 基础材质
Material.fromType('NormalMap')
- 法线贴图 - 基础材质
Material.fromType('SpecularMap')
- 高光贴图 - ...
具体的可以查看 Material
类的 API 文档,文档页面的最顶部就列举了若干种 type
对应的内置材质。fromType()
方法还可以传递第二个参数,第二个参数是这个材质所需要的 uniforms
,会应用到着色器对应的 uniform 变量上。
例如,文档中对透明度贴图的 uniform 描述是这样的:
你就可以通过传递这些 uniform 值,来决定着色器使用传入的 image
的哪个 channel
,以及要 repeat
的程度:
当然,Material 类也可以自己创建材质对象,分缓存和一次性使用两种创建方法。
区别就在 fabric.type
参数,只要有 fabric.type
,第一次创建就会缓存这个 fabric 材质,第二次就可以使用 fromType()
来访问缓存的材质了,并且不再需要传递完整的 fabric
对象,只需传递 type
和新的 uniforms
参数(如果需要更新)即可。
如果不传递 fabric.type
参数,那么创建的材质对象只能在生命周期内使用,CesiumJS 不会缓存,适合一次性使用。
创建好材质对象后,可以直接修改 uniform
的值完成动态更新效果,例如:
Fabric 材质中级(GLSL表达式、嵌套材质)
Fabric 材质规范允许在创建材质对象时,使用更细致的规则。当然可以使用完整的着色器函数代码,但是为了简单易用,CesiumJS 在“完整着色器函数”和“JavaScript API” 之间还设计了一层“GLSL表达式”来定制各个 成分组件(components,下文简称成分)。
举例:
从这个 components
对象可以看出,这一个材质对象设定了三个成分:
diffuse
,漫反射颜色,设为了 GLSL 表达式vec3(1.0, 0.0, 0.0)
,即纯红色specular
,高光强度,设为了0.1
alpha
,透明度,设为了0.6
这些都会合成到完整的着色器代码的对应分量上。
那么,这个 components
对象允许拥有哪些成分呢?这受限制于内置的 GLSL 结构体的成员:
也就是说,diffuse
(漫反射颜色)、specular
(高光强度)、shininess
(镜面反射强度)、normal
(相机或眼坐标中的法线)、emission
(自发光颜色)、alpha
(透明度)这 6 个都可以出现在 components
对象中,其值是字符串,必须是可以赋予给 GLSL 结构体对应成员的表达式。
什么意思呢?除了上面的举例 diffuse: 'vec3(1.0, 0.0, 0.0)'
外,任意的 GLSL 内置类型、内置函数均可使用,只要是表达式均可,例如 mix
、cos
、sin
、tan
、texture2D
(GLSL100)、texture
(GLSL300)。
举例,如果你在 uniforms
中传递了一个自定义的 image 作为纹理,那么你可以在 components.diffuse
中调用 texture2D
函数对这个 image 变量进行纹理采样:
其中,texture(image, materialInput.st).rgb
的 image
就是 uniforms.image
,materialInput.st
是来自输入变量 materialInput
的纹理坐标。至于 materialInput
,之后讲解 fabric.source
完整版着色器代码的用法时会介绍。
fabric
对象上已经介绍了 3 个成员了,即 fabric.type
、fabric.uniforms
、fabric.components
,那么现在介绍第四个 —— 允许材质组合的 fabric.materials
成员。
幸运的是,官方的文档有举简单的例子,我就直接抄过来说明了:
在 materials
中定义的两个子材质 diffuseMaterial
、specularMaterial
也是满足 Fabric
规范的,这里直接用了两个内置材质(漫反射贴图材质、高光贴图材质)。定义在 materials
中,然后在 components
和将来要介绍的 fabric.source
着色器完整代码中都能用了。
例如,这里的 components.diffuse
设为了 diffuseMaterial.diffuse
,实际上 diffuseMaterial
就是一个 CesiumJS 内置的 GLSL 结构体变量,在上文提过,结构体为 czm_material
。
子材质的 uniforms 也和普通材质的一样可以更新:
通常不建议嵌套太深,容易造成性能问题。
CesiumJS PrimitiveAPI 高级着色入门-索引
CesiumJS PrimitiveAPI 高级着色入门-参数化几何
CesiumJS PrimitiveAPI 高级着色入门-外观材质