我是THREEJS的新成员,过去我使用过AFRAME,CESIUM,XEOGL和BABYLONJS,但最后由于内存消耗和性能,我意识到制作CAD可视化器的最佳产品是THREEJS 。GLTF创建实例
BABYLONJS需要4分多钟才能载入一个大的GLTF文件(400MB),而THREEJS只需要30秒。 BABYLONJS的记忆力是THREEJS使用的4倍。
我知道仍然有一些问题能够从THREEJS中加载的GLTF文件创建实例(GPU),但我只需要在每个实例中更改位置和旋转角度,而不需要设置任何动画。
我试过用GLTF1.0和GLTF2.0,问题是一样的。 当我加载GLTF模型时,我得到一个场景。
从这个场景我试图从儿童数组获得缓冲区几何,但是当我试图créate一个实例它不起作用。
我的对象是静态的(根本没有动画)。
有什么办法可以创建一个Object3D的实例或从其缓冲区几何?
在BABYLONJS中,从加载的GLTF文件创建实例非常简单。
我确实需要使用实例来破坏RAM,并使用GPU来代替CPU资源。
我的场景需要加载很多次相同的对象来复合场景。
非常感谢您的支持。
我使用GLFT加载器看到的一些问题: 1.-您必须识别包含有效几何体的所有object3D。在这个例子中是第335行: var geo = data.scene.children [0] .children [0] .children [0] .children [0] .geometry 2.-合并示例不起作用。它在原始示例中不起作用。 3.-似乎实例化不提高性能都: - 10000对象,多材料 - >下4fps - 10000对象,singlematerial - >下4fps - 10000对象实例化 - >下4fps - 10000的对象,合并 - >不工作 4.-当选择了instanced时,几何图形不能正确渲染。
在这里,你已经用我的代码鸭GLTF例如:
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js webgl - interactive instances (gpu)</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<style>
body {
font-family: Monospace;
background-color: #f0f0f0;
margin: 0px;
overflow: hidden;
}
.info {
position: absolute;
background-color: black;
opacity: 0.8;
color: white;
text-align: center;
top: 0px;
width: 100%;
}
.info a {
color: #00ffff;
}
#notSupported {
width: 50%;
margin: auto;
border: 2px red solid;
margin-top: 20px;
padding: 10px;
}
</style>
</head>
<body>
<div class="info">
<a href="http://threejs.org" target="_blank" rel="noopener">three.js</a> webgl - gpu picking of geometry instances
<div id="notSupported" style="display:none">Sorry your graphics card + browser does not support hardware instancing</div>
<br/><br/>
<div>This demo compares different methods of constructing and rendering many instances of a single geometry.</div>
<br/>
<div>
<div style="display:inline-block;">
<span>number of<br/>geometry instances</span>
<br/>
<select id="instanceCount">
<option>100</option>
<option>500</option>
<option selected>1000</option>
<option>2000</option>
<option>3000</option>
<option>5000</option>
<option>10000</option>
<option>20000</option>
<option>30000</option>
<option>50000</option>
<option>100000</option>
</select>
</div>
<div style="display:inline-block;">
<span>method of<br/>construction/rendering</span>
<br/>
<select id="method">
<option>instanced</option>
<option>merged</option>
<option selected>singleMaterial</option>
<option>multiMaterial</option>
</select>
</div>
<div style="display:inline-block;">
<span>render continuously<br/>(to get fps reading)</span>
<br/>
<input id="animate" type="checkbox" />
</div>
<div style="display:inline-block;">
<span>use override material<br/>(only effects singleMaterial method)</span>
<br/>
<input id="override" type="checkbox" checked/>
</div>
<div style="display:inline-block;">
<span>construct anew<br/>(to get additional timings)</span>
<br/>
<button id="construct" type="button">do it</button>
</div>
</div>
<br/>
<div>
<span>Materials: #<span id="materialCount"></span></span>
<span>Objects: #<span id="objectCount"></span></span>
<span>Drawcalls: #<span id="drawcalls"></span></span>
<span>Construction time: <span id="initTime"></span> ms</span>
</div>
</div>
<div id="container"></div>
<script src="../build/three.js"></script>
<script src="js/controls/TrackballControls.js"></script>
<script src="js/libs/stats.min.js"></script>
<script src="js/loaders/GLTF2Loader.js"></script>
<script id="vertMerged" type="x-shader/x-vertex">
#define SHADER_NAME vertMerged
precision highp float;
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;
attribute vec3 position;
#ifdef PICKING
attribute vec3 pickingColor;
#else
attribute vec3 color;
varying vec3 vPosition;
#endif
varying vec3 vColor;
void main() {
vec3 positionEye = (modelViewMatrix * vec4(position, 1.0)).xyz;
#ifdef PICKING
vColor = pickingColor;
#else
vColor = color;
vPosition = positionEye;
#endif
gl_Position = projectionMatrix * vec4(positionEye, 1.0);
}
</script>
<script id="fragMerged" type="x-shader/x-fragment">
#define SHADER_NAME fragMerged
#extension GL_OES_standard_derivatives : enable
precision highp float;
varying vec3 vColor;
#ifndef PICKING
varying vec3 vPosition;
#endif
void main() {
#ifdef PICKING
gl_FragColor = vec4(vColor, 1.0);
#else
vec3 fdx = dFdx(vPosition);
vec3 fdy = dFdy(vPosition);
vec3 normal = normalize(cross(fdx, fdy));
float diffuse = dot(normal, vec3(0.0, 0.0, 1.0));
gl_FragColor = vec4(diffuse * vColor, 1.0);
#endif
}
</script>
<script id="vertInstanced" type="x-shader/x-vertex">
#define SHADER_NAME vertInstanced
precision highp float;
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;
attribute vec3 position;
attribute vec3 mcol0;
attribute vec3 mcol1;
attribute vec3 mcol2;
attribute vec3 mcol3;
#ifdef PICKING
attribute vec3 pickingColor;
#else
attribute vec3 color;
varying vec3 vPosition;
#endif
varying vec3 vColor;
void main() {
mat4 matrix = mat4(
vec4(mcol0, 0),
vec4(mcol1, 0),
vec4(mcol2, 0),
vec4(mcol3, 1)
);
vec3 positionEye = (modelViewMatrix * matrix * vec4(position, 1.0)).xyz;
#ifdef PICKING
vColor = pickingColor;
#else
vColor = color;
vPosition = positionEye;
#endif
gl_Position = projectionMatrix * vec4(positionEye, 1.0);
}
</script>
<script id="fragInstanced" type="x-shader/x-fragment">
#define SHADER_NAME fragInstanced
#extension GL_OES_standard_derivatives : enable
precision highp float;
varying vec3 vColor;
#ifndef PICKING
varying vec3 vPosition;
#endif
void main() {
#ifdef PICKING
gl_FragColor = vec4(vColor, 1.0);
#else
vec3 fdx = dFdx(vPosition);
vec3 fdy = dFdy(vPosition);
vec3 normal = normalize(cross(fdx, fdy));
float diffuse = dot(normal, vec3(0.0, 0.0, 1.0));
gl_FragColor = vec4(diffuse * vColor, 1.0);
#endif
}
</script>
<script id="vertMaterial" type="x-shader/x-vertex">
#define SHADER_NAME vertMaterial
precision highp float;
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;
attribute vec3 position;
#ifndef PICKING
varying vec3 vPosition;
#endif
void main() {
vec3 positionEye = (modelViewMatrix * vec4(position, 1.0)).xyz;
#ifndef PICKING
vPosition = positionEye;
#endif
gl_Position = projectionMatrix * vec4(positionEye, 1.0);
}
</script>
<script id="fragMaterial" type="x-shader/x-fragment">
#define SHADER_NAME fragMaterial
#extension GL_OES_standard_derivatives : enable
precision highp float;
#ifdef PICKING
uniform vec3 pickingColor;
#else
uniform vec3 color;
varying vec3 vPosition;
#endif
void main() {
#ifdef PICKING
gl_FragColor = vec4(pickingColor, 1.0);
#else
vec3 fdx = dFdx(vPosition);
vec3 fdy = dFdy(vPosition);
vec3 normal = normalize(cross(fdx, fdy));
float diffuse = dot(normal, vec3(0.0, 0.0, 1.0));
gl_FragColor = vec4(diffuse * color, 1.0);
#endif
}
</script>
<script>
var container, stats;
var camera, controls, scene, renderer;
var pickingData, pickingRenderTarget, pickingScene;
var useOverrideMaterial = true;
var singleMaterial, singlePickingMaterial;
var highlightBox;
var materialList = [];
var geometryList = [];
var objectCount = 0;
var geometrySize;
var mouse = new THREE.Vector2();
var scale = 1.03;
var loader = new THREE.GLTF2Loader();
var pixelBuffer = new Uint8Array(4);
var instanceCount, method, doAnimate;
gui();
init();
initMesh();
if (doAnimate) animate();
function gui() {
var instanceCountElm = document.getElementById('instanceCount');
instanceCount = parseInt(instanceCountElm.value);
instanceCountElm.addEventListener("change", function() {
instanceCount = parseInt(instanceCountElm.value);
initMesh();
});
var methodElm = document.getElementById('method');
method = methodElm.value;
methodElm.addEventListener("change", function() {
method = methodElm.value;
initMesh();
});
var animateElm = document.getElementById('animate');
doAnimate = animateElm.checked;
animateElm.addEventListener("click", function() {
doAnimate = animateElm.checked;
animate();
});
var overrideElm = document.getElementById('override');
useOverrideMaterial = overrideElm.checked;
overrideElm.addEventListener("click", function() {
useOverrideMaterial = overrideElm.checked;
initMesh();
});
var constructElm = document.getElementById('construct');
constructElm.addEventListener("click", function() {
initMesh();
});
}
function clean() {
THREE.Cache.clear();
materialList.forEach(function(m) {
m.dispose();
});
geometryList.forEach(function(g) {
g.dispose();
});
scene = new THREE.Scene();
scene.background = new THREE.Color(0xffffff);
scene.add(camera);
scene.add(highlightBox);
pickingScene = new THREE.Scene();
pickingData = {};
materialList = [];
geometryList = [];
objectCount = 0;
singleMaterial = undefined;
singlePickingMaterial = undefined;
}
var randomizeMatrix = function() {
var position = new THREE.Vector3();
var rotation = new THREE.Euler();
var quaternion = new THREE.Quaternion();
var scale = new THREE.Vector3();
return function(matrix) {
position.x = Math.random() * 40 - 20;
position.y = Math.random() * 40 - 20;
position.z = Math.random() * 40 - 20;
rotation.x = Math.random() * 2 * Math.PI;
rotation.y = Math.random() * 2 * Math.PI;
rotation.z = Math.random() * 2 * Math.PI;
quaternion.setFromEuler(rotation, false);
scale.x = scale.y = scale.z = 0.001;
matrix.compose(position, quaternion, scale);
};
}();
function initMesh() {
clean();
loader.load('models/gltf/Duck/glTF-Binary/Duck.glb', function (data) {
console.log(data);
var geo = data.scene.children[0].children[0].children[0].children[0].geometry
console.log("geo:");
console.log(geo);
geo.computeBoundingBox();
geometrySize = geo.boundingBox.getSize();
geometryList.push(geo);
var start = window.performance.now();
switch (method){
case "merged":
makeMerged(geo);
break;
case "instanced":
makeInstanced(geo);
break;
case "singleMaterial":
makeSingleMaterial(geo);
break;
case "multiMaterial":
makeMultiMaterial(geo);
break;
}
render();
var end = window.performance.now();
document.getElementById('materialCount').innerText = materialList.length;
document.getElementById('objectCount').innerText = objectCount;
document.getElementById('drawcalls').innerText = renderer.info.render.calls;
document.getElementById('initTime').innerText = (end - start).toFixed(2);
});
}
function makeMultiMaterial(geo) {
var vert = document.getElementById('vertMaterial').textContent;
var frag = document.getElementById('fragMaterial').textContent;
var material = new THREE.RawShaderMaterial({
vertexShader: vert,
fragmentShader: frag,
uniforms: {
color: {
value: new THREE.Color()
}
}
});
var pickingMaterial = new THREE.RawShaderMaterial({
vertexShader: "#define PICKING\n" + vert,
fragmentShader: "#define PICKING\n" + frag,
uniforms: {
pickingColor: {
value: new THREE.Color()
}
}
});
var matrix = new THREE.Matrix4();
for (var i = 0; i < instanceCount; i ++) {
var object = new THREE.Mesh(geo, material);
objectCount ++;
randomizeMatrix(matrix);
object.applyMatrix(matrix);
var pickingObject = object.clone();
objectCount ++;
object.material = material.clone();
object.material.uniforms.color.value.setHex(Math.random() * 0xffffff);
materialList.push(object.material);
pickingObject.material = pickingMaterial.clone();
pickingObject.material.uniforms.pickingColor.value.setHex(i + 1);
materialList.push(pickingObject.material);
pickingData[ i + 1 ] = object;
scene.add(object);
pickingScene.add(pickingObject);
}
material.dispose();
pickingMaterial.dispose();
}
function makeSingleMaterial(geo) {
var vert = document.getElementById('vertMaterial').textContent;
var frag = document.getElementById('fragMaterial').textContent;
var material = new THREE.RawShaderMaterial({
vertexShader: vert,
fragmentShader: frag,
uniforms: {
color: {
value: new THREE.Color()
}
}
});
materialList.push(material);
var pickingMaterial = new THREE.RawShaderMaterial({
vertexShader: "#define PICKING\n" + vert,
fragmentShader: "#define PICKING\n" + frag,
uniforms: {
pickingColor: {
value: new THREE.Color()
}
}
});
materialList.push(pickingMaterial);
if (useOverrideMaterial) {
singleMaterial = material;
singlePickingMaterial = pickingMaterial;
}
var matrix = new THREE.Matrix4();
function onBeforeRender(renderer, scene, camera, geometry, material, group){
var updateList = [];
var u = material.uniforms;
var d = this.userData;
if(u.pickingColor){
u.pickingColor.value.setHex(d.pickingColor);
updateList.push("pickingColor");
}
if(u.color){
u.color.value.setHex(d.color);
updateList.push("color");
}
if(updateList.length){
var materialProperties = renderer.properties.get(material);
if(materialProperties.program){
var gl = renderer.getContext();
var p = materialProperties.program;
gl.useProgram(p.program);
var pu = p.getUniforms();
updateList.forEach(function(name){
pu.setValue(gl, name, u[ name ].value);
});
}
}
}
for (var i = 0; i < instanceCount; i ++) {
var object = new THREE.Mesh(geo, material);
objectCount ++;
randomizeMatrix(matrix);
object.applyMatrix(matrix);
var pickingObject;
if (! useOverrideMaterial) {
pickingObject = object.clone();
objectCount ++;
}
object.material = material;
object.userData[ "color" ] = Math.random() * 0xffffff;
if (useOverrideMaterial) {
object.userData[ "pickingColor" ] = i + 1;
object.onBeforeRender = onBeforeRender;
}else {
pickingObject.material = pickingMaterial;
pickingObject.userData[ "pickingColor" ] = i + 1;
pickingObject.onBeforeRender = onBeforeRender;
}
pickingData[ i + 1 ] = object;
scene.add(object);
if (! useOverrideMaterial) pickingScene.add(pickingObject);
}
}
function makeMerged(geo) {
var vert = document.getElementById('vertMerged').textContent;
var frag = document.getElementById('fragMerged').textContent;
var material = new THREE.RawShaderMaterial({
vertexShader: vert,
fragmentShader: frag
});
materialList.push(material);
var pickingMaterial = new THREE.RawShaderMaterial({
vertexShader: "#define PICKING\n" + vert,
fragmentShader: "#define PICKING\n" + frag
});
materialList.push(pickingMaterial);
var bgeo = geo.clone();
geometryList.push(bgeo);
var mgeo = new THREE.BufferGeometry();
geometryList.push(mgeo);
var pos = bgeo.attributes.position;
var posLen = bgeo.attributes.position.count * 3;
var vertices = new THREE.BufferAttribute(
new Float32Array(instanceCount * posLen), 3
);
var matrix = new THREE.Matrix4();
for (var i = 0, ul = instanceCount; i < ul; i ++) {
randomizeMatrix(matrix);
var object = new THREE.Object3D();
objectCount ++;
object.applyMatrix(matrix);
pickingData[ i + 1 ] = object;
vertices.set(pos.array, i * posLen);
//matrix.applyToVector3Array(vertices.array, i * posLen, posLen)
}
mgeo.addAttribute('position', vertices);
var colCount = posLen/3;
var colors = new THREE.BufferAttribute(
new Float32Array(instanceCount * colCount * 3), 3
);
var randCol = function() {
return Math.random();
};
for (var i = 0, ul = instanceCount; i < ul; i ++) {
var r = randCol(), g = randCol(), b = randCol();
for (var j = i * colCount, jl = (i + 1) * colCount; j < jl; j ++) {
colors.setXYZ(j, r, g, b);
}
}
mgeo.addAttribute('color', colors);
var col = new THREE.Color();
var pickingColors = new THREE.BufferAttribute(
new Float32Array(instanceCount * colCount * 3), 3
);
for (var i = 0, ul = instanceCount; i < ul; i ++) {
col.setHex(i + 1);
for (var j = i * colCount, jl = (i + 1) * colCount; j < jl; j ++) {
pickingColors.setXYZ(j, col.r, col.g, col.b);
}
}
mgeo.addAttribute('pickingColor', pickingColors);
var mesh = new THREE.Mesh(mgeo, material);
scene.add(mesh);
var pickingMesh = new THREE.Mesh(mgeo, pickingMaterial);
pickingScene.add(pickingMesh);
}
function makeInstanced(geo) {
var vert = document.getElementById('vertInstanced').textContent;
var frag = document.getElementById('fragInstanced').textContent;
var material = new THREE.RawShaderMaterial({
vertexShader: vert,
fragmentShader: frag,
});
materialList.push(material);
var pickingMaterial = new THREE.RawShaderMaterial({
vertexShader: "#define PICKING\n" + vert,
fragmentShader: "#define PICKING\n" + frag
});
materialList.push(pickingMaterial);
var bgeo = geo.clone();
geometryList.push(bgeo);
var igeo = new THREE.InstancedBufferGeometry();
geometryList.push(igeo);
var vertices = bgeo.attributes.position.clone();
igeo.addAttribute('position', vertices);
var mcol0 = new THREE.InstancedBufferAttribute(
new Float32Array(instanceCount * 3), 3, 1
);
var mcol1 = new THREE.InstancedBufferAttribute(
new Float32Array(instanceCount * 3), 3, 1
);
var mcol2 = new THREE.InstancedBufferAttribute(
new Float32Array(instanceCount * 3), 3, 1
);
var mcol3 = new THREE.InstancedBufferAttribute(
new Float32Array(instanceCount * 3), 3, 1
);
var matrix = new THREE.Matrix4();
var me = matrix.elements;
for (var i = 0, ul = mcol0.count; i < ul; i ++) {
randomizeMatrix(matrix);
var object = new THREE.Object3D();
objectCount ++;
object.applyMatrix(matrix);
pickingData[ i + 1 ] = object;
mcol0.setXYZ(i, me[ 0 ], me[ 1 ], me[ 2 ]);
mcol1.setXYZ(i, me[ 4 ], me[ 5 ], me[ 6 ]);
mcol2.setXYZ(i, me[ 8 ], me[ 9 ], me[ 10 ]);
mcol3.setXYZ(i, me[ 12 ], me[ 13 ], me[ 14 ]);
}
igeo.addAttribute('mcol0', mcol0);
igeo.addAttribute('mcol1', mcol1);
igeo.addAttribute('mcol2', mcol2);
igeo.addAttribute('mcol3', mcol3);
var randCol = function() {
return Math.random();
};
var colors = new THREE.InstancedBufferAttribute(
new Float32Array(instanceCount * 3), 3, 1
);
for (var i = 0, ul = colors.count; i < ul; i ++) {
colors.setXYZ(i, randCol(), randCol(), randCol());
}
igeo.addAttribute('color', colors);
var col = new THREE.Color();
var pickingColors = new THREE.InstancedBufferAttribute(
new Float32Array(instanceCount * 3), 3, 1
);
for (var i = 0, ul = pickingColors.count; i < ul; i ++) {
col.setHex(i + 1);
pickingColors.setXYZ(i, col.r, col.g, col.b);
}
igeo.addAttribute('pickingColor', pickingColors);
var mesh = new THREE.Mesh(igeo, material);
scene.add(mesh);
var pickingMesh = new THREE.Mesh(igeo, pickingMaterial);
pickingScene.add(pickingMesh);
}
function init() {
camera = new THREE.PerspectiveCamera(
70, window.innerWidth/window.innerHeight, 1, 100
);
camera.position.z = 40;
pickingRenderTarget = new THREE.WebGLRenderTarget(
window.innerWidth, window.innerHeight
);
pickingRenderTarget.texture.generateMipmaps = false;
pickingRenderTarget.texture.minFilter = THREE.NearestFilter;
highlightBox = new THREE.Mesh(
new THREE.BoxGeometry(1, 1, 1),
new THREE.MeshLambertMaterial({
emissive: 0xffff00,
transparent: true,
opacity: 0.5,
side: THREE.FrontSide
})
);
container = document.getElementById("container");
renderer = new THREE.WebGLRenderer({
antialias: true,
alpha: true
});
if (renderer.extensions.get('ANGLE_instanced_arrays') === false) {
document.getElementById("notSupported").style.display = "";
return;
}
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
container.appendChild(renderer.domElement);
if (renderer.extensions.get('ANGLE_instanced_arrays') === false) {
throw 'ANGLE_instanced_arrays not supported';
}
controls = new THREE.TrackballControls(
camera, renderer.domElement
);
controls.staticMoving = true;
stats = new Stats();
container.appendChild(stats.dom);
renderer.domElement.addEventListener('mousemove', onMouseMove);
window.addEventListener('resize', onWindowResize, false);
}
//
function onMouseMove(e) {
mouse.x = e.clientX;
mouse.y = e.clientY;
controls.update();
requestAnimationFrame(render);
}
function onWindowResize(event) {
camera.aspect = window.innerWidth/window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
pickingRenderTarget.setSize(window.innerWidth, window.innerHeight);
}
function animate() {
if (doAnimate) {
requestAnimationFrame(animate);
}
controls.update();
stats.update();
document.getElementById('materialCount').innerText = materialList.length;
document.getElementById('objectCount').innerText = objectCount;
document.getElementById('drawcalls').innerText = renderer.info.render.calls;
render();
}
function pick() {
highlightBox.visible = false;
if (singlePickingMaterial) {
scene.overrideMaterial = singlePickingMaterial;
renderer.render(scene, camera, pickingRenderTarget);
scene.overrideMaterial = null;
}else {
renderer.render(pickingScene, camera, pickingRenderTarget);
}
renderer.readRenderTargetPixels(
pickingRenderTarget,
mouse.x,
pickingRenderTarget.height - mouse.y,
1,
1,
pixelBuffer
);
var id =
(pixelBuffer[ 0 ] << 16) |
(pixelBuffer[ 1 ] << 8) |
(pixelBuffer[ 2 ]);
var object = pickingData[ id ];
if (object) {
if (object.position && object.rotation && object.scale) {
highlightBox.position.copy(object.position);
highlightBox.rotation.copy(object.rotation);
highlightBox.scale.copy(object.scale)
.multiply(geometrySize)
.multiplyScalar(scale);
highlightBox.visible = true;
}
} else {
highlightBox.visible = false;
}
}
function render() {
pick();
renderer.render(scene, camera);
}
</script>
</body>
</html>
yeey谢谢你的插件! – pailhead