2017-01-26 37 views
3

相信這裏所述的「實例化」提供了一種具有用於的所有頂點/ indicies一個屬性,例如,200頂點模型:如何使用Three.js InstancedBufferGeometry&InstancedBufferAttribute?

http://blog.tojicode.com/2013/07/webgl-instancing-with.html

換句話說,這提供了一種方法來只有一個平移或方向屬性數組將被應用於模型的所有200個頂點。因此,「實例化」10K這些模型的場景只需要10K個屬性,而不是2000K。

顯然Three's InstancedBufferGeometry & InstancedBufferAttribute對象提供了這個,但我沒有找到文檔,除了對象的稀疏描述。我相信他們使用ShaderMaterial,雖然除了在「vanilla」Three.js中使用GLSL以外,還有其他方法。

有人可以解釋他們如何工作以及如何在Three.js中使用它們嗎?

+0

可能感興趣http://alteredqualia.com/three/examples/webgl_cubes.html – gaitat

+0

我讀這和搞糊塗了。什麼是替代即。沒有應用屬性「模型的所有200個頂點」?我認爲頂點是屬性。 「實例化10k ...」仍然只能保持具有200個頂點的屬性。渲染器會參考它10K次,不知道您的2000K號碼來自哪裏。 – pailhead

+0

等等,即使在參與這樣的論壇時,3D也沒有獎勵。查看34次,沒有評論,沒有接受:( – pailhead

回答

4

當我自己尋找這個答案時,我偶然發現了你的問題。這裏提供了兩個例子(直接從threejs.org/examples),其使用實例化:

的簡要說明:

THREE.InstancedBufferGeometryTHREE.BufferGeometry之間的主要區別在於前者可以使用將使用的特殊屬性(THREE.InstancedBufferAttributes每個實例

想象一下,您正在創建一個盒子,您希望爲其設置多個實例。頂點,法線和UV緩衝區都將是標準的THREE.BufferAttribute對象,因爲它們描述了基本形狀。但爲了將每個實例移動到自己的位置,您需要定義THREE.InstancedBufferAttribute來保存位置(這些示例通常將該屬性命名爲「offset」)。

頂點的引用數在THREE.InstancedBufferAttributes介紹你中有多少實例。例如,在offset中放置9個值表示將會有3個實例(這包括原始形狀)。您還可以通過設置THREE.InstancedBuferGeometry.maxInstancedCount值來控制其中的數量。

最後,你需要着色器來幫助控制實例化屬性。

小例子:

var cubeGeo = new THREE.InstancedBufferGeometry().copy(new THREE.BoxBufferGeometry(10, 10, 10)); 
 
//cubeGeo.maxInstancedCount = 8; 
 

 
cubeGeo.addAttribute("cubePos", new THREE.InstancedBufferAttribute(new Float32Array([ 
 
    25, 25, 25, 
 
    25, 25, -25, -25, 25, 25, -25, 25, -25, 
 
    25, -25, 25, 
 
    25, -25, -25, -25, -25, 25, -25, -25, -25 
 
]), 3, 1)); 
 

 
var vertexShader = [ 
 
    "precision highp float;", 
 
    "", 
 
    "uniform mat4 modelViewMatrix;", 
 
    "uniform mat4 projectionMatrix;", 
 
    "", 
 
    "attribute vec3 position;", 
 
    "attribute vec3 cubePos;", 
 
    "", 
 
    "void main() {", 
 
    "", 
 
    " \t gl_Position = projectionMatrix * modelViewMatrix * vec4(cubePos + position, 1.0);", 
 
    "", 
 
    "}" 
 
].join("\n"); 
 
var fragmentShader = [ 
 
    "precision highp float;", 
 
    "", 
 
    "void main() {", 
 
    "", 
 
    " \t gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);", 
 
    "", 
 
    "}" 
 
].join("\n"); 
 

 
var mat = new THREE.RawShaderMaterial({ 
 
    uniforms: {}, 
 
    vertexShader: vertexShader, 
 
    fragmentShader: fragmentShader, 
 
    side: THREE.DoubleSide, 
 
    transparent: false 
 
}); 
 

 
var mesh = new THREE.Mesh(cubeGeo, mat); 
 

 
scene.add(mesh);
html * { 
 
    padding: 0; 
 
    margin: 0; 
 
    width: 100%; 
 
    overflow: hidden; 
 
} 
 

 
#host { 
 
    width: 100%; 
 
    height: 100%; 
 
}
<script src="https://threejs.org/build/three.js"></script> 
 
<script src="https://threejs.org/examples/js/controls/TrackballControls.js"></script> 
 
<script src="https://threejs.org/examples/js/libs/stats.min.js"></script> 
 
<div id="host"></div> 
 

 
<script> 
 
    var WIDTH = window.innerWidth, 
 
    HEIGHT = window.innerHeight, 
 
    FOV = 35, 
 
    NEAR = 1, 
 
    FAR = 1000; 
 

 
    var renderer = new THREE.WebGLRenderer({ 
 
    antialias: true 
 
    }); 
 
    renderer.setSize(WIDTH, HEIGHT); 
 
    document.getElementById('host').appendChild(renderer.domElement); 
 

 
    var stats = new Stats(); 
 
    stats.domElement.style.position = 'absolute'; 
 
    stats.domElement.style.top = '0'; 
 
    document.body.appendChild(stats.domElement); 
 

 

 
    var camera = new THREE.PerspectiveCamera(FOV, WIDTH/HEIGHT, NEAR, FAR); 
 
    camera.position.z = 250; 
 

 
    var trackballControl = new THREE.TrackballControls(camera, renderer.domElement); 
 
    trackballControl.rotateSpeed = 5.0; // need to speed it up a little 
 

 
    var scene = new THREE.Scene(); 
 

 
    var light = new THREE.PointLight(0xffffff, 1, Infinity); 
 
    camera.add(light); 
 

 
    scene.add(light); 
 

 
    function render() { 
 
    if (typeof updateVertices !== "undefined") { 
 
     updateVertices(); 
 
    } 
 
    renderer.render(scene, camera); 
 
    stats.update(); 
 
    } 
 

 
    function animate() { 
 
    requestAnimationFrame(animate); 
 
    trackballControl.update(); 
 
    render(); 
 
    } 
 

 
    animate(); 
 
</script>

+0

大聲笑,我沒有解釋他們是如何工作的(繪製調用開銷和東西),但我沒有解釋如何使用它們。一旦我的PR被接受,你應該能夠使用默認材質,並且不需要額外的着色/緩衝工作。 – pailhead

1

這個問題有點混亂,但生病盡我所能。

我認爲你對一些事情感到困惑,我在three.js用戶中看到了很多。首先,我認爲屬性術語是錯誤的。你不是在創造幾千上萬的屬性,而是一個實體,如一個網格,可以有一個屬性 - position,或者多個uvnormalaMyAttribute

事實上,有屬性的WebGL的最大數量可以解決,它有所不同,但球場是16,而不是數千。

屬性可以包含數據來定義200個「頂點」,但這也是相對的,它們可以有2個組件,可以有4個組件。

當您從「200頂點模型」中製作多個「對象」時,您不會乘以幾何。屬性數不會增加,實際上我不確定制服會發生什麼,但就GPU而言,它仍然擁有200個頂點。 Javascript仍然包含一些屬性和統一的位置,以及一些Geometry類的實例。

你做的乘法是「節點/對象」,在javascript中你會有多個對象說Mesh。這將包含各種其他類型,矩陣,向量,四元數等。

當您調用渲染函數時,渲染器將爲每個這些節點發出繪製調用。每次它這樣做,它都需要設置webgl的狀態來處理特定的繪製調用。如果這是散佈在場景周圍的同一物體,唯一不同的是位置/旋轉/比例的制服。如果他們有材料,那麼它可能是一種顏色或不同紋理的統一體。無論哪種方式,這會產生開銷,並降低速度。

可以說你的模型是一棵樹。創建一個森林並渲染a forest而不是many trees將消除這種開銷,仍然有相同數量的着色器處理正在進行。每棵樹的每個頂點需要在其上運行着色器。

這當然會增加你的屬性持有的數據。現在,在一些方便的對象空間(更好地稱之爲「樹空間」)中,不需要保存200個頂點,它需要在「森林空間」中保存200 x N個頂點。 IE瀏覽器。tree 0的頂點存在於森林中某處,並且tree N的相同頂點存在於森林中的其他地方。如果您要爲每棵樹創建一個新的幾何圖形,將轉換烘焙到其頂點,然後再與其他圖形合併,則會發生這種情況。

實例化可以讓你對這種情況變得更聰明。與擁有共享共同屬性(它是同一棵樹)的所有單個頂點不同,您可以容納原始樹的200個頂點,以及描述它們將被繪製N次的屬性。因此,不是合併幾何體,而是分別對它們進行烘焙轉換,您將構建一個僅包含轉換的屬性。

您很可能無法將其與香草材料一起使用,因爲他們不知道如何處理您的自定義屬性。然而,隨着three.js如何處理着色器,注入一些邏輯並擴展現有材質並不難。