
返回文章
ARTICLE
Cesium 新版本如何创建自己的材质 Material(以 1.104.0 为例)
网上基本是找不到如何创建属于自己的材质,甚至于官网也没有(有但是晦涩难懂,而且跟我们常见的需求完全不搭边),所以总结了一下创建 Material 的方式,希望后续不用再死磕官方文档和网上的教程了。 /(ㄒoㄒ)/~~/(ㄒoㄒ)/~~ 经过对比可以发现,创建不同材料的模板都是一致的,都可以按照下面 Demo 的方式创建,直接复制即可使用,唯一不同的
Cesium 新版本如何创建自己的材质 Material(以 1.104.0 为例)
开发说明
网上基本是找不到如何创建属于自己的材质,甚至于官网也没有(有但是晦涩难懂,而且跟我们常见的需求完全不搭边),所以总结了一下创建 Material 的方式,希望后续不用再死磕官方文档和网上的教程了。
/(ㄒoㄒ)//(ㄒoㄒ)/
材质代码模板说明
经过对比可以发现,创建不同材料的模板都是一致的,都可以按照下面 Demo 的方式创建,直接复制即可使用,唯一不同的是材质的 source 以及传入值,每一种你需要的特殊材质你只要替换这些参数就可以实现。
当然,这里传入值大家都知道如何修改,麻烦的是 source 如何实现,这个就只能学习 glsl 语法自己实现了,或者网上找资源,问问 chatgpt 了
好了,话不多说,Demo 如下
流动线条材质
import { Cartesian2, Color, Event, Material, Property, createPropertyDescriptor, defaultValue, defined } from 'cesium'
import { requireImage } from '@/utils/common'
const defaultImage = requireImage('flow_line_red.png')
const defaultColor = Color.TRANSPARENT
const defaultForward = 1
const defaultSpeed = 1
const defaultRepeat = new Cartesian2(1.0, 1.0)
interface OptionIF {
color?: Color
image?: string
forward?: number
speed?: number
repeat?: Cartesian2
}
export class PolylineFlowMaterialProperty {
// 定义材质变量
_definitionChanged = new Event()
_color = undefined
_colorSubscription = undefined
_image = undefined
_imageSubscription = undefined
_forward = undefined
_forwardSubscription = undefined
_speed = undefined
_speedSubscription = undefined
_repeat = undefined
_repeatSubscription = undefined
constructor(options: OptionIF) {
options = defaultValue(options, defaultValue.EMPTY_OBJECT)
// 变量初始化,这些放到上面预先定义会报错,不知道为啥
this.color = options.color || defaultColor // 颜色
this.image = options.image || defaultImage // 材质图片
this.forward = options.forward || defaultForward // 前进方向,建议采用1和-1,再改变成其他值只会影响速度
this.speed = options.speed || defaultSpeed
this.repeat = options.repeat || defaultRepeat
}
// 材质类型
getType() {
return 'PolylineFlow'
}
// 这个方法在每次渲染时被调用,result的参数会传入glsl中。
getValue(time: any, result: OptionIF) {
if (!defined(result))
result = {}
result.color = Property.getValueOrClonedDefault(this._color, time, defaultColor, result.color)
result.image = Property.getValueOrClonedDefault(this._image, time, defaultImage, result.image)
result.forward = Property.getValueOrClonedDefault(this._forward, time, defaultForward, result.forward)
result.speed = Property.getValueOrClonedDefault(this._speed, time, defaultSpeed, result.speed)
result.repeat = Property.getValueOrClonedDefault(this._repeat, time, defaultRepeat, result.repeat)
return result
}
equals(other: any) {
return (
this === other
|| (other instanceof PolylineFlowMaterialProperty
&& Property.equals(this._color, other._color)
&& Property.equals(this._image, other._image)
&& Property.equals(this._forward, other._forward)
&& Property.equals(this._speed, other._speed)
&& Property.equals(this._repeat, other._repeat))
)
}
}
Object.defineProperties(PolylineFlowMaterialProperty.prototype, {
isConstant: {
get: function get() {
return (
Property.isConstant(this._color)
&& Property.isConstant(this._image)
&& Property.isConstant(this._forward)
&& Property.isConstant(this._speed)
&& Property.isConstant(this._repeat)
)
},
},
definitionChanged: {
get: function get() {
return this._definitionChanged
},
},
color: createPropertyDescriptor('color'),
image: createPropertyDescriptor('image'),
forward: createPropertyDescriptor('forward'),
speed: createPropertyDescriptor('speed'),
repeat: createPropertyDescriptor('repeat'),
})
Material.PolylineFlowType = 'PolylineFlow'
Material._materialCache.addMaterial(Material.PolylineFlowType, {
fabric: {
type: Material.PolylineFlowType,
uniforms: {
// uniforms参数跟我们上面定义的参数以及getValue方法中返回的result对应,这里值是默认值
color: defaultColor,
image: defaultImage,
forward: defaultForward,
speed: defaultSpeed,
repeat: defaultRepeat,
},
// source编写glsl,可以使用uniforms参数,值来自getValue方法的result
source: `czm_material czm_getMaterial(czm_materialInput materialInput)
{
czm_material material = czm_getDefaultMaterial(materialInput);
vec2 st = materialInput.st;
vec4 fragColor = texture(image, fract(vec2(st.s - speed*czm_frameNumber*0.001*forward, st.t)*repeat));
material.emission = fragColor.rgb;
material.alpha = fragColor.a;
return material;
}`,
},
translucent: true,
})
水波纹材质
import type { Cartesian2 } from 'cesium'
import { Color, Event, Material, Property, createPropertyDescriptor, defaultValue, defined } from 'cesium'
const defaultColor = Color.TRANSPARENT
const defaultCount = 3
const defaultSpeed = 10
const defaultGradient = 0.1
interface OptionIF {
color?: Color
count?: number
speed?: number
gradient?: Cartesian2
}
export class RippleCircleMaterialProperty {
// 定义材质变量
_definitionChanged = new Event()
_color = undefined
_colorSubscription = undefined
_count = undefined
_countSubscription = undefined
_speed = undefined
_speedSubscription = undefined
_gradient = undefined
_gradientSubscription = undefined
constructor(options: OptionIF) {
options = defaultValue(options, defaultValue.EMPTY_OBJECT)
// 变量初始化,这些放到上面预先定义会报错,不知道为啥
this.color = options.color || defaultColor // 颜色
this.count = options.count || defaultCount // 总圈数
this.speed = options.speed || defaultSpeed // 持续时间,毫秒
this.gradient = options.gradient || defaultGradient // 渐变曲率
}
// 材质类型
getType() {
return 'RippleCircle'
}
// 这个方法在每次渲染时被调用,result的参数会传入glsl中。
getValue(time: any, result: OptionIF) {
if (!defined(result))
result = {}
result.color = Property.getValueOrClonedDefault(this._color, time, defaultColor, result.color)
result.count = Property.getValueOrClonedDefault(this._count, time, defaultCount, result.count)
result.speed = Property.getValueOrClonedDefault(this._speed, time, defaultSpeed, result.speed)
result.gradient = Property.getValueOrClonedDefault(this._gradient, time, defaultGradient, result.gradient)
return result
}
equals(other: any) {
return (
this === other
|| (other instanceof RippleCircleMaterialProperty
&& Property.equals(this._color, other._color)
&& Property.equals(this._count, other._count)
&& Property.equals(this._speed, other._speed)
&& Property.equals(this._gradient, other._gradient))
)
}
}
Object.defineProperties(RippleCircleMaterialProperty.prototype, {
isConstant: {
get: function get() {
return (
Property.isConstant(this._color)
&& Property.isConstant(this._count)
&& Property.isConstant(this._speed)
&& Property.isConstant(this._gradient)
)
},
},
definitionChanged: {
get: function get() {
return this._definitionChanged
},
},
color: createPropertyDescriptor('color'),
count: createPropertyDescriptor('count'),
speed: createPropertyDescriptor('speed'),
gradient: createPropertyDescriptor('gradient'),
})
Material.RippleCircleType = 'RippleCircle'
Material._materialCache.addMaterial(Material.RippleCircleType, {
fabric: {
type: Material.RippleCircleType,
uniforms: {
// uniforms参数跟我们上面定义的参数以及getValue方法中返回的result对应,这里值是默认值
color: defaultColor,
count: defaultCount,
speed: defaultSpeed,
gradient: defaultGradient,
},
// source编写glsl,可以使用uniforms参数,值来自getValue方法的result
source: `
czm_material czm_getMaterial(czm_materialInput materialInput)
{
czm_material material = czm_getDefaultMaterial(materialInput);
material.diffuse = 1.5 * color.rgb;
vec2 st = materialInput.st;
float dis = distance(st, vec2(0.5, 0.5));
float per = fract(czm_frameNumber * speed / 1000.0);
if(count == 1.0){
if(dis > per * 0.5){
discard;
}else {
material.alpha = color.a * dis / per / 2.0;
}
} else {
vec3 str = materialInput.str;
if(abs(str.z) > 0.001){
discard;
}
if(dis > 0.5){
discard;
} else {
float perDis = 0.5 / count;
float disNum;
float bl = 0.0;
for(int i = 0; i <= 999; i++){
if(float(i) <= count){
disNum = perDis * float(i) - dis + per / count;
if(disNum > 0.0){
if(disNum < perDis){
bl = 1.0 - disNum / perDis;
}
else if(disNum - perDis < perDis){
bl = 1.0 - abs(1.0 - disNum / perDis);
}
material.alpha = pow(bl,(1.0 + 10.0 * (1.0 - gradient)));
}
}
}
}
}
return material;
}`,
},
translucent: true,
})
Box发光材质
import { Color, Event, Material, Property, createPropertyDescriptor, defaultValue, defined } from 'cesium'
const defaultColor = Color.TRANSPARENT
interface OptionIF {
color?: Color
}
export class GlowMaterialProperty {
// 定义材质变量
_definitionChanged = new Event()
_color = undefined
_colorSubscription = undefined
constructor(options: OptionIF) {
options = defaultValue(options, defaultValue.EMPTY_OBJECT)
// 变量初始化,这些放到上面预先定义会报错,不知道为啥
this.color = options.color || defaultColor // 颜色
}
// 材质类型
getType() {
return 'Glow'
}
// 这个方法在每次渲染时被调用,result的参数会传入glsl中。
getValue(time: any, result: OptionIF) {
if (!defined(result))
result = {}
result.color = Property.getValueOrClonedDefault(this._color, time, defaultColor, result.color)
return result
}
equals(other: any) {
return (this === other || (other instanceof GlowMaterialProperty && Property.equals(this._color, other._color)))
}
}
Object.defineProperties(GlowMaterialProperty.prototype, {
isConstant: {
get: function get() {
return (
Property.isConstant(this._color)
)
},
},
definitionChanged: {
get: function get() {
return this._definitionChanged
},
},
color: createPropertyDescriptor('color'),
})
Material.GlowType = 'Glow'
Material._materialCache.addMaterial(Material.GlowType, {
fabric: {
type: Material.GlowType,
uniforms: {
// uniforms参数跟我们上面定义的参数以及getValue方法中返回的result对应,这里值是默认值
color: defaultColor,
},
// source编写glsl,可以使用uniforms参数,值来自getValue方法的result
source: `
uniform vec4 color;
czm_material czm_getMaterial(czm_materialInput materialInput){
czm_material material = czm_getDefaultMaterial(materialInput);
vec2 st = materialInput.st;
// 使用球面坐标
vec2 sphereCoords = vec2(atan(st.s, st.t), length(st));
material.diffuse = color.rgb * 2.0;
material.alpha = color.a * (1.0 - fract(sphereCoords.x / (2.0 * 3.14159265))) * 0.8;
return material;
}`,
},
translucent: true,
})
调用案例
以第一种流动材质为例调用如下,参数因为设置了默认值可以不传
import { PolylineFlowMaterialProperty } from 'xxx'
viewer.entities.add({
polyline: {
positions: posArr,
width: 2,
material: new PolylineFlowMaterialProperty({color:color,image:image,forward:forward,speed:speed,repeat:repeat})
}
})