Three.js 中国旗帜教程

Three.js 中国旗帜教程

中国旗帜 ·China Flag· ▶ 在线运行案例

  • 案例合集:三维可视化功能案例(threehub.cn)
  • 开源仓库github地址:https://github.com/z2586300277/three-cesium-examples
  • 400个案例代码:网盘链接

你将学到什么

  • RawShaderMaterial手写完整 GLSL(含 projectionMatrix 等)
  • 高细分BoxGeometry当旗面
  • 多频率sin叠加 Z 向波浪 + 杆边xFactor衰减
  • vDark传递位移量做片元明暗

效果说明

本案例演示中国旗帜效果:基于 WebGL 实现「中国旗帜」可视化效果,附完整可运行源码;核心用到 RawShaderMaterial、OrbitControls。建议先打开文首在线案例查看动态画面,再对照下方源码逐步理解。

核心概念

顶点波浪

float xFactor = clamp((modelPosition.x + 1.25) / 2.0, 0.0, 2.0);

float vWave = sin(modelPosition.xuFrequency.x - uTime)xFactor * uStrength; vWave += sin(modelPosition.yuFrequency.y - uTime)xFactoruStrength0.5; modelPosition.z += vWave;

xFactor让靠近旗杆(x 较小)顶点几乎不动。

片元明暗

textColor.rgb *= vDark + 0.85;

波浪隆起处vDark较大,颜色略亮/略暗形成褶皱感。

RawShaderMaterial

需自行声明attribute/uniform mat4 projectionMatrix等;不自动注入 Three chunk。

实现步骤

  • TextureLoader 加载国旗 JPG
  • BoxGeometry(3, 2, 0.025, 64, 64) 高细分
  • uniforms:uTimeuFrequencyuStrengthuTexture
  • animate 里uTime += 0.06
  • 代码要点

    import * as THREE from 'three'

    import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'

    const box = document.getElementById('box')

    const scene = new THREE.Scene()

    const camera = new THREE.PerspectiveCamera(75, box.clientWidth / box.clientHeight, 0.1, 1000)

    camera.position.set(0.5, -0.5, 3)

    const renderer = new THREE.WebGLRenderer()

    renderer.setSize(box.clientWidth, box.clientHeight)

    box.appendChild(renderer.domElement)

    new OrbitControls(camera, renderer.domElement)

    window.onresize = () => {

    renderer.setSize(box.clientWidth, box.clientHeight)

    camera.aspect = box.clientWidth / box.clientHeight

    camera.updateProjectionMatrix()

    }

    const flagTexture = new THREE.TextureLoader().load(GLOBAL_CONFIG.getFileUrl("images/chinaFlag.jpg"))

    const flagMaterial = new THREE.RawShaderMaterial({

    vertexShader:uniform mat4 projectionMatrix; uniform mat4 modelMatrix; uniform mat4 viewMatrix; uniform vec2 uFrequency; uniform float uTime; uniform float uStrength; attribute vec3 position; attribute vec2 uv; varying float vDark; varying vec2 vUv; void main() { vec4 modelPosition = modelMatrix * vec4(position, 1.0); float xFactor = clamp((modelPosition.x + 1.25) / 2.0, 0.0, 2.0); float vWave = sin(modelPosition.xuFrequency.x - uTime )xFactor * uStrength ; vWave += sin(modelPosition.yuFrequency.y - uTime)xFactoruStrength0.5; modelPosition.y += sin(modelPosition.x2.0 + uTime0.5)0.05xFactor; modelPosition.z += vWave; vec4 viewPosition = viewMatrix * modelPosition; vec4 projectedPosition = projectionMatrix * viewPosition; gl_Position = projectedPosition; vUv = uv; vDark = vWave; },

    fragmentShader:precision mediump float; varying float vDark; uniform sampler2D uTexture; varying vec2 vUv; void main(){ vec4 textColor = texture2D(uTexture, vUv); textColor.rgb *= vDark + 0.85; gl_FragColor = textColor; },

    side: THREE.DoubleSide,

    uniforms: { uFrequency: { value: new THREE.Vector2(3, 3) }, uTime: { value: 0 }, uTexture: { value: flagTexture }, uStrength: { value: 0.2 } }

    })

    const flagGeometry = new THREE.BoxGeometry(3, 2, 0.025, 64, 64)

    const flagMesh = new THREE.Mesh(flagGeometry, flagMaterial)

    scene.add(flagMesh)

    animate()

    function animate() {

    flagMaterial.uniforms.uTime.value += 0.06

    renderer.render(scene, camera)

    requestAnimationFrame(animate)

    }

    完整源码:GitHub

    小结

    • 本文提供中国旗帜完整 Three.js 源码与在线 Demo,建议先运行案例再改 uniform/参数做二次实验
    • 更多 Three.js 实战案例见 three-cesium-examples 合集 与 GitHub 开源仓库