three.js 点精灵渲染问题
Point sprite rendering issues with three.js
我目前正在开展一个项目,该项目将通过渲染过多的动画描边圆圈来在浏览器上可视化数据。我开始评估 3D 库并最终尝试使用 three.js 创建概念验证应用程序。它能够在我的 1440p 显示器上以 60 fps 的速度动画和渲染多达 150,000 个点精灵。在您开始查看细节之前,一切看起来都很棒。它有两个渲染问题:
- 即使关闭动画也会产生奇怪的水平线
- 当您转动相机时,重叠点精灵的透明区域将显示背景而不是下面的点精灵
这是概念应用的证明:https://jsfiddle.net/tcpvfbsd/1/
var renderer, scene, camera, controls;
var points;
var stats;
var controls;
var worldWidth = 200;
var worldRadius = worldWidth / 2;
var patchSize = 10;
var pointsAmount = 100000;
function init() {
renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
scene = new THREE.Scene();
scene.background = new THREE.Color(0x1d252d);
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 2000);
camera.position.set(0, worldWidth * 1.5, 0);
controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.minDistance = 100;
controls.maxDistance = 1100;
scene.add(new THREE.GridHelper(2 * worldRadius, 2 * worldWidth / patchSize, 0x444444, 0x444444));
var geometry = new THREE.BufferGeometry();
var positions = new Float32Array(pointsAmount * 3);
var rotations = new Float32Array(pointsAmount * 1);
for (var i = 0; i < pointsAmount; i++) {
positions[i] = 0;
positions[i + 1] = 0;
positions[i + 2] = 0;
rotations[i] = 2 * Math.PI * Math.random();
}
controls = new function() {
this.speed = 10;
this.amount = 10;
};
var gui = new dat.GUI();
gui.add(controls, 'speed', 0, 100);
//gui.add(controls, 'amount', 0, 10000).step(1);;
geometry.addAttribute('position', new THREE.BufferAttribute(positions, 3));
geometry.addAttribute('rotation', new THREE.BufferAttribute(rotations, 1));
var loader = new THREE.TextureLoader();
loader.load('//i.imgur.com/AmQQnZc.png', function(texture) {
var material = new THREE.PointsMaterial({
size: 5,
transparent: true,
map: texture
});
points = new THREE.Points(geometry, material);
scene.add(points);
stats = new Stats();
document.body.appendChild(stats.dom);
animate();
});
}
function animate() {
requestAnimationFrame(animate);
var position = points.geometry.attributes.position;
var count = position.count;
var rotation = points.geometry.attributes.rotation;
var speed = patchSize * controls.speed / 100;
if (speed > 0) {
for (var i = 0; i < count; i++) {
var wiggle = Math.random() > 0.9 ? THREE.Math.randFloat(-0.1, 0.1) : 0;
var theta = rotation.getX(i) + wiggle;
let dx = speed * Math.cos(theta);
let dz = speed * Math.sin(theta);
var x0 = position.getX(i);
var z0 = position.getZ(i);
var x = THREE.Math.clamp(x0 + dx, -worldRadius, worldRadius);
var z = THREE.Math.clamp(z0 + dz, -worldRadius, worldRadius);
if (Math.abs(x) === worldRadius) dx = -dx;
if (Math.abs(z) === worldRadius) dz = -dz;
position.setX(i, x);
position.setZ(i, z);
position.setY(i, 1);
rotation.setX(i, Math.atan2(dz, dx));
}
}
position.needsUpdate = true;
stats.update();
renderer.render(scene, camera);
}
init();
查看问题的最佳方法是等待几秒钟让点精灵扩散到区域,使用右上角的速度控制暂停动画,并使用鼠标左键转动和旋转相机。
- 使用值为 0.5 的 alphaTest 在不影响其他圆的渲染的情况下剪掉圆的角
- 将透明值设置为 false 消除了将 alphaTest 设置为 0.5 后出现的错误淡入淡出效果
- 即使导致淡入淡出效果的透明设置被禁用,向纹理本身添加淡入淡出也会使圆圈的边界变得平滑
- 将 y 轴上的位置随机化 0.01 个单位,消除了奇怪的水平线
我目前正在开展一个项目,该项目将通过渲染过多的动画描边圆圈来在浏览器上可视化数据。我开始评估 3D 库并最终尝试使用 three.js 创建概念验证应用程序。它能够在我的 1440p 显示器上以 60 fps 的速度动画和渲染多达 150,000 个点精灵。在您开始查看细节之前,一切看起来都很棒。它有两个渲染问题:
- 即使关闭动画也会产生奇怪的水平线
- 当您转动相机时,重叠点精灵的透明区域将显示背景而不是下面的点精灵
这是概念应用的证明:https://jsfiddle.net/tcpvfbsd/1/
var renderer, scene, camera, controls;
var points;
var stats;
var controls;
var worldWidth = 200;
var worldRadius = worldWidth / 2;
var patchSize = 10;
var pointsAmount = 100000;
function init() {
renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
scene = new THREE.Scene();
scene.background = new THREE.Color(0x1d252d);
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 2000);
camera.position.set(0, worldWidth * 1.5, 0);
controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.minDistance = 100;
controls.maxDistance = 1100;
scene.add(new THREE.GridHelper(2 * worldRadius, 2 * worldWidth / patchSize, 0x444444, 0x444444));
var geometry = new THREE.BufferGeometry();
var positions = new Float32Array(pointsAmount * 3);
var rotations = new Float32Array(pointsAmount * 1);
for (var i = 0; i < pointsAmount; i++) {
positions[i] = 0;
positions[i + 1] = 0;
positions[i + 2] = 0;
rotations[i] = 2 * Math.PI * Math.random();
}
controls = new function() {
this.speed = 10;
this.amount = 10;
};
var gui = new dat.GUI();
gui.add(controls, 'speed', 0, 100);
//gui.add(controls, 'amount', 0, 10000).step(1);;
geometry.addAttribute('position', new THREE.BufferAttribute(positions, 3));
geometry.addAttribute('rotation', new THREE.BufferAttribute(rotations, 1));
var loader = new THREE.TextureLoader();
loader.load('//i.imgur.com/AmQQnZc.png', function(texture) {
var material = new THREE.PointsMaterial({
size: 5,
transparent: true,
map: texture
});
points = new THREE.Points(geometry, material);
scene.add(points);
stats = new Stats();
document.body.appendChild(stats.dom);
animate();
});
}
function animate() {
requestAnimationFrame(animate);
var position = points.geometry.attributes.position;
var count = position.count;
var rotation = points.geometry.attributes.rotation;
var speed = patchSize * controls.speed / 100;
if (speed > 0) {
for (var i = 0; i < count; i++) {
var wiggle = Math.random() > 0.9 ? THREE.Math.randFloat(-0.1, 0.1) : 0;
var theta = rotation.getX(i) + wiggle;
let dx = speed * Math.cos(theta);
let dz = speed * Math.sin(theta);
var x0 = position.getX(i);
var z0 = position.getZ(i);
var x = THREE.Math.clamp(x0 + dx, -worldRadius, worldRadius);
var z = THREE.Math.clamp(z0 + dz, -worldRadius, worldRadius);
if (Math.abs(x) === worldRadius) dx = -dx;
if (Math.abs(z) === worldRadius) dz = -dz;
position.setX(i, x);
position.setZ(i, z);
position.setY(i, 1);
rotation.setX(i, Math.atan2(dz, dx));
}
}
position.needsUpdate = true;
stats.update();
renderer.render(scene, camera);
}
init();
查看问题的最佳方法是等待几秒钟让点精灵扩散到区域,使用右上角的速度控制暂停动画,并使用鼠标左键转动和旋转相机。
- 使用值为 0.5 的 alphaTest 在不影响其他圆的渲染的情况下剪掉圆的角
- 将透明值设置为 false 消除了将 alphaTest 设置为 0.5 后出现的错误淡入淡出效果
- 即使导致淡入淡出效果的透明设置被禁用,向纹理本身添加淡入淡出也会使圆圈的边界变得平滑
- 将 y 轴上的位置随机化 0.01 个单位,消除了奇怪的水平线