Ale*_*súa 6 instance three.js gltf
我是 THREEJS 的新手。过去,我使用过 AFRAME、CESIUM、XEOGL 和 BABYLONJS。但最终,由于内存消耗和性能,我意识到制作 CAD 可视化工具的最佳产品是 THREEJS。
BABYLONJS 加载一个大的 GLTF 文件(400MB)需要超过 4 分钟,而 THREEJS 只需要 30 秒。BABYLONJS 占用的内存是 THREEJS 占用的 4 倍。
我知道仍然存在一些问题,无法从 THREEJS 中加载的 GLTF 文件创建实例 (GPU),但我只需要更改每个实例中的位置和旋转,无需设置任何动画。
我尝试过 GLTF1.0 和 GLTF2.0,问题是一样的。当我加载 GLTF 模型时,我得到了一个场景。在这个场景中,我试图从 children 数组中获取 buffergeometry。但是,当我尝试创建一个实例时,它不起作用。我的对象是静态的(根本没有动画)。
有没有办法创建 Object3D 的实例或从它的缓冲区几何?
在 BABYLONJS 中,从加载的 GLTF 文件创建实例非常简单。我真的需要使用实例来节省 RAM 并使用 GPU 而不是 CPU 资源。我的场景需要加载相同对象的多个副本来合成场景。
我在使用 GLFT 加载器时看到的一些问题:
var geo = data.scene.children[0].children[0].children[0].children[0].geometry
10000 个物体,多材质 --> 4FPS
10000 个物体,单一材质--> 4FPS
10000 个对象,实例化 --> 4FPS
10000 个对象,合并 --> 不工作
这里有我使用 Duck 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.M
似乎您的问题主要是,如何在three.js 中进行实例化?一旦你加载了一个模型,你用来创建它的格式并不重要。
既然如此,您可能只想查看three.js 实例化示例或使用其中一个帮助程序,例如three-instanced-mesh。
从模型中获取几何体后,第二个链接显示了如何继续:
// Assumes your model only contains one mesh.
var geometry;
model.traverse(function (node) => {
if (node.isMesh) {
geometry = node.geometry;
}
});
//material that the geometry will use
var material = new THREE.MeshPhongMaterial();
//the instance group
var cluster = new THREE.InstancedMesh(
geometry,
material,
10000, //instance count
false, //is it dynamic
false //does it have color
true, //uniform scale
);
var _v3 = new THREE.Vector3();
var _q = new THREE.Quaternion();
for ( var i ; i < 10000 ; i ++ ) {
cluster.setQuaternionAt( i , _q );
cluster.setPositionAt( i , v3.set( Math.random() , Math.random(), Math.random() ) );
cluster.setScaleAt( i , v3.set(1,1,1) );
}
scene.add( cluster );
Run Code Online (Sandbox Code Playgroud)