Created
February 20, 2025 21:20
-
-
Save erichlof/bd2bee54e96c704f160756fae8862052 to your computer and use it in GitHub Desktop.
js file for experimental sphere BVH (as opposed to typical AABB BVH)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // scene/demo-specific variables go here | |
| let modelMesh; | |
| let modelScale = 1.0; | |
| let modelPositionOffset = new THREE.Vector3(); | |
| let albedoTexture; | |
| let total_number_of_triangles = 0; | |
| let triangle_array; | |
| let triangleMaterialMarkers = []; | |
| let pathTracingMaterialList = []; | |
| let uniqueMaterialTextures = []; | |
| let meshList = []; | |
| let geoList = []; | |
| let triangleDataTexture; | |
| let aabb_array; | |
| let aabbDataTexture; | |
| let totalWorklist; | |
| let vp0 = new THREE.Vector3(); | |
| let vp1 = new THREE.Vector3(); | |
| let vp2 = new THREE.Vector3(); | |
| let vn0 = new THREE.Vector3(); | |
| let vn1 = new THREE.Vector3(); | |
| let vn2 = new THREE.Vector3(); | |
| let vt0 = new THREE.Vector2(); | |
| let vt1 = new THREE.Vector2(); | |
| let vt2 = new THREE.Vector2(); | |
| function MaterialObject() | |
| { | |
| // a list of material types and their corresponding numbers are found in the 'pathTracingCommon.js' file | |
| this.type = 1; // default is '1': diffuse type | |
| this.albedoTextureID = -1; // which diffuse map to use for model's color / '-1' = no textures are used | |
| this.color = new THREE.Color(1.0, 1.0, 1.0); // takes on different meanings, depending on 'type' above | |
| this.roughness = 0.0; // 0.0 to 1.0 range, perfectly smooth to extremely rough | |
| this.metalness = 0.0; // 0.0 to 1.0 range, usually either 0 or 1, either non-metal or metal | |
| this.opacity = 1.0; // 0.0 to 1.0 range, fully transparent to fully opaque | |
| this.refractiveIndex = 1.0; // 1.0=air, 1.33=water, 1.4=clearCoat, 1.5=glass, etc. | |
| } | |
| function load_GLTF_Model() | |
| { | |
| let gltfLoader = new GLTFLoader(); | |
| gltfLoader.load("models/StanfordBunny.glb", function (meshGroup) // Triangles: 30,338 | |
| //gltfLoader.load("models/UtahTeapot.glb", function( meshGroup ) // Triangles: 992 | |
| //gltfLoader.load("models/TheSentinel_tree.glb", function (meshGroup) // Triangles: 18-60 | |
| //gltfLoader.load("models/StanfordDragon.glb", function (meshGroup) // Triangles: 100,000 | |
| //gltfLoader.load("models/FairyScene.glb", function (meshGroup) // Triangles: 100,000 | |
| { | |
| if (meshGroup.scene) | |
| meshGroup = meshGroup.scene; | |
| meshGroup.traverse(function (child) | |
| { | |
| if (child.isMesh) | |
| { | |
| let mat = new MaterialObject(); | |
| mat.type = 1; | |
| mat.albedoTextureID = -1; | |
| mat.color = child.material.color; | |
| mat.roughness = child.material.roughness || 0.0; | |
| mat.metalness = child.material.metalness || 0.0; | |
| mat.opacity = child.material.opacity || 1.0; | |
| mat.refractiveIndex = 1.0; | |
| pathTracingMaterialList.push(mat); | |
| triangleMaterialMarkers.push(child.geometry.attributes.position.array.length / 9); | |
| meshList.push(child); | |
| } | |
| }); | |
| modelMesh = meshList[0].clone(); | |
| for (let i = 0; i < meshList.length; i++) | |
| { | |
| geoList.push(meshList[i].geometry); | |
| } | |
| modelMesh.geometry = mergeGeometries(geoList); | |
| if (modelMesh.geometry.index) | |
| modelMesh.geometry = modelMesh.geometry.toNonIndexed(); | |
| modelMesh.geometry.center(); | |
| for (let i = 1; i < triangleMaterialMarkers.length; i++) | |
| { | |
| triangleMaterialMarkers[i] += triangleMaterialMarkers[i - 1]; | |
| } | |
| /* for (let i = 0; i < meshList.length; i++) | |
| { | |
| if (meshList[i].material.map != undefined) | |
| uniqueMaterialTextures.push(meshList[i].material.map); | |
| } | |
| for (let i = 0; i < uniqueMaterialTextures.length; i++) | |
| { | |
| for (let j = i + 1; j < uniqueMaterialTextures.length; j++) | |
| { | |
| if (uniqueMaterialTextures[i].image.src == uniqueMaterialTextures[j].image.src) | |
| { | |
| uniqueMaterialTextures.splice(j, 1); | |
| j -= 1; | |
| } | |
| } | |
| } | |
| for (let i = 0; i < meshList.length; i++) | |
| { | |
| if (meshList[i].material.map != undefined) | |
| { | |
| for (let j = 0; j < uniqueMaterialTextures.length; j++) | |
| { | |
| if (meshList[i].material.map.image.src == uniqueMaterialTextures[j].image.src) | |
| { | |
| pathTracingMaterialList[i].albedoTextureID = j; | |
| } | |
| } | |
| } | |
| } */ | |
| // ********* different GLTF Model Settings ********** | |
| // settings for StanfordBunny model | |
| modelScale = 0.5;//0.04; | |
| modelPositionOffset.set(0, 27.6, -40); | |
| // settings for UtahTeapot model | |
| // modelScale = 1.0; | |
| // modelPositionOffset.set(0, 25.6, -40); | |
| // settings for TheSentinel models | |
| // modelScale = 20.0; | |
| // modelPositionOffset.set(0, 30, 0); | |
| // settings for StanfordDragon model | |
| // modelScale = 2.0; | |
| // modelPositionOffset.set(0, 28, -40); | |
| // settings for various test models | |
| // modelScale = 6; | |
| // modelPositionOffset.set(0, 20, 0); | |
| // now that the model has been loaded, we can init | |
| init(); | |
| }); | |
| } // end function load_GLTF_Model() | |
| // called automatically from within initTHREEjs() function (located in InitCommon.js file) | |
| function initSceneData() | |
| { | |
| demoFragmentShaderFileName = 'BVH_Point_Light_Source_Fragment.glsl'; | |
| // scene/demo-specific three.js objects setup goes here | |
| sceneIsDynamic = false; | |
| cameraFlightSpeed = 60; | |
| // pixelRatio is resolution - range: 0.5(half resolution) to 1.0(full resolution) | |
| pixelRatio = mouseControl ? 0.5 : 0.75; // less demanding on battery-powered mobile devices | |
| EPS_intersect = 0.001; | |
| // set camera's field of view | |
| worldCamera.fov = 60; | |
| focusDistance = 95.0; | |
| apertureChangeSpeed = 5; | |
| // position and orient camera | |
| cameraControlsObject.position.set(0, 30, 60); | |
| // look slightly downward | |
| //cameraControlsPitchObject.rotation.x = -0.2; | |
| total_number_of_triangles = modelMesh.geometry.attributes.position.array.length / 9; | |
| console.log("Triangle count:" + total_number_of_triangles); | |
| totalWorklist = new Uint32Array(total_number_of_triangles); | |
| triangle_array = new Float32Array(4096 * 4096 * 4); | |
| // 4096 = width of texture, 4096 = height of texture, 4 = r,g,b, and a components | |
| aabb_array = new Float32Array(4096 * 4096 * 4); | |
| // 4096 = width of texture, 4096 = height of texture, 4 = r,g,b, and a components | |
| let triangle_b_box_min = new THREE.Vector3(); | |
| let triangle_b_box_max = new THREE.Vector3(); | |
| let triangle_centroid = new THREE.Vector3(); | |
| //let triangle_b_box_centroid = new THREE.Vector3(); | |
| let vpa = new Float32Array(modelMesh.geometry.attributes.position.array); | |
| let vna = new Float32Array(modelMesh.geometry.attributes.normal.array); | |
| let vta = null; | |
| let modelHasUVs = false; | |
| if (modelMesh.geometry.attributes.uv !== undefined) | |
| { | |
| vta = new Float32Array(modelMesh.geometry.attributes.uv.array); | |
| modelHasUVs = true; | |
| } | |
| let materialNumber = 0; | |
| for (let i = 0; i < total_number_of_triangles; i++) | |
| { | |
| triangle_b_box_min.set(Infinity, Infinity, Infinity); | |
| triangle_b_box_max.set(-Infinity, -Infinity, -Infinity); | |
| for (let j = 0; j < pathTracingMaterialList.length; j++) | |
| { | |
| if (i < triangleMaterialMarkers[j]) | |
| { | |
| materialNumber = j; | |
| break; | |
| } | |
| } | |
| // record vertex texture coordinates (UVs) | |
| if (modelHasUVs) | |
| { | |
| vt0.set(vta[6 * i + 0], vta[6 * i + 1]); | |
| vt1.set(vta[6 * i + 2], vta[6 * i + 3]); | |
| vt2.set(vta[6 * i + 4], vta[6 * i + 5]); | |
| } | |
| else | |
| { | |
| vt0.set(-1, -1); | |
| vt1.set(-1, -1); | |
| vt2.set(-1, -1); | |
| } | |
| // record vertex normals | |
| vn0.set(vna[9 * i + 0], vna[9 * i + 1], vna[9 * i + 2]).normalize(); | |
| vn1.set(vna[9 * i + 3], vna[9 * i + 4], vna[9 * i + 5]).normalize(); | |
| vn2.set(vna[9 * i + 6], vna[9 * i + 7], vna[9 * i + 8]).normalize(); | |
| // record vertex positions | |
| vp0.set(vpa[9 * i + 0], vpa[9 * i + 1], vpa[9 * i + 2]); | |
| vp1.set(vpa[9 * i + 3], vpa[9 * i + 4], vpa[9 * i + 5]); | |
| vp2.set(vpa[9 * i + 6], vpa[9 * i + 7], vpa[9 * i + 8]); | |
| vp0.multiplyScalar(modelScale); | |
| vp1.multiplyScalar(modelScale); | |
| vp2.multiplyScalar(modelScale); | |
| vp0.add(modelPositionOffset); | |
| vp1.add(modelPositionOffset); | |
| vp2.add(modelPositionOffset); | |
| //slot 0 | |
| triangle_array[32 * i + 0] = vp0.x; // r or x | |
| triangle_array[32 * i + 1] = vp0.y; // g or y | |
| triangle_array[32 * i + 2] = vp0.z; // b or z | |
| triangle_array[32 * i + 3] = vp1.x; // a or w | |
| //slot 1 | |
| triangle_array[32 * i + 4] = vp1.y; // r or x | |
| triangle_array[32 * i + 5] = vp1.z; // g or y | |
| triangle_array[32 * i + 6] = vp2.x; // b or z | |
| triangle_array[32 * i + 7] = vp2.y; // a or w | |
| //slot 2 | |
| triangle_array[32 * i + 8] = vp2.z; // r or x | |
| triangle_array[32 * i + 9] = vn0.x; // g or y | |
| triangle_array[32 * i + 10] = vn0.y; // b or z | |
| triangle_array[32 * i + 11] = vn0.z; // a or w | |
| //slot 3 | |
| triangle_array[32 * i + 12] = vn1.x; // r or x | |
| triangle_array[32 * i + 13] = vn1.y; // g or y | |
| triangle_array[32 * i + 14] = vn1.z; // b or z | |
| triangle_array[32 * i + 15] = vn2.x; // a or w | |
| //slot 4 | |
| triangle_array[32 * i + 16] = vn2.y; // r or x | |
| triangle_array[32 * i + 17] = vn2.z; // g or y | |
| triangle_array[32 * i + 18] = vt0.x; // b or z | |
| triangle_array[32 * i + 19] = vt0.y; // a or w | |
| //slot 5 | |
| triangle_array[32 * i + 20] = vt1.x; // r or x | |
| triangle_array[32 * i + 21] = vt1.y; // g or y | |
| triangle_array[32 * i + 22] = vt2.x; // b or z | |
| triangle_array[32 * i + 23] = vt2.y; // a or w | |
| // the remaining slots are used for PBR material properties | |
| //slot 6 | |
| triangle_array[32 * i + 24] = pathTracingMaterialList[materialNumber].type; // r or x | |
| triangle_array[32 * i + 25] = pathTracingMaterialList[materialNumber].color.r; // g or y | |
| triangle_array[32 * i + 26] = pathTracingMaterialList[materialNumber].color.g; // b or z | |
| triangle_array[32 * i + 27] = pathTracingMaterialList[materialNumber].color.b; // a or w | |
| //slot 7 | |
| triangle_array[32 * i + 28] = pathTracingMaterialList[materialNumber].albedoTextureID; // r or x | |
| triangle_array[32 * i + 29] = 0; // g or y | |
| triangle_array[32 * i + 30] = 0; // b or z | |
| triangle_array[32 * i + 31] = 0; // a or w | |
| triangle_b_box_min.copy(triangle_b_box_min.min(vp0)); | |
| triangle_b_box_max.copy(triangle_b_box_max.max(vp0)); | |
| triangle_b_box_min.copy(triangle_b_box_min.min(vp1)); | |
| triangle_b_box_max.copy(triangle_b_box_max.max(vp1)); | |
| triangle_b_box_min.copy(triangle_b_box_min.min(vp2)); | |
| triangle_b_box_max.copy(triangle_b_box_max.max(vp2)); | |
| //triangle_b_box_centroid.copy(triangle_b_box_min).add(triangle_b_box_max).multiplyScalar(0.5); | |
| triangle_centroid.copy(vp0).add(vp1).add(vp2).multiplyScalar(0.3333333333333333); | |
| aabb_array[9 * i + 0] = triangle_b_box_min.x; | |
| aabb_array[9 * i + 1] = triangle_b_box_min.y; | |
| aabb_array[9 * i + 2] = triangle_b_box_min.z; | |
| aabb_array[9 * i + 3] = triangle_b_box_max.x; | |
| aabb_array[9 * i + 4] = triangle_b_box_max.y; | |
| aabb_array[9 * i + 5] = triangle_b_box_max.z; | |
| aabb_array[9 * i + 6] = triangle_centroid.x; | |
| aabb_array[9 * i + 7] = triangle_centroid.y; | |
| aabb_array[9 * i + 8] = triangle_centroid.z; | |
| totalWorklist[i] = i; | |
| } | |
| for (let i = 0; i < total_number_of_triangles * 2; i++) | |
| buildnodes[i] = new BVH_Node(); | |
| console.log("BvhGeneration..."); | |
| console.time("BvhGeneration"); | |
| BVH_QuickBuild(totalWorklist, aabb_array); | |
| console.timeEnd("BvhGeneration"); | |
| let box = new THREE.Box3(); | |
| let sphere = new THREE.Sphere(); | |
| let sphereRadiusPacked = 0; | |
| let leafOrChildID = 0; | |
| // Copy the buildnodes array into the aabb_array | |
| for (let n = 0; n < buildnodes.length; n++) | |
| { | |
| box.set(buildnodes[n].minCorner, buildnodes[n].maxCorner); | |
| box.getBoundingSphere(sphere); | |
| sphereRadiusPacked = sphere.radius * 0.001; // shove radius over into the fractional part of a float number | |
| leafOrChildID = Math.floor(buildnodes[n].leafOrChild_ID); // keep leftChildID in the integer part of a float number | |
| // this packed bounding volume node now takes only 1 rgba texel! | |
| if (buildnodes[n].primitiveCount == 0) // inner nodes are encoded as a negative number | |
| aabb_array[4 * n + 0] = -leafOrChildID - sphereRadiusPacked; // r or x component | |
| else | |
| aabb_array[4 * n + 0] = leafOrChildID + sphereRadiusPacked; // r or x component | |
| aabb_array[4 * n + 1] = sphere.center.x; // g or y component | |
| aabb_array[4 * n + 2] = sphere.center.y; // b or z component | |
| aabb_array[4 * n + 3] = sphere.center.z; // a or w component | |
| } | |
| triangleDataTexture = new THREE.DataTexture(triangle_array, | |
| 4096, | |
| 4096, | |
| THREE.RGBAFormat, | |
| THREE.FloatType, | |
| THREE.Texture.DEFAULT_MAPPING, | |
| THREE.ClampToEdgeWrapping, | |
| THREE.ClampToEdgeWrapping, | |
| THREE.NearestFilter, | |
| THREE.NearestFilter, | |
| 1, | |
| THREE.NoColorSpace); | |
| triangleDataTexture.flipY = false; | |
| triangleDataTexture.generateMipmaps = false; | |
| triangleDataTexture.needsUpdate = true; | |
| aabbDataTexture = new THREE.DataTexture(aabb_array, | |
| 4096, | |
| 4096, | |
| THREE.RGBAFormat, | |
| THREE.FloatType, | |
| THREE.Texture.DEFAULT_MAPPING, | |
| THREE.ClampToEdgeWrapping, | |
| THREE.ClampToEdgeWrapping, | |
| THREE.NearestFilter, | |
| THREE.NearestFilter, | |
| 1, | |
| THREE.NoColorSpace); | |
| aabbDataTexture.flipY = false; | |
| aabbDataTexture.generateMipmaps = false; | |
| aabbDataTexture.needsUpdate = true; | |
| // scene/demo-specific uniforms go here | |
| pathTracingUniforms.tTriangleTexture = { value: triangleDataTexture }; | |
| pathTracingUniforms.tAABBTexture = { value: aabbDataTexture }; | |
| } // end function initSceneData() | |
| // called automatically from within the animate() function (located in InitCommon.js file) | |
| function updateVariablesAndUniforms() | |
| { | |
| // INFO | |
| cameraInfoElement.innerHTML = "FOV: " + worldCamera.fov + " / Aperture: " + apertureSize.toFixed(2) + | |
| " / FocusDistance: " + focusDistance + "<br>" + "Samples: " + sampleCounter; | |
| } // end function updateUniforms() | |
| load_GLTF_Model(); // load model, init app, and start animating |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment