本實例主要講解內容
這個Three.js示例展示了WebGL 2環境下的整數屬性渲染技術。通過創建大量隨機分布的三角形,并為每個三角形分配不同的整數索引,實現了基于索引動態選擇紋理的效果。
核心技術包括:
- WebGL 2環境下的整數屬性支持
- 頂點著色器與片段著色器中的整數變量傳遞
- 多紋理動態切換
- 幾何體與材質的自定義著色器實現
完整代碼注釋
<!DOCTYPE html>
<html lang="en"><head><title>three.js WebGL 2 - buffergeometry - integer attributes</title><meta charset="utf-8"><meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"><link type="text/css" rel="stylesheet" href="main.css"></head><body><div id="container"></div><div id="info"><a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> WebGL 2 - buffergeometry - integer attributes</div><script type="importmap">{"imports": {"three": "../build/three.module.js","three/addons/": "./jsm/"}}</script><script type="module">import * as THREE from 'three';let camera, scene, renderer, mesh;init();function init() {// 初始化相機camera = new THREE.PerspectiveCamera( 27, window.innerWidth / window.innerHeight, 1, 3500 );camera.position.z = 2500;// 初始化場景scene = new THREE.Scene();scene.background = new THREE.Color( 0x050505 );scene.fog = new THREE.Fog( 0x050505, 2000, 3500 );// 創建幾何體const triangles = 10000; // 三角形數量const geometry = new THREE.BufferGeometry();// 存儲頂點位置、UV坐標和紋理索引const positions = [];const uvs = [];const textureIndices = [];// 三角形分布范圍和大小const n = 800, n2 = n / 2; // 三角形分布的立方體范圍const d = 50, d2 = d / 2; // 單個三角形的大小// 生成隨機三角形for ( let i = 0; i < triangles; i ++ ) {// 隨機位置const x = Math.random() * n - n2;const y = Math.random() * n - n2;const z = Math.random() * n - n2;// 三角形的三個頂點const ax = x + Math.random() * d - d2;const ay = y + Math.random() * d - d2;const az = z + Math.random() * d - d2;const bx = x + Math.random() * d - d2;const by = y + Math.random() * d - d2;const bz = z + Math.random() * d - d2;const cx = x + Math.random() * d - d2;const cy = y + Math.random() * d - d2;const cz = z + Math.random() * d - d2;// 添加頂點位置positions.push( ax, ay, az );positions.push( bx, by, bz );positions.push( cx, cy, cz );// 添加UV坐標uvs.push( 0, 0 );uvs.push( 0.5, 1 );uvs.push( 1, 0 );// 添加紋理索引(0,1,2 循環)const t = i % 3;textureIndices.push( t, t, t );}// 設置幾何體屬性geometry.setAttribute( 'position', new THREE.Float32BufferAttribute( positions, 3 ) );geometry.setAttribute( 'uv', new THREE.Float32BufferAttribute( uvs, 2 ) );geometry.setAttribute( 'textureIndex', new THREE.Int16BufferAttribute( textureIndices, 1 ) );geometry.attributes.textureIndex.gpuType = THREE.IntType; // 指定GPU使用整數類型geometry.computeBoundingSphere(); // 計算邊界球體// 加載紋理const loader = new THREE.TextureLoader();const map1 = loader.load( 'textures/crate.gif' );const map2 = loader.load( 'textures/floors/FloorsCheckerboard_S_Diffuse.jpg' );const map3 = loader.load( 'textures/terrain/grasslight-big.jpg' );// 創建自定義著色器材質const material = new THREE.ShaderMaterial( {uniforms: {uTextures: {value: [ map1, map2, map3 ] // 紋理數組}},vertexShader: /* glsl */`in int textureIndex; // 從幾何體傳入的紋理索引(整數)flat out int vIndex; // "flat" 表示不進行插值(整數屬性必須)out vec2 vUv; // UV坐標void main() {vIndex = textureIndex;vUv = uv;gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );}`,fragmentShader: /* glsl */`flat in int vIndex; // 從頂點著色器傳入的紋理索引in vec2 vUv; // UV坐標uniform sampler2D uTextures[ 3 ]; // 紋理數組out vec4 outColor; // 輸出顏色void main() {// 根據索引選擇不同的紋理if ( vIndex == 0 ) outColor = texture( uTextures[ 0 ], vUv );else if ( vIndex == 1 ) outColor = texture( uTextures[ 1 ], vUv );else if ( vIndex == 2 ) outColor = texture( uTextures[ 2 ], vUv );}`,side: THREE.DoubleSide, // 雙面渲染glslVersion: THREE.GLSL3 // 使用GLSL 3.0} );// 創建網格并添加到場景mesh = new THREE.Mesh( geometry, material );scene.add( mesh );// 初始化渲染器(必須支持WebGL 2)renderer = new THREE.WebGLRenderer( { antialias: true } );renderer.setPixelRatio( window.devicePixelRatio );renderer.setSize( window.innerWidth, window.innerHeight );renderer.setAnimationLoop( animate );document.body.appendChild( renderer.domElement );}// 動畫循環function animate() {const time = Date.now() * 0.001;// 旋轉網格mesh.rotation.x = time * 0.25;mesh.rotation.y = time * 0.5;renderer.render( scene, camera );}</script></body>
</html>
WebGL 2整數屬性技術解析
WebGL 2與整數屬性
WebGL 2相比WebGL 1提供了更強大的功能,包括對整數數據類型的直接支持:
- 整數頂點屬性:WebGL 2允許在頂點著色器中使用整數類型的屬性,這在WebGL 1中是受限的
- 更高的精度:支持16位和32位整數,提供更精確的數據表示
- 更高效的數據傳輸:整數數據可以更高效地從CPU傳輸到GPU
- 簡化的著色器邏輯:避免了在WebGL 1中需要將整數編碼為浮點數的復雜操作
在本示例中,我們使用了Int16BufferAttribute
和THREE.IntType
來定義和指定整數屬性。
頂點著色器與片段著色器通信
在著色器編程中,數據從頂點著色器傳遞到片段著色器需要特別注意:
- 插值行為:默認情況下,頂點著色器輸出的變量會在光柵化階段進行插值,這對于浮點數是合適的,但對于整數會導致錯誤
- flat限定符:使用
flat
限定符可以禁止插值,確保每個片段接收到的是頂點的原始整數值 - 數據類型匹配:頂點著色器和片段著色器中的變量類型必須嚴格匹配
在本示例中,我們使用了flat out int vIndex
和flat in int vIndex
來確保整數數據正確傳遞。
多紋理動態選擇
本示例展示了如何基于整數屬性動態選擇不同的紋理:
- 紋理數組:在著色器中定義
uniform sampler2D uTextures[3]
來存儲多個紋理 - 條件判斷:在片段著色器中使用
if-else
語句根據整數索引選擇相應的紋理 - 高效切換:通過整數索引直接訪問紋理數組,避免了使用多個材質的開銷
這種技術在需要大量不同紋理的場景中非常有用,比如地形渲染、角色換裝系統等。
性能優化考慮
使用整數屬性和自定義著色器時,需要注意以下幾點:
- 數據類型選擇:根據實際需求選擇合適的整數類型(Byte, Short, Int),避免浪費GPU內存
- 批處理:將多個具有相同材質的對象合并為一個,減少Draw Call
- 紋理管理:確保紋理尺寸合理,并考慮使用紋理圖集(Texture Atlas)減少紋理切換
- 著色器復雜度:避免在片段著色器中使用復雜的條件判斷,可能影響性能
這種技術適用于需要在單個幾何體上應用多種紋理或材質屬性的場景,通過減少材質和Draw Call數量來提高渲染性能。