diff --git a/public/index.html b/public/index.html index b0235a9..3b11361 100644 --- a/public/index.html +++ b/public/index.html @@ -1,16 +1,16 @@ - - - - - <%= htmlWebpackPlugin.options.title %> + + + + + <%= htmlWebpackPlugin.options.title %>
@@ -18,6 +18,8 @@ diff --git a/src/animation/Config.ts b/src/animation/Config.ts new file mode 100644 index 0000000..269b30f --- /dev/null +++ b/src/animation/Config.ts @@ -0,0 +1,30 @@ +import {Color} from "three"; + +/** + * Configurations + */ +export const config = { + + background: '#333', + + // Field of vision and cutoff frustum for near and far + cam: {fov: 50, near: 1, far: 5000}, + + // Mouse movement factor + mouseFactor: 20, + + // Smooth movements (speeds are in terms of pixels per ms) + smooth: {mouseSpeed: 10 * window.devicePixelRatio}, + + // Debug mode + debug: true +} + +export const colors = { + sky: { + top: new Color('#3284ff'), + bottom: new Color('#ffffff'), + ground: new Color('#ffc87f'), + dirLight: new Color('#fff4e5') + }, +} diff --git a/src/animation/Helpers.ts b/src/animation/Helpers.ts index 72007c1..aefaf27 100644 --- a/src/animation/Helpers.ts +++ b/src/animation/Helpers.ts @@ -37,12 +37,13 @@ export function box(width: number, height: number, depth: number): THREE.BufferG * @param color * @param z * @param r + * @param hollow */ -export function circle(color: number, z: number, r: number): THREE.Mesh +export function circle(color: number, z: number, r: number, hollow = false): THREE.Object3D { const geometry = new THREE.CircleGeometry(r, 32) const material = new THREE.MeshBasicMaterial({color}) - const circle = new THREE.Mesh(geometry, material) + const circle = hollow ? new THREE.Line(geometry, material) : new THREE.Mesh(geometry, material) circle.position.z = z return circle } diff --git a/src/animation/Home.ts b/src/animation/Home.ts index 9598868..ff2b0dd 100644 --- a/src/animation/Home.ts +++ b/src/animation/Home.ts @@ -1,40 +1,33 @@ import * as THREE from 'three' -import * as helper from "@/animation/Helpers"; -import {initMouseTracker, moused} from "@/animation/Trackers"; -import {circle} from "@/animation/Helpers"; +import * as helper from "@/animation/Helpers" +import {circle} from "@/animation/Helpers" +import {initMouseTracker, moused} from "@/animation/Trackers" +import {addDirLight, addGround, addHemiLight, addSky} from "@/animation/Shaders" +import {colors, config} from "@/animation/Config" let renderer: THREE.WebGLRenderer, scene: THREE.Scene, camera: THREE.PerspectiveCamera const clock = new THREE.Clock() -const objects = [] - -const config = { - background: '#333', - - // Field of vision and cutoff frustum for near and far - cam: {fov: 50, near: 1, far: 2000}, - - // Mouse movement factor - mouseFactor: 20, - - // Smooth movements (speeds are in terms of pixels per ms) - smooth: {mouseSpeed: 10 * window.devicePixelRatio}, -} +const objects: { [id: string]: THREE.Object3D } = {} // //////////////////// // Three.js scene code function init(): void { - const geometryBox = helper.box(50, 50, 50); + const geometryBox = helper.box(50, 50, 50) const lineSegments = new THREE.LineSegments(geometryBox, new THREE.LineDashedMaterial({ color: 0xffaa00, dashSize: 3, gapSize: 1 - })); + })) lineSegments.computeLineDistances() - objects.push(lineSegments) + const cursor = circle(0xffaa00, 100, 1) + objects.cursor = cursor + scene.add(cursor) + + objects.box = lineSegments scene.add(lineSegments) scene.add(circle(0xffff00, 0, 5)) @@ -59,16 +52,25 @@ function pn(b: boolean): number */ function update(dt: number): void { - smoothBuffer.cam.x = moused.x * config.mouseFactor - smoothBuffer.cam.y = moused.y * config.mouseFactor + // objects['cursor'].position.set(moused.x, moused.y, 150) + + const vector = new THREE.Vector3(moused.x, moused.y, 0.5) + vector.unproject(camera) + const dir = vector.sub(camera.position).normalize() + const distance = -camera.position.z / dir.z + const pos = camera.position.clone().add(dir.multiplyScalar(distance)) + objects.cursor.position.copy(pos) + + // smoothBuffer.cam.x = moused.x * config.mouseFactor + // smoothBuffer.cam.y = moused.y * config.mouseFactor smoothUpdate() // const time = Date.now() * 0.001 // scene.traverse((object) => // { - // object.rotation.x = 0.25 * time; - // object.rotation.y = 0.25 * time; - // }); + // object.rotation.x = 0.25 * time + // object.rotation.y = 0.25 * time + // }) function smoothUpdate(): void { @@ -79,11 +81,21 @@ function update(dt: number): void // Target position const tp = smoothBuffer.cam - if (Math.abs(cp.x - tp.x) > delta) { cp.x = cp.x + delta * pn(cp.x < tp.x) } - else { cp.x = tp.x } + if (Math.abs(cp.x - tp.x) > delta) + { + cp.x = cp.x + delta * pn(cp.x < tp.x) + } else + { + cp.x = tp.x + } - if (Math.abs(cp.y - tp.y) > delta) { cp.y = cp.y + delta * pn(cp.y < tp.y) } - else { cp.y = tp.y } + if (Math.abs(cp.y - tp.y) > delta) + { + cp.y = cp.y + delta * pn(cp.y < tp.y) + } else + { + cp.y = tp.y + } } } @@ -99,7 +111,6 @@ export function start(id: string): void { scene = new THREE.Scene() scene.background = new THREE.Color(config.background) - // Create camera camera = new THREE.PerspectiveCamera(config.cam.fov, window.innerWidth / window.innerHeight, config.cam.near, config.cam.far) @@ -111,11 +122,24 @@ export function start(id: string): void onWindowResize() window.addEventListener('resize', onWindowResize) + addLights() + init() initMouseTracker() animate() } +function addLights(): void +{ + objects.hemiLight = addHemiLight(scene) + objects.dirLight = addDirLight(scene) + objects.ground = addGround(scene) + objects.sky = addSky(scene) + + renderer.outputEncoding = THREE.sRGBEncoding + renderer.shadowMap.enabled = true +} + function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight diff --git a/src/animation/Shaders.ts b/src/animation/Shaders.ts new file mode 100644 index 0000000..1562486 --- /dev/null +++ b/src/animation/Shaders.ts @@ -0,0 +1,118 @@ +import * as THREE from 'three' +import {colors, config} from "@/animation/Config"; +import {DirectionalLight, HemisphereLight, Mesh} from "three"; + +export const vertexShader = ` + varying vec3 vWorldPosition; + + void main() { + vec4 worldPosition = modelMatrix * vec4( position, 1.0 ); + vWorldPosition = worldPosition.xyz; + + gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); + }`; + +export const fragmentShader = ` + uniform vec3 topColor; + uniform vec3 bottomColor; + uniform float offset; + uniform float exponent; + + varying vec3 vWorldPosition; + + void main() { + float h = normalize( vWorldPosition + offset ).y; + gl_FragColor = vec4( mix( bottomColor, topColor, max( pow( max( h , 0.0), exponent ), 0.0 ) ), 1.0 ); + }`; + +/** + * Create sky dome + * @param scene + */ +export function addSky(scene: THREE.Scene): Mesh +{ + const uniforms = { + "topColor": { value: colors.sky.top }, + "bottomColor": { value: colors.sky.bottom }, + "offset": { value: 33 }, + "exponent": { value: 0.6 } + } + + const skyGeo = new THREE.SphereGeometry( 4000, 32, 15 ); + const skyMat = new THREE.ShaderMaterial({ + uniforms: uniforms, + vertexShader: vertexShader, + fragmentShader: fragmentShader, + side: THREE.BackSide + }); + + const sky = new THREE.Mesh(skyGeo, skyMat) + scene.add(sky) + scene.fog = new THREE.Fog(colors.sky.bottom, 1, 5000) + + return sky +} + +/** + * Create ground + * @param scene + */ +export function addGround(scene: THREE.Scene): Mesh +{ + const groundGeo = new THREE.PlaneGeometry(10000, 10000) + const groundMat = new THREE.MeshLambertMaterial({color: colors.sky.ground}) + const ground = new THREE.Mesh(groundGeo, groundMat) + ground.position.y = -33 + ground.rotation.x = -Math.PI / 2 + ground.receiveShadow = true + scene.add(ground) + + return ground +} + +/** + * Add hemisphere light + * @param scene + */ +export function addHemiLight(scene: THREE.Scene): HemisphereLight +{ + const hemiLight = new THREE.HemisphereLight(colors.sky.top, colors.sky.ground, 0.6) + hemiLight.position.set(0, 50, 0) + scene.add(hemiLight) + + if (config.debug) scene.add(new THREE.HemisphereLightHelper(hemiLight, 10)) + + return hemiLight +} + +/** + * Add directional light + * @param scene + */ +export function addDirLight(scene: THREE.Scene): DirectionalLight +{ + // Directional light + const dirLight = new THREE.DirectionalLight(colors.sky.dirLight, 1) + dirLight.position.set(-1, 1.75, 1) + dirLight.position.multiplyScalar(30) + scene.add(dirLight) + + dirLight.castShadow = true + + dirLight.shadow.mapSize.width = 2048 + dirLight.shadow.mapSize.height = 2048 + + const d = 50 + + dirLight.shadow.camera.left = -d + dirLight.shadow.camera.right = d + dirLight.shadow.camera.top = d + dirLight.shadow.camera.bottom = -d + + dirLight.shadow.camera.far = 3500 + dirLight.shadow.bias = -0.0001 + + if (config.debug) scene.add(new THREE.DirectionalLightHelper(dirLight, 10)) + + return dirLight +}