一、實現一個光影牆 1. 根據自定義坐標點,輸出一個光影牆 /** * 添加光影牆 */ function addLightWall() { const geometry = new THREE.BufferGeometry(); const vertices = new Float32Array( ...
一、實現一個光影牆
1. 根據自定義坐標點,輸出一個光影牆
/** * 添加光影牆 */ function addLightWall() { const geometry = new THREE.BufferGeometry(); const vertices = new Float32Array([ 5, 0, 2, 3, 0, 5, -2, 0, 5, -4, 0, 2, -4, 5, 2, -2, 5, 5, 3, 5, 5, 5, 5, 2 ]); const indices = new Uint16Array([ 0, 1, 7, 1, 6, 7, 1, 2, 6, 2, 5, 6, 2, 3, 5, 3, 4, 5 ]) geometry.setAttribute('position', new THREE.BufferAttribute(vertices, 3)); geometry.setIndex(new THREE.BufferAttribute(indices, 1)); geometry.setAttribute('aHeight', new THREE.BufferAttribute(new Float32Array(new Array(geometry.getAttribute('position').count).fill(5.0)), 1)); const uniforms = { uTime: { value: 0.01 }, }; setShader(geometry, vertex, frag, [0, 2.5, 0], uniforms) } function setShader(geometry, vertexShader, fragmentShader, position = [0, 0, 0], uniforms = {}, isLine = false) { material = new THREE.ShaderMaterial({ vertexShader: vertexShader, fragmentShader: fragmentShader, side: THREE.DoubleSide, uniforms: uniforms, transparent: true, // blending: THREE.AdditiveBlending, // 多個元素的顏色相互疊加,顏色可能會變亮,會疊加setClearColor設置的背景色 }); material.depthTest = true; material.depthWrite = false; let planeMesh = new THREE.Mesh(geometry, material); if (isLine) { planeMesh = new THREE.Points(geometry, material); } planeMesh.position.x = position[0]; planeMesh.position.y = position[1]; planeMesh.position.z = position[2]; scene.add(planeMesh); }自定義坐標
2. 圓柱體的光影牆
/** * 添加光影牆 */ function addLightWall() { const geometry = new THREE.CylinderGeometry(3, 3, 5.0, 32, 32, true); geometry.setAttribute('aHeight', new THREE.BufferAttribute(new Float32Array(new Array(geometry.getAttribute('position').count).fill(5.0)), 1)); // 生成一個漸變色的光影牆 const vertex = ` varying vec3 vPosition; varying vec2 vUv; varying float vHeight; attribute float aHeight; void main() { vHeight = aHeight; vUv = uv; vPosition = position; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); } `; const frag = ` varying vec3 vPosition; varying vec2 vUv; varying float vHeight; void main() { float d = (vHeight - distance(vPosition, vec3(vPosition.x, -2.5, vPosition.z))) / vHeight; gl_FragColor = vec4(0.0, 1.0, 1.0, d); } `; const uniforms = { uTime: { value: 0.01 }, }; setShader(geometry, vertex, frag, [0, 2.5, 0], uniforms) } function setShader(geometry, vertexShader, fragmentShader, position = [0, 0, 0], uniforms = {}, isLine = false) { material = new THREE.ShaderMaterial({ vertexShader: vertexShader, fragmentShader: fragmentShader, side: THREE.DoubleSide, uniforms: uniforms, transparent: true, // blending: THREE.AdditiveBlending, // 多個元素的顏色相互疊加,顏色可能會變亮,會疊加setClearColor設置的背景色 }); material.depthTest = true; material.depthWrite = false; let planeMesh = new THREE.Mesh(geometry, material); if (isLine) { planeMesh = new THREE.Points(geometry, material); } planeMesh.position.x = position[0]; planeMesh.position.y = position[1]; planeMesh.position.z = position[2]; scene.add(planeMesh); }圓柱體光影牆
3. 為圓柱體添加可移動的線圈
/** * 添加光影牆 */ function addLightWall() { const geometry = new THREE.CylinderGeometry(3, 3, 5.0, 32, 32, true); geometry.setAttribute('aHeight', new THREE.BufferAttribute(new Float32Array(new Array(geometry.getAttribute('position').count).fill(5.0)), 1)); // 生成一個可以向上移動的牆體線 const vertex = ` varying vec3 vPosition; varying vec2 vUv; varying float vHeight; attribute float aHeight; void main() { vHeight = aHeight; vUv = uv; vPosition = position; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); } `; const frag = ` uniform float uTime; varying vec3 vPosition; varying vec2 vUv; varying float vHeight; void main() { float dis = distance(vPosition, vec3(vPosition.x, -2.5, vPosition.z)); float highlightPos = mod(uTime * 5.0, vHeight) - 2.5; float highlightDis = distance(vec3(vPosition.x, highlightPos, vPosition.z), vec3(vPosition.x, -2.5, vPosition.z)); float highlightOpa = (vHeight - highlightDis) / vHeight; float opacity = (vHeight - dis) / vHeight; if (abs(dis - highlightDis) < 0.05) { gl_FragColor = vec4(0.04, 0.95, 0.95, highlightOpa + 0.2); } else { gl_FragColor = vec4(0.0, 1.0, 1.0, opacity); } } `; const uniforms = { uTime: { value: 0.01 }, }; setShader(geometry, vertex, frag, [0, 2.5, 0], uniforms) } function setShader(geometry, vertexShader, fragmentShader, position = [0, 0, 0], uniforms = {}, isLine = false) { material = new THREE.ShaderMaterial({ vertexShader: vertexShader, fragmentShader: fragmentShader, side: THREE.DoubleSide, uniforms: uniforms, transparent: true, // blending: THREE.AdditiveBlending, // 多個元素的顏色相互疊加,顏色可能會變亮,會疊加setClearColor設置的背景色 }); material.depthTest = true; material.depthWrite = false; let planeMesh = new THREE.Mesh(geometry, material); if (isLine) { planeMesh = new THREE.Points(geometry, material); } planeMesh.position.x = position[0]; planeMesh.position.y = position[1]; planeMesh.position.z = position[2]; scene.add(planeMesh); }移動線圈光影牆
二、實現一個漸變色的波紋圓圈
1. 實現一個固定的漸變色圓圈
原理:
1) UV點的範圍是[0, 1],所以各個像素點距離圓心的距離範圍是0~0.5,如果乘以2剛好是透明度的範圍(0~1),這樣就可以實現一個簡單的漸變圓
2) 假設厚度為t,那麼顏色的透明度的範圍是[1, 1-t],而我們實際需要的是[1, 0],可以用圖二來表示兩個線性關係,可以得到兩個方程式
方程式1:y = -x + 1;
方程式2:y = -t + 1;
現在我們知道方程式二中的y的值(像素點到中心的距離distance),通過解方程式就可以得到方程式1中所對應的透明度的值為 (distance - 1) / t + 1;
/** * 添加一個擴散面 */ function addDiffuseCircle() { const geometry = new THREE.CircleGeometry(3, 48); // 繪製一個漸變圈寬度可控的圓弧 const vertex = ` varying vec2 vUv; void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); } `; const frag = ` uniform float uTime; uniform float uThickness; varying vec2 vUv; void main() { // 使用UV坐標計算各個點到中心點的距離,需要減0.5,將圓心移動到(0.5, 0.5)的位置,半徑為0.5,透明度範圍為0~1,所以需要乘以2 float distance = length(vUv - 0.5) * 2.0; float opacity = (distance - 1.0) / uThickness + 1.0; gl_FragColor = vec4(0.0, 1.0, 1.0, opacity); } `; const uniforms = { uThickness: { value: 0.8, range: [0, 1] }, // 漸變色的厚度 uSpeed: { value: 0.5, range: [0, 5] }, uTime: { value: 0.01 }, }; setGui(uniforms); setShader(geometry, vertex, frag, [0,0,0], uniforms); } function setShader(geometry, vertexShader, fragmentShader, position = [0, 0, 0], uniforms = {}, isLine = false) { material = new THREE.ShaderMaterial({ vertexShader: vertexShader, fragmentShader: fragmentShader, side: THREE.DoubleSide, uniforms: uniforms, transparent: true, // blending: THREE.AdditiveBlending, // 多個元素的顏色相互疊加,顏色可能會變亮,會疊加setClearColor設置的背景色 }); material.depthTest = true; material.depthWrite = false; let planeMesh = new THREE.Mesh(geometry, material); if (isLine) { planeMesh = new THREE.Points(geometry, material); } planeMesh.position.x = position[0]; planeMesh.position.y = position[1]; planeMesh.position.z = position[2]; planeMesh.rotateX(Math.PI / 2); scene.add(planeMesh); }厚度可變的漸變圓
2. 半徑自動縮放的漸變圓
/** * 添加一個擴散面 */ function addDiffuseCircle() { const geometry = new THREE.CircleGeometry(3, 48); // 創建一個大小可控的漸變圓弧 const vertex = ` varying vec2 vUv; void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); } `; const frag = ` uniform float uTime; uniform float uThickness; uniform float uSpeed; varying vec2 vUv; void main() { // 使用UV坐標計算各個點到中心點的距離,需要減0.5,將圓心移動到(0.5, 0.5)的位置,半徑為0.5,透明度範圍為0~1,所以需要乘以2 // 假設從內像外開始擴散,距離和時間關係是 最內部: 距離0,時間0;最外部:距離1,時間1,如果用1-時間的話, // 所以此時1-時間+距離和樣例1中的透明度相同 float timeDis = fract(uTime * uSpeed); float distance = length(vUv - 0.5) * 2.0; if (timeDis < distance) { gl_FragColor = vec4(0.0, 0.0, 1.0, 0.0); } else { float opacity = (1.0 - timeDis + distance - 1.0) / uThickness + 1.0; gl_FragColor = vec4(0.0, 1.0, 1.0, opacity); } } `; const uniforms = { uThickness: { value: 0.8, range: [0, 1] }, // 漸變色的厚度 uSpeed: { value: 0.5, range: [0, 5] }, uTime: { value: 0.01 }, }; setShader(geometry, vertex, frag, [0,0,0], uniforms); } function setShader(geometry, vertexShader, fragmentShader, position = [0, 0, 0], uniforms = {}, isLine = false) { material = new THREE.ShaderMaterial({ vertexShader: vertexShader, fragmentShader: fragmentShader, side: THREE.DoubleSide, uniforms: uniforms, transparent: true, // blending: THREE.AdditiveBlending, // 多個元素的顏色相互疊加,顏色可能會變亮,會疊加setClearColor設置的背景色 }); material.depthTest = true; material.depthWrite = false; let planeMesh = new THREE.Mesh(geometry, material); if (isLine) { planeMesh = new THREE.Points(geometry, material); } planeMesh.position.x = position[0]; planeMesh.position.y = position[1]; planeMesh.position.z = position[2]; planeMesh.rotateX(Math.PI / 2); scene.add(planeMesh); }自動縮放的漸變圓