├── AssetManager.js ├── EffectShader.js ├── LICENSE ├── diamond.glb ├── index.html ├── main.js ├── skybox ├── Box_Back.bmp ├── Box_Bottom.bmp ├── Box_Front.bmp ├── Box_Left.bmp ├── Box_Right.bmp └── Box_Top.bmp └── stats.js /AssetManager.js: -------------------------------------------------------------------------------- 1 | import * as THREE from 'https://cdn.skypack.dev/three@0.142.0'; 2 | import { 3 | GLTFLoader 4 | } from 'https://unpkg.com/three@0.142.0/examples/jsm/loaders/GLTFLoader.js'; 5 | const AssetManager = {}; 6 | AssetManager.gltfLoader = new GLTFLoader(); 7 | AssetManager.audioLoader = new THREE.AudioLoader(); 8 | AssetManager.loadGLTFAsync = (url) => { 9 | return new Promise((resolve, reject) => { 10 | AssetManager.gltfLoader.load(url, obj => { 11 | resolve(obj); 12 | }) 13 | }); 14 | } 15 | 16 | AssetManager.loadAudioAsync = (url) => { 17 | return new Promise((resolve, reject) => { 18 | AssetManager.audioLoader.load(url, (buffer) => { 19 | resolve(buffer); 20 | }); 21 | }) 22 | } 23 | 24 | AssetManager.loadTextureAsync = (url) => { 25 | return new Promise((resolve, reject) => { 26 | THREE.ImageUtils.loadTexture(url, null, (tex) => { 27 | resolve(tex); 28 | }) 29 | }) 30 | } 31 | AssetManager.loadAll = (promiseArr, element, message) => { 32 | let count = promiseArr.length; 33 | let results = []; 34 | element.innerHTML = `${message} (${promiseArr.length - count} / ${promiseArr.length})...` 35 | return new Promise((resolve, reject) => { 36 | promiseArr.forEach((promise, i) => { 37 | promise.then(result => { 38 | results[i] = result; 39 | count--; 40 | element.innerHTML = `${message} (${promiseArr.length - count} / ${promiseArr.length})...` 41 | if (count === 0) { 42 | resolve(results); 43 | } 44 | }) 45 | }) 46 | }); 47 | } 48 | export { AssetManager }; -------------------------------------------------------------------------------- /EffectShader.js: -------------------------------------------------------------------------------- 1 | import * as THREE from 'https://cdn.skypack.dev/three@0.142.0'; 2 | const EffectShader = { 3 | 4 | uniforms: { 5 | 6 | 'sceneDiffuse': { value: null } 7 | }, 8 | 9 | vertexShader: /* glsl */ ` 10 | varying vec2 vUv; 11 | void main() { 12 | vUv = uv; 13 | gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); 14 | }`, 15 | 16 | fragmentShader: /* glsl */ ` 17 | uniform sampler2D sceneDiffuse; 18 | varying vec2 vUv; 19 | void main() { 20 | vec4 diffuse = texture2D(sceneDiffuse, vUv); 21 | gl_FragColor = vec4(diffuse.rgb, 1.0); 22 | }` 23 | 24 | }; 25 | 26 | export { EffectShader }; -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2022 N8python 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /diamond.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N8python/diamonds/69b30cc5586195461f47e0b25ccf14578b292cc0/diamond.glb -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Total Internal Reflection 9 | 10 | 11 | 12 | 13 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /main.js: -------------------------------------------------------------------------------- 1 | import * as THREE from 'https://cdn.skypack.dev/three@0.142.0'; 2 | import { EffectComposer } from 'https://unpkg.com/three@0.142.0/examples/jsm/postprocessing/EffectComposer.js'; 3 | import { RenderPass } from 'https://unpkg.com/three@0.142.0/examples/jsm/postprocessing/RenderPass.js'; 4 | import { ShaderPass } from 'https://unpkg.com/three@0.142.0/examples/jsm/postprocessing/ShaderPass.js'; 5 | import { SMAAPass } from 'https://unpkg.com/three@0.142.0/examples/jsm/postprocessing/SMAAPass.js'; 6 | import { GammaCorrectionShader } from 'https://unpkg.com/three@0.142.0/examples/jsm/shaders/GammaCorrectionShader.js'; 7 | import { EffectShader } from "./EffectShader.js"; 8 | import { OrbitControls } from 'https://unpkg.com/three@0.142.0/examples/jsm/controls/OrbitControls.js'; 9 | import { AssetManager } from './AssetManager.js'; 10 | import { Stats } from "./stats.js"; 11 | import { 12 | MeshBVH, 13 | MeshBVHVisualizer, 14 | MeshBVHUniformStruct, 15 | FloatVertexAttributeTexture, 16 | shaderStructs, 17 | shaderIntersectFunction, 18 | SAH 19 | } from 'https://unpkg.com/three-mesh-bvh@0.5.10/build/index.module.js'; 20 | import { GUI } from 'https://unpkg.com/three@0.138.0/examples/jsm/libs/lil-gui.module.min.js'; 21 | async function main() { 22 | // Setup basic renderer, controls, and profiler 23 | const clientWidth = window.innerWidth * 0.99; 24 | const clientHeight = window.innerHeight * 0.98; 25 | const scene = new THREE.Scene(); 26 | const camera = new THREE.PerspectiveCamera(75, clientWidth / clientHeight, 0.1, 1000); 27 | camera.position.set(50, 75, 50); 28 | const renderer = new THREE.WebGLRenderer(); 29 | renderer.setSize(clientWidth, clientHeight); 30 | document.body.appendChild(renderer.domElement); 31 | renderer.shadowMap.enabled = true; 32 | renderer.shadowMap.type = THREE.VSMShadowMap; 33 | const controls = new OrbitControls(camera, renderer.domElement); 34 | controls.target.set(0, 25, 0); 35 | const stats = new Stats(); 36 | stats.showPanel(0); 37 | document.body.appendChild(stats.dom); 38 | // Setup scene 39 | // Skybox 40 | const environment = await new THREE.CubeTextureLoader().loadAsync([ 41 | "skybox/Box_Right.bmp", 42 | "skybox/Box_Left.bmp", 43 | "skybox/Box_Top.bmp", 44 | "skybox/Box_Bottom.bmp", 45 | "skybox/Box_Front.bmp", 46 | "skybox/Box_Back.bmp" 47 | ]); 48 | environment.encoding = THREE.sRGBEncoding; 49 | scene.background = environment; 50 | // Lighting 51 | const ambientLight = new THREE.AmbientLight(new THREE.Color(1.0, 1.0, 1.0), 0.25); 52 | scene.add(ambientLight); 53 | const directionalLight = new THREE.DirectionalLight(0xffffff, 0.35); 54 | directionalLight.position.set(150, 200, 50); 55 | // Shadows 56 | directionalLight.castShadow = true; 57 | directionalLight.shadow.mapSize.width = 1024; 58 | directionalLight.shadow.mapSize.height = 1024; 59 | directionalLight.shadow.camera.left = -75; 60 | directionalLight.shadow.camera.right = 75; 61 | directionalLight.shadow.camera.top = 75; 62 | directionalLight.shadow.camera.bottom = -75; 63 | directionalLight.shadow.camera.near = 0.1; 64 | directionalLight.shadow.camera.far = 500; 65 | directionalLight.shadow.bias = -0.001; 66 | directionalLight.shadow.blurSamples = 8; 67 | directionalLight.shadow.radius = 4; 68 | scene.add(directionalLight); 69 | const directionalLight2 = new THREE.DirectionalLight(0xffffff, 0.15); 70 | directionalLight2.color.setRGB(1.0, 1.0, 1.0); 71 | directionalLight2.position.set(-50, 200, -150); 72 | scene.add(directionalLight2); 73 | // Objects 74 | const ground = new THREE.Mesh(new THREE.PlaneGeometry(100, 100).applyMatrix4(new THREE.Matrix4().makeRotationX(-Math.PI / 2)), new THREE.MeshStandardMaterial({ side: THREE.DoubleSide })); 75 | ground.castShadow = true; 76 | ground.receiveShadow = true; 77 | scene.add(ground); 78 | const box = new THREE.Mesh(new THREE.BoxGeometry(10, 10, 10), new THREE.MeshStandardMaterial({ side: THREE.DoubleSide, color: new THREE.Color(1.0, 0.0, 0.0) })); 79 | box.castShadow = true; 80 | box.receiveShadow = true; 81 | box.position.y = 5.01; 82 | //scene.add(box); 83 | const sphere = new THREE.Mesh(new THREE.SphereGeometry(6.25, 32, 32), new THREE.MeshStandardMaterial({ side: THREE.DoubleSide, envMap: environment, metalness: 1.0, roughness: 0.25 })); 84 | sphere.position.y = 7.5; 85 | sphere.position.x = 25; 86 | sphere.position.z = 25; 87 | sphere.castShadow = true; 88 | sphere.receiveShadow = true; 89 | //scene.add(sphere); 90 | const torusKnot = new THREE.Mesh(new THREE.TorusKnotGeometry(5, 1.5, 200, 32), new THREE.MeshStandardMaterial({ side: THREE.DoubleSide, envMap: environment, metalness: 0.5, roughness: 0.5, color: new THREE.Color(0.0, 1.0, 0.0) })); 91 | torusKnot.position.y = 10; 92 | torusKnot.position.x = -25; 93 | torusKnot.position.z = -25; 94 | torusKnot.castShadow = true; 95 | torusKnot.receiveShadow = true; 96 | // scene.add(torusKnot); 97 | let diamondGeo = (await AssetManager.loadGLTFAsync("diamond.glb")).scene.children[0].children[0].children[0].children[0].children[0].geometry; 98 | diamondGeo.scale(10, 10, 10); 99 | diamondGeo.translate(0, 5, 0); 100 | const cubeRenderTarget = new THREE.WebGLCubeRenderTarget(256, { generateMipmaps: true, minFilter: THREE.LinearMipmapLinearFilter }); 101 | const cubeCamera = new THREE.CubeCamera(1, 100000, cubeRenderTarget); 102 | scene.add(cubeCamera); 103 | cubeCamera.position.set(0, 5, 0); 104 | cubeCamera.update(renderer, scene); 105 | //scene.background = cubeRenderTarget.texture; 106 | const makeDiamond = (geo, { 107 | color = new THREE.Color(1, 1, 1), 108 | ior = 2.4 109 | } = {}) => { 110 | const mergedGeometry = geo; 111 | mergedGeometry.boundsTree = new MeshBVH(mergedGeometry.toNonIndexed(), { lazyGeneration: false, strategy: SAH }); 112 | const collider = new THREE.Mesh(mergedGeometry); 113 | collider.material.wireframe = true; 114 | collider.material.opacity = 0.5; 115 | collider.material.transparent = true; 116 | collider.visible = false; 117 | collider.boundsTree = mergedGeometry.boundsTree; 118 | scene.add(collider); 119 | const visualizer = new MeshBVHVisualizer(collider, 20); 120 | visualizer.visible = false; 121 | visualizer.update(); 122 | scene.add(visualizer); 123 | const diamond = new THREE.Mesh(geo, new THREE.ShaderMaterial({ 124 | uniforms: { 125 | envMap: { value: environment }, 126 | bvh: { value: new MeshBVHUniformStruct() }, 127 | bounces: { value: 3 }, 128 | color: { value: color }, 129 | ior: { value: ior }, 130 | correctMips: { value: true }, 131 | projectionMatrixInv: { value: camera.projectionMatrixInverse }, 132 | viewMatrixInv: { value: camera.matrixWorld }, 133 | chromaticAberration: { value: true }, 134 | aberrationStrength: { value: 0.01 }, 135 | resolution: { value: new THREE.Vector2(clientWidth, clientHeight) } 136 | }, 137 | vertexShader: /*glsl*/ ` 138 | varying vec3 vWorldPosition; 139 | varying vec3 vNormal; 140 | uniform mat4 viewMatrixInv; 141 | void main() { 142 | vWorldPosition = (modelMatrix * vec4(position, 1.0)).xyz; 143 | vNormal = (viewMatrixInv * vec4(normalMatrix * normal, 0.0)).xyz; 144 | gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(position, 1.0); 145 | } 146 | `, 147 | fragmentShader: /*glsl*/ ` 148 | precision highp isampler2D; 149 | precision highp usampler2D; 150 | varying vec3 vWorldPosition; 151 | varying vec3 vNormal; 152 | uniform samplerCube envMap; 153 | uniform float bounces; 154 | ${ shaderStructs } 155 | ${ shaderIntersectFunction } 156 | uniform BVH bvh; 157 | uniform float ior; 158 | uniform vec3 color; 159 | uniform bool correctMips; 160 | uniform bool chromaticAberration; 161 | uniform mat4 projectionMatrixInv; 162 | uniform mat4 viewMatrixInv; 163 | uniform mat4 modelMatrix; 164 | uniform vec2 resolution; 165 | uniform bool chromaticAbberation; 166 | uniform float aberrationStrength; 167 | vec3 totalInternalReflection(vec3 ro, vec3 rd, vec3 normal, float ior, mat4 modelMatrixInverse) { 168 | vec3 rayOrigin = ro; 169 | vec3 rayDirection = rd; 170 | rayDirection = refract(rayDirection, normal, 1.0 / ior); 171 | rayOrigin = vWorldPosition + rayDirection * 0.001; 172 | rayOrigin = (modelMatrixInverse * vec4(rayOrigin, 1.0)).xyz; 173 | rayDirection = normalize((modelMatrixInverse * vec4(rayDirection, 0.0)).xyz); 174 | for(float i = 0.0; i < bounces; i++) { 175 | uvec4 faceIndices = uvec4( 0u ); 176 | vec3 faceNormal = vec3( 0.0, 0.0, 1.0 ); 177 | vec3 barycoord = vec3( 0.0 ); 178 | float side = 1.0; 179 | float dist = 0.0; 180 | bvhIntersectFirstHit( bvh, rayOrigin, rayDirection, faceIndices, faceNormal, barycoord, side, dist ); 181 | vec3 hitPos = rayOrigin + rayDirection * max(dist - 0.001, 0.0); 182 | // faceNormal *= side; 183 | vec3 tempDir = refract(rayDirection, faceNormal, ior); 184 | if (length(tempDir) != 0.0) { 185 | rayDirection = tempDir; 186 | break; 187 | } 188 | rayDirection = reflect(rayDirection, faceNormal); 189 | rayOrigin = hitPos + rayDirection * 0.01; 190 | } 191 | rayDirection = normalize((modelMatrix * vec4(rayDirection, 0.0)).xyz); 192 | return rayDirection; 193 | } 194 | void main() { 195 | mat4 modelMatrixInverse = inverse(modelMatrix); 196 | vec2 uv = gl_FragCoord.xy / resolution; 197 | vec3 directionCamPerfect = (projectionMatrixInv * vec4(uv * 2.0 - 1.0, 0.0, 1.0)).xyz; 198 | directionCamPerfect = (viewMatrixInv * vec4(directionCamPerfect, 0.0)).xyz; 199 | directionCamPerfect = normalize(directionCamPerfect); 200 | vec3 normal = vNormal; 201 | vec3 rayOrigin = cameraPosition; 202 | vec3 rayDirection = normalize(vWorldPosition - cameraPosition); 203 | vec3 finalColor; 204 | if (chromaticAberration) { 205 | vec3 rayDirectionR = totalInternalReflection(rayOrigin, rayDirection, normal, max(ior * (1.0 - aberrationStrength), 1.0), modelMatrixInverse); 206 | vec3 rayDirectionG = totalInternalReflection(rayOrigin, rayDirection, normal, max(ior, 1.0), modelMatrixInverse); 207 | vec3 rayDirectionB = totalInternalReflection(rayOrigin, rayDirection, normal, max(ior * (1.0 + aberrationStrength), 1.0), modelMatrixInverse); 208 | float finalColorR = textureGrad(envMap, rayDirectionR, dFdx(correctMips ? directionCamPerfect: rayDirection), dFdy(correctMips ? directionCamPerfect: rayDirection)).r; 209 | float finalColorG = textureGrad(envMap, rayDirectionG, dFdx(correctMips ? directionCamPerfect: rayDirection), dFdy(correctMips ? directionCamPerfect: rayDirection)).g; 210 | float finalColorB = textureGrad(envMap, rayDirectionB, dFdx(correctMips ? directionCamPerfect: rayDirection), dFdy(correctMips ? directionCamPerfect: rayDirection)).b; 211 | finalColor = vec3(finalColorR, finalColorG, finalColorB) * color; 212 | } else { 213 | rayDirection = totalInternalReflection(rayOrigin, rayDirection, normal, max(ior, 1.0), modelMatrixInverse); 214 | finalColor = textureGrad(envMap, rayDirection, dFdx(correctMips ? directionCamPerfect: rayDirection), dFdy(correctMips ? directionCamPerfect: rayDirection)).rgb; 215 | finalColor *= color; 216 | } 217 | gl_FragColor = vec4(vec3(finalColor), 1.0); 218 | } 219 | ` 220 | })); 221 | diamond.material.uniforms.bvh.value.updateFrom(collider.boundsTree); 222 | diamond.castShadow = true; 223 | diamond.receiveShadow = true; 224 | return diamond; 225 | } 226 | /*const gemGeo = new THREE.BufferGeometry(); 227 | const baseX = 1; 228 | const baseY = 1; 229 | const height = 5; 230 | const heightTop = 3; 231 | const scale = 1.5; 232 | const positions = []; 233 | positions.push(baseX / 2, baseY / 2, 0); 234 | positions.push(baseX / 2, -baseY / 2, 0); 235 | positions.push(-baseX / 2, -baseY / 2, 0); 236 | positions.push(-baseX / 2, -baseY / 2, 0); 237 | positions.push(-baseX / 2, baseY / 2, 0); 238 | positions.push(baseX / 2, baseY / 2, 0); 239 | 240 | positions.push(baseX / 2 * scale, -baseY / 2 * scale, height); 241 | positions.push(-baseX / 2 * scale, -baseY / 2 * scale, height); 242 | positions.push(-baseX / 2, -baseY / 2, 0); 243 | positions.push(-baseX / 2, -baseY / 2, 0); 244 | positions.push(baseX / 2, -baseY / 2, 0); 245 | positions.push(baseX / 2 * scale, -baseY / 2 * scale, height); 246 | 247 | positions.push(baseX / 2 * scale, baseY / 2 * scale, height); 248 | positions.push(baseX / 2 * scale, -baseY / 2 * scale, height); 249 | positions.push(baseX / 2, -baseY / 2, 0); 250 | positions.push(baseX / 2, -baseY / 2, 0); 251 | positions.push(baseX / 2, baseY / 2, 0); 252 | positions.push(baseX / 2 * scale, baseY / 2 * scale, height); 253 | 254 | positions.push(-baseX / 2 * scale, baseY / 2 * scale, height); 255 | positions.push(baseX / 2 * scale, baseY / 2 * scale, height); 256 | positions.push(baseX / 2, baseY / 2, 0); 257 | positions.push(baseX / 2, baseY / 2, 0); 258 | positions.push(-baseX / 2, baseY / 2, 0); 259 | positions.push(-baseX / 2 * scale, baseY / 2 * scale, height); 260 | 261 | positions.push(-baseX / 2 * scale, -baseY / 2 * scale, height); 262 | positions.push(-baseX / 2 * scale, baseY / 2 * scale, height); 263 | positions.push(-baseX / 2, baseY / 2, 0); 264 | positions.push(-baseX / 2, baseY / 2, 0); 265 | positions.push(-baseX / 2, -baseY / 2, 0); 266 | positions.push(-baseX / 2 * scale, -baseY / 2 * scale, height); 267 | 268 | positions.push(-baseX / 2 * scale, -baseY / 2 * scale, height); 269 | positions.push(baseX / 2 * scale, -baseY / 2 * scale, height); 270 | positions.push(0, 0, height + heightTop); 271 | positions.push(baseX / 2 * scale, -baseY / 2 * scale, height); 272 | positions.push(baseX / 2 * scale, baseY / 2 * scale, height); 273 | positions.push(0, 0, height + heightTop); 274 | positions.push(baseX / 2 * scale, baseY / 2 * scale, height); 275 | positions.push(-baseX / 2 * scale, baseY / 2 * scale, height); 276 | positions.push(0, 0, height + heightTop); 277 | positions.push(-baseX / 2 * scale, baseY / 2 * scale, height); 278 | positions.push(-baseX / 2 * scale, -baseY / 2 * scale, height); 279 | positions.push(0, 0, height + heightTop); 280 | 281 | gemGeo.setAttribute("position", new THREE.BufferAttribute(new Float32Array(positions), 3)); 282 | gemGeo.computeVertexNormals(); 283 | //gemGeo.scale(10, 10, 10); 284 | //gemGeo.rotateX(-Math.PI / 2); 285 | 286 | //const gem 287 | for (let x = 0; x < 5; x++) { 288 | for (let y = 0; y < 5; y++) { 289 | const clusterColor = new THREE.Vector3(Math.random(), Math.random(), Math.random()); 290 | for (let i = 0; i < 10; i++) { 291 | const newGeo = gemGeo.clone(); 292 | newGeo.scale(1.0 + Math.random(), 1.0 + Math.random(), 1.0 + Math.random()); 293 | newGeo.rotateX(-Math.PI / 2 * (0.85 + 0.3 * Math.random())); 294 | newGeo.rotateZ((2.5 / 1.5) * (Math.random() - 0.5)); 295 | newGeo.rotateY((3.0 / 1.5) * (Math.random() - 0.5)); 296 | newGeo.translate(-32 + x * 16, 0, -32 + y * 16); 297 | const diamond = makeDiamond(newGeo, { 298 | color: clusterColor.add(new THREE.Vector3(Math.random() - 0.5, Math.random() - 0.5, Math.random() - 0.5).multiplyScalar(0.25)), 299 | }); 300 | scene.add(diamond); 301 | } 302 | } 303 | }*/ 304 | const diamond = makeDiamond(diamondGeo); 305 | scene.add(diamond); 306 | // Build postprocessing stack 307 | // Render Targets 308 | const defaultTexture = new THREE.WebGLRenderTarget(clientWidth, clientHeight, { 309 | minFilter: THREE.LinearFilter, 310 | magFilter: THREE.NearestFilter 311 | }); 312 | defaultTexture.depthTexture = new THREE.DepthTexture(clientWidth, clientHeight, THREE.FloatType); 313 | // Post Effects 314 | const composer = new EffectComposer(renderer); 315 | const smaaPass = new SMAAPass(clientWidth, clientHeight); 316 | const effectPass = new ShaderPass(EffectShader); 317 | composer.addPass(effectPass); 318 | composer.addPass(new ShaderPass(GammaCorrectionShader)); 319 | composer.addPass(smaaPass); 320 | const effectController = { 321 | bounces: 3.0, 322 | ior: 2.4, 323 | correctMips: true, 324 | chromaticAberration: true, 325 | aberrationStrength: 0.01 326 | }; 327 | const gui = new GUI(); 328 | gui.add(effectController, "bounces", 1.0, 10.0, 1.0).name("Bounces"); 329 | gui.add(effectController, "ior", 1.0, 5.0, 0.01).name("IOR"); 330 | gui.add(effectController, "correctMips"); 331 | gui.add(effectController, "chromaticAberration"); 332 | gui.add(effectController, "aberrationStrength", 0.00, 1.0, 0.0001).name("Aberration Strength"); 333 | 334 | function animate() { 335 | diamond.material.uniforms.bounces.value = effectController.bounces; 336 | diamond.material.uniforms.ior.value = effectController.ior; 337 | diamond.material.uniforms.correctMips.value = effectController.correctMips; 338 | diamond.material.uniforms.chromaticAberration.value = effectController.chromaticAberration; 339 | diamond.material.uniforms.aberrationStrength.value = effectController.aberrationStrength; 340 | diamond.rotation.y += 0.01; 341 | diamond.updateMatrix(); 342 | diamond.updateMatrixWorld(); 343 | renderer.setRenderTarget(defaultTexture); 344 | renderer.clear(); 345 | renderer.render(scene, camera); 346 | effectPass.uniforms["sceneDiffuse"].value = defaultTexture.texture; 347 | composer.render(); 348 | controls.update(); 349 | stats.update(); 350 | requestAnimationFrame(animate); 351 | } 352 | requestAnimationFrame(animate); 353 | } 354 | main(); -------------------------------------------------------------------------------- /skybox/Box_Back.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N8python/diamonds/69b30cc5586195461f47e0b25ccf14578b292cc0/skybox/Box_Back.bmp -------------------------------------------------------------------------------- /skybox/Box_Bottom.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N8python/diamonds/69b30cc5586195461f47e0b25ccf14578b292cc0/skybox/Box_Bottom.bmp -------------------------------------------------------------------------------- /skybox/Box_Front.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N8python/diamonds/69b30cc5586195461f47e0b25ccf14578b292cc0/skybox/Box_Front.bmp -------------------------------------------------------------------------------- /skybox/Box_Left.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N8python/diamonds/69b30cc5586195461f47e0b25ccf14578b292cc0/skybox/Box_Left.bmp -------------------------------------------------------------------------------- /skybox/Box_Right.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N8python/diamonds/69b30cc5586195461f47e0b25ccf14578b292cc0/skybox/Box_Right.bmp -------------------------------------------------------------------------------- /skybox/Box_Top.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N8python/diamonds/69b30cc5586195461f47e0b25ccf14578b292cc0/skybox/Box_Top.bmp -------------------------------------------------------------------------------- /stats.js: -------------------------------------------------------------------------------- 1 | var Stats = function() { 2 | 3 | var mode = 0; 4 | 5 | var container = document.createElement('div'); 6 | container.style.cssText = 'position:fixed;top:0;left:0;cursor:pointer;opacity:0.9;z-index:10000'; 7 | container.addEventListener('click', function(event) { 8 | 9 | event.preventDefault(); 10 | showPanel(++mode % container.children.length); 11 | 12 | }, false); 13 | 14 | // 15 | 16 | function addPanel(panel) { 17 | 18 | container.appendChild(panel.dom); 19 | return panel; 20 | 21 | } 22 | 23 | function showPanel(id) { 24 | 25 | for (var i = 0; i < container.children.length; i++) { 26 | 27 | container.children[i].style.display = i === id ? 'block' : 'none'; 28 | 29 | } 30 | 31 | mode = id; 32 | 33 | } 34 | 35 | // 36 | 37 | var beginTime = (performance || Date).now(), 38 | prevTime = beginTime, 39 | frames = 0; 40 | 41 | var fpsPanel = addPanel(new Stats.Panel('FPS', '#0ff', '#002')); 42 | var msPanel = addPanel(new Stats.Panel('MS', '#0f0', '#020')); 43 | 44 | if (self.performance && self.performance.memory) { 45 | 46 | var memPanel = addPanel(new Stats.Panel('MB', '#f08', '#201')); 47 | 48 | } 49 | 50 | showPanel(0); 51 | 52 | return { 53 | 54 | REVISION: 16, 55 | 56 | dom: container, 57 | 58 | addPanel: addPanel, 59 | showPanel: showPanel, 60 | 61 | begin: function() { 62 | 63 | beginTime = (performance || Date).now(); 64 | 65 | }, 66 | 67 | end: function() { 68 | 69 | frames++; 70 | 71 | var time = (performance || Date).now(); 72 | 73 | msPanel.update(time - beginTime, 200); 74 | 75 | if (time >= prevTime + 1000) { 76 | 77 | fpsPanel.update((frames * 1000) / (time - prevTime), 100); 78 | 79 | prevTime = time; 80 | frames = 0; 81 | 82 | if (memPanel) { 83 | 84 | var memory = performance.memory; 85 | memPanel.update(memory.usedJSHeapSize / 1048576, memory.jsHeapSizeLimit / 1048576); 86 | 87 | } 88 | 89 | } 90 | 91 | return time; 92 | 93 | }, 94 | 95 | update: function() { 96 | 97 | beginTime = this.end(); 98 | 99 | }, 100 | 101 | // Backwards Compatibility 102 | 103 | domElement: container, 104 | setMode: showPanel 105 | 106 | }; 107 | 108 | }; 109 | 110 | Stats.Panel = function(name, fg, bg) { 111 | 112 | var min = Infinity, 113 | max = 0, 114 | round = Math.round; 115 | var PR = round(window.devicePixelRatio || 1); 116 | 117 | var WIDTH = 80 * PR, 118 | HEIGHT = 48 * PR, 119 | TEXT_X = 3 * PR, 120 | TEXT_Y = 2 * PR, 121 | GRAPH_X = 3 * PR, 122 | GRAPH_Y = 15 * PR, 123 | GRAPH_WIDTH = 74 * PR, 124 | GRAPH_HEIGHT = 30 * PR; 125 | 126 | var canvas = document.createElement('canvas'); 127 | canvas.width = WIDTH; 128 | canvas.height = HEIGHT; 129 | canvas.style.cssText = 'width:80px;height:48px'; 130 | 131 | var context = canvas.getContext('2d'); 132 | context.font = 'bold ' + (9 * PR) + 'px Helvetica,Arial,sans-serif'; 133 | context.textBaseline = 'top'; 134 | 135 | context.fillStyle = bg; 136 | context.fillRect(0, 0, WIDTH, HEIGHT); 137 | 138 | context.fillStyle = fg; 139 | context.fillText(name, TEXT_X, TEXT_Y); 140 | context.fillRect(GRAPH_X, GRAPH_Y, GRAPH_WIDTH, GRAPH_HEIGHT); 141 | 142 | context.fillStyle = bg; 143 | context.globalAlpha = 0.9; 144 | context.fillRect(GRAPH_X, GRAPH_Y, GRAPH_WIDTH, GRAPH_HEIGHT); 145 | 146 | return { 147 | 148 | dom: canvas, 149 | 150 | update: function(value, maxValue) { 151 | 152 | min = Math.min(min, value); 153 | max = Math.max(max, value); 154 | 155 | context.fillStyle = bg; 156 | context.globalAlpha = 1; 157 | context.fillRect(0, 0, WIDTH, GRAPH_Y); 158 | context.fillStyle = fg; 159 | context.fillText(round(value) + ' ' + name + ' (' + round(min) + '-' + round(max) + ')', TEXT_X, TEXT_Y); 160 | 161 | context.drawImage(canvas, GRAPH_X + PR, GRAPH_Y, GRAPH_WIDTH - PR, GRAPH_HEIGHT, GRAPH_X, GRAPH_Y, GRAPH_WIDTH - PR, GRAPH_HEIGHT); 162 | 163 | context.fillRect(GRAPH_X + GRAPH_WIDTH - PR, GRAPH_Y, PR, GRAPH_HEIGHT); 164 | 165 | context.fillStyle = bg; 166 | context.globalAlpha = 0.9; 167 | context.fillRect(GRAPH_X + GRAPH_WIDTH - PR, GRAPH_Y, PR, round((1 - (value / maxValue)) * GRAPH_HEIGHT)); 168 | 169 | } 170 | 171 | }; 172 | 173 | }; 174 | export { Stats }; --------------------------------------------------------------------------------