Created
May 14, 2025 18:38
-
-
Save erichlof/c5432180f73585e5f6121cebcf0a7784 to your computer and use it in GitHub Desktop.
Making more interesting and complex models using CSG techniques
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 boxGeometry, boxMaterial; | |
| let boxMeshes = []; | |
| let boxGeometries = []; | |
| let totalNumberOfShapes = 0; | |
| let shape = new THREE.Object3D(); | |
| let invMatrix = new THREE.Matrix4(); | |
| let el; // elements of the invMatrix | |
| let shapeBoundingBox_minCorner = new THREE.Vector3(); | |
| let shapeBoundingBox_maxCorner = new THREE.Vector3(); | |
| let shapeBoundingBox_centroid = new THREE.Vector3(); | |
| let shape_array; | |
| let shapeDataTexture; | |
| let aabb_array; | |
| let aabbDataTexture; | |
| let totalWorklist; | |
| let outline_IntensityObject, outline_IntensityController; | |
| let needChangeOutlineIntensity = false; | |
| let outline_ColorObject, outline_ColorController; | |
| let needChangeOutlineColor = false; | |
| let outlineColor; | |
| function Begin_CSG_Section() | |
| { | |
| } | |
| function End_CSG_Section() | |
| { | |
| } | |
| function Add_CSG_Shape(shapeA) | |
| { | |
| } | |
| function Operation_Union(shapeB) | |
| { | |
| } | |
| function Operation_Difference(shapeB) | |
| { | |
| } | |
| function Operation_Overlap(shapeB) | |
| { | |
| } | |
| // called automatically from within initTHREEjs() function (located in InitCommon.js file) | |
| function initSceneData() | |
| { | |
| demoFragmentShaderFileName = 'CSG_Model_Rendering_Fragment.glsl'; | |
| // scene/demo-specific three.js objects setup goes here | |
| sceneIsDynamic = false; | |
| cameraFlightSpeed = 100; | |
| // pixelRatio is resolution - range: 0.5(half resolution) to 1.0(full resolution) | |
| pixelRatio = mouseControl ? 1.0 : 0.75; | |
| EPS_intersect = 0.01; | |
| // set camera's field of view | |
| worldCamera.fov = 50; | |
| focusDistance = 120.0; | |
| apertureChangeSpeed = 5; | |
| // position and orient camera | |
| cameraControlsObject.position.set(0, -20, 120); | |
| ///cameraControlsYawObject.rotation.y = 0.0; | |
| // look slightly upward | |
| cameraControlsPitchObject.rotation.x = 0.005; | |
| boxMaterial = new THREE.MeshBasicMaterial(); | |
| totalNumberOfShapes = 50; // 50 | |
| console.log("Shape count: " + totalNumberOfShapes); | |
| totalWorklist = new Uint32Array(totalNumberOfShapes); | |
| shape_array = new Float32Array(2048 * 2048 * 4); | |
| // 2048 = width of texture, 2048 = height of texture, 4 = r,g,b, and a components | |
| aabb_array = new Float32Array(2048 * 2048 * 4); | |
| // 2048 = width of texture, 2048 = height of texture, 4 = r,g,b, and a components | |
| let ix32 = 0; | |
| let ix9 = 0; | |
| for (let i = 0; i < totalNumberOfShapes; i++) | |
| { | |
| ix32 = i * 32; | |
| ix9 = i * 9; | |
| shape.position.set((Math.random() * 2 - 1) * 50, (Math.random() * 2 - 1) * 50, (Math.random() * 2 - 1) * 50); | |
| shape.rotation.set((Math.random() * 2 - 1) * Math.PI, (Math.random() * 2 - 1) * Math.PI, (Math.random() * 2 - 1) * Math.PI); | |
| shape.scale.set((Math.random() * 8) + 1, (Math.random() * 8) + 1, (Math.random() * 8) + 1); | |
| shape.updateMatrixWorld(true); // 'true' forces immediate matrix update | |
| invMatrix.copy(shape.matrixWorld).invert(); | |
| el = invMatrix.elements; | |
| //slot 0 Shape transform Matrix 4x4 (16 elements total) | |
| shape_array[ix32 + 0] = el[0]; // r or x // shape transform Matrix element[0] | |
| shape_array[ix32 + 1] = el[1]; // g or y // shape transform Matrix element[1] | |
| shape_array[ix32 + 2] = el[2]; // b or z // shape transform Matrix element[2] | |
| shape_array[ix32 + 3] = el[3]; // a or w // shape transform Matrix element[3] | |
| //slot 1 | |
| shape_array[ix32 + 4] = el[4]; // r or x // shape transform Matrix element[4] | |
| shape_array[ix32 + 5] = el[5]; // g or y // shape transform Matrix element[5] | |
| shape_array[ix32 + 6] = el[6]; // b or z // shape transform Matrix element[6] | |
| shape_array[ix32 + 7] = el[7]; // a or w // shape transform Matrix element[7] | |
| //slot 2 | |
| shape_array[ix32 + 8] = el[8]; // r or x // shape transform Matrix element[8] | |
| shape_array[ix32 + 9] = el[9]; // g or y // shape transform Matrix element[9] | |
| shape_array[ix32 + 10] = el[10]; // b or z // shape transform Matrix element[10] | |
| shape_array[ix32 + 11] = el[11]; // a or w // shape transform Matrix element[11] | |
| //slot 3 | |
| shape_array[ix32 + 12] = el[12]; // r or x // shape transform Matrix element[12] | |
| shape_array[ix32 + 13] = el[13]; // g or y // shape transform Matrix element[13] | |
| shape_array[ix32 + 14] = el[14]; // b or z // shape transform Matrix element[14] | |
| shape_array[ix32 + 15] = el[15]; // a or w // shape transform Matrix element[15] | |
| //slot 4 | |
| shape_array[ix32 + 16] = Math.floor(Math.random() * 5); // r or x // shape type id# (0: box, 1: sphere, 2: cylinder, 3: cone, 4: paraboloid, etc) | |
| shape_array[ix32 + 17] = 4; // g or y // material type id# (0: LIGHT, 1: DIFF, 2: REFR, 3: SPEC, 4: COAT, etc) | |
| shape_array[ix32 + 18] = 0.0; // b or z // material Metalness - default: 0.0(no metal, a dielectric), 1.0 would be purely metal | |
| shape_array[ix32 + 19] = 0.0; // a or w // material Roughness - default: 0.0(no roughness, totally smooth) | |
| //slot 5 | |
| shape_array[ix32 + 20] = Math.random(); // r or x // material albedo color R (if LIGHT, this is also its emissive color R) | |
| shape_array[ix32 + 21] = Math.random(); // g or y // material albedo color G (if LIGHT, this is also its emissive color G) | |
| shape_array[ix32 + 22] = Math.random(); // b or z // material albedo color B (if LIGHT, this is also its emissive color B) | |
| shape_array[ix32 + 23] = 1.0; // a or w // material Opacity (Alpha) - default: 1.0 (fully opaque), 0.0 is fully transparent | |
| //slot 6 | |
| shape_array[ix32 + 24] = 1.0; // r or x // material Index of Refraction(IoR) - default: 1.0(air) (or 1.5(glass), 1.33(water), etc) | |
| shape_array[ix32 + 25] = 0.0; // g or y // material ClearCoat Roughness - default: 0.0 (no roughness, totally smooth) | |
| shape_array[ix32 + 26] = 1.5; // b or z // material ClearCoat IoR - default: 1.5(thick ClearCoat) | |
| shape_array[ix32 + 27] = 0; // a or w // material data | |
| //slot 7 | |
| shape_array[ix32 + 28] = 0; // r or x // material data | |
| shape_array[ix32 + 29] = 0; // g or y // material data | |
| shape_array[ix32 + 30] = 0; // b or z // material data | |
| shape_array[ix32 + 31] = 0; // a or w // material data | |
| // if this shape is a Box, use THREE.BoxGeometry as starting point for this shape's AABB | |
| if (shape_array[ix32 + 16] == 0) | |
| boxGeometries[i] = new THREE.BoxGeometry( 2, 2, 2 ); | |
| else // else use THREE.SphereGeometry, as it produces a tighter-fitting AABB when shape is rotated | |
| boxGeometries[i] = new THREE.SphereGeometry(1.4); | |
| boxMeshes[i] = new THREE.Mesh( boxGeometries[i], boxMaterial ); | |
| boxMeshes[i].geometry.applyMatrix4(shape.matrixWorld); | |
| boxMeshes[i].geometry.computeBoundingBox(); | |
| shapeBoundingBox_minCorner.copy(boxMeshes[i].geometry.boundingBox.min); | |
| shapeBoundingBox_maxCorner.copy(boxMeshes[i].geometry.boundingBox.max); | |
| boxMeshes[i].geometry.boundingBox.getCenter(shapeBoundingBox_centroid); | |
| aabb_array[ix9 + 0] = shapeBoundingBox_minCorner.x; | |
| aabb_array[ix9 + 1] = shapeBoundingBox_minCorner.y; | |
| aabb_array[ix9 + 2] = shapeBoundingBox_minCorner.z; | |
| aabb_array[ix9 + 3] = shapeBoundingBox_maxCorner.x; | |
| aabb_array[ix9 + 4] = shapeBoundingBox_maxCorner.y; | |
| aabb_array[ix9 + 5] = shapeBoundingBox_maxCorner.z; | |
| aabb_array[ix9 + 6] = shapeBoundingBox_centroid.x; | |
| aabb_array[ix9 + 7] = shapeBoundingBox_centroid.y; | |
| aabb_array[ix9 + 8] = shapeBoundingBox_centroid.z; | |
| totalWorklist[i] = i; | |
| } // end for (let i = 0; i < totalNumberOfShapes; i++) | |
| for (let i = 0; i < totalNumberOfShapes * 2; i++) | |
| buildnodes[i] = new BVH_Node(); | |
| console.log("BvhGeneration..."); | |
| console.time("BvhGeneration"); | |
| BVH_QuickBuild(totalWorklist, aabb_array); | |
| console.timeEnd("BvhGeneration"); | |
| shapeDataTexture = new THREE.DataTexture(shape_array, | |
| 2048, | |
| 2048, | |
| THREE.RGBAFormat, | |
| THREE.FloatType, | |
| THREE.Texture.DEFAULT_MAPPING, | |
| THREE.ClampToEdgeWrapping, | |
| THREE.ClampToEdgeWrapping, | |
| THREE.NearestFilter, | |
| THREE.NearestFilter, | |
| 1, | |
| THREE.NoColorSpace); | |
| shapeDataTexture.flipY = false; | |
| shapeDataTexture.generateMipmaps = false; | |
| shapeDataTexture.needsUpdate = true; | |
| aabbDataTexture = new THREE.DataTexture(aabb_array, | |
| 2048, | |
| 2048, | |
| 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; | |
| // In addition to the default GUI on all demos, add any special GUI elements that this particular demo requires | |
| outline_IntensityObject = { Outline_Intensity: 2.0 }; | |
| outline_ColorObject = { Outline_Color: [1, 1, 1] }; | |
| function handleOutlineColorChange() { needChangeOutlineColor = true; } | |
| function handleOutlineIntensityChange() { needChangeOutlineIntensity = true; } | |
| outline_IntensityController = gui.add(outline_IntensityObject, 'Outline_Intensity', 0, 20, 0.5).onChange(handleOutlineIntensityChange); | |
| outline_ColorController = gui.addColor(outline_ColorObject, 'Outline_Color').onChange(handleOutlineColorChange); | |
| handleOutlineColorChange(); | |
| handleOutlineIntensityChange(); | |
| // scene/demo-specific uniforms go here | |
| pathTracingUniforms.tShape_DataTexture = { value: shapeDataTexture }; | |
| pathTracingUniforms.tAABB_DataTexture = { value: aabbDataTexture }; | |
| pathTracingUniforms.uOutlineIntensity = { value: 2.0 }; | |
| pathTracingUniforms.uOutlineColor = { value: new THREE.Color() }; | |
| } // end function initSceneData() | |
| // called automatically from within the animate() function (located in InitCommon.js file) | |
| function updateVariablesAndUniforms() | |
| { | |
| if (needChangeOutlineIntensity) | |
| { | |
| pathTracingUniforms.uOutlineIntensity.value = outline_IntensityController.getValue(); | |
| cameraIsMoving = true; | |
| needChangeOutlineIntensity = false; | |
| } | |
| if (needChangeOutlineColor) | |
| { | |
| outlineColor = outline_ColorController.getValue(); | |
| pathTracingUniforms.uOutlineColor.value.setRGB(outlineColor[0], outlineColor[1], outlineColor[2]); | |
| cameraIsMoving = true; | |
| needChangeOutlineColor = false; | |
| } | |
| // INFO | |
| cameraInfoElement.innerHTML = "FOV: " + worldCamera.fov + " / Aperture: " + apertureSize.toFixed(2) + " / FocusDistance: " + focusDistance + "<br>" + "Samples: " + sampleCounter; | |
| } // end function updateVariablesAndUniforms() | |
| init(); // init app and start animating |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment