问题描述
要求
通过webgl渲染非常大的几何图形(> 100万个三角形),并通过用户交互更改某些三角形的颜色。应保持60fps。无法简化几何图形,应按原样使用所有三角形进行渲染。
问题
渲染周期花费太多时间,有时长达100ms。
我尝试过的事情
我尝试通过THREE.js缓冲的几何体渲染场景,将共享相同颜色的三角形分组(只要它们的索引连续),然后通过materialIndex
组中的材料进行重用。
//Declare three.js variables
let camera,scene,renderer,mesh
//assign three.js objects to each variable
function init() {
//camera
camera = new THREE.PerspectiveCamera(50,window.innerWidth / window.innerHeight);
camera.position.z = 2000;
//scene
scene = new THREE.Scene();
//renderer
renderer = new THREE.Webglrenderer({
antialias: true
});
//set the size of the renderer
renderer.setSize(window.innerWidth,window.innerHeight);
//add the renderer to the html document body
document.querySelector('.webgl').appendChild(renderer.domElement);
}
function addMesh() {
const bufferGeometries = []
for (var i = 0; i < 50000; i++) {
var geo = new THREE.BoxGeometry(15,15,15)
geo.applyMatrix4(new THREE.Matrix4().makeTranslation(Math.random() * 1500 - 500,Math.random() * 1500 - 500,0));
geo.rotateX(Math.random() * 1)
geo.rotateY(Math.random() * 1)
bufferGeometries.push(new THREE.BufferGeometry().fromGeometry(geo))
}
const mergedBufferGeometries = mergeBufferGeometries(bufferGeometries,true)
mesh = new THREE.Mesh(mergedBufferGeometries,new THREE.MeshnormalMaterial());
mesh.material = [0,1,2,3,4,5,6,7,8,9].map((value) => new THREE.MeshphongMaterial({
emissive: new THREE.Color(`hsl(${value * i},100%,50%)`),side: THREE.DoubleSide,polygonOffset: true,polygonOffsetFactor: 1,polygonOffsetUnits: 1,transparent: false,depthWrite: false
}))
mesh.geometry.groups = mesh.geometry.groups.map(group => ({
...group,materialIndex: 0
})) // assign the first material too all the groups
scene.add(mesh);
}
window.animate = function() {
mesh.geometry.groups = mesh.geometry.groups.map((group,i) =>
i < 100 ? // assign random materials to the first 1,00 items
({
...group,materialIndex: Math.floor(Math.random() * 10)
}) :
group
)
performance.mark('a');
render()
performance.measure('duration','a')
const entries = performance.getEntriesByType("measure")
document.getElementById('time').innerText = Math.round(entries[0].duration)
performance.clearMeasures()
performance.clearMarks()
}
function render() {
//render the scene
renderer.render(scene,camera);
}
init();
addMesh();
render();
window.animate();// assign random colors one first time
// below is copied from https://github.com/mrdoob/three.js/blob/dev/examples/js/utils/BufferGeometryUtils.js
function mergeBufferAttributes(attributes) {
var TypedArray;
var itemSize;
var normalized;
var arrayLength = 0;
for (var i = 0; i < attributes.length; ++i) {
var attribute = attributes[i];
if (attribute.isInterleavedBufferAttribute) {
console.error('THREE.BufferGeometryUtils: .mergeBufferAttributes() Failed. InterleavedBufferAttributes are not supported.');
return null;
}
if (TypedArray === undefined) TypedArray = attribute.array.constructor;
if (TypedArray !== attribute.array.constructor) {
console.error('THREE.BufferGeometryUtils: .mergeBufferAttributes() Failed. BufferAttribute.array must be of consistent array types across matching attributes.');
return null;
}
if (itemSize === undefined) itemSize = attribute.itemSize;
if (itemSize !== attribute.itemSize) {
console.error('THREE.BufferGeometryUtils: .mergeBufferAttributes() Failed. BufferAttribute.itemSize must be consistent across matching attributes.');
return null;
}
if (normalized === undefined) normalized = attribute.normalized;
if (normalized !== attribute.normalized) {
console.error('THREE.BufferGeometryUtils: .mergeBufferAttributes() Failed. BufferAttribute.normalized must be consistent across matching attributes.');
return null;
}
arrayLength += attribute.array.length;
}
var array = new TypedArray(arrayLength);
var offset = 0;
for (var i = 0; i < attributes.length; ++i) {
array.set(attributes[i].array,offset);
offset += attributes[i].array.length;
}
return new THREE.BufferAttribute(array,itemSize,normalized);
}
function mergeBufferGeometries(geometries,useGroups) {
var isIndexed = geometries[0].index !== null;
var attributesUsed = new Set(Object.keys(geometries[0].attributes));
var morphAttributesUsed = new Set(Object.keys(geometries[0].morphAttributes));
var attributes = {};
var morphAttributes = {};
var morphTargetsRelative = geometries[0].morphTargetsRelative;
var mergedGeometry = new THREE.BufferGeometry();
var offset = 0;
for (var i = 0; i < geometries.length; ++i) {
var geometry = geometries[i];
var attributesCount = 0;
// ensure that all geometries are indexed,or none
if (isIndexed !== (geometry.index !== null)) {
console.error('THREE.BufferGeometryUtils: .mergeBufferGeometries() Failed with geometry at index ' + i + '. All geometries must have compatible attributes; make sure index attribute exists among all geometries,or in none of them.');
return null;
}
// gather attributes,exit early if they're different
for (var name in geometry.attributes) {
if (!attributesUsed.has(name)) {
console.error('THREE.BufferGeometryUtils: .mergeBufferGeometries() Failed with geometry at index ' + i + '. All geometries must have compatible attributes; make sure "' + name + '" attribute exists among all geometries,or in none of them.');
return null;
}
if (attributes[name] === undefined) attributes[name] = [];
attributes[name].push(geometry.attributes[name]);
attributesCount++;
}
// ensure geometries have the same number of attributes
if (attributesCount !== attributesUsed.size) {
console.error('THREE.BufferGeometryUtils: .mergeBufferGeometries() Failed with geometry at index ' + i + '. Make sure all geometries have the same number of attributes.');
return null;
}
// gather morph attributes,exit early if they're different
if (morphTargetsRelative !== geometry.morphTargetsRelative) {
console.error('THREE.BufferGeometryUtils: .mergeBufferGeometries() Failed with geometry at index ' + i + '. .morphTargetsRelative must be consistent throughout all geometries.');
return null;
}
for (var name in geometry.morphAttributes) {
if (!morphAttributesUsed.has(name)) {
console.error('THREE.BufferGeometryUtils: .mergeBufferGeometries() Failed with geometry at index ' + i + '. .morphAttributes must be consistent throughout all geometries.');
return null;
}
if (morphAttributes[name] === undefined) morphAttributes[name] = [];
morphAttributes[name].push(geometry.morphAttributes[name]);
}
// gather .userData
mergedGeometry.userData.mergedUserData = mergedGeometry.userData.mergedUserData || [];
mergedGeometry.userData.mergedUserData.push(geometry.userData);
if (useGroups) {
var count;
if (isIndexed) {
count = geometry.index.count;
} else if (geometry.attributes.position !== undefined) {
count = geometry.attributes.position.count;
} else {
console.error('THREE.BufferGeometryUtils: .mergeBufferGeometries() Failed with geometry at index ' + i + '. The geometry must have either an index or a position attribute');
return null;
}
mergedGeometry.addGroup(offset,count,i);
offset += count;
}
}
// merge indices
if (isIndexed) {
var indexOffset = 0;
var mergedindex = [];
for (var i = 0; i < geometries.length; ++i) {
var index = geometries[i].index;
for (var j = 0; j < index.count; ++j) {
mergedindex.push(index.getX(j) + indexOffset);
}
indexOffset += geometries[i].attributes.position.count;
}
mergedGeometry.setIndex(mergedindex);
}
// merge attributes
for (var name in attributes) {
var mergedAttribute = mergeBufferAttributes(attributes[name]);
if (!mergedAttribute) {
console.error('THREE.BufferGeometryUtils: .mergeBufferGeometries() Failed while trying to merge the ' + name + ' attribute.');
return null;
}
mergedGeometry.setAttribute(name,mergedAttribute);
}
// merge morph attributes
for (var name in morphAttributes) {
var numMorphTargets = morphAttributes[name][0].length;
if (numMorphTargets === 0) break;
mergedGeometry.morphAttributes = mergedGeometry.morphAttributes || {};
mergedGeometry.morphAttributes[name] = [];
for (var i = 0; i < numMorphTargets; ++i) {
var morphAttributesToMerge = [];
for (var j = 0; j < morphAttributes[name].length; ++j) {
morphAttributesToMerge.push(morphAttributes[name][j][i]);
}
var mergedMorphAttribute = mergeBufferAttributes(morphAttributesToMerge);
if (!mergedMorphAttribute) {
console.error('THREE.BufferGeometryUtils: .mergeBufferGeometries() Failed while trying to merge the ' + name + ' morphAttribute.');
return null;
}
mergedGeometry.morphAttributes[name].push(mergedMorphAttribute);
}
}
return mergedGeometry;
}
html,body {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
font-family: Arial,"Helvetica Neue",Helvetica,sans-serif;
}
.webgl {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: -1;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r119/three.min.js"></script>
<div class="webgl"></div>
<button onClick='window.animate()'>refresh colors</button>
<div style='color: white'>requied time <span id='time'>ms</span></div>
理想的解决方案
仅渲染 所需的颜色变化的三角形/像素。我尝试阅读THREE.js文档以进行部分渲染,但无济于事。我只发现setDrawRange
仅呈现某些索引,但实际上“放弃”了我不想要的其余索引。
解决方法
暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!
如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。
小编邮箱:dio#foxmail.com (将#修改为@)