CesiumJS PrimitiveAPI 高级着色入门-外观材质

CesiumJS 是一个非常强大的开源 JavaScript 库,用于在 web 浏览器中创建 3D 地球仪和 2D 地图。它提供了丰富的API,允许开发者构建复杂的地理空间可视化应用。在 CesiumJS 中,PrimitiveAPI 允许你以较低级别的方式渲染几何体,如点、线和多边形等,这为自定义着色和渲染效果提供了可能。

CesiumJS PrimitiveAPI 高级着色入门-外观材质

这一节讲 Primitive API 配套的第二个大类,Appearance + Material API,也叫外观材质 API,它允许开发者为自己的 Primitive 编写着色器。

3D模型在线预览提供多种低代码平台3D模型在线预览解决方案,实现了将多种3D模型格式无缝集成到低代码业务表单中。这意味着用户可以在不离开低代码平台的情况下,直接查看和操作3D模型,极大地提升了数据可视化的效果和用户交互体验。

使用材质

外观 API

CesiumJS 提供了如下几个具体的 Appearance 类:

  • MaterialAppearance - 材质外观,通用型,适用于上一章中大部分 Geometry
  • EllipsoidSurfaceAppearance - 上一个的子类,允许用在椭球面上的一些几何,例如 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 内置类型、内置函数均可使用,只要是表达式均可,例如 mixcossintantexture2D(GLSL100)、texture(GLSL300)。

举例,如果你在 uniforms 中传递了一个自定义的 image 作为纹理,那么你可以在 components.diffuse 中调用 texture2D 函数对这个 image 变量进行纹理采样:

其中,texture(image, materialInput.st).rgbimage 就是 uniforms.imagematerialInput.st 是来自输入变量 materialInput 的纹理坐标。至于 materialInput,之后讲解 fabric.source 完整版着色器代码的用法时会介绍。

fabric 对象上已经介绍了 3 个成员了,即 fabric.typefabric.uniformsfabric.components,那么现在介绍第四个 —— 允许材质组合的 fabric.materials 成员。

幸运的是,官方的文档有举简单的例子,我就直接抄过来说明了:

materials 中定义的两个子材质 diffuseMaterialspecularMaterial 也是满足 Fabric 规范的,这里直接用了两个内置材质(漫反射贴图材质、高光贴图材质)。定义在 materials 中,然后在 components 和将来要介绍的 fabric.source 着色器完整代码中都能用了。

例如,这里的 components.diffuse 设为了 diffuseMaterial.diffuse,实际上 diffuseMaterial 就是一个 CesiumJS 内置的 GLSL 结构体变量,在上文提过,结构体为 czm_material

子材质的 uniforms 也和普通材质的一样可以更新:

通常不建议嵌套太深,容易造成性能问题。

CesiumJS PrimitiveAPI 高级着色入门-索引

CesiumJS PrimitiveAPI 高级着色入门-参数化几何

CesiumJS PrimitiveAPI 高级着色入门-外观材质

CesiumJS PrimitiveAPI 高级着色入门-使用 GLSL 着色器

CesiumJS PrimitiveAPI 高级着色入门-底层知识

NSDT场景编辑器 | NSDT 数字孪生 | GLTF在线编辑器 | 3D模型在线转换 | UnrealSynth虚幻合成数据生成器 | 3D模型自动纹理化工具
2023 power by nsdt©鄂ICP备2023000829号