先看效果:
想要做到旗帜飘扬的效果,仅仅依靠标准的几何体与材质是不够的,还需要对之加以“改装”。
通常制作一面国旗用简单的立方体贴上纹理,效果如下,比较生硬死板,整体感官一般。
那么如何制作一面飘扬的旗帜呢?
在threejs中可以通过更改材质的方式和编写着色器的方式来达到我们想要的效果。但是MeshStandardMaterial材质非常复杂,涉及较多的代码与计算,且性能开销比较大,所以编写自己的着色器无疑是最简单的方式。
首先需要创建一个特定的材质,这里我们选择ShaderMaterial,因为此材质可以将一些代码自动添加到着色器代码中,这里我们提供顶点着色器和片段着色器,并在uniforms中提供三个参数,频率、时间和纹理,频率控制方向的位移,时间控制旗帜的活跃程度:
const material = new THREE.ShaderMaterial({
vertexShader: testVertexShader,
fragmentShader: testFragmentShader,
uniforms:
{
uFrequency: { value: new THREE.Vector2(10, 5) },
uTime: { value: 0 },
uColor: { value: new THREE.Color('orange') },
uTexture: { value: flagTexture }
}
})
顶点着色器代码:
uniform vec2 uFrequency;
uniform float uTime;
varying vec2 vUv;
varying float vElevation;
void main()
{
vec4 modelPosition = modelMatrix * vec4(position, 1.0);
float elevation = sin(modelPosition.x * uFrequency.x - uTime) * 0.1;
elevation += sin(modelPosition.y * uFrequency.y - uTime) * 0.1;
modelPosition.z += elevation;
vec4 viewPosition = viewMatrix * modelPosition;
vec4 projectedPosition = projectionMatrix * viewPosition;
gl_Position = projectedPosition;
vUv = uv;
vElevation = elevation;
}
片段着色器代码:
uniform vec3 uColor;
uniform sampler2D uTexture;
varying vec2 vUv;
varying float vElevation;
void main()
{
vec4 textureColor = texture2D(uTexture, vUv);
textureColor.rgb *= vElevation * 2.0 + 0.65;
gl_FragColor = textureColor;
// gl_FragColor = vec4(vUv, 1.0, 1.0);
}
在旗帜飘扬的过程中,还增加了阴影,过程如下:
//在顶点着色器中,我们将风高程存储在一个变量中
void main()
{
// ...
float elevation = sin(modelPosition.x * uFrequency.x - uTime) * 0.1;
elevation += sin(modelPosition.y * uFrequency.y - uTime) * 0.1;
modelPosition.z += elevation;
// ...
}
//变量将提升发送到片段
// ...
varying float vElevation;
void main()
{
// ...
vElevation = elevation;
}
检索片段着色器中的变量
// ...
varying float vElevation;
void main()
{
vec4 textureColor = texture2D(uTexture, vUv);
textureColor.rgb *= vElevation * 2.0 + 0.5;
gl_FragColor = textureColor;
}
更新材质:
const elapsedTime = clock.getElapsedTime()
// Update material
material.uniforms.uTime.value = elapsedTime