├── AssetManager.js ├── Butterfly.js ├── CapsuleEntity.js ├── CustomLightShadowFragment.js ├── EffectCompositer.js ├── EffectShader.js ├── GodRays.js ├── SSAOShader.js ├── background.png ├── barkcolor.jpeg ├── barkmap.jpeg ├── butterfly.glb ├── dandelion.png ├── dandelionsmall.png ├── dandelionspore.png ├── grassalbido.jpeg ├── grassalpha.jpg ├── index.html ├── leafalpha.png ├── leafcolor.jpeg ├── leafnormal.png ├── leafnormal2.png ├── main.js ├── noise.js ├── rockcolor1.jpeg ├── rockcolor2.jpeg ├── rockcolor3.jpeg ├── rockcolor4.jpeg ├── rockcolor5.jpeg ├── rocknormal1.png ├── rocknormal2.png ├── rocknormal3.png ├── rocknormal4.png ├── rocknormal5.png ├── skybox ├── Box_Back.bmp ├── Box_Bottom.bmp ├── Box_Front.bmp ├── Box_Left.bmp ├── Box_Right.bmp └── Box_Top.bmp ├── stats.js ├── terraincolor.png ├── waternormal.jpeg └── waternormal2.png /AssetManager.js: -------------------------------------------------------------------------------- 1 | import * as THREE from 'https://cdn.skypack.dev/three@0.138.0'; 2 | import { 3 | GLTFLoader 4 | } from 'https://unpkg.com/three@0.138.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 }; -------------------------------------------------------------------------------- /Butterfly.js: -------------------------------------------------------------------------------- 1 | import * as THREE from 'https://cdn.skypack.dev/three@0.138.0'; 2 | class Butterfly { 3 | constructor(model, animations, { 4 | position, 5 | scene 6 | }) { 7 | this.mesh = model.clone(); 8 | this.mixer = new THREE.AnimationMixer(this.mesh); 9 | this.animations = []; 10 | animations.forEach(clip => { 11 | this.animations.push(this.mixer.clipAction(clip)); 12 | }); 13 | this.mesh.position.copy(position); 14 | const timing = Math.random(); 15 | this.animations.forEach(animation => { 16 | animation.timeScale = 2; 17 | animation.time = timing; 18 | animation.play(); 19 | }); 20 | this.state = "fly"; 21 | this.oldDirection = new THREE.Quaternion(); 22 | this.newDirection = new THREE.Quaternion(); 23 | this.target = this.mesh.position.clone(); 24 | this.newTarget = null; 25 | this.scene = scene; 26 | this.marker = new THREE.Mesh(new THREE.SphereGeometry(1, 32, 32), new THREE.MeshStandardMaterial({ color: new THREE.Color(1, 1, 0) })); 27 | this.marker.visible = false; 28 | this.transitionFactor = 0; 29 | this.aimUp = false; 30 | this.toLand = false; 31 | this.noFollow = 0; 32 | scene.add(this.marker); 33 | } 34 | update(delta, collider, { 35 | flower, 36 | flowerPos, 37 | playerPos, 38 | butterflies, 39 | timeScale 40 | }) { 41 | this.mixer.update(delta); 42 | this.noFollow--; 43 | if (this.state === "fly") { 44 | if (!this.newTarget) { 45 | for (let i = 0; i < 1000; i++) { 46 | this.newTarget = this.mesh.position.clone().add(new THREE.Vector3(Math.random() * 100 - 50, Math.random() * 20 - 10, Math.random() * 100 - 50)); 47 | if (this.newTarget.x < -256 || this.newTarget.x > 256 || this.newTarget.z < -256 || this.newTarget.z > 256 || 48 | this.newTarget.y < 10 || this.newTarget.y > 100) { 49 | continue; 50 | } 51 | 52 | const ray = new THREE.Ray(this.mesh.position, this.newTarget.clone().sub(this.mesh.position).normalize()); 53 | if (this.runAway) { 54 | if (ray.direction.dot(playerPos.clone().sub(this.mesh.position).normalize()) > 0) { 55 | continue; 56 | } 57 | } 58 | let hit = collider.geometry.boundsTree.raycastFirst(ray, THREE.DoubleSide); 59 | if (!hit || 60 | (hit.point.distanceTo(this.mesh.position) > this.mesh.position.distanceTo(this.newTarget) + 5)) { 61 | this.mesh.lookAt(this.target); 62 | this.oldDirection.copy(this.mesh.quaternion); 63 | this.mesh.lookAt(this.newTarget); 64 | this.newDirection.copy(this.mesh.quaternion); 65 | this.transitionFactor = 0; 66 | break; 67 | } 68 | if (hit && hit.face.normal.dot(ray.direction) < 0 && (hit.point.distanceTo(this.mesh.position) < this.mesh.position.distanceTo(this.newTarget))) { 69 | this.newTarget = hit.point; 70 | this.mesh.lookAt(this.target); 71 | this.oldDirection.copy(this.mesh.quaternion); 72 | this.mesh.lookAt(this.newTarget); 73 | this.newDirection.copy(this.mesh.quaternion); 74 | this.transitionFactor = 0; 75 | this.hitNormal = hit.face.normal; 76 | this.toLand = true; 77 | this.cameFrom = this.mesh.position.clone(); 78 | break; 79 | } 80 | } 81 | this.runAway = false; 82 | } 83 | if (this.newTarget === null) { 84 | return; 85 | } 86 | this.transitionFactor += 0.05; 87 | this.target.lerp(this.newTarget, 0.1); 88 | this.marker.position.copy(this.target); 89 | this.mesh.quaternion.slerpQuaternions(this.oldDirection, this.newDirection, Math.min(this.transitionFactor, 1)); 90 | this.mesh.position.add(this.newTarget.clone().sub(this.mesh.position).normalize().multiplyScalar(0.25 * timeScale)); 91 | this.mesh.rotateX(Math.PI / 3); 92 | if (this.mesh.position.distanceTo(this.target) < 1) { 93 | if (this.toLand && this.noFollow < -180) { 94 | this.toLand = false; 95 | this.state = "land"; 96 | } else { 97 | this.newTarget = null; 98 | } 99 | } 100 | if (this.mesh.position.distanceTo(flowerPos) < 50 && flower && this.noFollow < 1 && butterflies.filter(b => b.state === "follow").length < 3) { 101 | this.state = "follow"; 102 | } 103 | if (this.mesh.position.distanceTo(playerPos) < 37.5 && !flower && this.noFollow < 1) { 104 | //this.state = "flee"; 105 | this.noFollow = 180; 106 | this.newTarget = null; 107 | this.runAway = true; 108 | } 109 | } else if (this.state === "land") { 110 | if (this.newTarget === null) { 111 | this.state = "fly"; 112 | this.newTarget = this.cameFrom; 113 | this.mesh.lookAt(this.target); 114 | this.oldDirection.copy(this.mesh.quaternion); 115 | this.mesh.lookAt(this.newTarget); 116 | this.newDirection.copy(this.mesh.quaternion); 117 | this.transitionFactor = 0; 118 | return; 119 | } 120 | this.animations.forEach(animation => { 121 | animation.time += (0.83333333333 - animation.time) / 10; 122 | animation.timeScale = 0; 123 | }); 124 | this.target.lerp(this.newTarget, 0.1); 125 | this.mesh.position.copy(this.newTarget); 126 | const oldQuaternion = this.mesh.quaternion.clone(); 127 | this.mesh.lookAt(this.newTarget.clone().add(this.hitNormal)); 128 | const newQuaternion = this.mesh.quaternion.clone(); 129 | this.mesh.quaternion.slerpQuaternions(oldQuaternion, newQuaternion, 0.1); 130 | const ray = new THREE.Ray(this.mesh.position, this.hitNormal.clone().multiplyScalar(-1)); 131 | let hit = collider.geometry.boundsTree.raycastFirst(ray, THREE.DoubleSide); 132 | if (Math.random() < 0.0025 || 133 | (!hit || hit.point.distanceTo(this.mesh.position) > 5)) { 134 | this.animations.forEach(animation => { 135 | animation.timeScale = 2; 136 | }); 137 | this.state = "fly"; 138 | this.newTarget = this.cameFrom; 139 | this.mesh.lookAt(this.target); 140 | this.oldDirection.copy(this.mesh.quaternion); 141 | this.mesh.lookAt(this.newTarget); 142 | this.newDirection.copy(this.mesh.quaternion); 143 | this.transitionFactor = 0; 144 | } 145 | } else if (this.state === "follow") { 146 | this.noFollow = 0; 147 | this.mesh.rotateX(-Math.PI / 3); 148 | const oldQuaternion = this.mesh.quaternion.clone(); 149 | this.mesh.lookAt(flowerPos); 150 | const newQuaternion = this.mesh.quaternion.clone(); 151 | this.mesh.quaternion.slerpQuaternions(oldQuaternion, newQuaternion, 0.1); 152 | if (this.mesh.position.distanceTo(flowerPos) > 0.25) { 153 | this.mesh.position.add(flowerPos.clone().sub(this.mesh.position).normalize().multiplyScalar(0.25 * timeScale)); 154 | } 155 | this.mesh.rotateX(Math.PI / 3); 156 | const ray = new THREE.Ray(this.mesh.position, flowerPos.clone().sub(this.mesh.position).normalize()); 157 | let hit = collider.geometry.boundsTree.raycastFirst(ray, THREE.DoubleSide); 158 | let fly = false; 159 | if (hit) { 160 | if (hit.point.distanceTo(this.mesh.position) < flowerPos.distanceTo(this.mesh.position)) { 161 | fly = true; 162 | } 163 | } 164 | if (!flower || this.mesh.position.distanceTo(flowerPos) > 75 || fly) { 165 | this.newTarget = null; 166 | this.target = flowerPos.clone(); 167 | this.state = "fly"; 168 | this.noFollow = 120 + 120 * Math.random(); 169 | this.mesh.rotateX(-Math.PI / 3); 170 | } 171 | butterflies.forEach(butterfly => { 172 | if (butterfly !== this) { 173 | if (butterfly.mesh.position.distanceTo(this.mesh.position) < 5) { 174 | this.newTarget = null; 175 | this.target = flowerPos.clone(); 176 | this.state = "fly"; 177 | this.noFollow = 15 + 15 * Math.random(); 178 | this.mesh.rotateX(-Math.PI / 3); 179 | } 180 | } 181 | }) 182 | } 183 | } 184 | } 185 | export { Butterfly }; -------------------------------------------------------------------------------- /CapsuleEntity.js: -------------------------------------------------------------------------------- 1 | import * as THREE from 'https://cdn.skypack.dev/three@0.138.0'; 2 | class CapsuleEntity extends THREE.Object3D { 3 | constructor(radius, size) { 4 | super(); 5 | this.box = new THREE.Box3(); 6 | this.velocity = new THREE.Vector3(); 7 | this.horizontalVelocity = new THREE.Vector3(); 8 | this.radius = radius; 9 | this.size = size; 10 | this.onGround = false; 11 | this.gravity = -300; 12 | this.segment = new THREE.Line3(new THREE.Vector3(), new THREE.Vector3(0, -size, 0.0)); 13 | this.friction = 0.99; 14 | } 15 | update(delta, bvh) { 16 | const collider = bvh; 17 | this.velocity.y += this.onGround ? 0 : delta * this.gravity; 18 | this.position.addScaledVector(this.velocity, delta); 19 | this.position.add(this.horizontalVelocity); 20 | this.horizontalVelocity.multiplyScalar(this.friction); 21 | this.updateMatrixWorld(); 22 | const tempBox = new THREE.Box3(); 23 | const tempMat = new THREE.Matrix4(); 24 | const tempSegment = new THREE.Line3(); 25 | tempBox.makeEmpty(); 26 | tempMat.copy(collider.matrixWorld).invert(); 27 | tempSegment.copy(this.segment); 28 | tempSegment.start.applyMatrix4(this.matrixWorld).applyMatrix4(tempMat); 29 | tempSegment.end.applyMatrix4(this.matrixWorld).applyMatrix4(tempMat); 30 | tempBox.expandByPoint(tempSegment.start); 31 | tempBox.expandByPoint(tempSegment.end); 32 | tempBox.min.addScalar(-this.radius); 33 | tempBox.max.addScalar(this.radius); 34 | const tempVector = new THREE.Vector3(); 35 | const tempVector2 = new THREE.Vector3(); 36 | collider.geometry.boundsTree.shapecast({ 37 | 38 | intersectsBounds: box => box.intersectsBox(tempBox), 39 | 40 | intersectsTriangle: tri => { 41 | 42 | // check if the triangle is intersecting the capsule and adjust the 43 | // capsule position if it is. 44 | const triPoint = tempVector; 45 | const capsulePoint = tempVector2; 46 | 47 | const distance = tri.closestPointToSegment(tempSegment, triPoint, capsulePoint); 48 | if (distance < this.radius) { 49 | 50 | const depth = this.radius - distance; 51 | const direction = capsulePoint.sub(triPoint).normalize(); 52 | 53 | tempSegment.start.addScaledVector(direction, depth); 54 | tempSegment.end.addScaledVector(direction, depth); 55 | 56 | } 57 | 58 | } 59 | 60 | }); 61 | const newPosition = tempVector; 62 | newPosition.copy(tempSegment.start).applyMatrix4(collider.matrixWorld); 63 | 64 | const deltaVector = tempVector2; 65 | deltaVector.subVectors(newPosition, this.position); 66 | this.onGround = deltaVector.y > Math.abs(delta * this.velocity.y * 0.25); 67 | const offset = Math.max(0.0, deltaVector.length() - 1e-5); 68 | deltaVector.normalize().multiplyScalar(offset); 69 | this.position.add(deltaVector); 70 | if (!this.onGround) { 71 | 72 | deltaVector.normalize(); 73 | this.velocity.addScaledVector(deltaVector, -deltaVector.dot(this.velocity)); 74 | 75 | } else { 76 | this.velocity.set(0, 0, 0); 77 | } 78 | } 79 | } 80 | 81 | export { CapsuleEntity }; -------------------------------------------------------------------------------- /CustomLightShadowFragment.js: -------------------------------------------------------------------------------- 1 | export default /* glsl */ ` 2 | /** 3 | * This is a template that can be used to light a material, it uses pluggable 4 | * RenderEquations (RE)for specific lighting scenarios. 5 | * 6 | * Instructions for use: 7 | * - Ensure that both RE_Direct, RE_IndirectDiffuse and RE_IndirectSpecular are defined 8 | * - If you have defined an RE_IndirectSpecular, you need to also provide a Material_LightProbeLOD. <---- ??? 9 | * - Create a material parameter that is to be passed as the third parameter to your lighting functions. 10 | * 11 | * TODO: 12 | * - Add area light support. 13 | * - Add sphere light support. 14 | * - Add diffuse light probe (irradiance cubemap) support. 15 | */ 16 | GeometricContext geometry; 17 | geometry.position = - vViewPosition; 18 | geometry.normal = normal; 19 | geometry.viewDir = ( isOrthographic ) ? vec3( 0, 0, 1 ) : normalize( vViewPosition ); 20 | #ifdef USE_CLEARCOAT 21 | geometry.clearcoatNormal = clearcoatNormal; 22 | #endif 23 | IncidentLight directLight; 24 | #if ( NUM_POINT_LIGHTS > 0 ) && defined( RE_Direct ) 25 | PointLight pointLight; 26 | #if defined( USE_SHADOWMAP ) && NUM_POINT_LIGHT_SHADOWS > 0 27 | PointLightShadow pointLightShadow; 28 | #endif 29 | #pragma unroll_loop_start 30 | for ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) { 31 | pointLight = pointLights[ i ]; 32 | getPointLightInfo( pointLight, geometry, directLight ); 33 | #if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_POINT_LIGHT_SHADOWS ) 34 | pointLightShadow = pointLightShadows[ i ]; 35 | directLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getPointShadow( pointShadowMap[ i ], pointLightShadow.shadowMapSize, pointLightShadow.shadowBias, pointLightShadow.shadowRadius, vPointShadowCoord[ i ], pointLightShadow.shadowCameraNear, pointLightShadow.shadowCameraFar ) : 1.0; 36 | #endif 37 | RE_Direct( directLight, geometry, material, reflectedLight ); 38 | } 39 | #pragma unroll_loop_end 40 | #endif 41 | #if ( NUM_SPOT_LIGHTS > 0 ) && defined( RE_Direct ) 42 | SpotLight spotLight; 43 | #if defined( USE_SHADOWMAP ) && NUM_SPOT_LIGHT_SHADOWS > 0 44 | SpotLightShadow spotLightShadow; 45 | #endif 46 | #pragma unroll_loop_start 47 | for ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) { 48 | spotLight = spotLights[ i ]; 49 | getSpotLightInfo( spotLight, geometry, directLight ); 50 | #if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS ) 51 | spotLightShadow = spotLightShadows[ i ]; 52 | directLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getShadow( spotShadowMap[ i ], spotLightShadow.shadowMapSize, spotLightShadow.shadowBias, spotLightShadow.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0; 53 | #endif 54 | RE_Direct( directLight, geometry, material, reflectedLight ); 55 | } 56 | #pragma unroll_loop_end 57 | #endif 58 | #if ( NUM_DIR_LIGHTS > 0 ) && defined( RE_Direct ) 59 | DirectionalLight directionalLight; 60 | #if defined( USE_SHADOWMAP ) && NUM_DIR_LIGHT_SHADOWS > 0 61 | DirectionalLightShadow directionalLightShadow; 62 | #endif 63 | #pragma unroll_loop_start 64 | for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) { 65 | directionalLight = directionalLights[ i ]; 66 | getDirectionalLightInfo( directionalLight, geometry, directLight ); 67 | directLight.color *= s; 68 | RE_Direct( directLight, geometry, material, reflectedLight ); 69 | } 70 | #pragma unroll_loop_end 71 | #endif 72 | #if ( NUM_RECT_AREA_LIGHTS > 0 ) && defined( RE_Direct_RectArea ) 73 | RectAreaLight rectAreaLight; 74 | #pragma unroll_loop_start 75 | for ( int i = 0; i < NUM_RECT_AREA_LIGHTS; i ++ ) { 76 | rectAreaLight = rectAreaLights[ i ]; 77 | RE_Direct_RectArea( rectAreaLight, geometry, material, reflectedLight ); 78 | } 79 | #pragma unroll_loop_end 80 | #endif 81 | #if defined( RE_IndirectDiffuse ) 82 | vec3 iblIrradiance = vec3( 0.0 ); 83 | vec3 irradiance = getAmbientLightIrradiance( ambientLightColor ); 84 | irradiance += getLightProbeIrradiance( lightProbe, geometry.normal ); 85 | #if ( NUM_HEMI_LIGHTS > 0 ) 86 | #pragma unroll_loop_start 87 | for ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) { 88 | irradiance += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry.normal ); 89 | } 90 | #pragma unroll_loop_end 91 | #endif 92 | #endif 93 | #if defined( RE_IndirectSpecular ) 94 | vec3 radiance = vec3( 0.0 ); 95 | vec3 clearcoatRadiance = vec3( 0.0 ); 96 | #endif 97 | `; -------------------------------------------------------------------------------- /EffectCompositer.js: -------------------------------------------------------------------------------- 1 | import * as THREE from 'https://cdn.skypack.dev/pin/three@v0.137.0-X5O2PK3x44y1WRry67Kr/mode=imports/optimized/three.js'; 2 | 3 | const EffectCompositer = { 4 | uniforms: { 5 | 6 | 'sceneDiffuse': { value: null }, 7 | 'sceneDepth': { value: null }, 8 | 'tDiffuse': { value: null }, 9 | 'projMat': { value: new THREE.Matrix4() }, 10 | 'viewMat': { value: new THREE.Matrix4() }, 11 | 'projectionMatrixInv': { value: new THREE.Matrix4() }, 12 | 'viewMatrixInv': { value: new THREE.Matrix4() }, 13 | 'cameraPos': { value: new THREE.Vector3() }, 14 | 'resolution': { value: new THREE.Vector2() }, 15 | 'time': { value: 0.0 }, 16 | }, 17 | vertexShader: /* glsl */ ` 18 | varying vec2 vUv; 19 | void main() { 20 | vUv = uv; 21 | gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); 22 | }`, 23 | fragmentShader: /* glsl */ ` 24 | uniform sampler2D sceneDiffuse; 25 | uniform sampler2D sceneDepth; 26 | uniform sampler2D tDiffuse; 27 | uniform vec2 resolution; 28 | varying vec2 vUv; 29 | highp float linearize_depth(highp float d, highp float zNear,highp float zFar) 30 | { 31 | highp float z_n = 2.0 * d - 1.0; 32 | return 2.0 * zNear * zFar / (zFar + zNear - z_n * (zFar - zNear)); 33 | } 34 | void main() { 35 | gl_FragColor = vec4(texture2D(sceneDiffuse, vUv).rgb * vec3(pow(texture2D(tDiffuse, vUv).r, 2.0)), 1.0); 36 | } 37 | ` 38 | 39 | } 40 | export { EffectCompositer }; -------------------------------------------------------------------------------- /EffectShader.js: -------------------------------------------------------------------------------- 1 | import * as THREE from 'https://cdn.skypack.dev/three@0.138.0'; 2 | const EffectShader = { 3 | 4 | uniforms: { 5 | 6 | 'sceneDiffuse': { value: null }, 7 | 'reflectDiffuse': { value: null }, 8 | 'tDiffuse': { value: null }, 9 | 'time': { value: 0 }, 10 | 'sceneDepth': { value: null }, 11 | 'projectionMatrixInv': { value: new THREE.Matrix4() }, 12 | 'viewMatrixInv': { value: new THREE.Matrix4() }, 13 | 'cameraPos': { value: new THREE.Vector3() }, 14 | 'time': { value: 0.0 }, 15 | 'resolution': { value: new THREE.Vector2() }, 16 | 'waterNormal1': { value: null }, 17 | 'waterNormal2': { value: null }, 18 | 'projMat': { value: new THREE.Matrix4() }, 19 | 'viewMat': { value: new THREE.Matrix4() }, 20 | 'environment': { value: null }, 21 | 'lightPos': { value: new THREE.Vector3() } 22 | }, 23 | 24 | vertexShader: /* glsl */ ` 25 | varying vec2 vUv; 26 | void main() { 27 | vUv = uv; 28 | gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); 29 | }`, 30 | 31 | fragmentShader: /* glsl */ ` 32 | uniform sampler2D sceneDiffuse; 33 | uniform sampler2D reflectDiffuse; 34 | uniform sampler2D sceneDepth; 35 | uniform sampler2D waterNormal1; 36 | uniform sampler2D waterNormal2; 37 | uniform samplerCube environment; 38 | uniform mat4 projectionMatrixInv; 39 | uniform mat4 viewMatrixInv; 40 | uniform mat4 viewMat; 41 | uniform mat4 projMat; 42 | uniform vec3 cameraPos; 43 | uniform vec2 resolution; 44 | uniform vec3 lightPos; 45 | uniform float time; 46 | uniform sampler2D tDiffuse; 47 | varying vec2 vUv; 48 | float random(vec2 n) { 49 | return fract(sin(dot(n, vec2(12.9898, 4.1414))) * 43758.5453); 50 | } 51 | vec4 permute(vec4 x){return mod(((x*34.0)+1.0)*x, 289.0);} 52 | vec4 taylorInvSqrt(vec4 r){return 1.79284291400159 - 0.85373472095314 * r;} 53 | 54 | float snoise(vec3 v){ 55 | const vec2 C = vec2(1.0/6.0, 1.0/3.0) ; 56 | const vec4 D = vec4(0.0, 0.5, 1.0, 2.0); 57 | 58 | // First corner 59 | vec3 i = floor(v + dot(v, C.yyy) ); 60 | vec3 x0 = v - i + dot(i, C.xxx) ; 61 | 62 | // Other corners 63 | vec3 g = step(x0.yzx, x0.xyz); 64 | vec3 l = 1.0 - g; 65 | vec3 i1 = min( g.xyz, l.zxy ); 66 | vec3 i2 = max( g.xyz, l.zxy ); 67 | 68 | // x0 = x0 - 0. + 0.0 * C 69 | vec3 x1 = x0 - i1 + 1.0 * C.xxx; 70 | vec3 x2 = x0 - i2 + 2.0 * C.xxx; 71 | vec3 x3 = x0 - 1. + 3.0 * C.xxx; 72 | 73 | // Permutations 74 | i = mod(i, 289.0 ); 75 | vec4 p = permute( permute( permute( 76 | i.z + vec4(0.0, i1.z, i2.z, 1.0 )) 77 | + i.y + vec4(0.0, i1.y, i2.y, 1.0 )) 78 | + i.x + vec4(0.0, i1.x, i2.x, 1.0 )); 79 | 80 | // Gradients 81 | // ( N*N points uniformly over a square, mapped onto an octahedron.) 82 | float n_ = 1.0/7.0; // N=7 83 | vec3 ns = n_ * D.wyz - D.xzx; 84 | 85 | vec4 j = p - 49.0 * floor(p * ns.z *ns.z); // mod(p,N*N) 86 | 87 | vec4 x_ = floor(j * ns.z); 88 | vec4 y_ = floor(j - 7.0 * x_ ); // mod(j,N) 89 | 90 | vec4 x = x_ *ns.x + ns.yyyy; 91 | vec4 y = y_ *ns.x + ns.yyyy; 92 | vec4 h = 1.0 - abs(x) - abs(y); 93 | 94 | vec4 b0 = vec4( x.xy, y.xy ); 95 | vec4 b1 = vec4( x.zw, y.zw ); 96 | 97 | vec4 s0 = floor(b0)*2.0 + 1.0; 98 | vec4 s1 = floor(b1)*2.0 + 1.0; 99 | vec4 sh = -step(h, vec4(0.0)); 100 | 101 | vec4 a0 = b0.xzyw + s0.xzyw*sh.xxyy ; 102 | vec4 a1 = b1.xzyw + s1.xzyw*sh.zzww ; 103 | 104 | vec3 p0 = vec3(a0.xy,h.x); 105 | vec3 p1 = vec3(a0.zw,h.y); 106 | vec3 p2 = vec3(a1.xy,h.z); 107 | vec3 p3 = vec3(a1.zw,h.w); 108 | 109 | //Normalise gradients 110 | vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3))); 111 | p0 *= norm.x; 112 | p1 *= norm.y; 113 | p2 *= norm.z; 114 | p3 *= norm.w; 115 | 116 | // Mix final noise value 117 | vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0); 118 | m = m * m; 119 | return 42.0 * dot( m*m, vec4( dot(p0,x0), dot(p1,x1), 120 | dot(p2,x2), dot(p3,x3) ) ); 121 | } 122 | vec3 getWorldPos(float depth, vec2 coord) { 123 | float z = depth * 2.0 - 1.0; 124 | vec4 clipSpacePosition = vec4(coord * 2.0 - 1.0, z, 1.0); 125 | vec4 viewSpacePosition = projectionMatrixInv * clipSpacePosition; 126 | // Perspective division 127 | viewSpacePosition /= viewSpacePosition.w; 128 | vec4 worldSpacePosition = viewMatrixInv * viewSpacePosition; 129 | return worldSpacePosition.xyz; 130 | } 131 | vec3 computeNormal(vec3 worldPos) { 132 | vec2 downUv = vUv + vec2(0.0, 1.0 / resolution.y); 133 | vec3 downPos = getWorldPos(texture2D(sceneDepth, downUv).x, downUv).xyz; 134 | vec2 rightUv = vUv + vec2(1.0 / resolution.x, 0.0);; 135 | vec3 rightPos = getWorldPos(texture2D(sceneDepth, rightUv).x, rightUv).xyz; 136 | vec2 upUv = vUv - vec2(0.0, 1.0 / resolution.y); 137 | vec3 upPos = getWorldPos(texture2D(sceneDepth, upUv).x, upUv).xyz; 138 | vec2 leftUv = vUv - vec2(1.0 / resolution.x, 0.0); 139 | vec3 leftPos = getWorldPos(texture2D(sceneDepth, leftUv).x, leftUv).xyz; 140 | int hChoice; 141 | int vChoice; 142 | if (length(leftPos - worldPos) < length(rightPos - worldPos)) { 143 | hChoice = 0; 144 | } else { 145 | hChoice = 1; 146 | } 147 | if (length(upPos - worldPos) < length(downPos - worldPos)) { 148 | vChoice = 0; 149 | } else { 150 | vChoice = 1; 151 | } 152 | vec3 hVec; 153 | vec3 vVec; 154 | if (hChoice == 0 && vChoice == 0) { 155 | hVec = leftPos - worldPos; 156 | vVec = upPos - worldPos; 157 | } else if (hChoice == 0 && vChoice == 1) { 158 | hVec = leftPos - worldPos; 159 | vVec = worldPos - downPos; 160 | } else if (hChoice == 1 && vChoice == 1) { 161 | hVec = rightPos - worldPos; 162 | vVec = downPos - worldPos; 163 | } else if (hChoice == 1 && vChoice == 0) { 164 | hVec = rightPos - worldPos; 165 | vVec = worldPos - upPos; 166 | } 167 | return normalize(cross(hVec, vVec)); 168 | } 169 | mat3 GetTangentSpace(vec3 normal) 170 | { 171 | // Choose a helper vector for the cross product 172 | vec3 helper = vec3(1.0, 0.0, 0.0); 173 | if (abs(normal.x) > 0.99) 174 | helper = vec3(0.0, 0.0, 1.0); 175 | // Generate vectors 176 | vec3 tangent = normalize(cross(normal, helper)); 177 | vec3 binormal = normalize(cross(normal, tangent)); 178 | return mat3(tangent, binormal, normal); 179 | } 180 | float sdBox( in vec2 p, in vec2 b ) 181 | { 182 | vec2 d = abs(p)-b; 183 | return length(max(d,0.0)) + min(max(d.x,d.y),0.0); 184 | } 185 | highp float linearize_depth(highp float d, highp float zNear,highp float zFar) 186 | { 187 | highp float z_n = 2.0 * d - 1.0; 188 | return 2.0 * zNear * zFar / (zFar + zNear - z_n * (zFar - zNear)); 189 | } 190 | void main() { 191 | vec4 diffuse = texture2D(tDiffuse, vUv); 192 | float depth = texture2D(sceneDepth, vUv).x; 193 | vec3 worldPos = getWorldPos(depth, vUv); 194 | vec3 origin = cameraPos; 195 | float linDepth = length(origin - worldPos); 196 | vec3 direction = (projectionMatrixInv * vec4(vUv * 2.0 - 1.0, 0.0, 1.0)).xyz; 197 | direction = (viewMatrixInv * vec4(direction, 0.0)).xyz; 198 | direction = normalize(direction); 199 | vec3 rayDir = direction; 200 | float t = -origin.y / rayDir.y; 201 | if ((t < linDepth || depth == 1.0) && t > 0.0 && cameraPos.y > 0.0) { 202 | if (depth == 1.0) { 203 | linDepth = 1000.0; 204 | } 205 | vec3 normal = vec3(0.0, 1.0, 0.0); 206 | vec2 worldSampleCoord = (origin + rayDir * t).xz; 207 | worldSampleCoord.y *= resolution.y / resolution.x; 208 | vec3 normalMap = texture2D(waterNormal1, worldSampleCoord * 0.01 + time * 0.01).xyz; 209 | vec3 normalMap2 = texture2D(waterNormal2, worldSampleCoord * 0.01 - time * 0.01).xyz; 210 | normalMap = normalMap * 2.0 - 1.0; 211 | normalMap2 = normalMap2 * 2.0 - 1.0; 212 | mat3 TBN = GetTangentSpace(normal); 213 | normal = normalize(mix(normal, normalize(mix(normalize(TBN * normalMap), normalize(TBN * normalMap2), snoise(vec3(worldSampleCoord.x * 0.1, time * 0.5, worldSampleCoord.y * 0.1)))), 0.25)); 214 | 215 | float rayLength = min(linDepth - t, 10.0); 216 | vec3 refractDir = refract(rayDir, normal, 1.0 / 1.3); 217 | vec3 refractColor = vec3(0.0); 218 | vec2 samplePos = vUv + normal.xz * (0.001 + 1.0 / t) * 10.0; 219 | if (getWorldPos(texture2D(sceneDepth, samplePos).r, samplePos).y < 0.0) { 220 | refractColor = texture2D(tDiffuse, samplePos).rgb; 221 | } else { 222 | refractColor = texture2D(tDiffuse, vUv).rgb; 223 | samplePos = vUv; 224 | } 225 | vec3 reflectColor = vec3(0.0); 226 | vec2 rCoord = vUv + normal.xz * (0.001 + 1.0 / t) * 10.0; 227 | if (getWorldPos(texture2D(sceneDepth, rCoord.xy).r, rCoord.xy).y < 0.0) { 228 | reflectColor = texture2D(reflectDiffuse, rCoord).rgb; 229 | } 230 | if (refractColor == vec3(0.0)) { 231 | refractColor = textureCube(environment, refractDir).rgb; 232 | } 233 | if (reflectColor == vec3(0.0)) { 234 | reflectColor = textureCube(environment, reflect(rayDir, normal)).rgb; 235 | } 236 | float theta = max( dot( -rayDir, normal ), 0.0 ); 237 | float reflectance = 0.02 + ( 1.0 - 0.02 ) * pow( ( 1.0 - theta ), 5.0 ); 238 | if (cameraPos.y < 0.0) { 239 | reflectance = 0.0; 240 | } 241 | float yCoord = getWorldPos(texture2D(sceneDepth, samplePos).r, samplePos).y; 242 | float deepness = 0.1 + 0.9 * clamp(1.0 - exp(-0.15 * (depth < 1.0 ? -yCoord : 100.0)), 0.0, 1.0); 243 | float deepWaterC = clamp(depth < 1.0 ? -yCoord / 25.0 : 100.0, 0.0, 1.0);//clamp(1.0 - pow(3.0, -0.05 * (depth < 1.0 ? -worldPos.y : 100.0)), 0.0, 1.0); 244 | refractColor = mix(refractColor, mix(vec3(0.0, 0.9, 1.0), vec3(0.0, 0.5, 1.0), deepWaterC), deepness); 245 | diffuse.rgb = mix(refractColor, reflectColor, reflectance) * (0.5 + 0.5 * dot(normal, normalize(lightPos))); 246 | diffuse.rgb += pow(max(dot(reflect(normalize(lightPos), normal), rayDir), 0.0), 32.0); 247 | if (depth < 1.0) { 248 | diffuse.rgb += vec3(max(min(sin(worldPos.y - 2.5 * time) * pow(200.0, 0.1 * worldPos.y) * min(pow(200.0, -0.25 * (worldPos.y + 1.0)), 1.0), 1.0), 0.0) * 0.5); 249 | } 250 | } else if (cameraPos.y < 0.0) { 251 | vec3 normal = vec3(0.0, 1.0, 0.0); 252 | vec2 scaledUv = vec2(vUv.x * (resolution.x / resolution.y), vUv.y); 253 | vec3 normalMap = texture2D(waterNormal1, scaledUv + time * 0.01).xyz; 254 | vec3 normalMap2 = texture2D(waterNormal2, scaledUv - time * 0.01).xyz; 255 | normalMap = normalMap * 2.0 - 1.0; 256 | normalMap2 = normalMap2 * 2.0 - 1.0; 257 | mat3 TBN = GetTangentSpace(normal); 258 | normal = normalize(mix(normal, normalize(mix(normalize(TBN * normalMap), normalize(TBN * normalMap2), snoise(vec3(scaledUv.x, time * 0.5, scaledUv.y)))), 0.1)); 259 | //diffuse.rgb = vec3(normal.xz, 0.0); 260 | diffuse.rgb = texture2D(tDiffuse, vUv + 0.25 * normal.xz).rgb; 261 | vec3 worldPosSample = getWorldPos(texture2D(sceneDepth, vUv + 0.25 * normal.xz).r, vUv + 0.25 * normal.xz); 262 | float dist = min( linearize_depth(texture2D(sceneDepth, vUv + 0.25 * normal.xz).r, 0.1, 1000.0), rayDir.y > 0.0 ? -cameraPos.y / rayDir.y: 1e20); 263 | diffuse.rgb = mix(diffuse.rgb, vec3(0.0, 0.5, 1.0), clamp(1.0 - (exp((worldPosSample.y > 0.0 ? -0.033 :-0.01) * dist) + (worldPosSample.y > 0.0 ? -0.33 : 0.0)), 0.0, 1.0)); 264 | } 265 | gl_FragColor = vec4(diffuse.rgb, 1.0); 266 | }` 267 | 268 | }; 269 | 270 | export { EffectShader }; -------------------------------------------------------------------------------- /GodRays.js: -------------------------------------------------------------------------------- 1 | import * as THREE from 'https://cdn.skypack.dev/three@0.138.0'; 2 | 3 | const GodRaysShader = { 4 | uniforms: { 5 | 'sceneDiffuse': { value: null }, 6 | 'sceneDepth': { value: null }, 7 | 'tDiffuse': { value: null }, 8 | 'projMat': { value: new THREE.Matrix4() }, 9 | 'viewMat': { value: new THREE.Matrix4() }, 10 | 'projViewMat': { value: new THREE.Matrix4() }, 11 | 'projectionMatrixInv': { value: new THREE.Matrix4() }, 12 | 'viewMatrixInv': { value: new THREE.Matrix4() }, 13 | 'cameraPos': { value: new THREE.Vector3() }, 14 | 'resolution': { value: new THREE.Vector2() }, 15 | 'time': { value: 0.0 }, 16 | }, 17 | vertexShader: /*glsl*/ ` 18 | varying vec2 vUv; 19 | void main() { 20 | vUv = uv; 21 | gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); 22 | } 23 | `, 24 | fragmentShader: /*glsl*/ ` 25 | uniform vec2 resolution; 26 | uniform vec3 cameraPos; 27 | uniform mat4 viewMatrixInv; 28 | uniform mat4 projectionMatrixInv; 29 | uniform mat4 projViewMat; 30 | uniform mat4 projMat; 31 | uniform mat4 viewMat; 32 | uniform sampler2D sceneDiffuse; 33 | uniform sampler2D sceneDepth; 34 | uniform sampler2D tDiffuse; 35 | uniform float time; 36 | varying vec2 vUv; 37 | float seed = 0.0; 38 | highp float random(vec2 co) 39 | { 40 | highp float a = 12.9898; 41 | highp float b = 78.233; 42 | highp float c = 43758.5453; 43 | highp float dt= dot(co.xy ,vec2(a,b)); 44 | highp float sn= mod(dt,3.14); 45 | return fract(sin(sn) * c); 46 | } 47 | float rand() 48 | { 49 | /*float result = fract(sin(seed + mod(time, 1000.0) + dot(gl_FragCoord.xy, vec2(12.9898, 78.233))) * 43758.5453); 50 | //_Seed += 1.0; 51 | seed += 1.0; 52 | return result;*/ 53 | float result = random(vUv + seed / 10.0 + mod(time / 100.0, 100.0)); 54 | seed += 1.0; 55 | return result; 56 | } 57 | 58 | void main() { 59 | /*vec3 lightDir = normalize(vec3(300.0, 200.0, 100.0)); 60 | vec4 projLightPos = projMat * viewMat * vec4(lightDir, 0.0); 61 | projLightPos.xyz /= projLightPos.w; 62 | projLightPos.xyz = projLightPos.xyz * 0.5 + 0.5; 63 | vec2 uv = projLightPos.xy; 64 | projLightPos.y *= resolution.x / resolution.y; 65 | if (distance(vUv, uv) < 0.1) { 66 | gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0); 67 | } else { 68 | gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0); 69 | }*/ 70 | vec3 lightDir = normalize(vec3(300.0, 200.0, 100.0)); 71 | vec3 direction = (projectionMatrixInv * vec4(vUv * 2.0 - 1.0, 0.0, 1.0)).xyz; 72 | direction = (viewMatrixInv * vec4(direction, 0.0)).xyz; 73 | direction = normalize(direction); 74 | vec4 lightCenter = projMat * viewMat * vec4(cameraPos + lightDir * 1000.0, 1.0); 75 | lightCenter.xyz /= lightCenter.w; 76 | lightCenter.xyz = lightCenter.xyz * 0.5 + 0.5; 77 | if (distance(vUv, lightCenter.xy) < 0.001) { 78 | gl_FragColor.xyz = vec3(1.0, 0.0, 0.0); 79 | } 80 | float accumulate = 0.0; 81 | float samples = round(10.0 + 10.0 * rand()); 82 | float iterations = 0.0; 83 | if (dot(direction, lightDir) > 0.975 && texture2D(sceneDepth, vUv).r == 1.0) { 84 | accumulate = 1.0; 85 | } else { 86 | for(float i = 0.0; i < samples; i++) { 87 | vec2 samplePos = mix(vUv, lightCenter.xy, (i / samples)); 88 | if (samplePos.x < 0.0 || samplePos.x > 1.0 || samplePos.y < 0.0 || samplePos.y > 1.0) { 89 | break; 90 | } else { 91 | iterations += 1.0; 92 | } 93 | vec3 d = (projectionMatrixInv * vec4(samplePos * 2.0 - 1.0, 0.0, 1.0)).xyz; 94 | d = (viewMatrixInv * vec4(d, 0.0)).xyz; 95 | d = normalize(d); 96 | if (dot(d, lightDir) > 0.975) { 97 | if (texture2D(sceneDepth, samplePos).r == 1.0) { 98 | accumulate += 1.0; 99 | } 100 | 101 | } 102 | } 103 | if (iterations >= 1.0) { 104 | accumulate /= iterations; 105 | } 106 | } 107 | gl_FragColor = vec4(mix(texture2D(tDiffuse, vUv).rgb, vec3(1.0, 1.0, 1.0), accumulate), 1.0); 108 | } 109 | ` 110 | } 111 | export { GodRaysShader }; -------------------------------------------------------------------------------- /SSAOShader.js: -------------------------------------------------------------------------------- 1 | import * as THREE from 'https://cdn.skypack.dev/pin/three@v0.137.0-X5O2PK3x44y1WRry67Kr/mode=imports/optimized/three.js'; 2 | const SSAOShader = { 3 | 4 | uniforms: { 5 | 6 | 'sceneDiffuse': { value: null }, 7 | 'sceneDepth': { value: null }, 8 | 'projMat': { value: new THREE.Matrix4() }, 9 | 'viewMat': { value: new THREE.Matrix4() }, 10 | 'projViewMat': { value: new THREE.Matrix4() }, 11 | 'projectionMatrixInv': { value: new THREE.Matrix4() }, 12 | 'viewMatrixInv': { value: new THREE.Matrix4() }, 13 | 'cameraPos': { value: new THREE.Vector3() }, 14 | 'resolution': { value: new THREE.Vector2() }, 15 | 'time': { value: 0.0 }, 16 | }, 17 | 18 | vertexShader: /* glsl */ ` 19 | varying vec2 vUv; 20 | void main() { 21 | vUv = uv; 22 | gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); 23 | }`, 24 | 25 | fragmentShader: /* glsl */ ` 26 | uniform sampler2D sceneDiffuse; 27 | uniform sampler2D sceneDepth; 28 | uniform mat4 projectionMatrixInv; 29 | uniform mat4 viewMatrixInv; 30 | uniform mat4 projMat; 31 | uniform mat4 viewMat; 32 | uniform mat4 projViewMat; 33 | uniform vec3 cameraPos; 34 | uniform vec2 resolution; 35 | uniform float time; 36 | varying vec2 vUv; 37 | highp float linearize_depth(highp float d, highp float zNear,highp float zFar) 38 | { 39 | highp float z_n = 2.0 * d - 1.0; 40 | return 2.0 * zNear * zFar / (zFar + zNear - z_n * (zFar - zNear)); 41 | } 42 | vec3 getWorldPos(float depth, vec2 coord) { 43 | float z = depth * 2.0 - 1.0; 44 | vec4 clipSpacePosition = vec4(coord * 2.0 - 1.0, z, 1.0); 45 | vec4 viewSpacePosition = projectionMatrixInv * clipSpacePosition; 46 | // Perspective division 47 | vec4 worldSpacePosition = viewMatrixInv * viewSpacePosition; 48 | worldSpacePosition.xyz /= worldSpacePosition.w; 49 | return worldSpacePosition.xyz; 50 | } 51 | vec3 computeNormal(vec3 worldPos, vec2 vUv) { 52 | vec2 downUv = vUv + vec2(0.0, 1.0 / resolution.y); 53 | vec3 downPos = getWorldPos( texture2D(sceneDepth, downUv).x, downUv).xyz; 54 | vec2 rightUv = vUv + vec2(1.0 / resolution.x, 0.0); 55 | vec3 rightPos = getWorldPos(texture2D(sceneDepth, rightUv).x, rightUv).xyz; 56 | vec2 upUv = vUv - vec2(0.0, 1.0 / resolution.y); 57 | vec3 upPos = getWorldPos(texture2D(sceneDepth, upUv).x, upUv).xyz; 58 | vec2 leftUv = vUv - vec2(1.0 / resolution.x, 0.0);; 59 | vec3 leftPos = getWorldPos(texture2D(sceneDepth, leftUv).x, leftUv).xyz; 60 | int hChoice; 61 | int vChoice; 62 | if (length(leftPos - worldPos) < length(rightPos - worldPos)) { 63 | hChoice = 0; 64 | } else { 65 | hChoice = 1; 66 | } 67 | if (length(upPos - worldPos) < length(downPos - worldPos)) { 68 | vChoice = 0; 69 | } else { 70 | vChoice = 1; 71 | } 72 | vec3 hVec; 73 | vec3 vVec; 74 | if (hChoice == 0 && vChoice == 0) { 75 | hVec = leftPos - worldPos; 76 | vVec = upPos - worldPos; 77 | } else if (hChoice == 0 && vChoice == 1) { 78 | hVec = leftPos - worldPos; 79 | vVec = worldPos - downPos; 80 | } else if (hChoice == 1 && vChoice == 1) { 81 | hVec = rightPos - worldPos; 82 | vVec = downPos - worldPos; 83 | } else if (hChoice == 1 && vChoice == 0) { 84 | hVec = rightPos - worldPos; 85 | vVec = worldPos - upPos; 86 | } 87 | return normalize(cross(hVec, vVec)); 88 | } 89 | float rand(float n){return fract(sin(n) * 43758.5453123);} 90 | float rand(vec2 n) { 91 | return fract(sin(dot(n, vec2(12.9898, 4.1414))) * 43758.5453); 92 | } 93 | 94 | float noise(float p){ 95 | float fl = floor(p); 96 | float fc = fract(p); 97 | return mix(rand(fl), rand(fl + 1.0), fc); 98 | } 99 | 100 | float noise(vec2 n) { 101 | const vec2 d = vec2(0.0, 1.0); 102 | vec2 b = floor(n), f = smoothstep(vec2(0.0), vec2(1.0), fract(n)); 103 | return mix(mix(rand(b), rand(b + d.yx), f.x), mix(rand(b + d.xy), rand(b + d.yy), f.x), f.y); 104 | } 105 | highp float random(vec2 co) 106 | { 107 | highp float a = 12.9898; 108 | highp float b = 78.233; 109 | highp float c = 43758.5453; 110 | highp float dt= dot(co.xy ,vec2(a,b)); 111 | highp float sn= mod(dt,3.14); 112 | return fract(sin(sn) * c); 113 | } 114 | float seed = 0.0; 115 | float rand() 116 | { 117 | /*float result = fract(sin(seed + mod(time, 1000.0) + dot(gl_FragCoord.xy, vec2(12.9898, 78.233))) * 43758.5453); 118 | //_Seed += 1.0; 119 | seed += 1.0; 120 | return result;*/ 121 | float result = random(vUv + seed / 10.0 + mod(time / 100.0, 100.0)); 122 | seed += 1.0; 123 | return result; 124 | } 125 | 126 | void main() { 127 | vec4 diffuse = texture2D(sceneDiffuse, vUv); 128 | float depth = texture2D(sceneDepth, vUv).x; 129 | if (depth == 1.0) { 130 | gl_FragColor = vec4(vec3(1.0), 1.0); 131 | return; 132 | } 133 | vec3 worldPos = getWorldPos(depth, vUv); 134 | vec3 normal = computeNormal(worldPos, vUv); 135 | float occluded = 0.0; 136 | for(float i = 0.0; i < 16.0; i++) { 137 | vec3 sampleDirection = normalize(vec3( 138 | rand() /*rand (vUv.x + vUv.y + i)*/ - 0.5, 139 | rand() /*rand (vUv.x + vUv.y + i * (i + 1.0))*/ - 0.5, 140 | rand() /*rand (vUv.x + vUv.y + i * (i + 1.0) * (i + 2.0))*/ - 0.5 141 | )); 142 | if (dot(normal, sampleDirection) < 0.0) { 143 | sampleDirection *= -1.0; 144 | } 145 | const float radius = 2.5; 146 | float moveAmt = rand() * rand() /*rand(vUv.x + vUv.y + i) * rand(2.0 * (vUv.x + vUv.y + i))*/; 147 | vec3 samplePos = worldPos + radius * moveAmt * sampleDirection; 148 | vec4 offset = projViewMat * vec4(samplePos, 1.0); 149 | offset.xyz /= offset.w; 150 | offset.xyz = offset.xyz * 0.5 + 0.5; 151 | float sampleDepth = texture2D(sceneDepth, offset.xy).x; 152 | float distSample =linearize_depth(sampleDepth, 0.1, 1000.0);// length(getWorldPos(sampleDepth, vUv) - cameraPos); 153 | float distWorld = linearize_depth(offset.z, 0.1, 1000.0);//length(samplePos - cameraPos); 154 | float rangeCheck = smoothstep(0.0, 1.0, radius / abs(distSample - distWorld)); 155 | if (distSample + 1.0 < distWorld) { 156 | occluded += rangeCheck; 157 | } 158 | } 159 | gl_FragColor = vec4(vec3(1.0 - occluded / 16.0), 1.0); 160 | }` 161 | 162 | }; 163 | 164 | export { SSAOShader }; -------------------------------------------------------------------------------- /background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N8python/theIsland/e985e32ab371351c7f5cca6fc427b743e4fdc95d/background.png -------------------------------------------------------------------------------- /barkcolor.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N8python/theIsland/e985e32ab371351c7f5cca6fc427b743e4fdc95d/barkcolor.jpeg -------------------------------------------------------------------------------- /barkmap.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N8python/theIsland/e985e32ab371351c7f5cca6fc427b743e4fdc95d/barkmap.jpeg -------------------------------------------------------------------------------- /butterfly.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N8python/theIsland/e985e32ab371351c7f5cca6fc427b743e4fdc95d/butterfly.glb -------------------------------------------------------------------------------- /dandelion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N8python/theIsland/e985e32ab371351c7f5cca6fc427b743e4fdc95d/dandelion.png -------------------------------------------------------------------------------- /dandelionsmall.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N8python/theIsland/e985e32ab371351c7f5cca6fc427b743e4fdc95d/dandelionsmall.png -------------------------------------------------------------------------------- /dandelionspore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N8python/theIsland/e985e32ab371351c7f5cca6fc427b743e4fdc95d/dandelionspore.png -------------------------------------------------------------------------------- /grassalbido.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N8python/theIsland/e985e32ab371351c7f5cca6fc427b743e4fdc95d/grassalbido.jpeg -------------------------------------------------------------------------------- /grassalpha.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N8python/theIsland/e985e32ab371351c7f5cca6fc427b743e4fdc95d/grassalpha.jpg -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Island 9 | 15 | 16 | 17 | 18 |

Loading...

19 | 20 | 21 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /leafalpha.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N8python/theIsland/e985e32ab371351c7f5cca6fc427b743e4fdc95d/leafalpha.png -------------------------------------------------------------------------------- /leafcolor.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N8python/theIsland/e985e32ab371351c7f5cca6fc427b743e4fdc95d/leafcolor.jpeg -------------------------------------------------------------------------------- /leafnormal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N8python/theIsland/e985e32ab371351c7f5cca6fc427b743e4fdc95d/leafnormal.png -------------------------------------------------------------------------------- /leafnormal2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N8python/theIsland/e985e32ab371351c7f5cca6fc427b743e4fdc95d/leafnormal2.png -------------------------------------------------------------------------------- /main.js: -------------------------------------------------------------------------------- 1 | import * as THREE from 'https://cdn.skypack.dev/three@0.138.0'; 2 | import { EffectComposer } from 'https://unpkg.com/three@0.138.0/examples/jsm/postprocessing/EffectComposer.js'; 3 | import { RenderPass } from 'https://unpkg.com/three@0.138.0/examples/jsm/postprocessing/RenderPass.js'; 4 | import { ShaderPass } from 'https://unpkg.com/three@0.138.0/examples/jsm/postprocessing/ShaderPass.js'; 5 | import { SMAAPass } from 'https://unpkg.com/three@0.138.0/examples/jsm/postprocessing/SMAAPass.js'; 6 | import { GammaCorrectionShader } from 'https://unpkg.com/three@0.138.0/examples/jsm/shaders/GammaCorrectionShader.js'; 7 | import { EffectShader } from "./EffectShader.js"; 8 | import { EffectCompositer } from "./EffectCompositer.js"; 9 | import { OrbitControls } from 'https://unpkg.com/three@0.138.0/examples/jsm/controls/OrbitControls.js'; 10 | import { MeshSurfaceSampler } from 'https://unpkg.com/three@0.138.0/examples/jsm/math/MeshSurfaceSampler.js'; 11 | import { TeapotGeometry } from 'https://unpkg.com/three@0.138.0/examples/jsm/geometries/TeapotGeometry.js'; 12 | import { GodRaysShader } from "./GodRays.js"; 13 | import * as MeshBVHLib from 'https://unpkg.com/three-mesh-bvh@0.5.10/build/index.module.js'; 14 | import * as BufferGeometryUtils from "https://unpkg.com/three@0.138.0/examples/jsm/utils/BufferGeometryUtils.js"; 15 | import { AssetManager } from './AssetManager.js'; 16 | import { GUI } from 'https://unpkg.com/three@0.138.0/examples/jsm/libs/lil-gui.module.min.js'; 17 | import { Stats } from "./stats.js"; 18 | import { SSAOShader } from "./SSAOShader.js"; 19 | import CustomLightShadowFragment from './CustomLightShadowFragment.js'; 20 | import { CapsuleEntity } from './CapsuleEntity.js'; 21 | import { PointerLockControls } from 'https://unpkg.com/three@0.138.0/examples/jsm/controls/PointerLockControls.js'; 22 | import { Butterfly } from "./Butterfly.js"; 23 | async function main() { 24 | // Setup basic renderer, controls, and profiler 25 | function nextFrame() { 26 | return new Promise((resolve, reject) => { 27 | setTimeout(() => { 28 | resolve() 29 | }) 30 | }) 31 | } 32 | const loadingElement = document.getElementById("loading"); 33 | const clientWidth = window.innerWidth * 0.99; 34 | const clientHeight = window.innerHeight * 0.98; 35 | const scene = new THREE.Scene(); 36 | const camera = new THREE.PerspectiveCamera(75, clientWidth / clientHeight, 0.1, 1000); 37 | //camera.position.set(50, 75, 50); 38 | const renderer = new THREE.WebGLRenderer(); 39 | renderer.setSize(clientWidth, clientHeight); 40 | renderer.shadowMap.enabled = true; 41 | renderer.shadowMap.type = THREE.VSMShadowMap; 42 | const controls = new PointerLockControls(camera, document.body); 43 | scene.add(controls.getObject()); 44 | const stats = new Stats(); 45 | stats.showPanel(0); 46 | // Setup scene 47 | // Skybox 48 | const environment = new THREE.CubeTextureLoader().load([ 49 | "skybox/Box_Right.bmp", 50 | "skybox/Box_Left.bmp", 51 | "skybox/Box_Top.bmp", 52 | "skybox/Box_Bottom.bmp", 53 | "skybox/Box_Front.bmp", 54 | "skybox/Box_Back.bmp" 55 | ]); 56 | scene.background = environment; 57 | // Lighting 58 | const ambientLight = new THREE.AmbientLight(new THREE.Color(1.0, 1.0, 1.0), 0.25); 59 | scene.add(ambientLight); 60 | const directionalLight = new THREE.DirectionalLight(0xffffff, 0.35); 61 | directionalLight.position.set(300, 200, 100); 62 | //scene.add(directionalLight); 63 | // Shadows 64 | directionalLight.castShadow = true; 65 | directionalLight.shadow.mapSize.width = 4096; 66 | directionalLight.shadow.mapSize.height = 4096; 67 | directionalLight.shadow.camera.left = -256 * 3 / 2; 68 | directionalLight.shadow.camera.right = 256 * 3 / 2; 69 | directionalLight.shadow.camera.top = 256 * 3 / 2; 70 | directionalLight.shadow.camera.bottom = -256 * 3 / 2; 71 | directionalLight.shadow.camera.near = 0.1; 72 | directionalLight.shadow.camera.far = 1000; 73 | directionalLight.shadow.bias = -0.001; 74 | directionalLight.shadow.blurSamples = 8; 75 | directionalLight.shadow.radius = 4; 76 | scene.add(directionalLight); 77 | //scene.add(new THREE.CameraHelper(directionalLight.shadow.camera)); 78 | /* const directionalLight2 = new THREE.DirectionalLight(0xffffff, 0.15); 79 | directionalLight2.color.setRGB(1.0, 1.0, 1.0); 80 | directionalLight2.position.set(-50, 200, -150); 81 | scene.add(directionalLight2);*/ 82 | // Objects 83 | /*const ground = new THREE.Mesh(new THREE.PlaneGeometry(100, 100).applyMatrix4(new THREE.Matrix4().makeRotationX(-Math.PI / 2)), new THREE.MeshStandardMaterial({ side: THREE.DoubleSide })); 84 | ground.castShadow = true; 85 | ground.receiveShadow = true; 86 | scene.add(ground); 87 | 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) })); 88 | box.castShadow = true; 89 | box.receiveShadow = true; 90 | box.position.y = 5.01; 91 | scene.add(box); 92 | 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 })); 93 | sphere.position.y = 7.5; 94 | sphere.position.x = 25; 95 | sphere.position.z = 25; 96 | sphere.castShadow = true; 97 | sphere.receiveShadow = true; 98 | scene.add(sphere); 99 | 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) })); 100 | torusKnot.position.y = 10; 101 | torusKnot.position.x = -25; 102 | torusKnot.position.z = -25; 103 | torusKnot.castShadow = true; 104 | torusKnot.receiveShadow = true; 105 | scene.add(torusKnot);*/ 106 | // Build postprocessing stack 107 | // Render Targets 108 | noise.seed(Math.random()); 109 | loadingElement.innerHTML = "Building Terrain..."; 110 | await nextFrame(); 111 | const terrainGeo = new THREE.PlaneGeometry(1024, 1024, 1024, 1024); 112 | terrainGeo.applyMatrix4(new THREE.Matrix4().makeRotationX(Math.PI / 2)); 113 | // scene.add(new THREE.Mesh(terrainGeo.clone(), new THREE.MeshStandardMaterial({ side: THREE.DoubleSide, transparent: true, color: new THREE.Color(0.0, 0.5, 1.0), opacity: 0.5 }))); 114 | const heightFalloff = (x) => (x + 1) * Math.log(x + 1); 115 | 116 | function smax(d1, d2, k) { 117 | const h = Math.min(Math.max(0.5 - 0.5 * (d2 - d1) / k, 0.0), 1.0); 118 | return (d2 + (d1 - d2) * h) + k * h * (1.0 - h); 119 | } 120 | for (let x = 0; x < 1025; x++) { 121 | for (let z = 0; z < 1025; z++) { 122 | let amt = 0.0; 123 | let frequency = 0.0025; 124 | let amplitude = 0.5; 125 | for (let i = 0; i < 12; i++) { 126 | amt += amplitude * (0.5 + 0.5 * noise.simplex2(x * frequency + 128, z * frequency + 128)); 127 | amplitude /= 2; 128 | frequency *= 2; 129 | } 130 | let currHeight = (amt) * 100.0 - 0.0625 * heightFalloff(Math.max(Math.hypot(x - 512, z - 512) - (200 + 50 * (amt)), 0)); 131 | currHeight = smax(currHeight, -25.0 - amt * 15.0, 10.0); 132 | terrainGeo.attributes.position.setY(1025 * z + x, currHeight); 133 | } 134 | } 135 | terrainGeo.computeVertexNormals() 136 | const terrainMesh = new THREE.Mesh(terrainGeo, new THREE.MeshStandardMaterial({ side: THREE.DoubleSide })); 137 | terrainMesh.castShadow = true; 138 | terrainMesh.receiveShadow = true; 139 | let terrainShader; 140 | const terrainColor = new THREE.TextureLoader().load("terraincolor.png"); 141 | terrainColor.wrapS = THREE.RepeatWrapping; 142 | terrainColor.wrapT = THREE.RepeatWrapping; 143 | terrainMesh.material.onBeforeCompile = (shader) => { 144 | shader.uniforms.reflect = { value: false }; 145 | shader.uniforms.cameraPos = { value: camera.position }; 146 | shader.uniforms.diffuseMap = { value: terrainColor }; 147 | shader.uniforms.background = { value: environment }; 148 | terrainShader = shader; 149 | shader.fragmentShader = "varying vec3 vWorldPosition;\nuniform samplerCube background;\n" + ` 150 | uniform bool reflect; 151 | uniform vec3 cameraPos; 152 | uniform sampler2D diffuseMap; 153 | float mod289(float x){return x - floor(x * (1.0 / 289.0)) * 289.0;} 154 | vec4 mod289(vec4 x){return x - floor(x * (1.0 / 289.0)) * 289.0;} 155 | vec4 perm(vec4 x){return mod289(((x * 34.0) + 1.0) * x);} 156 | 157 | float noise(vec3 p){ 158 | vec3 a = floor(p); 159 | vec3 d = p - a; 160 | d = d * d * (3.0 - 2.0 * d); 161 | 162 | vec4 b = a.xxyy + vec4(0.0, 1.0, 0.0, 1.0); 163 | vec4 k1 = perm(b.xyxy); 164 | vec4 k2 = perm(k1.xyxy + b.zzww); 165 | 166 | vec4 c = k2 + a.zzzz; 167 | vec4 k3 = perm(c); 168 | vec4 k4 = perm(c + 1.0); 169 | 170 | vec4 o1 = fract(k3 * (1.0 / 41.0)); 171 | vec4 o2 = fract(k4 * (1.0 / 41.0)); 172 | 173 | vec4 o3 = o2 * d.z + o1 * (1.0 - d.z); 174 | vec2 o4 = o3.yw * d.x + o3.xz * (1.0 - d.x); 175 | 176 | return o4.y * d.y + o4.x * (1.0 - d.y); 177 | } 178 | float fbm(vec3 x) { 179 | float v = 0.0; 180 | float a = 0.5; 181 | vec3 shift = vec3(100); 182 | for (int i = 0; i < 5; ++i) { 183 | v += a * noise(x); 184 | x = x * 2.0 + shift; 185 | a *= 0.5; 186 | } 187 | return v; 188 | } 189 | ` + shader.fragmentShader; 190 | shader.fragmentShader = shader.fragmentShader.replace("#include ", ` 191 | #include 192 | mat4 viewMatrixInv = inverse(viewMatrix); 193 | if ((reflect && vWorldPosition.y > 0.0) 194 | ) { 195 | discard; 196 | } 197 | if ((reflect && (viewMatrixInv * vec4(normal, 0.0)).y > 0.0)) { 198 | gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0); 199 | return; 200 | } 201 | vec3 position = vWorldPosition; 202 | if (reflect) { 203 | position.y *= -1.0; 204 | } 205 | float fbmVal = fbm(position * 0.25); 206 | float verticalness = 1.0 - pow(max(dot((viewMatrixInv * vec4(normal, 0.0)).xyz, vec3(0.0, reflect ? -1.0 : 1.0, 0.0)), 0.0), 3.0 + fbmVal); 207 | vec3 terrainColor = mix(vec3(126.0 / 255.0, 200.0 / 255.0, 80.0 / 255.0), vec3(155.0 / 255.0, 118.0 / 255.0, 83.0 / 255.0), verticalness); 208 | diffuseColor = vec4(mix(terrainColor, vec3(246.0 / 255.0, 228.0 / 255.0, 173.0 / 255.0), smoothstep(0.0, 1.0, clamp(-(position.y - 15.0 - 5.0 * fbmVal) * 0.1, 0.0, 1.0))), 1.0); 209 | diffuseColor.rgb *= (0.65 + 0.35 * fbmVal); 210 | vec2 yUv = vWorldPosition.xz * 0.01; 211 | vec2 xUv = vWorldPosition.zy * 0.01; 212 | vec2 zUv = vWorldPosition.xy * 0.01; 213 | vec3 yDiff = texture2D(diffuseMap, yUv).xyz; 214 | vec3 xDiff = texture2D(diffuseMap, xUv).xyz; 215 | vec3 zDiff = texture2D(diffuseMap, zUv).xyz; 216 | vec3 blendWeights = abs((viewMatrixInv * vec4(vNormal, 0.0)).xyz); 217 | blendWeights = blendWeights / (blendWeights.x + blendWeights.y + blendWeights.z); 218 | diffuseColor *= 0.25 + 0.75 * vec4((xDiff * blendWeights.x + yDiff * blendWeights.y + zDiff * blendWeights.z), 1.0); 219 | `) 220 | .replace("#include ", ` 221 | #include 222 | vec3 vDir = normalize(vWorldPosition - cameraPos); 223 | gl_FragColor = mix(gl_FragColor, textureCube(background, vDir), cameraPos.y < 0.0 ? 0.0 : clamp(-0.1 * vWorldPosition.y, 0.0, 1.0)); 224 | `); 225 | shader.vertexShader = shader.vertexShader.replace("#ifdef USE_TRANSMISSION", "").replace("#ifdef USE_TRANSMISSION", ""); 226 | shader.vertexShader = shader.vertexShader.replace("#endif", "").replace("#endif", ""); 227 | shader.vertexShader = shader.vertexShader.replace("#include ", ` 228 | vec4 worldPosition = vec4( transformed, 1.0 ); 229 | #ifdef USE_INSTANCING 230 | worldPosition = instanceMatrix * worldPosition; 231 | #endif 232 | worldPosition = modelMatrix * worldPosition; 233 | `) 234 | } 235 | scene.add(terrainMesh); 236 | const grassSampler = new MeshSurfaceSampler(terrainMesh).build(); 237 | const grassGeo = BufferGeometryUtils.mergeBufferGeometries([ 238 | new THREE.PlaneGeometry(10, 10).applyMatrix4(new THREE.Matrix4().makeRotationY(Math.PI / 4)), 239 | new THREE.PlaneGeometry(10, 10).applyMatrix4(new THREE.Matrix4().makeRotationY(-Math.PI / 4)), 240 | new THREE.PlaneGeometry(10, 10) 241 | ]); 242 | grassGeo.applyMatrix4(new THREE.Matrix4().makeTranslation(0, 5, 0)); 243 | grassGeo.applyMatrix4(new THREE.Matrix4().makeRotationX(-Math.PI / 2)); 244 | //console.log(grassGeo.attributes.position); 245 | /*for (let i = 0; i < grassGeo.attributes.normal.count; i++) { 246 | grassGeo.attributes.normal.setX(i, 0); 247 | grassGeo.attributes.normal.setY(i, 1); 248 | grassGeo.attributes.normal.setZ(i, 0); 249 | }*/ 250 | loadingElement.innerHTML = "Placing Grass & Flora..."; 251 | await nextFrame(); 252 | const instancedMesh = new THREE.InstancedMesh(grassGeo, new THREE.MeshBasicMaterial({ side: THREE.DoubleSide, color: new THREE.Color(0.4, 0.4, 0.2), map: new THREE.TextureLoader().load("grassalbido.jpeg"), alphaMap: new THREE.TextureLoader().load("grassalpha.jpg"), alphaTest: 0.5 }), 100000); 253 | let grassShader; 254 | instancedMesh.material.onBeforeCompile = (shader) => { 255 | shader.uniforms.time = { value: 0.0 }; 256 | shader.uniforms.shadowTex = { value: null }; 257 | shader.uniforms.lightViewMat = { value: directionalLight.shadow.camera.matrixWorldInverse }; 258 | shader.uniforms.lightProjMat = { value: directionalLight.shadow.camera.projectionMatrix }; 259 | setTimeout(() => { 260 | shader.uniforms.shadowTex = { value: directionalLight.shadow.map.texture } 261 | }); 262 | shader.uniforms.cameraPos = { value: camera.position }; 263 | grassShader = shader; 264 | shader.vertexShader = /*glsl*/ ` 265 | varying vec2 vUv; 266 | varying vec3 vColor; 267 | varying vec3 vWorldPosition; 268 | attribute vec3 grassNormal; 269 | attribute float offset; 270 | uniform float time; 271 | uniform vec3 cameraPos; 272 | mat3 GetTangentSpace(vec3 normal) 273 | { 274 | // Choose a helper vector for the cross product 275 | vec3 helper = vec3(1.0, 0.0, 0.0); 276 | if (abs(normal.x) > 0.99) 277 | helper = vec3(0.0, 0.0, 1.0); 278 | // Generate vectors 279 | vec3 tangent = normalize(cross(normal, helper)); 280 | vec3 binormal = normalize(cross(normal, tangent)); 281 | return mat3(tangent, binormal, normal); 282 | } 283 | void main() { 284 | vec3 transformed = position; 285 | vec3 origin = (instanceMatrix * vec4(0.0, 0.0, 0.0, 1.0)).xyz; 286 | //if (transformed.y > 5.0) { 287 | //transformed.x += 10.0 * sin(time); 288 | //} 289 | bool top = false; 290 | if (transformed.z < -9.0) { 291 | top = true; 292 | transformed.x += 2.0 * sin(time + 0.025 * origin.z + offset); 293 | transformed.z += 2.0 * cos(time + 0.025 * origin.x + offset); 294 | } 295 | vec3 worldPos = (instanceMatrix * vec4(transformed, 1.0)).xyz; 296 | vec3 contactPos = cameraPos; 297 | contactPos.y -= 15.0; 298 | vec3 awayFromCamera = normalize(worldPos - contactPos); 299 | awayFromCamera.x *= -1.0; 300 | awayFromCamera.z *= -1.0; 301 | //if (transformed.z < -9.0) { 302 | if (top) { 303 | // worldPos -= awayFromCamera * 0.25 * max(20.0 - distance(worldPos, contactPos), 0.0); 304 | //worldPos.y -= 0.75 * max(20.0 - distance(worldPos, contactPos), 0.0); 305 | worldPos -= awayFromCamera * max(20.0 - distance(worldPos.xz, contactPos.xz), 0.0) * max(100.0 - abs(worldPos.y - contactPos.y) * abs(worldPos.y - contactPos.y), 0.0) / 100.0; 306 | } 307 | //} 308 | gl_Position = projectionMatrix * viewMatrix * vec4(worldPos, 1.0); 309 | vUv = uv; 310 | vColor = instanceColor; 311 | vWorldPosition = worldPos; 312 | } 313 | `; 314 | shader.fragmentShader = ` 315 | uniform vec3 diffuse; 316 | uniform float opacity; 317 | uniform sampler2D shadowTex; 318 | uniform mat4 lightViewMat; 319 | uniform mat4 lightProjMat; 320 | varying vec3 vWorldPosition; 321 | #ifndef FLAT_SHADED 322 | varying vec3 vNormal; 323 | #endif 324 | #include 325 | #include 326 | #include 327 | #include 328 | #include 329 | #include 330 | #include 331 | #include 332 | #include 333 | #include 334 | #include 335 | #include 336 | #include 337 | #include 338 | #include 339 | #include 340 | #include 341 | vec2 unpackRGBATo2Half( vec4 v ) { 342 | return vec2( v.x + ( v.y / 255.0 ), v.z + ( v.w / 255.0 ) ); 343 | } 344 | vec2 texture2DDistribution( sampler2D shadow, vec2 uv ) { 345 | return unpackRGBATo2Half( texture2D( shadow, uv ) ); 346 | } 347 | float VSMShadow (sampler2D shadow, vec2 uv, float compare ){ 348 | float occlusion = 1.0; 349 | vec2 distribution = texture2DDistribution( shadow, uv ); 350 | float hard_shadow = step( compare , distribution.x ); // Hard Shadow 351 | if (hard_shadow != 1.0 ) { 352 | float distance = compare - distribution.x ; 353 | float variance = max( 0.00000, distribution.y * distribution.y ); 354 | float softness_probability = variance / (variance + distance * distance ); // Chebeyshevs inequality 355 | softness_probability = clamp( ( softness_probability - 0.3 ) / ( 0.95 - 0.3 ), 0.0, 1.0 ); // 0.3 reduces light bleed 356 | occlusion = clamp( max( hard_shadow, softness_probability ), 0.0, 1.0 ); 357 | } 358 | return occlusion; 359 | } 360 | void main() { 361 | #include 362 | vec4 diffuseColor = vec4( diffuse, opacity ); 363 | #include 364 | #include 365 | #include 366 | #include 367 | #include 368 | #include 369 | ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); 370 | // accumulation (baked indirect lighting only) 371 | #ifdef USE_LIGHTMAP 372 | vec4 lightMapTexel = texture2D( lightMap, vUv2 ); 373 | reflectedLight.indirectDiffuse += lightMapTexel.rgb * lightMapIntensity * RECIPROCAL_PI; 374 | #else 375 | vec4 projCoords = lightProjMat * lightViewMat * vec4(vWorldPosition, 1.0); 376 | projCoords.xyz /= projCoords.w; 377 | projCoords.xyz = projCoords.xyz * 0.5 + 0.5; 378 | float s = 1.0; 379 | if (projCoords.x > 0.0 && projCoords.x < 1.0 && projCoords.y > 0.0 && projCoords.y < 1.0 && projCoords.z > 0.0 && projCoords.z < 1.0) { 380 | s = (0.5 + 0.5 * VSMShadow(shadowTex, projCoords.xy, projCoords.z)); 381 | } 382 | reflectedLight.indirectDiffuse += vec3( 1.0 ) * s; 383 | #endif 384 | // modulation 385 | #include 386 | reflectedLight.indirectDiffuse *= diffuseColor.rgb; 387 | vec3 outgoingLight = reflectedLight.indirectDiffuse; 388 | #include 389 | #include 390 | #include 391 | #include 392 | #include 393 | #include 394 | #include 395 | } 396 | `; 397 | } 398 | let dummy = new THREE.Object3D(); 399 | let normal = new THREE.Vector3(); 400 | const up = new THREE.Vector3(0, -1, 0); 401 | const normalAttribute = []; 402 | const offsetAttribute = []; 403 | for (let i = 0; i < 100000; i++) { 404 | grassSampler.sample(dummy.position, normal); 405 | // console.log(i); 406 | // console.log(normal.clone().dot(up)); 407 | if (Math.random() > Math.pow(normal.clone().dot(up), 10.0) || 408 | Math.random() > dummy.position.y * 0.1) { 409 | i--; 410 | continue; 411 | } 412 | let target = dummy.position.clone().add(normal); 413 | const normalArr = [...normal.clone()]; 414 | normalArr[1] *= -1; 415 | normalAttribute.push(...normalArr); 416 | dummy.lookAt(target.x, target.y, target.z); 417 | dummy.scale.set(0.75 + Math.random() * 0.5, 0.75 + Math.random() * 0.5, 0.75 + Math.random() * 0.5); 418 | dummy.updateMatrix(); 419 | let noiseSamplePos = target.clone().multiplyScalar(0.01); 420 | const color = new THREE.Vector3(1, 1, 1); 421 | let mag = 0.25; 422 | for (let i = 0; i < 12; i++) { 423 | color.add(new THREE.Vector3(noise.simplex3(noiseSamplePos.x, noiseSamplePos.y, noiseSamplePos.z), noise.simplex3(noiseSamplePos.x + 256, noiseSamplePos.y + 256, noiseSamplePos.z + 256), noise.simplex3(noiseSamplePos.x + 512, noiseSamplePos.y + 512, noiseSamplePos.z + 512)).multiplyScalar(mag)); 424 | noiseSamplePos.multiplyScalar(2); 425 | mag /= 2; 426 | } 427 | instancedMesh.setColorAt(i, new THREE.Color(color.x, color.y, color.z)); 428 | instancedMesh.setMatrixAt(i, dummy.matrix.clone()); 429 | offsetAttribute.push(Math.random() * Math.PI * 2); 430 | } 431 | instancedMesh.geometry.setAttribute('grassNormal', new THREE.InstancedBufferAttribute(new Float32Array(normalAttribute), 3)); 432 | instancedMesh.geometry.setAttribute('offset', new THREE.InstancedBufferAttribute(new Float32Array(offsetAttribute), 1)); 433 | scene.add(instancedMesh); 434 | const stems = new THREE.InstancedMesh(new THREE.CylinderGeometry(0.1, 0.2, 8, 8), new THREE.MeshStandardMaterial({ color: new THREE.Color(0.5, 1, 0.0), map: new THREE.TextureLoader().load("terraincolor.png") }), 1000); 435 | stems.geometry.applyMatrix4(new THREE.Matrix4().makeTranslation(0, 4, 0)); 436 | stems.geometry.applyMatrix4(new THREE.Matrix4().makeRotationX(-Math.PI / 2)); 437 | stems.material.onBeforeCompile = (shader) => { 438 | tickers.push(shader); 439 | shader.uniforms.time = { value: 0.0 }; 440 | shader.vertexShader = ` 441 | uniform float time; 442 | attribute float offset; 443 | mat4 rotationMatrix(vec3 axis, float angle) 444 | { 445 | axis = normalize(axis); 446 | float s = sin(angle); 447 | float c = cos(angle); 448 | float oc = 1.0 - c; 449 | 450 | return mat4(oc * axis.x * axis.x + c, oc * axis.x * axis.y - axis.z * s, oc * axis.z * axis.x + axis.y * s, 0.0, 451 | oc * axis.x * axis.y + axis.z * s, oc * axis.y * axis.y + c, oc * axis.y * axis.z - axis.x * s, 0.0, 452 | oc * axis.z * axis.x - axis.y * s, oc * axis.y * axis.z + axis.x * s, oc * axis.z * axis.z + c, 0.0, 453 | 0.0, 0.0, 0.0, 1.0); 454 | }\n` + shader.vertexShader; 455 | shader.vertexShader = shader.vertexShader.replace("#include ", /*glsl*/ ` 456 | mat4 sway = rotationMatrix(vec3(1.0, 0.0, 0.0), 0.1 * sin(time + offset)); 457 | mat4 sway2 = rotationMatrix(vec3(0.0, 1.0, 0.0), 0.1 * cos(time + offset)); 458 | gl_Position = projectionMatrix * viewMatrix * instanceMatrix * sway * sway2 * vec4(transformed, 1.0); 459 | vec4 mvPosition = viewMatrix * instanceMatrix * sway * sway2 * vec4(transformed, 1.0); 460 | `); 461 | } 462 | const flowers = new THREE.InstancedMesh(new THREE.PlaneGeometry(4, 4), new THREE.MeshBasicMaterial({ side: THREE.DoubleSide, map: new THREE.TextureLoader().load("dandelion.png"), transparent: true, alphaTest: 0.125 }), 1000); 463 | flowers.renderOrder = 999999; 464 | flowers.geometry.applyMatrix4(new THREE.Matrix4().makeRotationX(-Math.PI / 2)); 465 | flowers.material.onBeforeCompile = (shader) => { 466 | tickers.push(shader); 467 | shader.uniforms.time = { value: 0.0 }; 468 | shader.vertexShader = ` 469 | uniform float time; 470 | attribute float offset; 471 | mat4 rotationMatrix(vec3 axis, float angle) 472 | { 473 | axis = normalize(axis); 474 | float s = sin(angle); 475 | float c = cos(angle); 476 | float oc = 1.0 - c; 477 | 478 | return mat4(oc * axis.x * axis.x + c, oc * axis.x * axis.y - axis.z * s, oc * axis.z * axis.x + axis.y * s, 0.0, 479 | oc * axis.x * axis.y + axis.z * s, oc * axis.y * axis.y + c, oc * axis.y * axis.z - axis.x * s, 0.0, 480 | oc * axis.z * axis.x - axis.y * s, oc * axis.y * axis.z + axis.x * s, oc * axis.z * axis.z + c, 0.0, 481 | 0.0, 0.0, 0.0, 1.0); 482 | }\n` + shader.vertexShader; 483 | shader.vertexShader = shader.vertexShader.replace("#include ", /*glsl*/ ` 484 | /*vec4 mvPosition = vec4( transformed, 1.0 ); 485 | #ifdef USE_INSTANCING 486 | mvPosition = instanceMatrix * mvPosition; 487 | #endif 488 | vec3 camera_Up = vec3(viewMatrix[0][0], viewMatrix[1][0], viewMatrix[2][0]); 489 | vec3 camera_Right = vec3(viewMatrix[0][1], viewMatrix[1][1], viewMatrix[2][1]); 490 | mvPosition = vec4(mvPosition.xyz + camera_Up * ); 491 | mvPosition = modelViewMatrix * mvPosition; 492 | gl_Position = projectionMatrix * mvPosition;*/ 493 | mat4 sway = rotationMatrix(vec3(1.0, 0.0, 0.0), 0.1 * sin(time + offset)); 494 | mat4 sway2 = rotationMatrix(vec3(0.0, 1.0, 0.0), 0.1 * cos(time + offset)); 495 | vec3 origin = (instanceMatrix * sway * sway2 * vec4(0.0, 0.0, -9.5, 1.0)).xyz; 496 | vec3 camera_Up = vec3(viewMatrix[0][0], viewMatrix[1][0], viewMatrix[2][0]); 497 | vec3 camera_Right = vec3(viewMatrix[0][1], viewMatrix[1][1], viewMatrix[2][1]); 498 | vec3 p = vec3(origin + camera_Up * transformed.x * (1.0) + camera_Right * transformed.z * (1.0)); 499 | gl_Position = projectionMatrix * viewMatrix * vec4(p, 1.0); 500 | `) 501 | } 502 | dummy = new THREE.Object3D(); 503 | normal = new THREE.Vector3(); 504 | const flowerOffsets = []; 505 | const flowerBoxes = []; 506 | stems.castShadow = true; 507 | stems.receiveShadow = true; 508 | scene.add(stems); 509 | scene.add(flowers); 510 | const bigFlower = new THREE.Mesh(new THREE.CylinderGeometry(0.1, 0.2, 8, 8), new THREE.MeshStandardMaterial({ color: new THREE.Color(0.5, 1, 0.0), map: new THREE.TextureLoader().load("terraincolor.png") })); 511 | bigFlower.position.z = -7; 512 | bigFlower.position.x = 4; 513 | bigFlower.position.y = -3; 514 | const coreTex = new THREE.TextureLoader().load("dandelionsmall.png"); 515 | coreTex.wrapS = THREE.RepeatWrapping; 516 | coreTex.wrapT = THREE.RepeatWrapping; 517 | coreTex.repeat.set(2, 1); 518 | const bigFlowerCore = new THREE.Mesh(new THREE.SphereGeometry(0.25, 16, 16), new THREE.MeshStandardMaterial({ color: new THREE.Color(1, 1, 1), map: coreTex })); 519 | bigFlowerCore.position.y = 4; 520 | bigFlowerCore.rotation.y = -Math.PI - Math.PI / 5 + 0.1; 521 | bigFlower.add(bigFlowerCore); 522 | const spores = new THREE.InstancedMesh(new THREE.CylinderGeometry(0.005, 0.005, 1, 8), new THREE.MeshStandardMaterial({ transparent: true, opacity: 0.5 }), 1000); 523 | spores.geometry.applyMatrix4(new THREE.Matrix4().makeTranslation(0, 0.5, 0)); 524 | spores.geometry.applyMatrix4(new THREE.Matrix4().makeRotationX(-Math.PI / 2)); 525 | const sporeFlowers = new THREE.InstancedMesh(new THREE.PlaneGeometry(0.25, 0.25), new THREE.MeshBasicMaterial({ side: THREE.FrontSide, transparent: true, map: new THREE.TextureLoader().load("dandelionspore.png"), alphaTest: 0.125 }), 1000); 526 | const spores2 = new THREE.InstancedMesh(new THREE.CylinderGeometry(0.005, 0.005, 1, 8), new THREE.MeshStandardMaterial({ transparent: true, opacity: 0.5 }), 1000); 527 | spores2.geometry.applyMatrix4(new THREE.Matrix4().makeTranslation(0, 0.5, 0)); 528 | spores2.geometry.applyMatrix4(new THREE.Matrix4().makeRotationX(-Math.PI / 2)); 529 | const sporeFlowers2 = new THREE.InstancedMesh(new THREE.PlaneGeometry(0.25, 0.25), new THREE.MeshBasicMaterial({ side: THREE.FrontSide, transparent: true, map: new THREE.TextureLoader().load("dandelionspore.png"), alphaTest: 0.125 }), 1000); 530 | scene.add(spores2); 531 | scene.add(sporeFlowers2); 532 | //sporeFlowers.geometry.applyMatrix4(new THREE.Matrix4().makeRotationX(Math.PI / 2)); 533 | sporeFlowers.geometry.applyMatrix4(new THREE.Matrix4().makeTranslation(0, 0.0, 1.0)); 534 | sporeFlowers2.geometry.applyMatrix4(new THREE.Matrix4().makeTranslation(0, 0.0, 1.0)); 535 | dummy = new THREE.Object3D(); 536 | for (let i = 0; i < 1000; i++) { 537 | const direction = new THREE.Vector3(Math.random() - 0.5, Math.random() - 0.5, Math.random() - 0.5); 538 | direction.normalize(); 539 | //dummy.position.copy(direction.clone().multiplyScalar(0.5)); 540 | dummy.lookAt(direction.x, direction.y, direction.z); 541 | dummy.updateMatrix(); 542 | spores.setMatrixAt(i, dummy.matrix); 543 | sporeFlowers.setMatrixAt(i, dummy.matrix); 544 | } 545 | spores.renderOrder = 10000; 546 | sporeFlowers.renderOrder = 10000; 547 | spores2.renderOrder = 10000; 548 | sporeFlowers2.renderOrder = 10000; 549 | bigFlowerCore.add(spores); 550 | bigFlowerCore.add(sporeFlowers); 551 | bigFlowerCore.scale.set(1.5, 1.5, 1.5); 552 | //bigFlower.geometry.applyMatrix4(new THREE.Matrix4().makeTranslation(0, 4, 0)); 553 | //bigFlower.geometry.applyMatrix4(new THREE.Matrix4().makeRotationX(-Math.PI / 2)); 554 | camera.add(bigFlower); 555 | bigFlower.visible = true; 556 | const reflectScene = new THREE.Scene(); 557 | const ambientLight2 = new THREE.AmbientLight(new THREE.Color(1.0, 1.0, 1.0), 0.25); 558 | reflectScene.add(ambientLight2); 559 | const directionalLight2 = new THREE.DirectionalLight(0xffffff, 0.35); 560 | directionalLight2.position.set(150, -100, 50); 561 | reflectScene.add(directionalLight2); 562 | const reflectTerrain = new THREE.Mesh(terrainMesh.geometry, terrainMesh.material); 563 | reflectTerrain.scale.y = -1; 564 | reflectScene.add(reflectTerrain) 565 | const defaultTexture = new THREE.WebGLRenderTarget(clientWidth, clientHeight, { 566 | minFilter: THREE.LinearFilter, 567 | magFilter: THREE.NearestFilter 568 | }); 569 | defaultTexture.depthTexture = new THREE.DepthTexture(clientWidth, clientHeight, THREE.FloatType); 570 | const reflectTexture = new THREE.WebGLRenderTarget(clientWidth, clientHeight, { 571 | minFilter: THREE.LinearFilter, 572 | magFilter: THREE.NearestFilter 573 | }); 574 | reflectTexture.depthTexture = new THREE.DepthTexture(clientWidth, clientHeight, THREE.FloatType); 575 | // Post Effects 576 | const composer = new EffectComposer(renderer); 577 | const smaaPass = new SMAAPass(clientWidth, clientHeight); 578 | const effectPass = new ShaderPass(EffectShader); 579 | const ssaoPass = new ShaderPass(SSAOShader); 580 | const effectCompositer = new ShaderPass(EffectCompositer); 581 | const godRayPass = new ShaderPass(GodRaysShader); 582 | //composer.addPass(effectPass); 583 | composer.addPass(ssaoPass); 584 | composer.addPass(effectCompositer); 585 | composer.addPass(godRayPass); 586 | composer.addPass(effectPass); 587 | composer.addPass(smaaPass); 588 | const makeTBN = normal => { 589 | let helper = new THREE.Vector3(1, 0, 0); 590 | if (Math.abs(normal.x) > 0.99) { 591 | helper = new THREE.Vector3(0, 0, 1); 592 | } 593 | const tangent = normal.clone().cross(helper).normalize(); 594 | const binormal = normal.clone().cross(tangent).normalize(); 595 | const mat = new THREE.Matrix4(); 596 | mat.makeBasis( 597 | tangent, 598 | binormal, 599 | normal 600 | ); 601 | return mat; 602 | } 603 | const waterNormalMap = new THREE.TextureLoader().load("waternormal.jpeg"); 604 | const waterNormalMap2 = new THREE.TextureLoader().load("waternormal2.png"); 605 | waterNormalMap.wrapS = THREE.RepeatWrapping; 606 | waterNormalMap.wrapT = THREE.RepeatWrapping; 607 | waterNormalMap2.wrapS = THREE.RepeatWrapping; 608 | waterNormalMap2.wrapT = THREE.RepeatWrapping; 609 | /*const bottomSize = 10; 610 | const topSize = 5; 611 | const direction = new THREE.Vector3(0, 1, 0); 612 | const height = 75; 613 | const cylinderGeo = new THREE.CylinderGeometry(topSize, bottomSize, height, 32); 614 | cylinderGeo.applyMatrix4(new THREE.Matrix4().makeTranslation(0, height / 2, 0)); 615 | cylinderGeo.applyMatrix4(new THREE.Matrix4().makeRotationX(Math.PI / 2)); 616 | cylinderGeo.applyMatrix4(makeTBN(direction)); 617 | scene.add(new THREE.Mesh(cylinderGeo, new THREE.MeshStandardMaterial({ color: new THREE.Color(1.0, 0.5, 0.25) })));*/ 618 | const makeTree = (geometries, position, bottomSize, topSize, direction, height, tier = 0) => { 619 | //console.log(position); 620 | const cylinderGeo = new THREE.CylinderGeometry(topSize, bottomSize, height, Math.max(32 / Math.pow(2, tier), 4)); 621 | cylinderGeo.applyMatrix4(new THREE.Matrix4().makeTranslation(0, height / 2, 0)); 622 | cylinderGeo.applyMatrix4(new THREE.Matrix4().makeRotationX(Math.PI / 2)); 623 | cylinderGeo.applyMatrix4(makeTBN(direction)); 624 | cylinderGeo.applyMatrix4(new THREE.Matrix4().makeTranslation(position.x, position.y, position.z)); 625 | cylinderGeo.setAttribute('leafiness', new THREE.BufferAttribute(new Float32Array(Array(cylinderGeo.attributes.position.count).fill(Math.max(tier - 1, 0))), 1)); 626 | geometries.push(cylinderGeo); 627 | if (topSize > 0.1) { 628 | const branchAmount = Math.random() < 0.5 ? 3 : 2; 629 | const directions = []; 630 | for (let i = 0; i < branchAmount; i++) { 631 | let dir; 632 | for (let i = 0; i < 100; i++) { 633 | dir = direction.clone().add(new THREE.Vector3(Math.random() - 0.5, Math.random() - 0.5, Math.random() - 0.5)).normalize(); 634 | let moveAlong = false; 635 | for (let j = 0; j < directions.length; j++) { 636 | if (directions[j].dot(dir) > 0.75) { 637 | moveAlong = true; 638 | } 639 | } 640 | if (!moveAlong) { 641 | break; 642 | } 643 | } 644 | directions.push(dir); 645 | } 646 | const joint = new THREE.SphereGeometry(topSize, Math.max(32 / Math.pow(2, tier), 4), Math.max(32 / Math.pow(2, tier), 4)); 647 | joint.setAttribute('leafiness', new THREE.BufferAttribute(new Float32Array(Array(joint.attributes.position.count).fill(0)), 1)); 648 | const newPosition = position.clone().add(direction.clone().multiplyScalar(height)); 649 | joint.applyMatrix4(new THREE.Matrix4().makeTranslation(newPosition.x, newPosition.y, newPosition.z)); 650 | geometries.push(joint); 651 | for (let i = 0; i < directions.length; i++) { 652 | makeTree(geometries, newPosition, topSize, topSize / (1.875 + Math.random() * 0.25), directions[i], height * (0.625 + 0.25 * Math.random()), tier + 1); 653 | } 654 | //makeTree(geometries, position.clone().add(direction.clone().multiplyScalar(height)), topSize, topSize / 2, direction.clone().add(new THREE.Vector3(Math.random() - 0.5, Math.random() - 0.5, Math.random() - 0.5).multiplyScalar(1.0)).normalize(), height * 0.75); 655 | } 656 | return geometries; 657 | } 658 | const leafAlpha = new THREE.TextureLoader().load("leafalpha.png"); 659 | const leafColor = new THREE.TextureLoader().load("leafcolor.jpeg"); 660 | const leafNormal = new THREE.TextureLoader().load("leafnormal2.png"); 661 | const barkMap = new THREE.TextureLoader().load("barkmap.jpeg"); 662 | const barkColor = new THREE.TextureLoader().load("barkcolor.jpeg"); 663 | loadingElement.innerHTML = "Growing Trees..."; 664 | await nextFrame(); 665 | let tickers = []; 666 | let treePositions = []; 667 | let trees = []; 668 | for (let i = 0; i < 100; i++) { 669 | if (i % 10 === 0) { 670 | loadingElement.innerHTML = `Growing Trees ${i}/100...`; 671 | await nextFrame(); 672 | } 673 | const extraHeight = Math.random(); 674 | const width = 10 * (0.75 + 0.5 * Math.random()) + 5 * extraHeight; 675 | const treeParts = makeTree([], new THREE.Vector3(0, 0, 0), width, width / (1.875 + Math.random() * 0.25), new THREE.Vector3(0, 1, 0), 32 * (0.75 + 0.5 * Math.random()) + 32 * extraHeight); 676 | const treeMesh = new THREE.Mesh(BufferGeometryUtils.mergeBufferGeometries(treeParts), new THREE.MeshStandardMaterial({ color: new THREE.Color((144 / 255) * (0.75 + 0.5 * Math.random()), (92 / 255) * (0.75 + 0.5 * Math.random()), (66 / 255) * (0.75 + 0.5 * Math.random())), normalMap: barkMap, map: barkColor })); 677 | treeMesh.material.onBeforeCompile = (shader) => { 678 | shader.uniforms.shadowTex = { value: null }; 679 | shader.uniforms.lightViewMat = { value: directionalLight.shadow.camera.matrixWorldInverse }; 680 | shader.uniforms.lightProjMat = { value: directionalLight.shadow.camera.projectionMatrix }; 681 | setTimeout(() => { 682 | shader.uniforms.shadowTex = { value: directionalLight.shadow.map.texture } 683 | }); 684 | shader.vertexShader = shader.vertexShader.replace("#ifdef USE_TRANSMISSION", "").replace("#ifdef USE_TRANSMISSION", ""); 685 | shader.vertexShader = shader.vertexShader.replace("#endif", "").replace("#endif", ""); 686 | shader.vertexShader = shader.vertexShader.replace("#include ", ` 687 | vec4 worldPosition = vec4( transformed, 1.0 ); 688 | #ifdef USE_INSTANCING 689 | worldPosition = instanceMatrix * worldPosition; 690 | #endif 691 | worldPosition = modelMatrix * worldPosition; 692 | `); 693 | shader.fragmentShader = "varying vec3 vWorldPosition;\nuniform mat4 lightViewMat;\nuniform mat4 lightProjMat;\nuniform sampler2D shadowTex;\n" + shader.fragmentShader.replace("#include ", ` 694 | #include 695 | vec4 projCoords = lightProjMat * lightViewMat * vec4(vWorldPosition, 1.0); 696 | projCoords.xyz /= projCoords.w; 697 | projCoords.xyz = projCoords.xyz * 0.5 + 0.5; 698 | float s = 1.0; 699 | if (projCoords.x > 0.0 && projCoords.x < 1.0 && projCoords.y > 0.0 && projCoords.y < 1.0 && projCoords.z > 0.0 && projCoords.z < 1.0) { 700 | s = (VSMShadow(shadowTex, projCoords.xy, projCoords.z - 0.001)); 701 | } 702 | `).replace(`#include `, CustomLightShadowFragment); 703 | } 704 | treeMesh.geometry.computeBoundingBox(); 705 | /* treeMesh.position.x = Math.floor(i / 10) * 102.4 - 512; 706 | treeMesh.position.z = (i % 10) * 102.4 - 512;*/ 707 | treeMesh.scale.set(0.5, 0.5, 0.5); 708 | let surfaceDir = new THREE.Vector3(); 709 | let targetDir = new THREE.Vector3(0, -1, 0); 710 | while (treeMesh.position.y < 10 || surfaceDir.dot(targetDir) < 0.95 || 711 | treePositions.some(position => Math.hypot(position.x - treeMesh.position.x, position.z - treeMesh.position.z) < 10)) { 712 | grassSampler.sample(treeMesh.position, surfaceDir); 713 | } 714 | // treeMesh.rotateX(Math.PI / 2); 715 | treeMesh.lookAt(treeMesh.position.clone().sub(surfaceDir)); 716 | treeMesh.rotateX(Math.PI / 2); 717 | treeMesh.castShadow = true; 718 | treeMesh.receiveShadow = true; 719 | scene.add(treeMesh); 720 | const leavesMesh = new THREE.InstancedMesh(new THREE.PlaneGeometry(10, 30), new THREE.MeshStandardMaterial({ side: THREE.DoubleSide, color: new THREE.Color(Math.random(), 2.5 + 1.0 * Math.random(), Math.random()), alphaMap: leafAlpha, map: leafColor, normalMap: leafNormal, alphaTest: 0.5 }), 1000 + 1000 * extraHeight); 721 | let currLeavesShader; 722 | const leafCenter = new THREE.Vector3(); 723 | leavesMesh.material.onBeforeCompile = (shader) => { 724 | currLeavesShader = shader; 725 | tickers.push(shader); 726 | shader.uniforms.time = { value: 0 }; 727 | shader.uniforms.leafCenter = { value: leafCenter }; 728 | shader.uniforms.cameraPos = { value: camera.position }; 729 | shader.vertexShader = /*glsl*/ ` 730 | #define STANDARD 731 | varying vec3 vViewPosition; 732 | varying vec3 vWorldPosition; 733 | uniform float time; 734 | #include 735 | #include 736 | #include 737 | #include 738 | #include 739 | #include 740 | #include 741 | #include 742 | #include 743 | #include 744 | #include 745 | #include 746 | void main() { 747 | #include 748 | #include 749 | #include 750 | #include 751 | #include 752 | #include 753 | #include 754 | #include 755 | #include 756 | #include 757 | #include 758 | vec3 origin = (instanceMatrix * vec4(0.0, 0.0, 0.0, 1.0)).xyz; 759 | if (transformed.z < -1.0) { 760 | transformed.x += 2.0 * sin(time + 0.025 * origin.z); 761 | transformed.y += 2.0 * cos(time + 0.025 * origin.x); 762 | } 763 | #include 764 | #include 765 | #include 766 | #include 767 | #include 768 | #include 769 | vViewPosition = - mvPosition.xyz; 770 | #include 771 | #include 772 | #include 773 | vWorldPosition = worldPosition.xyz; 774 | } 775 | `; 776 | shader.fragmentShader = "uniform vec3 leafCenter;\nvarying vec3 vWorldPosition;\nuniform vec3 cameraPos;\n" + shader.fragmentShader.replace("#include ", /*glsl*/ ` 777 | #include 778 | mat4 viewMatrixInv = inverse(viewMatrix); 779 | //vec3 fromCenter = normalize(vWorldPosition - leafCenter); 780 | vec3 scatteringHalf = normalize(normalize(vec3(300.0, 200.0, 100.0)) + 0.25 * fromCenter); 781 | float scatteringDot = pow(clamp(dot(normalize(cameraPos - vWorldPosition), -scatteringHalf), 0.0, 1.0), 8.0); 782 | //gl_FragColor = vec4(vec3(scatteringDot), 1.0); 783 | //return; 784 | totalEmissiveRadiance += scatteringDot * vec3(0.07, 0.76, 0.1); 785 | //totalEmissiveRadiance += 0.5 * (max(dot(fromCenter, normalize(vec3(300.0, 200.0, 100.0))), 0.0)) * vec3(0.07, 0.76, 0.1); 786 | `); 787 | shader.uniforms.shadowTex = { value: null }; 788 | shader.uniforms.lightViewMat = { value: directionalLight.shadow.camera.matrixWorldInverse }; 789 | shader.uniforms.lightProjMat = { value: directionalLight.shadow.camera.projectionMatrix }; 790 | setTimeout(() => { 791 | shader.uniforms.shadowTex = { value: directionalLight.shadow.map.texture } 792 | }); 793 | shader.fragmentShader = "uniform mat4 lightViewMat;\nuniform mat4 lightProjMat;\nuniform sampler2D shadowTex;\n" + shader.fragmentShader.replace("#include ", ` 794 | #include 795 | vec3 fromCenter = normalize(vWorldPosition - leafCenter); 796 | vec4 projCoords = lightProjMat * lightViewMat * vec4(vWorldPosition, 1.0); 797 | projCoords.xyz /= projCoords.w; 798 | projCoords.xyz = projCoords.xyz * 0.5 + 0.5; 799 | float s = 1.0; 800 | if (projCoords.x > 0.0 && projCoords.x < 1.0 && projCoords.y > 0.0 && projCoords.y < 1.0 && projCoords.z > 0.0 && projCoords.z < 1.0) { 801 | s = max(dot(fromCenter, normalize(vec3(300.0, 200.0, 100.0))), 0.0); 802 | } 803 | `).replace(`#include `, CustomLightShadowFragment); 804 | shader.fragmentShader = shader.fragmentShader.replace("#include ", ` 805 | float faceDirection = gl_FrontFacing ? 1.0 : - 1.0; 806 | vec3 normal = normalize(vWorldPosition - leafCenter); 807 | vec3 geometryNormal = normal; 808 | `); 809 | } 810 | leavesMesh.geometry.applyMatrix4(new THREE.Matrix4().makeTranslation(0, 15, 0)); 811 | leavesMesh.geometry.applyMatrix4(new THREE.Matrix4().makeRotationX(-Math.PI / 2)); 812 | leavesMesh.castShadow = true; 813 | leavesMesh.receiveShadow = true; 814 | //console.log(leavesMesh.geometry.attributes.position); 815 | const sampler = new MeshSurfaceSampler(treeMesh).setWeightAttribute('leafiness').build(); 816 | const dummy = new THREE.Object3D(); 817 | for (let i = 0; i < leavesMesh.count; i++) { 818 | sampler.sample(dummy.position, normal); 819 | leafCenter.add(dummy.position); 820 | let target = dummy.position.clone().add(normal); 821 | dummy.lookAt(target.x, target.y, target.z); 822 | dummy.updateMatrix(); 823 | leavesMesh.setMatrixAt(i, dummy.matrix); 824 | } 825 | leafCenter.divideScalar(leavesMesh.count); 826 | leafCenter.applyMatrix4(treeMesh.matrix); 827 | treeMesh.add(leavesMesh); 828 | trees.push(treeMesh); 829 | treePositions.push(treeMesh.position); 830 | } 831 | let rockPositions = []; 832 | const rockTextures = [ 833 | new THREE.TextureLoader().load("rockcolor1.jpeg"), 834 | new THREE.TextureLoader().load("rockcolor2.jpeg"), 835 | new THREE.TextureLoader().load("rockcolor3.jpeg"), 836 | new THREE.TextureLoader().load("rockcolor4.jpeg"), 837 | new THREE.TextureLoader().load("rockcolor5.jpeg") 838 | ]; 839 | rockTextures.forEach(rockT => { 840 | rockT.wrapS = THREE.RepeatWrapping; 841 | rockT.wrapT = THREE.RepeatWrapping; 842 | }); 843 | const rockNormals = [ 844 | new THREE.TextureLoader().load("rocknormal1.png"), 845 | new THREE.TextureLoader().load("rocknormal2.png"), 846 | new THREE.TextureLoader().load("rocknormal3.png"), 847 | new THREE.TextureLoader().load("rocknormal4.png"), 848 | new THREE.TextureLoader().load("rocknormal5.png") 849 | ]; 850 | rockNormals.forEach(rockN => { 851 | rockN.wrapS = THREE.RepeatWrapping; 852 | rockN.wrapT = THREE.RepeatWrapping; 853 | }) 854 | loadingElement.innerHTML = "Carving Rocks..."; 855 | await nextFrame(); 856 | const levelGeos = [ 857 | new THREE.IcosahedronGeometry(5, 40), 858 | new THREE.IcosahedronGeometry(5, 20), 859 | new THREE.IcosahedronGeometry(5, 10), 860 | new THREE.IcosahedronGeometry(5, 5), 861 | new THREE.IcosahedronGeometry(5, 2) 862 | ]; 863 | let rocks = []; 864 | for (let i = 0; i < 100; i++) { 865 | if (i % 10 === 0) { 866 | loadingElement.innerHTML = `Carving Rocks ${i}/100...`; 867 | await nextFrame(); 868 | } 869 | const lodMesh = new THREE.LOD(); 870 | let surfaceDir = new THREE.Vector3(); 871 | let targetDir = new THREE.Vector3(0, -1, 0); 872 | while (lodMesh.position.y < 10 || surfaceDir.dot(targetDir) < 0.95 || 873 | treePositions.some(position => Math.hypot(position.x - lodMesh.position.x, position.z - lodMesh.position.z) < 10) || 874 | rockPositions.some(position => Math.hypot(position.x - lodMesh.position.x, position.z - lodMesh.position.z) < 10)) { 875 | grassSampler.sample(lodMesh.position, surfaceDir); 876 | } 877 | surfaceDir.y *= -1; 878 | lodMesh.position.add(surfaceDir.clone().multiplyScalar(5)); 879 | let verticalMajor = Math.random() < 0.2; 880 | lodMesh.scale.set(0.5 + 1 * Math.random() + 0.5 * verticalMajor, verticalMajor ? 0.5 + 1.25 * Math.random() : 0.5 + 0.5 * Math.random(), 0.5 + 1 * Math.random() + 0.5 * verticalMajor); 881 | const rockIndex = Math.floor(Math.random() * rockTextures.length); 882 | const rockMat = new THREE.MeshStandardMaterial({ color: new THREE.Color(0.75 + Math.random() * 0.25, 0.75 + Math.random() * 0.25, 0.75 + Math.random() * 0.25), map: rockTextures[rockIndex], normalMap: rockNormals[rockIndex] }); 883 | rockMat.onBeforeCompile = (shader) => { 884 | shader.uniforms.shadowTex = { value: null }; 885 | shader.uniforms.lightViewMat = { value: directionalLight.shadow.camera.matrixWorldInverse }; 886 | shader.uniforms.lightProjMat = { value: directionalLight.shadow.camera.projectionMatrix }; 887 | setTimeout(() => { 888 | shader.uniforms.shadowTex = { value: directionalLight.shadow.map.texture } 889 | }); 890 | shader.vertexShader = shader.vertexShader.replace("#ifdef USE_TRANSMISSION", "").replace("#ifdef USE_TRANSMISSION", ""); 891 | shader.vertexShader = shader.vertexShader.replace("#endif", "").replace("#endif", ""); 892 | shader.vertexShader = shader.vertexShader.replace("#include ", ` 893 | vec4 worldPosition = vec4( transformed, 1.0 ); 894 | #ifdef USE_INSTANCING 895 | worldPosition = instanceMatrix * worldPosition; 896 | #endif 897 | worldPosition = modelMatrix * worldPosition; 898 | `); 899 | shader.fragmentShader = "varying vec3 vWorldPosition;\nuniform mat4 lightViewMat;\nuniform mat4 lightProjMat;\nuniform sampler2D shadowTex;\n" + shader.fragmentShader.replace("#include ", ` 900 | mat4 viewMatrixInv = inverse(viewMatrix); 901 | vec2 yUv = vWorldPosition.xz * 0.1; 902 | vec2 xUv = vWorldPosition.zy * 0.1; 903 | vec2 zUv = vWorldPosition.xy * 0.1; 904 | vec3 yDiff = texture2D(map, yUv).xyz; 905 | vec3 xDiff = texture2D(map, xUv).xyz; 906 | vec3 zDiff = texture2D(map, zUv).xyz; 907 | vec3 blendWeights = abs((viewMatrixInv * vec4(vNormal, 0.0)).xyz); 908 | blendWeights = blendWeights / (blendWeights.x + blendWeights.y + blendWeights.z); 909 | diffuseColor *= vec4((xDiff * blendWeights.x + yDiff * blendWeights.y + zDiff * blendWeights.z), 1.0); 910 | vec4 projCoords = lightProjMat * lightViewMat * vec4(vWorldPosition, 1.0); 911 | projCoords.xyz /= projCoords.w; 912 | projCoords.xyz = projCoords.xyz * 0.5 + 0.5; 913 | float s = 1.0; 914 | if (projCoords.x > 0.0 && projCoords.x < 1.0 && projCoords.y > 0.0 && projCoords.y < 1.0 && projCoords.z > 0.0 && projCoords.z < 1.0) { 915 | s = (VSMShadow(shadowTex, projCoords.xy, projCoords.z - 0.001)); 916 | } 917 | `).replace("#include ", ` 918 | vec3 yNormal = texture2D(normalMap, yUv).xyz; 919 | vec3 xNormal = texture2D(normalMap, xUv).xyz; 920 | vec3 zNormal = texture2D(normalMap, zUv).xyz; 921 | 922 | vec3 mapN = (xNormal * blendWeights.x + yNormal * blendWeights.y + zNormal * blendWeights.z) * 2.0 - 1.0; 923 | mapN.xy *= normalScale; 924 | #ifdef USE_TANGENT 925 | normal = normalize( vTBN * mapN ); 926 | #else 927 | normal = perturbNormal2Arb( - vViewPosition, normal, mapN, faceDirection ); 928 | #endif 929 | `).replace(`#include `, CustomLightShadowFragment); 930 | }; 931 | //new THREE.IcosahedronGeometry(5, Math.round(80 / (2 ** level))) 932 | for (let level = 0; level < levelGeos.length; level++) { 933 | const rockMesh = new THREE.Mesh(levelGeos[level].clone(), rockMat); 934 | const posAttr = rockMesh.geometry.attributes.position; 935 | for (let j = 0; j < posAttr.count; j++) { 936 | const position = new THREE.Vector3(posAttr.getX(j), posAttr.getY(j), posAttr.getZ(j)); 937 | let amt = 1.0; 938 | let mag = 0.25; 939 | let freq = 0.1; 940 | let shift = 0.0; 941 | for (let k = 0; k < 12 - 2 * level; k++) { 942 | amt += mag * noise.simplex3(freq * position.x + i * 100 + 512 + shift, freq * position.y + i * 200 + 1024 + shift, freq * position.z + i * 300 + 2048 + shift); 943 | mag /= 2; 944 | freq *= 2; 945 | shift += 128.0; 946 | } 947 | const magnitude = amt; 948 | position.multiplyScalar(magnitude); 949 | posAttr.setXYZ(j, position.x, position.y, position.z); 950 | } 951 | rockMesh.geometry.computeVertexNormals(); 952 | rockMesh.geometry.computeBoundingBox(); 953 | rockMesh.castShadow = true; 954 | rockMesh.receiveShadow = true; 955 | lodMesh.addLevel(rockMesh, 20 + (level) * 40); 956 | } 957 | lodMesh.lookAt(lodMesh.position.clone().add(surfaceDir.clone().multiplyScalar(5))); 958 | lodMesh.rotateX(Math.PI / 2); 959 | lodMesh.castShadow = true; 960 | lodMesh.receiveShadow = true; 961 | rockPositions.push(lodMesh.position); 962 | lodMesh.autoUpdate = false; 963 | lodMesh._currentLevel = 0; 964 | scene.add(lodMesh); 965 | rocks.push(lodMesh); 966 | } 967 | for (let i = 0; i < 1000; i++) { 968 | grassSampler.sample(dummy.position, normal); 969 | // console.log(i); 970 | // console.log(normal.clone().dot(up)); 971 | if (Math.random() > Math.pow(normal.clone().dot(up), 10.0) || 972 | Math.random() > dummy.position.y * 0.1 || 973 | treePositions.some(position => Math.hypot(position.x - dummy.position.x, position.z - dummy.position.z) < 10) || 974 | rockPositions.some(position => Math.hypot(position.x - dummy.position.x, position.z - dummy.position.z) < 10)) { 975 | i--; 976 | continue; 977 | } 978 | let target = dummy.position.clone().add(normal); 979 | const normalArr = [...normal.clone()]; 980 | normalArr[1] *= -1; 981 | normalAttribute.push(...normalArr); 982 | dummy.lookAt(target.x, target.y, target.z); 983 | // dummy.scale.set(0.75 + Math.random() * 0.5, 0.75 + Math.random() * 0.5, 0.75 + Math.random() * 0.5); 984 | dummy.updateMatrix(); 985 | stems.setMatrixAt(i, dummy.matrix.clone()); 986 | //dummy.position.copy(dummy.position.clone().add(normal.clone().multiplyScalar(-9.5))); 987 | //dummy.updateMatrix(); 988 | flowers.setMatrixAt(i, dummy.matrix.clone()); 989 | flowerOffsets.push(Math.random() * 2 * Math.PI); 990 | flowerBoxes.push(new THREE.Box3().setFromCenterAndSize(dummy.position.clone().add(normal.clone().multiplyScalar(-5)), new THREE.Vector3(5, 10, 5))); 991 | } 992 | flowerBoxes.forEach((box, i) => { 993 | box.index = i; 994 | }); 995 | flowers.geometry.setAttribute('offset', new THREE.InstancedBufferAttribute(new Float32Array(flowerOffsets), 1)); 996 | stems.geometry.setAttribute('offset', new THREE.InstancedBufferAttribute(new Float32Array(flowerOffsets), 1)); 997 | //scene.add(new THREE.Mesh(BufferGeometryUtils.mergeBufferGeometries(treeParts), new THREE.MeshStandardMaterial({ color: new THREE.Color(1.0, 0.5, 0.25) }))); 998 | loadingElement.innerHTML = "Summoning Butterflies..."; 999 | await nextFrame(); 1000 | const butterfly = await AssetManager.loadGLTFAsync("butterfly.glb"); 1001 | butterfly.scene.traverse(mesh => { 1002 | if (mesh.material) { 1003 | mesh.material.envMap = environment; 1004 | mesh.material.needsUpdate = true; 1005 | mesh.castShadow = true; 1006 | mesh.receiveShadow = true; 1007 | } 1008 | }) 1009 | //scene.add(butterfly.scene); 1010 | loadingElement.innerHTML = "Baking Collision Mesh (0 / 3)..."; 1011 | await nextFrame(); 1012 | let frame = 0; 1013 | let geometries = []; 1014 | [terrainMesh, ...rocks.map(rock => rock.levels[2].object), ...trees].forEach(object => { 1015 | const cloned = new THREE.Mesh(object.geometry, object.material); 1016 | object.getWorldPosition(cloned.position); 1017 | if (object.geometry && object.visible) { 1018 | const cloned = object.geometry.clone(); 1019 | cloned.applyMatrix4(object.matrixWorld); 1020 | for (const key in cloned.attributes) { 1021 | if (key !== 'position') { cloned.deleteAttribute(key); } 1022 | } 1023 | geometries.push(cloned); 1024 | } 1025 | }); 1026 | loadingElement.innerHTML = "Baking Collision Mesh (1 / 3)..."; 1027 | await nextFrame(); 1028 | const mergedGeometry = BufferGeometryUtils.mergeBufferGeometries(geometries.map(geom => geom.toNonIndexed()), false); 1029 | mergedGeometry.boundsTree = new MeshBVHLib.MeshBVH(mergedGeometry, { lazyGeneration: false, strategy: MeshBVHLib.SAH }); 1030 | const collider = new THREE.Mesh(mergedGeometry); 1031 | collider.material.wireframe = true; 1032 | collider.material.opacity = 0.5; 1033 | collider.material.transparent = true; 1034 | collider.visible = false; 1035 | collider.geometry.boundsTree = mergedGeometry.boundsTree; 1036 | loadingElement.innerHTML = "Baking Collision Mesh (2 / 3)..."; 1037 | await nextFrame(); 1038 | terrainMesh.geometry.boundsTree = new MeshBVHLib.MeshBVH(terrainMesh.geometry, { lazyGeneration: false, strategy: MeshBVHLib.SAH }); 1039 | scene.add(collider); 1040 | 1041 | const visualizer = new MeshBVHLib.MeshBVHVisualizer(collider, 20); 1042 | visualizer.visible = false; 1043 | visualizer.update(); 1044 | scene.add(visualizer); 1045 | loadingElement.innerHTML = "Placing Butterflies..."; 1046 | await nextFrame(); 1047 | const playerCapsule = new CapsuleEntity(2.5, 15); 1048 | while (true) { 1049 | /* const position = new THREE.Vector3(-256 + Math.random() * 512, -256 + Math.random() * 512); 1050 | const mesh = butterfly.scene.clone(); 1051 | const animations = */ 1052 | const xPos = 0 + (Math.random() - 0.5) * Math.random() * 512; 1053 | const zPos = 0 + (Math.random() - 0.5) * Math.random() * 512; 1054 | if ( 1055 | treePositions.some(position => Math.hypot(position.x - xPos, position.z - zPos) < 10) || 1056 | rockPositions.some(position => Math.hypot(position.x - xPos, position.z - zPos) < 10)) { 1057 | continue; 1058 | } 1059 | const height = terrainMesh.geometry.boundsTree.raycastFirst(new THREE.Ray(new THREE.Vector3(xPos, 1000, zPos), new THREE.Vector3(0, -1, 0)), THREE.DoubleSide); 1060 | const badHeight = collider.geometry.boundsTree.raycastFirst(new THREE.Ray(new THREE.Vector3(xPos, 1000, zPos), new THREE.Vector3(0, -1, 0)), THREE.DoubleSide); 1061 | if (badHeight > height + 0.1) { 1062 | continue; 1063 | } 1064 | const position = new THREE.Vector3(xPos, height.point.y + 15 + 10 * Math.random(), zPos); 1065 | playerCapsule.position.copy(position); 1066 | break; 1067 | } 1068 | const ogPosition = playerCapsule.position.clone(); 1069 | const butterflies = []; 1070 | for (let i = 0; i < 30; i++) { 1071 | /* const position = new THREE.Vector3(-256 + Math.random() * 512, -256 + Math.random() * 512); 1072 | const mesh = butterfly.scene.clone(); 1073 | const animations = */ 1074 | const xPos = -256 + Math.random() * 512; 1075 | const zPos = -256 + Math.random() * 512; 1076 | if ( 1077 | treePositions.some(position => Math.hypot(position.x - xPos, position.z - zPos) < 10) || 1078 | rockPositions.some(position => Math.hypot(position.x - xPos, position.z - zPos) < 10)) { 1079 | i--; 1080 | continue; 1081 | } 1082 | const height = terrainMesh.geometry.boundsTree.raycastFirst(new THREE.Ray(new THREE.Vector3(xPos, 1000, zPos), new THREE.Vector3(0, -1, 0)), THREE.DoubleSide); //new THREE.Raycaster(new THREE.Vector3(xPos, 1000, zPos), new THREE.Vector3(0, -1, 0)).intersectObject(terrainMesh); 1083 | const position = new THREE.Vector3(xPos, height.point.y + 15 + 10 * Math.random(), zPos); 1084 | const b = new Butterfly(butterfly.scene, butterfly.animations, { 1085 | position, 1086 | scene 1087 | }); 1088 | scene.add(b.mesh); 1089 | butterflies.push(b); 1090 | } 1091 | let lastTime = performance.now(); 1092 | const keys = {}; 1093 | document.onkeydown = (e) => { 1094 | keys[e.key] = true; 1095 | } 1096 | document.onkeyup = (e) => { 1097 | keys[e.key] = false; 1098 | } 1099 | const playerDirection = new THREE.Vector3(); 1100 | const getForwardVector = function(camera) { 1101 | camera.getWorldDirection(playerDirection); 1102 | playerDirection.y = 0; 1103 | playerDirection.normalize(); 1104 | return playerDirection; 1105 | } 1106 | 1107 | const getSideVector = function(camera) { 1108 | camera.getWorldDirection(playerDirection); 1109 | playerDirection.y = 0; 1110 | playerDirection.normalize(); 1111 | playerDirection.cross(camera.up); 1112 | return playerDirection; 1113 | } 1114 | let jumped = 0; 1115 | let mouseDown = false; 1116 | document.addEventListener("mousedown", e => { 1117 | mouseDown = true; 1118 | if (controls.isLocked && !bigFlower.visible) { 1119 | const ray = new THREE.Ray(camera.position, camera.getWorldDirection(new THREE.Vector3())); 1120 | flowerBoxes.sort((a, b) => { 1121 | return a.getCenter(new THREE.Vector3()).distanceTo(camera.position) - 1122 | b.getCenter(new THREE.Vector3()).distanceTo(camera.position); 1123 | }) 1124 | for (let i = 0; i < flowerBoxes.length; i++) { 1125 | const box = flowerBoxes[i]; 1126 | const intersectPos = new THREE.Vector3(); 1127 | if (ray.intersectBox(box, intersectPos) && !box.disabled) { 1128 | if (camera.position.distanceTo(intersectPos) < 25) { 1129 | bigFlower.visible = true; 1130 | bigFlower.position.y = -3; 1131 | bigFlower.position.z = -12; 1132 | bigFlower.rotation.x = 0; 1133 | dummy = new THREE.Object3D(); 1134 | for (let j = 0; j < 1000; j++) { 1135 | const direction = new THREE.Vector3(Math.random() - 0.5, Math.random() - 0.5, Math.random() - 0.5); 1136 | direction.normalize(); 1137 | //dummy.position.copy(direction.clone().multiplyScalar(0.5)); 1138 | dummy.lookAt(direction.x, direction.y, direction.z); 1139 | dummy.updateMatrix(); 1140 | spores.setMatrixAt(j, dummy.matrix); 1141 | sporeFlowers.setMatrixAt(j, dummy.matrix); 1142 | // spores2.setMatrixAt(j, new THREE.Matrix4()); 1143 | //sporeFlowers2.setMatrixAt(j, new THREE.Matrix4()); 1144 | spores.instanceMatrix.needsUpdate = true; 1145 | sporeFlowers.instanceMatrix.needsUpdate = true; 1146 | } 1147 | box.disabled = true; 1148 | stems.setMatrixAt(box.index, new THREE.Matrix4()); 1149 | flowers.setMatrixAt(box.index, new THREE.Matrix4()); 1150 | stems.instanceMatrix.needsUpdate = true; 1151 | flowers.instanceMatrix.needsUpdate = true; 1152 | removeIndex = 0; 1153 | //removedVelocities = []; 1154 | //removedAccelerations = []; 1155 | break; 1156 | } 1157 | } 1158 | } 1159 | } 1160 | }) 1161 | document.addEventListener("mouseup", e => { 1162 | mouseDown = false; 1163 | }) 1164 | let removeIndex = 0; 1165 | let removedVelocities = []; 1166 | let removedAccelerations = []; 1167 | scene.add(controls.getObject()); 1168 | document.getElementById("background").style.display = "none"; 1169 | document.body.appendChild(stats.dom); 1170 | document.body.appendChild(renderer.domElement); 1171 | document.addEventListener("click", () => { 1172 | controls.lock(); 1173 | }); 1174 | loadingElement.innerHTML = "Done!"; 1175 | 1176 | function animate() { 1177 | //console.log(mouseDown); 1178 | if (mouseDown && bigFlower.visible) { 1179 | for (let i = 0; i < 10; i++) { 1180 | if (removeIndex > 999) { 1181 | break; 1182 | } 1183 | const m = new THREE.Matrix4(); 1184 | spores.getMatrixAt(removeIndex, m); 1185 | m.premultiply(spores.matrixWorld); 1186 | spores2.setMatrixAt(removeIndex, m); 1187 | sporeFlowers.getMatrixAt(removeIndex, m); 1188 | m.premultiply(sporeFlowers.matrixWorld); 1189 | sporeFlowers2.setMatrixAt(removeIndex, m); 1190 | spores.setMatrixAt(removeIndex, new THREE.Matrix4()); 1191 | sporeFlowers.setMatrixAt(removeIndex, new THREE.Matrix4()); 1192 | spores.instanceMatrix.needsUpdate = true; 1193 | sporeFlowers.instanceMatrix.needsUpdate = true; 1194 | removedVelocities[removeIndex] = camera.getWorldDirection(new THREE.Vector3()).add(new THREE.Vector3(Math.random() - 0.5, Math.random() - 0.5, Math.random() - 0.5).multiplyScalar(0.25)).normalize().add(playerCapsule.horizontalVelocity.clone().multiplyScalar(25)); 1195 | removedAccelerations[removeIndex] = new THREE.Vector3(Math.random() - 0.5, Math.random() - 0.5, Math.random() - 0.5).normalize(); 1196 | removeIndex++; 1197 | } 1198 | } 1199 | if (removeIndex > 999) { 1200 | bigFlower.position.y *= 1.05; 1201 | bigFlower.rotation.x -= 0.05; 1202 | bigFlower.rotation.x *= 1.05; 1203 | if (bigFlower.position.y < -10) { 1204 | bigFlower.visible = false; 1205 | } 1206 | } 1207 | if (bigFlower.position.z < -7) { 1208 | bigFlower.position.z += 0.5; 1209 | } else { 1210 | bigFlower.position.z = -7; 1211 | } 1212 | for (let i = 0; i < 1000; i++) { 1213 | if (!removedVelocities[i]) { 1214 | continue; 1215 | } 1216 | const m = new THREE.Matrix4(); 1217 | spores2.getMatrixAt(i, m); 1218 | m.premultiply(new THREE.Matrix4().makeTranslation(removedVelocities[i].x * 0.25, removedVelocities[i].y * 0.25, removedVelocities[i].z * 0.25)); 1219 | spores2.setMatrixAt(i, m); 1220 | sporeFlowers2.getMatrixAt(i, m); 1221 | m.premultiply(new THREE.Matrix4().makeTranslation(removedVelocities[i].x * 0.25, removedVelocities[i].y * 0.25, removedVelocities[i].z * 0.25)); 1222 | sporeFlowers2.setMatrixAt(i, m); 1223 | if (removedVelocities[i].length() > 0.5) { 1224 | removedVelocities[i].multiplyScalar(0.995); 1225 | } 1226 | removedVelocities[i].add(removedAccelerations[i].clone().multiplyScalar(0.01)); 1227 | removedAccelerations[i].multiplyScalar(0.99); 1228 | } 1229 | spores2.instanceMatrix.needsUpdate = true; 1230 | sporeFlowers2.instanceMatrix.needsUpdate = true; 1231 | if (frame < 10) { 1232 | renderer.shadowMap.autoUpdate = true; 1233 | rocks.forEach(rock => { 1234 | rock._currentLevel = 0; 1235 | rock.autoUpdate = true; 1236 | }); 1237 | } else { 1238 | renderer.shadowMap.autoUpdate = false; 1239 | rocks.forEach(rock => { 1240 | rock.autoUpdate = true; 1241 | }); 1242 | } 1243 | if (frame > 60) { 1244 | loadingElement.style.display = "none"; 1245 | } 1246 | if (frame === 1) { 1247 | bigFlower.visible = false; 1248 | } 1249 | if (terrainShader) { 1250 | terrainShader.uniforms.reflect.value = false; 1251 | } 1252 | const delta = Math.min((performance.now() - lastTime) / 1000, 0.1); 1253 | const timeScale = delta / (1 / 60); 1254 | for (let i = 0; i < 5; i++) { 1255 | playerCapsule.update(delta / 5, collider); 1256 | } 1257 | camera.position.copy(playerCapsule.position); 1258 | const slowOffset = 1.0 * Math.sin(performance.now() / 1000); 1259 | const fastOffset = 1.0 * Math.sin(performance.now() / 333); 1260 | camera.position.y += slowOffset + (fastOffset - slowOffset) * Math.min(10 * playerCapsule.horizontalVelocity.length(), 1); 1261 | const speedFactor = 0.33; 1262 | if (controls.isLocked) { 1263 | if (keys["w"]) { 1264 | playerCapsule.horizontalVelocity.add(getForwardVector(camera).multiplyScalar(speedFactor * delta)); 1265 | } 1266 | 1267 | if (keys["s"]) { 1268 | playerCapsule.horizontalVelocity.add(getForwardVector(camera).multiplyScalar(-speedFactor * delta)); 1269 | } 1270 | 1271 | if (keys["a"]) { 1272 | playerCapsule.horizontalVelocity.add(getSideVector(camera).multiplyScalar(-speedFactor * delta)); 1273 | } 1274 | 1275 | if (keys["d"]) { 1276 | playerCapsule.horizontalVelocity.add(getSideVector(camera).multiplyScalar(speedFactor * delta)); 1277 | } 1278 | if (keys[" "]) { 1279 | if (playerCapsule.onGround) { 1280 | playerCapsule.velocity.y = 125.0; 1281 | } 1282 | } 1283 | } 1284 | if (playerCapsule.position.y < -250) { 1285 | playerCapsule.position.copy(ogPosition); 1286 | } 1287 | butterflies.forEach(butterfly => { 1288 | butterfly.update(delta, collider, { 1289 | flower: bigFlower.visible, 1290 | flowerPos: bigFlower.children[0].getWorldPosition(new THREE.Vector3()), 1291 | playerPos: playerCapsule.position, 1292 | butterflies, 1293 | timeScale 1294 | }); 1295 | }); 1296 | lastTime = performance.now(); 1297 | renderer.setRenderTarget(defaultTexture); 1298 | renderer.clear(); 1299 | renderer.render(scene, camera); 1300 | if (terrainShader) { 1301 | terrainShader.uniforms.reflect.value = true; 1302 | } 1303 | if (grassShader) { 1304 | grassShader.uniforms.time.value = performance.now() / 1000; 1305 | } 1306 | tickers.forEach(ticker => { 1307 | try { 1308 | ticker.uniforms.time.value = performance.now() / 1000; 1309 | } catch (e) { 1310 | 1311 | } 1312 | }) 1313 | 1314 | renderer.setRenderTarget(reflectTexture); 1315 | renderer.clear(); 1316 | renderer.render(reflectScene, camera); 1317 | effectPass.uniforms["sceneDiffuse"].value = defaultTexture.texture; 1318 | effectPass.uniforms["reflectDiffuse"].value = reflectTexture.texture; 1319 | effectPass.uniforms["sceneDepth"].value = defaultTexture.depthTexture; 1320 | effectPass.uniforms["projectionMatrixInv"].value = camera.projectionMatrixInverse; 1321 | effectPass.uniforms["viewMatrixInv"].value = camera.matrixWorld; 1322 | effectPass.uniforms["projMat"].value = camera.projectionMatrix; 1323 | effectPass.uniforms["viewMat"].value = camera.matrixWorldInverse; 1324 | effectPass.uniforms["waterNormal1"].value = waterNormalMap; 1325 | effectPass.uniforms["waterNormal2"].value = waterNormalMap2; 1326 | effectPass.uniforms["cameraPos"].value = camera.position; 1327 | effectPass.uniforms["time"].value = performance.now() / 1000; 1328 | effectPass.uniforms["environment"].value = environment; 1329 | effectPass.uniforms["lightPos"].value = directionalLight.position; 1330 | effectPass.uniforms["resolution"].value = new THREE.Vector2(clientWidth, clientHeight); 1331 | godRayPass.uniforms["sceneDiffuse"].value = defaultTexture.texture; 1332 | godRayPass.uniforms["sceneDepth"].value = defaultTexture.depthTexture; 1333 | ssaoPass.uniforms["sceneDiffuse"].value = defaultTexture.texture; 1334 | ssaoPass.uniforms["sceneDepth"].value = defaultTexture.depthTexture; 1335 | camera.updateMatrixWorld(); 1336 | ssaoPass.uniforms["projMat"].value = camera.projectionMatrix; 1337 | ssaoPass.uniforms["viewMat"].value = camera.matrixWorldInverse; 1338 | ssaoPass.uniforms["projViewMat"].value = camera.projectionMatrix.clone().multiply(camera.matrixWorldInverse.clone()); 1339 | ssaoPass.uniforms["projectionMatrixInv"].value = camera.projectionMatrixInverse; 1340 | ssaoPass.uniforms["viewMatrixInv"].value = camera.matrixWorld; 1341 | ssaoPass.uniforms["cameraPos"].value = camera.position; 1342 | ssaoPass.uniforms['resolution'].value = new THREE.Vector2(clientWidth, clientHeight); 1343 | ssaoPass.uniforms['time'].value = performance.now() / 1000; 1344 | godRayPass.uniforms["projMat"].value = camera.projectionMatrix; 1345 | godRayPass.uniforms["viewMat"].value = camera.matrixWorldInverse; 1346 | godRayPass.uniforms["projViewMat"].value = camera.projectionMatrix.clone().multiply(camera.matrixWorldInverse.clone()); 1347 | godRayPass.uniforms["projectionMatrixInv"].value = camera.projectionMatrixInverse; 1348 | godRayPass.uniforms["viewMatrixInv"].value = camera.matrixWorld; 1349 | godRayPass.uniforms["cameraPos"].value = camera.position; 1350 | godRayPass.uniforms['resolution'].value = new THREE.Vector2(clientWidth, clientHeight); 1351 | godRayPass.uniforms['time'].value = performance.now() / 1000; 1352 | effectCompositer.uniforms["sceneDiffuse"].value = defaultTexture.texture; 1353 | effectCompositer.uniforms["sceneDepth"].value = defaultTexture.depthTexture; 1354 | effectCompositer.uniforms["resolution"].value = new THREE.Vector2(clientWidth, clientHeight); 1355 | composer.render(); 1356 | stats.update(); 1357 | requestAnimationFrame(animate); 1358 | frame++; 1359 | } 1360 | requestAnimationFrame(animate); 1361 | } 1362 | main(); -------------------------------------------------------------------------------- /noise.js: -------------------------------------------------------------------------------- 1 | /* 2 | * A speed-improved perlin and simplex noise algorithms for 2D. 3 | * 4 | * Based on example code by Stefan Gustavson (stegu@itn.liu.se). 5 | * Optimisations by Peter Eastman (peastman@drizzle.stanford.edu). 6 | * Better rank ordering method by Stefan Gustavson in 2012. 7 | * Converted to Javascript by Joseph Gentle. 8 | * 9 | * Version 2012-03-09 10 | * 11 | * This code was placed in the public domain by its original author, 12 | * Stefan Gustavson. You may use it as you see fit, but 13 | * attribution is appreciated. 14 | * 15 | */ 16 | 17 | (function(global) { 18 | var module = global.noise = {}; 19 | 20 | function Grad(x, y, z) { 21 | this.x = x; 22 | this.y = y; 23 | this.z = z; 24 | } 25 | 26 | Grad.prototype.dot2 = function(x, y) { 27 | return this.x * x + this.y * y; 28 | }; 29 | 30 | Grad.prototype.dot3 = function(x, y, z) { 31 | return this.x * x + this.y * y + this.z * z; 32 | }; 33 | 34 | var grad3 = [new Grad(1, 1, 0), new Grad(-1, 1, 0), new Grad(1, -1, 0), new Grad(-1, -1, 0), 35 | new Grad(1, 0, 1), new Grad(-1, 0, 1), new Grad(1, 0, -1), new Grad(-1, 0, -1), 36 | new Grad(0, 1, 1), new Grad(0, -1, 1), new Grad(0, 1, -1), new Grad(0, -1, -1) 37 | ]; 38 | 39 | var p = [151, 160, 137, 91, 90, 15, 40 | 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69, 142, 8, 99, 37, 240, 21, 10, 23, 41 | 190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219, 203, 117, 35, 11, 32, 57, 177, 33, 42 | 88, 237, 149, 56, 87, 174, 20, 125, 136, 171, 168, 68, 175, 74, 165, 71, 134, 139, 48, 27, 166, 43 | 77, 146, 158, 231, 83, 111, 229, 122, 60, 211, 133, 230, 220, 105, 92, 41, 55, 46, 245, 40, 244, 44 | 102, 143, 54, 65, 25, 63, 161, 1, 216, 80, 73, 209, 76, 132, 187, 208, 89, 18, 169, 200, 196, 45 | 135, 130, 116, 188, 159, 86, 164, 100, 109, 198, 173, 186, 3, 64, 52, 217, 226, 250, 124, 123, 46 | 5, 202, 38, 147, 118, 126, 255, 82, 85, 212, 207, 206, 59, 227, 47, 16, 58, 17, 182, 189, 28, 42, 47 | 223, 183, 170, 213, 119, 248, 152, 2, 44, 154, 163, 70, 221, 153, 101, 155, 167, 43, 172, 9, 48 | 129, 22, 39, 253, 19, 98, 108, 110, 79, 113, 224, 232, 178, 185, 112, 104, 218, 246, 97, 228, 49 | 251, 34, 242, 193, 238, 210, 144, 12, 191, 179, 162, 241, 81, 51, 145, 235, 249, 14, 239, 107, 50 | 49, 192, 214, 31, 181, 199, 106, 157, 184, 84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254, 51 | 138, 236, 205, 93, 222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61, 156, 180 52 | ]; 53 | // To remove the need for index wrapping, double the permutation table length 54 | var perm = new Array(512); 55 | var gradP = new Array(512); 56 | 57 | // This isn't a very good seeding function, but it works ok. It supports 2^16 58 | // different seed values. Write something better if you need more seeds. 59 | module.seed = function(seed) { 60 | if (seed > 0 && seed < 1) { 61 | // Scale the seed out 62 | seed *= 65536; 63 | } 64 | 65 | seed = Math.floor(seed); 66 | if (seed < 256) { 67 | seed |= seed << 8; 68 | } 69 | 70 | for (var i = 0; i < 256; i++) { 71 | var v; 72 | if (i & 1) { 73 | v = p[i] ^ (seed & 255); 74 | } else { 75 | v = p[i] ^ ((seed >> 8) & 255); 76 | } 77 | 78 | perm[i] = perm[i + 256] = v; 79 | gradP[i] = gradP[i + 256] = grad3[v % 12]; 80 | } 81 | }; 82 | 83 | module.seed(0); 84 | 85 | /* 86 | for(var i=0; i<256; i++) { 87 | perm[i] = perm[i + 256] = p[i]; 88 | gradP[i] = gradP[i + 256] = grad3[perm[i] % 12]; 89 | }*/ 90 | 91 | // Skewing and unskewing factors for 2, 3, and 4 dimensions 92 | var F2 = 0.5 * (Math.sqrt(3) - 1); 93 | var G2 = (3 - Math.sqrt(3)) / 6; 94 | 95 | var F3 = 1 / 3; 96 | var G3 = 1 / 6; 97 | 98 | // 2D simplex noise 99 | module.simplex2 = function(xin, yin) { 100 | var n0, n1, n2; // Noise contributions from the three corners 101 | // Skew the input space to determine which simplex cell we're in 102 | var s = (xin + yin) * F2; // Hairy factor for 2D 103 | var i = Math.floor(xin + s); 104 | var j = Math.floor(yin + s); 105 | var t = (i + j) * G2; 106 | var x0 = xin - i + t; // The x,y distances from the cell origin, unskewed. 107 | var y0 = yin - j + t; 108 | // For the 2D case, the simplex shape is an equilateral triangle. 109 | // Determine which simplex we are in. 110 | var i1, j1; // Offsets for second (middle) corner of simplex in (i,j) coords 111 | if (x0 > y0) { // lower triangle, XY order: (0,0)->(1,0)->(1,1) 112 | i1 = 1; 113 | j1 = 0; 114 | } else { // upper triangle, YX order: (0,0)->(0,1)->(1,1) 115 | i1 = 0; 116 | j1 = 1; 117 | } 118 | // A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and 119 | // a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where 120 | // c = (3-sqrt(3))/6 121 | var x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords 122 | var y1 = y0 - j1 + G2; 123 | var x2 = x0 - 1 + 2 * G2; // Offsets for last corner in (x,y) unskewed coords 124 | var y2 = y0 - 1 + 2 * G2; 125 | // Work out the hashed gradient indices of the three simplex corners 126 | i &= 255; 127 | j &= 255; 128 | var gi0 = gradP[i + perm[j]]; 129 | var gi1 = gradP[i + i1 + perm[j + j1]]; 130 | var gi2 = gradP[i + 1 + perm[j + 1]]; 131 | // Calculate the contribution from the three corners 132 | var t0 = 0.5 - x0 * x0 - y0 * y0; 133 | if (t0 < 0) { 134 | n0 = 0; 135 | } else { 136 | t0 *= t0; 137 | n0 = t0 * t0 * gi0.dot2(x0, y0); // (x,y) of grad3 used for 2D gradient 138 | } 139 | var t1 = 0.5 - x1 * x1 - y1 * y1; 140 | if (t1 < 0) { 141 | n1 = 0; 142 | } else { 143 | t1 *= t1; 144 | n1 = t1 * t1 * gi1.dot2(x1, y1); 145 | } 146 | var t2 = 0.5 - x2 * x2 - y2 * y2; 147 | if (t2 < 0) { 148 | n2 = 0; 149 | } else { 150 | t2 *= t2; 151 | n2 = t2 * t2 * gi2.dot2(x2, y2); 152 | } 153 | // Add contributions from each corner to get the final noise value. 154 | // The result is scaled to return values in the interval [-1,1]. 155 | return 70 * (n0 + n1 + n2); 156 | }; 157 | 158 | // 3D simplex noise 159 | module.simplex3 = function(xin, yin, zin) { 160 | var n0, n1, n2, n3; // Noise contributions from the four corners 161 | 162 | // Skew the input space to determine which simplex cell we're in 163 | var s = (xin + yin + zin) * F3; // Hairy factor for 2D 164 | var i = Math.floor(xin + s); 165 | var j = Math.floor(yin + s); 166 | var k = Math.floor(zin + s); 167 | 168 | var t = (i + j + k) * G3; 169 | var x0 = xin - i + t; // The x,y distances from the cell origin, unskewed. 170 | var y0 = yin - j + t; 171 | var z0 = zin - k + t; 172 | 173 | // For the 3D case, the simplex shape is a slightly irregular tetrahedron. 174 | // Determine which simplex we are in. 175 | var i1, j1, k1; // Offsets for second corner of simplex in (i,j,k) coords 176 | var i2, j2, k2; // Offsets for third corner of simplex in (i,j,k) coords 177 | if (x0 >= y0) { 178 | if (y0 >= z0) { i1 = 1; 179 | j1 = 0; 180 | k1 = 0; 181 | i2 = 1; 182 | j2 = 1; 183 | k2 = 0; } else if (x0 >= z0) { i1 = 1; 184 | j1 = 0; 185 | k1 = 0; 186 | i2 = 1; 187 | j2 = 0; 188 | k2 = 1; } else { i1 = 0; 189 | j1 = 0; 190 | k1 = 1; 191 | i2 = 1; 192 | j2 = 0; 193 | k2 = 1; } 194 | } else { 195 | if (y0 < z0) { i1 = 0; 196 | j1 = 0; 197 | k1 = 1; 198 | i2 = 0; 199 | j2 = 1; 200 | k2 = 1; } else if (x0 < z0) { i1 = 0; 201 | j1 = 1; 202 | k1 = 0; 203 | i2 = 0; 204 | j2 = 1; 205 | k2 = 1; } else { i1 = 0; 206 | j1 = 1; 207 | k1 = 0; 208 | i2 = 1; 209 | j2 = 1; 210 | k2 = 0; } 211 | } 212 | // A step of (1,0,0) in (i,j,k) means a step of (1-c,-c,-c) in (x,y,z), 213 | // a step of (0,1,0) in (i,j,k) means a step of (-c,1-c,-c) in (x,y,z), and 214 | // a step of (0,0,1) in (i,j,k) means a step of (-c,-c,1-c) in (x,y,z), where 215 | // c = 1/6. 216 | var x1 = x0 - i1 + G3; // Offsets for second corner 217 | var y1 = y0 - j1 + G3; 218 | var z1 = z0 - k1 + G3; 219 | 220 | var x2 = x0 - i2 + 2 * G3; // Offsets for third corner 221 | var y2 = y0 - j2 + 2 * G3; 222 | var z2 = z0 - k2 + 2 * G3; 223 | 224 | var x3 = x0 - 1 + 3 * G3; // Offsets for fourth corner 225 | var y3 = y0 - 1 + 3 * G3; 226 | var z3 = z0 - 1 + 3 * G3; 227 | 228 | // Work out the hashed gradient indices of the four simplex corners 229 | i &= 255; 230 | j &= 255; 231 | k &= 255; 232 | var gi0 = gradP[i + perm[j + perm[k]]]; 233 | var gi1 = gradP[i + i1 + perm[j + j1 + perm[k + k1]]]; 234 | var gi2 = gradP[i + i2 + perm[j + j2 + perm[k + k2]]]; 235 | var gi3 = gradP[i + 1 + perm[j + 1 + perm[k + 1]]]; 236 | 237 | // Calculate the contribution from the four corners 238 | var t0 = 0.6 - x0 * x0 - y0 * y0 - z0 * z0; 239 | if (t0 < 0) { 240 | n0 = 0; 241 | } else { 242 | t0 *= t0; 243 | n0 = t0 * t0 * gi0.dot3(x0, y0, z0); // (x,y) of grad3 used for 2D gradient 244 | } 245 | var t1 = 0.6 - x1 * x1 - y1 * y1 - z1 * z1; 246 | if (t1 < 0) { 247 | n1 = 0; 248 | } else { 249 | t1 *= t1; 250 | n1 = t1 * t1 * gi1.dot3(x1, y1, z1); 251 | } 252 | var t2 = 0.6 - x2 * x2 - y2 * y2 - z2 * z2; 253 | if (t2 < 0) { 254 | n2 = 0; 255 | } else { 256 | t2 *= t2; 257 | n2 = t2 * t2 * gi2.dot3(x2, y2, z2); 258 | } 259 | var t3 = 0.6 - x3 * x3 - y3 * y3 - z3 * z3; 260 | if (t3 < 0) { 261 | n3 = 0; 262 | } else { 263 | t3 *= t3; 264 | n3 = t3 * t3 * gi3.dot3(x3, y3, z3); 265 | } 266 | // Add contributions from each corner to get the final noise value. 267 | // The result is scaled to return values in the interval [-1,1]. 268 | return 32 * (n0 + n1 + n2 + n3); 269 | 270 | }; 271 | 272 | // ##### Perlin noise stuff 273 | 274 | function fade(t) { 275 | return t * t * t * (t * (t * 6 - 15) + 10); 276 | } 277 | 278 | function lerp(a, b, t) { 279 | return (1 - t) * a + t * b; 280 | } 281 | 282 | // 2D Perlin Noise 283 | module.perlin2 = function(x, y) { 284 | // Find unit grid cell containing point 285 | var X = Math.floor(x), 286 | Y = Math.floor(y); 287 | // Get relative xy coordinates of point within that cell 288 | x = x - X; 289 | y = y - Y; 290 | // Wrap the integer cells at 255 (smaller integer period can be introduced here) 291 | X = X & 255; 292 | Y = Y & 255; 293 | 294 | // Calculate noise contributions from each of the four corners 295 | var n00 = gradP[X + perm[Y]].dot2(x, y); 296 | var n01 = gradP[X + perm[Y + 1]].dot2(x, y - 1); 297 | var n10 = gradP[X + 1 + perm[Y]].dot2(x - 1, y); 298 | var n11 = gradP[X + 1 + perm[Y + 1]].dot2(x - 1, y - 1); 299 | 300 | // Compute the fade curve value for x 301 | var u = fade(x); 302 | 303 | // Interpolate the four results 304 | return lerp( 305 | lerp(n00, n10, u), 306 | lerp(n01, n11, u), 307 | fade(y)); 308 | }; 309 | 310 | // 3D Perlin Noise 311 | module.perlin3 = function(x, y, z) { 312 | // Find unit grid cell containing point 313 | var X = Math.floor(x), 314 | Y = Math.floor(y), 315 | Z = Math.floor(z); 316 | // Get relative xyz coordinates of point within that cell 317 | x = x - X; 318 | y = y - Y; 319 | z = z - Z; 320 | // Wrap the integer cells at 255 (smaller integer period can be introduced here) 321 | X = X & 255; 322 | Y = Y & 255; 323 | Z = Z & 255; 324 | 325 | // Calculate noise contributions from each of the eight corners 326 | var n000 = gradP[X + perm[Y + perm[Z]]].dot3(x, y, z); 327 | var n001 = gradP[X + perm[Y + perm[Z + 1]]].dot3(x, y, z - 1); 328 | var n010 = gradP[X + perm[Y + 1 + perm[Z]]].dot3(x, y - 1, z); 329 | var n011 = gradP[X + perm[Y + 1 + perm[Z + 1]]].dot3(x, y - 1, z - 1); 330 | var n100 = gradP[X + 1 + perm[Y + perm[Z]]].dot3(x - 1, y, z); 331 | var n101 = gradP[X + 1 + perm[Y + perm[Z + 1]]].dot3(x - 1, y, z - 1); 332 | var n110 = gradP[X + 1 + perm[Y + 1 + perm[Z]]].dot3(x - 1, y - 1, z); 333 | var n111 = gradP[X + 1 + perm[Y + 1 + perm[Z + 1]]].dot3(x - 1, y - 1, z - 1); 334 | 335 | // Compute the fade curve value for x, y, z 336 | var u = fade(x); 337 | var v = fade(y); 338 | var w = fade(z); 339 | 340 | // Interpolate 341 | return lerp( 342 | lerp( 343 | lerp(n000, n100, u), 344 | lerp(n001, n101, u), w), 345 | lerp( 346 | lerp(n010, n110, u), 347 | lerp(n011, n111, u), w), 348 | v); 349 | }; 350 | 351 | })(this); -------------------------------------------------------------------------------- /rockcolor1.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N8python/theIsland/e985e32ab371351c7f5cca6fc427b743e4fdc95d/rockcolor1.jpeg -------------------------------------------------------------------------------- /rockcolor2.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N8python/theIsland/e985e32ab371351c7f5cca6fc427b743e4fdc95d/rockcolor2.jpeg -------------------------------------------------------------------------------- /rockcolor3.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N8python/theIsland/e985e32ab371351c7f5cca6fc427b743e4fdc95d/rockcolor3.jpeg -------------------------------------------------------------------------------- /rockcolor4.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N8python/theIsland/e985e32ab371351c7f5cca6fc427b743e4fdc95d/rockcolor4.jpeg -------------------------------------------------------------------------------- /rockcolor5.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N8python/theIsland/e985e32ab371351c7f5cca6fc427b743e4fdc95d/rockcolor5.jpeg -------------------------------------------------------------------------------- /rocknormal1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N8python/theIsland/e985e32ab371351c7f5cca6fc427b743e4fdc95d/rocknormal1.png -------------------------------------------------------------------------------- /rocknormal2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N8python/theIsland/e985e32ab371351c7f5cca6fc427b743e4fdc95d/rocknormal2.png -------------------------------------------------------------------------------- /rocknormal3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N8python/theIsland/e985e32ab371351c7f5cca6fc427b743e4fdc95d/rocknormal3.png -------------------------------------------------------------------------------- /rocknormal4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N8python/theIsland/e985e32ab371351c7f5cca6fc427b743e4fdc95d/rocknormal4.png -------------------------------------------------------------------------------- /rocknormal5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N8python/theIsland/e985e32ab371351c7f5cca6fc427b743e4fdc95d/rocknormal5.png -------------------------------------------------------------------------------- /skybox/Box_Back.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N8python/theIsland/e985e32ab371351c7f5cca6fc427b743e4fdc95d/skybox/Box_Back.bmp -------------------------------------------------------------------------------- /skybox/Box_Bottom.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N8python/theIsland/e985e32ab371351c7f5cca6fc427b743e4fdc95d/skybox/Box_Bottom.bmp -------------------------------------------------------------------------------- /skybox/Box_Front.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N8python/theIsland/e985e32ab371351c7f5cca6fc427b743e4fdc95d/skybox/Box_Front.bmp -------------------------------------------------------------------------------- /skybox/Box_Left.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N8python/theIsland/e985e32ab371351c7f5cca6fc427b743e4fdc95d/skybox/Box_Left.bmp -------------------------------------------------------------------------------- /skybox/Box_Right.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N8python/theIsland/e985e32ab371351c7f5cca6fc427b743e4fdc95d/skybox/Box_Right.bmp -------------------------------------------------------------------------------- /skybox/Box_Top.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N8python/theIsland/e985e32ab371351c7f5cca6fc427b743e4fdc95d/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 }; -------------------------------------------------------------------------------- /terraincolor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N8python/theIsland/e985e32ab371351c7f5cca6fc427b743e4fdc95d/terraincolor.png -------------------------------------------------------------------------------- /waternormal.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N8python/theIsland/e985e32ab371351c7f5cca6fc427b743e4fdc95d/waternormal.jpeg -------------------------------------------------------------------------------- /waternormal2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/N8python/theIsland/e985e32ab371351c7f5cca6fc427b743e4fdc95d/waternormal2.png --------------------------------------------------------------------------------