├── 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
--------------------------------------------------------------------------------