在循环中逐渐显示圆圈
Progressively reveal circle in loop
我目前有这个:Codepen(下面的嵌入代码段)。
我希望能够在扫描线之后逐渐改变颜色,就像这样:
在网上搜索并试图在 three.js / WebGL 中找到一些东西后,我未能获得我想要的东西,可能是因为我不太清楚我应该搜索什么。
你能帮我解决问题或者给我指明正确的方向吗?
我考虑了以下可能性:
- 有第二个绿色圆圈和一个在扫描线后显示它的动态遮罩。
- 如何在 three.js 中创建一个可以显示角度 θ 增加的切片的蒙版?
- CircleGeometry 具有创建角度为 θ 的切片的参数。但是不断改变我的网格的几何形状听起来不是很聪明。
- 在扫描线通过后添加微小的圆形切片,这样会产生显示圆形的印象,但实际上它只是添加微小的切片。
P.S。 - 我正在使用 three.js 因为稍后这个项目会有 3d 元素。
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const innerRadius = 1;
const outerRadius = innerRadius*2;
const barLenght = innerRadius;
// create scanline
const outerMaterial = new THREE.MeshBasicMaterial({color: 0x34ebd2});
const outerCircle = new THREE.Mesh(new THREE.CircleGeometry(outerRadius, 60), outerMaterial);
scene.add(outerCircle);
// Create innerCircle
const innerMaterial = new THREE.MeshBasicMaterial({color: 0x0000ff});
const innerCircle = new THREE.Mesh(new THREE.CircleGeometry(innerRadius, 60), innerMaterial);
scene.add(innerCircle);
// create static line
const staticLine = new THREE.Mesh(new THREE.PlaneGeometry(0.05, barLenght), new THREE.MeshBasicMaterial({color: 0xff0000, side: THREE.DoubleSide}));
scene.add(staticLine);
// create scan line
const scanLine = new THREE.Mesh(new THREE.PlaneGeometry(0.05, barLenght), new THREE.MeshBasicMaterial({color: 0xff0000, side: THREE.DoubleSide}));
scene.add(scanLine);
// position static line
staticLine.position.y = innerRadius + barLenght/2;
// position scan line
scanLine.position.y = innerRadius + barLenght/2;
// create pivot to rotate dateline
const pivot = new THREE.Group();
pivot.position.set( 0.0, 0.0, 0 );
pivot.add(scanLine);
scene.add(pivot);
camera.position.z = 5;
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
pivot.rotation.z -= 0.005;
}
animate();
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
这是渐进弧的二合一示例(片段和顶点着色器实现),只是从头开始。以此为起点。
body{
overflow: hidden;
margin: 0;
}
<script type="module">
import * as THREE from "https://cdn.skypack.dev/three@0.132.2";
import {OrbitControls} from "https://cdn.skypack.dev/three@0.132.2/examples/jsm/controls/OrbitControls.js";
let scene = new THREE.Scene();
let camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 1, 1000);
camera.position.set(-5, 3, 8);
let renderer = new THREE.WebGLRenderer();
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);
let controls = new OrbitControls(camera, renderer.domElement);
// fragment shader option
let g = new THREE.CircleGeometry(5, 64);
let m = new THREE.MeshBasicMaterial({
color: 0x7f7f7f,
side: THREE.DoubleSide,
onBeforeCompile: shader => {
shader.uniforms.time = m.userData.uniforms.time;
shader.uniforms.currColor = m.userData.uniforms.currColor;
shader.uniforms.prevColor = m.userData.uniforms.prevColor;
shader.fragmentShader = `
uniform float time;
uniform vec3 currColor;
uniform vec3 prevColor;
${shader.fragmentShader}
`.replace(
`#include <color_fragment>`,
`#include <color_fragment>
vec2 cUv = vUv - 0.5;
float dist = length(cUv);
vec3 col = prevColor;
float ang = mod(atan(cUv.y, cUv.x) + PI * 3.5, PI2);
float aRatio = 1. - ang / PI2;
float slice = 1. - step(time, aRatio);
col = mix(prevColor, currColor, slice);
float innerCirc = 1. - step(0.25, dist);
col = mix(col, diffuseColor.rgb, innerCirc);
diffuseColor.rgb = col;
`
);
console.log(shader.fragmentShader);
}
})
m.defines = {
"USE_UV": " "
};
m.userData = {
uniforms: {
time: {
value: 0.5
},
currColor: {
value: new THREE.Color(0xff00ff)
},
prevColor: {
value: new THREE.Color(0x00ffff)
}
}
}
let o = new THREE.Mesh(g, m);
scene.add(o);
// vertex shader option
let g2 = new THREE.PlaneGeometry(1, 1, 180, 1);
let m2 = new THREE.MeshBasicMaterial({
color: 0xffff00,
wireframe: true,
onBeforeCompile: shader => {
shader.uniforms.rMin = m2.userData.uniforms.rMin;
shader.uniforms.rMax = m2.userData.uniforms.rMax;
shader.uniforms.arcRatio = m2.userData.uniforms.arcRatio;
shader.vertexShader = `
uniform float rMin;
uniform float rMax;
uniform float arcRatio;
mat2 rot(float a){return mat2(cos(a), -sin(a), sin(a), cos(a));}
${shader.vertexShader}
`.replace(
`#include <begin_vertex>`,
`#include <begin_vertex>
float rDiff = rMax - rMin;
float r = rMin + (rDiff * uv.y);
float ang = PI2 * uv.x * arcRatio;
transformed.xy = rot(ang) * vec2(0., r);
`
);
console.log(shader.vertexShader);
}
});
m2.userData = {
uniforms: {
rMin: {value: 2.5},
rMax: {value: 5},
arcRatio: {value: 0.25} // 0..1
}
}
let o2 = new THREE.Mesh(g2, m2);
o2.position.z = 2;
scene.add(o2);
let clock = new THREE.Clock();
window.addEventListener("resize", onResize);
renderer.setAnimationLoop(_ => {
let t = (clock.getElapsedTime() * 0.1) % 1;
m.userData.uniforms.time.value = t;
m2.userData.uniforms.arcRatio.value = t;
renderer.render(scene, camera);
})
function onResize(){
camera.aspect = innerWidth / innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(innerWidth, innerHeight);
}
</script>
我目前有这个:Codepen(下面的嵌入代码段)。
我希望能够在扫描线之后逐渐改变颜色,就像这样:
在网上搜索并试图在 three.js / WebGL 中找到一些东西后,我未能获得我想要的东西,可能是因为我不太清楚我应该搜索什么。
你能帮我解决问题或者给我指明正确的方向吗? 我考虑了以下可能性:
- 有第二个绿色圆圈和一个在扫描线后显示它的动态遮罩。
- 如何在 three.js 中创建一个可以显示角度 θ 增加的切片的蒙版?
- CircleGeometry 具有创建角度为 θ 的切片的参数。但是不断改变我的网格的几何形状听起来不是很聪明。
- 在扫描线通过后添加微小的圆形切片,这样会产生显示圆形的印象,但实际上它只是添加微小的切片。
P.S。 - 我正在使用 three.js 因为稍后这个项目会有 3d 元素。
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const innerRadius = 1;
const outerRadius = innerRadius*2;
const barLenght = innerRadius;
// create scanline
const outerMaterial = new THREE.MeshBasicMaterial({color: 0x34ebd2});
const outerCircle = new THREE.Mesh(new THREE.CircleGeometry(outerRadius, 60), outerMaterial);
scene.add(outerCircle);
// Create innerCircle
const innerMaterial = new THREE.MeshBasicMaterial({color: 0x0000ff});
const innerCircle = new THREE.Mesh(new THREE.CircleGeometry(innerRadius, 60), innerMaterial);
scene.add(innerCircle);
// create static line
const staticLine = new THREE.Mesh(new THREE.PlaneGeometry(0.05, barLenght), new THREE.MeshBasicMaterial({color: 0xff0000, side: THREE.DoubleSide}));
scene.add(staticLine);
// create scan line
const scanLine = new THREE.Mesh(new THREE.PlaneGeometry(0.05, barLenght), new THREE.MeshBasicMaterial({color: 0xff0000, side: THREE.DoubleSide}));
scene.add(scanLine);
// position static line
staticLine.position.y = innerRadius + barLenght/2;
// position scan line
scanLine.position.y = innerRadius + barLenght/2;
// create pivot to rotate dateline
const pivot = new THREE.Group();
pivot.position.set( 0.0, 0.0, 0 );
pivot.add(scanLine);
scene.add(pivot);
camera.position.z = 5;
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
pivot.rotation.z -= 0.005;
}
animate();
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
这是渐进弧的二合一示例(片段和顶点着色器实现),只是从头开始。以此为起点。
body{
overflow: hidden;
margin: 0;
}
<script type="module">
import * as THREE from "https://cdn.skypack.dev/three@0.132.2";
import {OrbitControls} from "https://cdn.skypack.dev/three@0.132.2/examples/jsm/controls/OrbitControls.js";
let scene = new THREE.Scene();
let camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 1, 1000);
camera.position.set(-5, 3, 8);
let renderer = new THREE.WebGLRenderer();
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);
let controls = new OrbitControls(camera, renderer.domElement);
// fragment shader option
let g = new THREE.CircleGeometry(5, 64);
let m = new THREE.MeshBasicMaterial({
color: 0x7f7f7f,
side: THREE.DoubleSide,
onBeforeCompile: shader => {
shader.uniforms.time = m.userData.uniforms.time;
shader.uniforms.currColor = m.userData.uniforms.currColor;
shader.uniforms.prevColor = m.userData.uniforms.prevColor;
shader.fragmentShader = `
uniform float time;
uniform vec3 currColor;
uniform vec3 prevColor;
${shader.fragmentShader}
`.replace(
`#include <color_fragment>`,
`#include <color_fragment>
vec2 cUv = vUv - 0.5;
float dist = length(cUv);
vec3 col = prevColor;
float ang = mod(atan(cUv.y, cUv.x) + PI * 3.5, PI2);
float aRatio = 1. - ang / PI2;
float slice = 1. - step(time, aRatio);
col = mix(prevColor, currColor, slice);
float innerCirc = 1. - step(0.25, dist);
col = mix(col, diffuseColor.rgb, innerCirc);
diffuseColor.rgb = col;
`
);
console.log(shader.fragmentShader);
}
})
m.defines = {
"USE_UV": " "
};
m.userData = {
uniforms: {
time: {
value: 0.5
},
currColor: {
value: new THREE.Color(0xff00ff)
},
prevColor: {
value: new THREE.Color(0x00ffff)
}
}
}
let o = new THREE.Mesh(g, m);
scene.add(o);
// vertex shader option
let g2 = new THREE.PlaneGeometry(1, 1, 180, 1);
let m2 = new THREE.MeshBasicMaterial({
color: 0xffff00,
wireframe: true,
onBeforeCompile: shader => {
shader.uniforms.rMin = m2.userData.uniforms.rMin;
shader.uniforms.rMax = m2.userData.uniforms.rMax;
shader.uniforms.arcRatio = m2.userData.uniforms.arcRatio;
shader.vertexShader = `
uniform float rMin;
uniform float rMax;
uniform float arcRatio;
mat2 rot(float a){return mat2(cos(a), -sin(a), sin(a), cos(a));}
${shader.vertexShader}
`.replace(
`#include <begin_vertex>`,
`#include <begin_vertex>
float rDiff = rMax - rMin;
float r = rMin + (rDiff * uv.y);
float ang = PI2 * uv.x * arcRatio;
transformed.xy = rot(ang) * vec2(0., r);
`
);
console.log(shader.vertexShader);
}
});
m2.userData = {
uniforms: {
rMin: {value: 2.5},
rMax: {value: 5},
arcRatio: {value: 0.25} // 0..1
}
}
let o2 = new THREE.Mesh(g2, m2);
o2.position.z = 2;
scene.add(o2);
let clock = new THREE.Clock();
window.addEventListener("resize", onResize);
renderer.setAnimationLoop(_ => {
let t = (clock.getElapsedTime() * 0.1) % 1;
m.userData.uniforms.time.value = t;
m2.userData.uniforms.arcRatio.value = t;
renderer.render(scene, camera);
})
function onResize(){
camera.aspect = innerWidth / innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(innerWidth, innerHeight);
}
</script>