一、说明

本文章主讲shader,不是浅浅的教大家怎么在cesium方面自定义材质,而是由最简单的效果逐步深入,了解圆形、线、渐变、动画的思路。所以最好需要有一点shader的基础,当然下文我也会稍微介绍一下。如果想看视频版的话点击这里跳转b站

二、webgl shader language基础知识

webgl的介绍啥的就不多赘述了,这里主要了解一下shader中数据来源以及之后用到的函数。

1、shader中的四种数据来源

类型 中文 说明
attribute 属性 主要是顶点着色器用来从缓冲(Buffer)中获取数据
uniform 统一值 在每一次渲染中全局有效且不变的值

texture

纹理 一个数据序列,大多数情况下可以理解为图像数据
varying 可变量 顶点着色器向片元着色器传值的方式,并且会以插值的形式传递

2、文章出现的shader内置函数   

函数名(参数) 说明
pow(x,y) x的y次方。如果x小于0,结果是未定义的。同样,如果x=0并且y<=0,结果也是未定义的。
sqrt(x) 计算x的开方。如果x小于0,结果是未定义的。
fract(x) 返回x-floor(x),即返回x的小数部分
step(edge, x)  如果x < edge,返回0.0,否则返回1.0
smoothstep(edge0, edge1, x)  如果x <= edge0,返回0.0 ;如果x >= edge1 返回1.0;如果edge0 < x < edge1,则执行0~1之间的平滑埃尔米特差值。 如果edge0 >= edge1,结果则是从1~0之间递减,也就是x=edge0会返回1.0。(最好不要这样做)。
distance(a,b) 计算a,b两点间的距离,二维三维都可以

三、cesium中为几何体自定义材质

下面的代码直接复制即可使用,当然我这里不包含viewer的初始化(自己初始化一个就行)。如何自定义材质步骤我就简单介绍一下:

1.按照cesium格式要求写一个czm_getMaterial函数,然后将代码字符串放入Material中

2.再用这个Material组成MaterialAppearance或者PolylineMaterialAppearance

3.在primitive中使用这个材质就行。

如果想在entity中使用,那可以随便找一篇文章参考一下,自定义材质最重要的还是shader部分,其他都是大差不差的。

1、模块导入

import {
	Cartesian3,
	Color,
	Primitive,
	PolygonGeometry,
	BoxGeometry,
	PolygonHierarchy,
	PolylineGeometry,
	EllipsoidGeometry,
	GeometryInstance,
	Material,
	MaterialAppearance,
	PolylineMaterialAppearance,
	Transforms,
} from 'cesium'

2、材质声明以及在几何体中使用

这里我用了四种几何体来观察材质的效果,所以代码会有点多,复制后只需要关注shader就行。代码我是写在TS里的,我看了一遍没有类型标注啥的,应该不用修改就可以直接在JS里运行

const shaderSource = `
	uniform vec4 color;
	
	czm_material czm_getMaterial(czm_materialInput materialInput)
	{
		vec4 outColor = color;
		czm_material material = czm_getDefaultMaterial(materialInput);
		material.diffuse = czm_gammaCorrect(outColor.rgb);
		material.alpha = outColor.a;
		return material;
	}
`

const myMaterial = new Material({
	translucent: false,
	fabric: {
		type: 'test',
		uniforms: {
			color: new Color(1, 0, 0, 1),
		},
		source: shaderSource
	}
})


const appearance = new MaterialAppearance({
	material: myMaterial,
})

const polylineAppearance = new PolylineMaterialAppearance({
	material: myMaterial
})

const polygonPrimitive = new Primitive({
	geometryInstances: new GeometryInstance({
		geometry: new PolygonGeometry({
			polygonHierarchy: new PolygonHierarchy(Cartesian3.fromDegreesArray([
				114, 25,
				114.01, 25,
				114.01, 25.01,
				114, 25.01,
			])),
			height: 1000
		})
	}),
	appearance
})

const boxPrimitive = new Primitive({
	geometryInstances: new GeometryInstance({
		geometry: BoxGeometry.fromDimensions({
			dimensions: new Cartesian3(1000, 1000, 1000)
		}),
		modelMatrix: Transforms.eastNorthUpToFixedFrame(Cartesian3.fromDegrees(114.005, 25.02, 1000))
	}),
	appearance
})

const polylinePrimitive = new Primitive({
	geometryInstances: new GeometryInstance({
		geometry: new PolylineGeometry({
			positions: Cartesian3.fromDegreesArrayHeights([
				114.02, 25.02, 1000,
				114.05, 25.02, 1000
			]),
			width: 2,
		})
	}),
	appearance: polylineAppearance
})

const ellipsoidPrimitive = new Primitive({
	geometryInstances: new GeometryInstance({
		geometry: new EllipsoidGeometry({
			radii: new Cartesian3(1000, 1000, 1000),
		}),
		modelMatrix: Transforms.eastNorthUpToFixedFrame(Cartesian3.fromDegrees(114.03, 25.005, 1000))
	}),
	appearance
})

viewer.scene.primitives.add(polygonPrimitive)
viewer.scene.primitives.add(boxPrimitive)
viewer.scene.primitives.add(polylinePrimitive)
viewer.scene.primitives.add(ellipsoidPrimitive)

viewer.camera.flyTo({
	destination: Cartesian3.fromDegrees(114.02, 25.01, 10000),
	duration: 0
})

3、cesium材质shader需求说明

我们将材质的shader单独拎出,让我们看看cesium材质需要的source是什么样的。

uniform vec4 color;
		
czm_material czm_getMaterial(czm_materialInput materialInput)
{
	vec4 outColor = color;
	czm_material material = czm_getDefaultMaterial(materialInput);
	material.diffuse = czm_gammaCorrect(outColor.rgb);
	material.alpha = outColor.a;
	return material;
}

 上面的代码就是一个最简单的cesium材质,效果和cesium自带的Color材质一模一样,代码有些细微差别。

cesium所需的材质shader具体来说就是需要写一个 czm_getMaterial 函数。然后这个函数返回一个czm_material 类型的结构体(结构体可以类比为JS中的对象)。这个结构体最好通过 czm_getDefaultMaterial 函数获得,再在此基础上修改。

我们可以在这个函数上方声明一些uniform来从JS中获取一些数据,比如这个最简单的示例就从JS中获取颜色值,并赋予 czm_material 类型的结构体的diffuse属性。

4、czm_materialInput 结构体说明

czm_materialInput 类型的结构体是 czm_getMaterial 函数的参数,cesium会在几何体的片元着色器中调用 czm_getMaterial ,并将参数传递给该函数。

属性名 类型 说明
s float 一维纹理坐标
st vec2 二维纹理坐标
str vec3 三维纹理坐标
normalEC vec3 眼坐标系中的未扰动表面法线
tangentToEyeMatrix mat3 将切线空间法线转换为眼空间的矩阵
positionToEyeEC vec3 从片段到眼睛的向量(在眼坐标系中)。其大小为片段到眼睛的距离(以米为单位)

5、czm_material 结构体说明

cesium对该结构体的说明是:

保存可用于照明的材质信息。由所有 czm_getMaterial 函数返回。

 从中我们可以了解到该结构体不仅仅保存着材质信息,并且该结构体还可以被用于光照(cesium使用的冯氏光照模型第二个参数就是该结构体)。

属性名 类型 说明
diffuse vec3 向各个方向均匀散射的入射光,默认值vec3(0.0)。(漫反射颜色)
specular float 沿单个方向反射的入射光的强度,默认值0.0。(镜面高光强度)
shininess float 镜面反射的清晰度。值越高,创建的镜面高光越小、越聚焦。默认值1.0
normal vec3 表面的法线在眼睛坐标中。它用于法线贴图等效果。默认值为曲面的未修改法线。(就是normalEC)
emission vec3 材质在所有方向上均匀发射的光。默认值为 vec3(0.0),它不发光。
alpha float 此材质的不透明度。0.0 是完全透明的;1.0 是完全不透明的。默认值1.0

四、shader深入

1、最简单的shader

先简单介绍一下fragmentShader,在渲染管线中其主要负责光栅化,通俗的来讲就是确定每一个像素的颜色。在一次绘制中,对于每一次像素都会调用一次fragmentShader,其中有些东西是变化的,例如像素的坐标、可变量的值;但也有些东西是不变的,例如uniform的值。

uniform vec4 color;
		
czm_material czm_getMaterial(czm_materialInput materialInput)
{
	vec4 outColor = color;
	czm_material material = czm_getDefaultMaterial(materialInput);
	material.diffuse = czm_gammaCorrect(outColor.rgb);
	material.alpha = outColor.a;
	return material;
}

再次将最简单的shader拿过来,这次看看在这个简单的shader中我们做了什么。首先我们声明了一个 uniform(统一值) ,类型为vec4,名称为 color 。在之后我们将其保存在 outColor 中。为什么要这样多此一举呢,因为uniform的值我们是无法改变的,为了之后能改变颜色值,所以我们用一个变量接收一下。

然后在通过 czm_getDefaultMaterial 获得 czm_material 类型的结构体,前面说过,czm_material 最好通过czm_getDefaultMaterial 来获取一个默认的,再在此基础上改变,而不是直接自己声明一个。

最后我们将传递的颜色的rgb值赋予diffuse属性,a值赋予alpha属性。alpha属性应该不用过多介绍了。而diffuse属性则是比较重要的一个属性,这里我们需要分为两种情况,一种是应用光照,一种是不应用光照。

#ifdef FLAT
    out_FragColor = vec4(material.diffuse + material.emission, material.alpha);
#else
    out_FragColor = czm_phong(normalize(positionToEyeEC), material, czm_lightDirectionEC);
#endif

  1. 不应用光照:在上面的代码中,如果我们定义了FLAT属性,也就不会使用光照模型,这时候像元的颜色就等于material的diffuse属性与emission属性之和,而emission属性我们一般不会去使用。所以这时候可以将diffuse属性当成最终的颜色
  2. 应用光照:如果应用了光照,那么就会将material传入czm_phong这个函数中,下面是cesium中的冯氏光照模型的具体实现。我们从代码中可以看到cesium的冯氏光照模型将传递过来的diffuse分为了两半,一半变成了ambient (环境光),另一半则是与计算出来的diffuse(漫反射光强)与光的颜色相乘作为最终的漫反射光。从代码中我们就可以得到一个信息,那就是在没有自发光emission的情况下,cesium物体最暗的颜色就是material.diffuse的一半。

vec4 czm_phong(vec3 toEye, czm_material material, vec3 lightDirectionEC)
{
    
    float diffuse = czm_private_getLambertDiffuseOfMaterial(vec3(0.0, 0.0, 1.0), material);
    if (czm_sceneMode == czm_sceneMode3D) {
        diffuse += czm_private_getLambertDiffuseOfMaterial(vec3(0.0, 1.0, 0.0), material);
    }

    float specular = czm_private_getSpecularOfMaterial(lightDirectionEC, toEye, material);

    
    vec3 materialDiffuse = material.diffuse * 0.5;

    vec3 ambient = materialDiffuse;
    vec3 color = ambient + material.emission;
    color += materialDiffuse * diffuse * czm_lightColor;
    color += material.specular * specular * czm_lightColor;

    return vec4(color, material.alpha);
}

2、渐变

渐变的产生大多需要借助纹理坐标,在webgl中,纹理的坐标系如下图所示,cesium纹理坐标设置大多都是完全覆盖面的。假如绘制了一个正方形,那左下角为纹理坐标的原点,右上角坐标则为(1,1)。不管实际上物体是正方形还是长方形还是什么形状,纹理坐标右上角就是(1,1)。利用好这一点就可以产生渐变效果了

纹理坐标我们可以从czm_materialInput.st中获取,其中s为横坐标,t为纵坐标。我们可以简单的将outColor的r值设置为s或者t观察效果。

uniform vec4 color;

czm_material czm_getMaterial(czm_materialInput materialInput)
{
	vec4 outColor = color;
	czm_material material = czm_getDefaultMaterial(materialInput);

	vec2 st = materialInput.st;
	// outColor.r = st.s;
	outColor.r = st.t;

	material.diffuse = czm_gammaCorrect(outColor.rgb);
	material.alpha = outColor.a;
	return material;
}

左边将r值设置为st.s,右边则是st.t 。

        

我们当然也可以将alpha设置为纹理坐标的某个值

        

那怎么生成多个渐变,渐变的原理我们大概已经了解了,因为纹理坐标都是0-1的,而rgba四个值也都是0-1之间的,如果想要在横坐标生成3个渐变效果,是不是应该需要3个0-1之间的值,这怎么得到呢。很简单,我们只需要将st的s乘以3,那么s就在0-3之间波动了,这时候我们再取其小数部分,因为小数部分就是在 [0 - 1) 之间的,这样我们就得到三个0-1了。我们可以将需要生成的数量通过uniform来传递(不要忘记在js中传递uniform值)。

uniform vec4 color;
uniform float gradationNumber;

czm_material czm_getMaterial(czm_materialInput materialInput) {
	vec4 outColor = color;
	czm_material material = czm_getDefaultMaterial(materialInput);

	vec2 st = materialInput.st;
	// outColor.r = st.s;
	// outColor.r = st.t;
	// outColor.a = st.s;
	// outColor.a = st.t;
	outColor.a = fract(st.s * gradationNumber);

	material.diffuse = czm_gammaCorrect(outColor.rgb);
	material.alpha = outColor.a;
	return material;
}

3、如何画圆

画圆的话我们可以先了解一下圆的定义:

在同一平面内到定点的距离等于定长的点的集合叫做圆

其中关键就是距离,但是距离取多少合适这谁知道。不要紧,因为我们有纹理坐标,纹理坐标范围是固定的,如果我们以(0.5,0.5)为圆心,那么与范围相切的圆的半径就是0.5,包含整个范围的圆的半径就是 \sqrt{2} / 2 。而距离的话我们知道webgl有个distance函数可以很方便的计算。

uniform vec4 color;

czm_material czm_getMaterial(czm_materialInput materialInput) {
	vec4 outColor = color;
	czm_material material = czm_getDefaultMaterial(materialInput);

	vec2 st = materialInput.st;
	vec2 origin = vec2(0.5, 0.5);
	float dis = distance(origin, st);
	if(dis <= 0.5) {
		outColor.rgb = vec3(0.0, 1.0, 0.0);
	}

	material.diffuse = czm_gammaCorrect(outColor.rgb);
	material.alpha = outColor.a;
	return material;
}

 稍微调整一下代码,将圆以外部分的透明度的调整为0

 if(dis > 0.5){

        outColor.a = 0.0;

}

ps:上图中左下角是椭圆是因为绘制的就是个长方形,因为虽然设置的长宽都是0.01度,但是因为不在赤道附近经纬度一度的长度差异有点大 。

4、如何画线

在材质的绘制中有时候也需要画线,例如绘制十字,圆线等等。实际上材质中是不能绘制线的,但是我们可以绘制一个长方体,让其一条边非常短,这样就类似线了。

通过uniform得到一个线宽(演示中设置的是0.02),然后如果在 (线的位置 - 线宽,线的位置 + 线宽)之间的话就将outColor换成线的颜色,这样就可以在材质中绘制一条线了。

uniform vec4 color;
uniform float lineWidth;

czm_material czm_getMaterial(czm_materialInput materialInput) {
	vec4 outColor = color;
	czm_material material = czm_getDefaultMaterial(materialInput);

	vec2 st = materialInput.st;
	// 绘制竖线
	if(st.s > 0.5 - lineWidth / 2.0 && st.s < 0.5 + lineWidth / 2.0) {
		outColor.rgb = vec3(0.0, 1.0, 0.0);
	}
	// 绘制横线
	if(st.t > 0.5 - lineWidth / 2.0 && st.t < 0.5 + lineWidth / 2.0) {
		outColor.rgb = vec3(0.0, 1.0, 0.0);
	}

	material.diffuse = czm_gammaCorrect(outColor.rgb);
	material.alpha = outColor.a;
	return material;
}

 有了这个思路我们可很轻松的绘制圆形线

uniform vec4 color;
uniform float lineWidth;

czm_material czm_getMaterial(czm_materialInput materialInput) {
	vec4 outColor = color;
	czm_material material = czm_getDefaultMaterial(materialInput);

	vec2 st = materialInput.st;
	vec2 origin = vec2(0.5, 0.5);
	float radius = 0.5;
	float dis = distance(origin, st);
	if(dis > radius - lineWidth / 2.0 && dis < radius + lineWidth / 2.0) {
		outColor.rgb = vec3(0.0, 1.0, 0.0);
	}

	material.diffuse = czm_gammaCorrect(outColor.rgb);
	material.alpha = outColor.a;
	return material;
}

5、让线动起来

cesium中的材质动画离不开cesium的一个自动uniform,那就是czm_frameNumber,这个uniform表示当前处于cesium的第几帧绘制,从0开始,每次绘制cesium都会将其递增1。

我们现在假设cesium帧率为60,那么我们使用 czm_frameNumber / 60.0 。这样得到的结果如果只看整数部分那就是每秒钟递增1,只看小数部分那就是一直在 [0,1) 之间循环,循环时间为1秒。那假如我们将这个结果赋予alpha值,是不是就是物体透明的不断地从0-1变化,循环时间为1秒。那怎么通过这个让线动起来呢,我们知道纹理坐标的值也是0-1之间的,那如果我们将这个值赋予作为线的位置呢(我们之前线的位置为0.5),让我们试试看吧。

uniform vec4 color;
uniform float lineWidth;

czm_material czm_getMaterial(czm_materialInput materialInput) {
	vec4 outColor = color;
	czm_material material = czm_getDefaultMaterial(materialInput);

	vec2 st = materialInput.st;
	float time = fract(czm_frameNumber / 144.0);  // 在144帧之内从0-1不断循环的数字
	float linePositionX = time;				// 将其作为竖线的位置
	if(st.s > linePositionX - lineWidth / 2.0 && st.s < linePositionX + lineWidth / 2.0) {
		outColor.rgb = vec3(0.0, 1.0, 0.0);
	}

	material.diffuse = czm_gammaCorrect(outColor.rgb);
	material.alpha = outColor.a;
	return material;
}

ps:下图的第二个效果只是将st.s换成st.t即可,以后就不再说明了。 


        

6、移动的渐变区域

cesium中很多好看的材质本质上都是一个移动的渐变区域,例如扫描、飞线、穿梭线、波纹等等、区别在于移动的方向、时间函数等具体实现细节,大致思路都基本是一样的。

这里我们继续沿用上一次的代码,首先我们可以将上一次代码中的lineWidth换成percent,因为不再是线而是区域了,并且起始位置不再是中心,区域所占的比例用percent表示,然后我们将percent设为0.4,然后再看看效果。

uniform vec4 color;
uniform float percent;

czm_material czm_getMaterial(czm_materialInput materialInput) {
	vec4 outColor = color;
	czm_material material = czm_getDefaultMaterial(materialInput);

	vec2 st = materialInput.st;
	float time = fract(czm_frameNumber / 144.0);
	float startPosition = time;
	if(st.s > startPosition - percent / 2.0 && st.s < startPosition + percent / 2.0) {
		outColor.rgb = vec3(0.0, 1.0, 0.0);
	}

	material.diffuse = czm_gammaCorrect(outColor.rgb);
	material.alpha = outColor.a;
	return material;
}

         

现在我们成功将一部分区域换成了另一个颜色,那么怎么换成渐变色呢。这时候就需要使用数学中的映射了(应该是叫映射),举个例子,例如现在0.2-0.6区域我们将其改为了绿色,那么我们就需要在此区域内,将0.2-0.6转为0-1,再将转化后的0-1生成出渐变。

我们先将outColor的a值设为0。我们知道的是在if判断中,st.s的值就是我们需要映射为0-1的值,我们先来进行一下线性映射。原始的边界值我们知道是 (startPosition - percent,startPosition),我们需要映射为(0,1);原始值是st.s,我们设为x,映射后的值我们设为y。那么就有 x - (startPosition - percent)  /  startPosition - (startPosition  - percent)  =  y - 0 / 1 - 0。结果就是 y = x -  startPosition +percent / percent 。其中x就是st.s,我们将其转为代码试试。

uniform vec4 color;
uniform float percent;

czm_material czm_getMaterial(czm_materialInput materialInput) {
	vec4 outColor = color;
	czm_material material = czm_getDefaultMaterial(materialInput);

	vec2 st = materialInput.st;
	float time = fract(czm_frameNumber / 144.0);
	float startPosition = time;
	if(st.s > startPosition - percent && st.s < startPosition) {
		float value = (st.s - (startPosition - percent)) / percent;
		outColor.rgb = vec3(0.0, value, 0.0);
	}

	material.diffuse = czm_gammaCorrect(outColor.rgb);
	material.alpha = outColor.a;
	return material;
}

可以看到我们已经成功的生成出了渐变,绿色值由0-1。

我们稍微调整一下效果,让默认alpha为0,区间内alpha由0-1递增。有了透明部分不要忘记将translucent设置为true

uniform vec4 color;
uniform float percent;

czm_material czm_getMaterial(czm_materialInput materialInput) {
	vec4 outColor = color;
	czm_material material = czm_getDefaultMaterial(materialInput);

	vec2 st = materialInput.st;
	float time = fract(czm_frameNumber / 144.0);
	float startPosition = time;
	outColor.a = 0.0;  // 透明度默认为0
	if(st.s > startPosition - percent && st.s < startPosition) {
		float value = (st.s - (startPosition - percent)) / percent;
		outColor.a = value; 
	}

	material.diffuse = czm_gammaCorrect(outColor.rgb);
	material.alpha = outColor.a;
	return material;
}

        

上面我们是自己将区域内的纹理值映射到0-1之间的,这样的映射也是一种线性的映射。webgl中就有这种功能的函数,smoothstep,并且该函数的映射是非线性的,我们可以尝试一下。

uniform vec4 color;
uniform float percent;

czm_material czm_getMaterial(czm_materialInput materialInput) {
	vec4 outColor = color;
	czm_material material = czm_getDefaultMaterial(materialInput);

	vec2 st = materialInput.st;
	float time = fract(czm_frameNumber / 144.0);
	float startPosition = time;
	outColor.a = 0.0;
	if(st.s > startPosition - percent && st.s < startPosition) {
		// 使用smoothstep替换原本的线性映射
		float value = smoothstep(startPosition - percent, startPosition, st.s);
		outColor.a = value;
	}

	material.diffuse = czm_gammaCorrect(outColor.rgb);
	material.alpha = outColor.a;
	return material;
}

左边为smoothstep的结果,右边是原本的结果,肉眼看起来好像没有多大的差别。

        

五、引申

1.减少条件语句的使用

shader中条件语句有可能会导致效率的降低,具体情况大家可以百度了解,这里就不再说明。可以的情况下我们最好将条件语句转为使用step等

uniform vec4 color;
uniform float percent;

czm_material czm_getMaterial(czm_materialInput materialInput) {
	vec4 outColor = color;
	czm_material material = czm_getDefaultMaterial(materialInput);

	vec2 st = materialInput.st;
	float time = fract(czm_frameNumber / 144.0);
	float startPosition = time;
	// 将if判断取消,转而使用step函数
	float value = smoothstep(startPosition - percent, startPosition, st.s) * step(-time, -st.s);
	outColor.a = value;

	material.diffuse = czm_gammaCorrect(outColor.rgb);
	material.alpha = outColor.a;
	return material;
}

这里将4.6最后的代码搬来,取消掉其中的if判断,转而将结果 乘以一个 step(-time, -st.s) ,因为我们需要不在区间内的其他地方alpha值为0.0,如果小于区间smoothstep函数回返回0的,但是大于区间的地方还是1。而乘上的那个 step(-time, -st.s) 在大于区间的地方值为0,这样也就将区间外的地方透明的变为了0。

2.非线性动画

上面的动画的时间函数都是线性的,如何做到非线性的变换,例如先缓慢再迅速再缓缓,就如同CSS中过渡的ease-in-out一样。这里我只提供思路,我们知道time是0-1之间的,我们可以将其作为x,然后我们就可以使用我们想要的时间函数,也就是 y = f(x) 。如果y的值域不在0-1之间,那我们就将其映射到0-1之间即可。例如我们可以让 time = pow(time, 2.0),结果同样在0-1之间,这样得到的time就不再是线性的了,动画也就有了速率上的变换。例如下面的gif图片就是 time = pow(time ,4.0) 后的结果,与上面4.6的结果对比可以明显的看到先慢后快的。

3.精确控制动画时间

上面的代码time都来自czm_frameNumber / 144.0的小数部分,如果想要控制速率呢,这也十分简单。我们可以声明一个float类型的统一值,名字叫speed,用其乘以czm_frameNumber即可,然后后面的分母我们可以调大些,比如10000。

float time = fract(czm_frameNumber * speed / 10000.0);

 这样我们就可以通过控制speed来控制动画的速率。

那如果想精确的控制动画的时间呢,这就不是传递一个uniform可以办到的了。思路就是:我们在每一次绘制时传递时间差值,就和frameNumber类似,从0开始增加,每次绘制传递距离第一次绘制经过的毫秒数,然后用这个来精确控制动画的时间。传递的方式就是通过requestAnimationFrame不断的改变创建的Material的uniform。

 六、总结

文章只是提供从0开始的简单的材质实现,讲到这也该结束了,虽然还有很多效果我没有讲,例如圆形的扩散、扇形的扫描效果、线的交替等等,但是那都可以在我讲了的效果的基础上延申出去,而且也十分简单。

最后如果本文章对你有帮助不要忘了点赞收藏哦。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐