动画线框线
Animated wireframe lines
我很好奇有没有人知道如何实现这样的线框"fade in"逐行绘制效果?
可能不准确,但类似于这样的 svg 动画,使其更清晰,更易于可视化 https://maxwellito.github.io/vivus/
此处为 Webgl 示例 https://www.orano.group/experience/innovation/en/slider 如果您在幻灯片之间切换。
您需要按照您希望绘制的顺序为每个要绘制的元素指定一个编号。例如,如果你想绘制一个线框,按照你想要绘制的顺序为每个顶点传递一个数字,将该数字从顶点着色器传递到片段着色器,然后传递一个时间。如果数字大于数字discard
(或以其他方式不绘制)
示例:
'use strict';
/* global THREE */
function main() {
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({canvas: canvas});
const fov = 40;
const aspect = 2; // the canvas default
const near = 0.1;
const far = 1000;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.z = 25;
const scene = new THREE.Scene();
scene.background = new THREE.Color('white');
const objects = [];
{
const width = 8;
const height = 8;
const depth = 8;
// using edges just to get rid of the lines triangles
const geometry = new THREE.EdgesGeometry(new THREE.BoxBufferGeometry(width, height, depth));
const numVertices = geometry.getAttribute('position').count;
const counts = new Float32Array(numVertices);
// every 2 points is one line segment so we want the numbers to go
// 0, 1, 1, 2, 2, 3, 3, 4, 4, 5 etc
const numSegments = numVertices / 2;
for (let seg = 0; seg < numSegments; ++seg) {
const off = seg * 2;
counts[off + 0] = seg;
counts[off + 1] = seg + 1;
}
const itemSize = 1;
const normalized = false;
const colorAttrib = new THREE.BufferAttribute(counts, itemSize, normalized); geometry.addAttribute('count', colorAttrib);
const timeLineShader = {
uniforms: {
color: { value: new THREE.Color('red'), },
time: { value: 0 },
},
vertexShader: `
attribute float count;
varying float vCount;
void main() {
vCount = count;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1);
}
`,
fragmentShader: `
#include <common>
varying float vCount;
uniform vec3 color;
uniform float time;
void main() {
if (vCount > time) {
discard;
}
gl_FragColor = vec4(color, 1);
}
`,
};
const material = new THREE.ShaderMaterial(timeLineShader);
const mesh = new THREE.LineSegments(geometry, material);
scene.add(mesh);
objects.push(mesh);
}
function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
function render(time) {
time *= 0.001;
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
objects.forEach((obj, ndx) => {
const speed = .1 + ndx * .05;
const rot = time * speed;
obj.rotation.x = rot;
obj.rotation.y = rot;
obj.material.uniforms.time.value = (time * 4) % 15;
});
renderer.render(scene, camera);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
main();
body { margin: 0; }
#c { width: 100vw; height: 100vh; display: block; }
<canvas id="c"></canvas>
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r98/three.min.js"></script>
如果你想连续绘制多个对象只需调整每个对象的时间
'use strict';
/* global THREE */
function main() {
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({canvas: canvas});
const fov = 40;
const aspect = 2; // the canvas default
const near = 0.1;
const far = 1000;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.z = 15;
const scene = new THREE.Scene();
scene.background = new THREE.Color('white');
const objects = [];
{
const width = 2;
const height = 2;
const depth = 2;
// using edges just to get rid of the lines triangles
const geometry = new THREE.EdgesGeometry(new THREE.BoxBufferGeometry(width, height, depth));
const numVertices = geometry.getAttribute('position').count;
const counts = new Float32Array(numVertices);
// every 2 points is one line segment so we want the numbers to go
// 0, 1, 1, 2, 2, 3, 3, 4, 4, 5 etc
const numSegments = numVertices / 2;
for (let seg = 0; seg < numSegments; ++seg) {
const off = seg * 2;
counts[off + 0] = seg;
counts[off + 1] = seg + 1;
}
const itemSize = 1;
const normalized = false;
const colorAttrib = new THREE.BufferAttribute(counts, itemSize, normalized); geometry.addAttribute('count', colorAttrib);
const timeLineShader = {
vertexShader: `
attribute float count;
varying float vCount;
void main() {
vCount = count;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1);
}
`,
fragmentShader: `
#include <common>
varying float vCount;
uniform vec3 color;
uniform float time;
void main() {
if (vCount > time) {
discard;
}
gl_FragColor = vec4(color, 1);
}
`,
};
for (let x = -2; x <= 2; x += 1) {
timeLineShader.uniforms = {
color: { value: new THREE.Color('red'), },
time: { value: 0 },
};
const material = new THREE.ShaderMaterial(timeLineShader);
const mesh = new THREE.LineSegments(geometry, material);
scene.add(mesh);
mesh.position.x = x * 4;
objects.push(mesh);
}
}
function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
function render(time) {
time *= 0.001;
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
objects.forEach((obj, ndx) => {
const rotSpeed = .1;
const rot = time * rotSpeed;
obj.rotation.x = rot;
obj.rotation.y = rot;
const segmentsPer = 12;
const speed = 8;
const totalTime = segmentsPer * objects.length + 5 * speed;
obj.material.uniforms.time.value = ((time * speed) % totalTime) - ndx * segmentsPer;
});
renderer.render(scene, camera);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
main();
body { margin: 0; }
#c { width: 100vw; height: 100vh; display: block; }
<canvas id="c"></canvas>
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r98/three.min.js"></script>
请注意,使用计数将使每个片段花费相同的时间出现。如果您希望它们按距离花费更长的时间而不是向每个段添加 1,则需要将距离添加到下一个点
distanceSoFar = 0;
for each segment
data.push(distanceSoFar);
distanceSoFar += distance(segmentStartPosition, segmentEndPosition);
data.push(distanceSoFar);
}
我很好奇有没有人知道如何实现这样的线框"fade in"逐行绘制效果?
可能不准确,但类似于这样的 svg 动画,使其更清晰,更易于可视化 https://maxwellito.github.io/vivus/
此处为 Webgl 示例 https://www.orano.group/experience/innovation/en/slider 如果您在幻灯片之间切换。
您需要按照您希望绘制的顺序为每个要绘制的元素指定一个编号。例如,如果你想绘制一个线框,按照你想要绘制的顺序为每个顶点传递一个数字,将该数字从顶点着色器传递到片段着色器,然后传递一个时间。如果数字大于数字discard
(或以其他方式不绘制)
示例:
'use strict';
/* global THREE */
function main() {
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({canvas: canvas});
const fov = 40;
const aspect = 2; // the canvas default
const near = 0.1;
const far = 1000;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.z = 25;
const scene = new THREE.Scene();
scene.background = new THREE.Color('white');
const objects = [];
{
const width = 8;
const height = 8;
const depth = 8;
// using edges just to get rid of the lines triangles
const geometry = new THREE.EdgesGeometry(new THREE.BoxBufferGeometry(width, height, depth));
const numVertices = geometry.getAttribute('position').count;
const counts = new Float32Array(numVertices);
// every 2 points is one line segment so we want the numbers to go
// 0, 1, 1, 2, 2, 3, 3, 4, 4, 5 etc
const numSegments = numVertices / 2;
for (let seg = 0; seg < numSegments; ++seg) {
const off = seg * 2;
counts[off + 0] = seg;
counts[off + 1] = seg + 1;
}
const itemSize = 1;
const normalized = false;
const colorAttrib = new THREE.BufferAttribute(counts, itemSize, normalized); geometry.addAttribute('count', colorAttrib);
const timeLineShader = {
uniforms: {
color: { value: new THREE.Color('red'), },
time: { value: 0 },
},
vertexShader: `
attribute float count;
varying float vCount;
void main() {
vCount = count;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1);
}
`,
fragmentShader: `
#include <common>
varying float vCount;
uniform vec3 color;
uniform float time;
void main() {
if (vCount > time) {
discard;
}
gl_FragColor = vec4(color, 1);
}
`,
};
const material = new THREE.ShaderMaterial(timeLineShader);
const mesh = new THREE.LineSegments(geometry, material);
scene.add(mesh);
objects.push(mesh);
}
function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
function render(time) {
time *= 0.001;
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
objects.forEach((obj, ndx) => {
const speed = .1 + ndx * .05;
const rot = time * speed;
obj.rotation.x = rot;
obj.rotation.y = rot;
obj.material.uniforms.time.value = (time * 4) % 15;
});
renderer.render(scene, camera);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
main();
body { margin: 0; }
#c { width: 100vw; height: 100vh; display: block; }
<canvas id="c"></canvas>
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r98/three.min.js"></script>
如果你想连续绘制多个对象只需调整每个对象的时间
'use strict';
/* global THREE */
function main() {
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({canvas: canvas});
const fov = 40;
const aspect = 2; // the canvas default
const near = 0.1;
const far = 1000;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.z = 15;
const scene = new THREE.Scene();
scene.background = new THREE.Color('white');
const objects = [];
{
const width = 2;
const height = 2;
const depth = 2;
// using edges just to get rid of the lines triangles
const geometry = new THREE.EdgesGeometry(new THREE.BoxBufferGeometry(width, height, depth));
const numVertices = geometry.getAttribute('position').count;
const counts = new Float32Array(numVertices);
// every 2 points is one line segment so we want the numbers to go
// 0, 1, 1, 2, 2, 3, 3, 4, 4, 5 etc
const numSegments = numVertices / 2;
for (let seg = 0; seg < numSegments; ++seg) {
const off = seg * 2;
counts[off + 0] = seg;
counts[off + 1] = seg + 1;
}
const itemSize = 1;
const normalized = false;
const colorAttrib = new THREE.BufferAttribute(counts, itemSize, normalized); geometry.addAttribute('count', colorAttrib);
const timeLineShader = {
vertexShader: `
attribute float count;
varying float vCount;
void main() {
vCount = count;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1);
}
`,
fragmentShader: `
#include <common>
varying float vCount;
uniform vec3 color;
uniform float time;
void main() {
if (vCount > time) {
discard;
}
gl_FragColor = vec4(color, 1);
}
`,
};
for (let x = -2; x <= 2; x += 1) {
timeLineShader.uniforms = {
color: { value: new THREE.Color('red'), },
time: { value: 0 },
};
const material = new THREE.ShaderMaterial(timeLineShader);
const mesh = new THREE.LineSegments(geometry, material);
scene.add(mesh);
mesh.position.x = x * 4;
objects.push(mesh);
}
}
function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
function render(time) {
time *= 0.001;
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
objects.forEach((obj, ndx) => {
const rotSpeed = .1;
const rot = time * rotSpeed;
obj.rotation.x = rot;
obj.rotation.y = rot;
const segmentsPer = 12;
const speed = 8;
const totalTime = segmentsPer * objects.length + 5 * speed;
obj.material.uniforms.time.value = ((time * speed) % totalTime) - ndx * segmentsPer;
});
renderer.render(scene, camera);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
main();
body { margin: 0; }
#c { width: 100vw; height: 100vh; display: block; }
<canvas id="c"></canvas>
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r98/three.min.js"></script>
请注意,使用计数将使每个片段花费相同的时间出现。如果您希望它们按距离花费更长的时间而不是向每个段添加 1,则需要将距离添加到下一个点
distanceSoFar = 0;
for each segment
data.push(distanceSoFar);
distanceSoFar += distance(segmentStartPosition, segmentEndPosition);
data.push(distanceSoFar);
}