Hello,
I am a newbie on threeJS. I’m trying to replicate the webgl effect on the site - Особенности - Деловой квартал «Сколково Парк» for practice. So far, I have Skolkovo Rebuild.
On mouse movement, a point light follows the cursor, I want a form of easing on the point light movement, so it takes some time before it catches up with the cursor position as is on the reference site. Also, the model rotates on the x-axis when the cursor moves along the y-axis and vice versa. I want an easing on the rotation as well, just as it is on the reference site.
Please, how can I achieve this?
import * as THREE from 'three'
import OrbitControls from 'https://cdn.skypack.dev/threejs-orbit-controls'
import { GLTFLoader } from 'https://cdn.skypack.dev/three/examples/jsm/loaders/GLTFLoader'
import GUI from 'https://cdn.jsdelivr.net/npm/lil-gui@0.10.0/dist/lil-gui.esm.min.js'
//Scene
const scene = new THREE.Scene()
//Canvas
const canvas = document.querySelector('.webgl')
//Debug
const gui = new GUI()
const debugObject = {}
//Object
//Lights
const ambientLight = new THREE.AmbientLight('#ffffff', 1)
scene.add(ambientLight)
const directionalLight = new THREE.DirectionalLight('#ffffff', 2)
gui.add(directionalLight.position, 'x', -10, 10, 0.01).name('directional light x')
gui.add(directionalLight.position, 'y', -10, 10, 0.01).name('directional light y')
gui.add(directionalLight.position, 'z', -10, 10, 0.01).name('directional light z')
directionalLight.position.set(3, 4, 4)
scene.add(directionalLight)
const pointLight = new THREE.PointLight('#E79C13', 2)
scene.add(pointLight)
pointLight.position.y = 1
debugObject.pointLightIntensity = 50
debugObject.pointLightDistance = 3
pointLight.castShadow = true
pointLight.shadow.camera.far = 6
pointLight.shadow.camera.near = 0.5
pointLight.shadow.mapSize.set(1024, 1024)
pointLight.intensity = debugObject.pointLightIntensity
pointLight.distance = debugObject.pointLightDistance
gui.add(pointLight.position, 'x', -100, 100, 0.001).name('pointLightX')
gui.add(pointLight.position, 'y', -100, 100, 0.001).name('pointLightY')
gui.add(pointLight.position, 'z', -100, 100, 0.001).name('pointLightZ')
gui.add(debugObject, 'pointLightIntensity', 0, 50, 1).onChange(()=>{
pointLight.intensity = debugObject.pointLightIntensity
})
gui.add(debugObject, 'pointLightDistance', 0, 10, 0.001).onChange(() => {
pointLight.distance = debugObject.pointLightDistance
})
const cameraHelper = new THREE.CameraHelper(pointLight.shadow.camera)
const sphereSize = 1;
const pointLightHelper = new THREE.PointLightHelper( pointLight, sphereSize );
//Sizes
const sizes = {
width: window.innerWidth,
height: window.innerHeight
}
//Camera
const camera = new THREE.PerspectiveCamera(50, sizes.width/sizes.height)
scene.add(camera)
camera.position.z = 10
gui.add(camera.position, 'x', -100, 100, 1).name('camera position x')
gui.add(camera.position, 'y', -100, 100, 1).name('camera position y')
gui.add(camera.position, 'z', -100, 100, 1).name('camera position z')
debugObject.maxPolarAngle = 0.95
debugObject.minPolarAngle = 0.95
//Controls
const controls = new OrbitControls(camera, canvas)
controls.enableDamping = true
controls.enablePanning = false
controls.maxPolarAngle = debugObject.maxPolarAngle
controls.minPolarAngle = debugObject.minPolarAngle
controls.rotateSpeed = 0.1
controls.dampingFactor = 0.05
//Texture loader
const textureLoader = new THREE.TextureLoader()
const texture = textureLoader.load('https://uploads-ssl.webflow.com/6257f5b3771a401dee5b6377/6293a7e8ddc7b22cae335a50_bake_all.jpg')
texture.encoding = THREE.sRGBEncoding
const material = new THREE.MeshStandardMaterial({map: texture, aoMap: texture, aoMapIntensity: 0.8})
//Models
let architecture
const gltfLoader = new GLTFLoader()
gltfLoader.load('https://volt-three-js.netlify.app/models/architecture/building.gltf', (gltf) => {
architecture = gltf.scene
architecture.traverse(child => {
if(child instanceof THREE.Mesh && child.material instanceof THREE.MeshStandardMaterial){
child.material = material
child.castShadow = true
child.receiveShadow = true
}
})
architecture.scale.set(1.5, 1.5)
architecture.rotation.y = 0.1
let box = new THREE.Box3().setFromObject( architecture )
const size = box.getSize( new THREE.Vector3() ).length()
const centerPosition = box.getCenter( new THREE.Vector3() )
architecture.position.x += ( architecture.position.x - centerPosition.x )
architecture.position.y += ( architecture.position.y - centerPosition.y )
architecture.position.z += ( architecture.position.z - centerPosition.z )
scene.add(architecture)
gui.add(architecture.scale, 'x', 0, 10, 0.001).name('model scale x')
gui.add(architecture.scale, 'y', 0, 10, 0.001).name('model scale y')
gui.add(architecture.scale, 'z', 0, 10, 0.001).name('model scale z')
gui.add(architecture.position, 'x', 0, 10, 1).name('model x')
gui.add(architecture.position, 'y', 0, 10, 1).name('model y')
gui.add(architecture.position, 'z', 0, 10, 1).name('model z')
gui.add(architecture.rotation, 'x', -10, 10, 0.001).name('model rotation x')
gui.add(architecture.rotation, 'y', -10, 10, 0.001).name('model rotation y')
gui.add(architecture.rotation, 'z', -10, 10, 0.001).name('model rotation z')
})
gui.add(debugObject, 'maxPolarAngle', -Math.PI, Math.PI, 0.001).onChange(() => {
controls.maxPolarAngle = debugObject.maxPolarAngle
})
gui.add(debugObject, 'minPolarAngle', -Math.PI, Math.PI, 0.001).onChange(() => {
controls.minPolarAngle = debugObject.minPolarAngle
})
//Renderer
const renderer = new THREE.WebGLRenderer({
canvas,
antialias: true
})
//Resizing
window.addEventListener('resize', () => {
//Update the sizes object
sizes.width = window.innerWidth
sizes.height = window.innerHeight
//Update camera
camera.aspect = sizes.width / sizes.height
camera.updateProjectionMatrix()
//Update the renderer
renderer.setSize(sizes.width, sizes.height)
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
})
renderer.setSize(sizes.width, sizes.height)
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
renderer.render(scene, camera)
renderer.physicallyCorrectLights = true
renderer.outputEncoding = THREE.sRGBEncoding
renderer.toneMapping = THREE.ReinhardToneMapping
renderer.toneMappingExposure = 3
renderer.shadowMap.enabled = true
renderer.shadowMap.type = THREE.PCFSoftShadowMap
renderer.setClearColor('#131312')
gui.add(renderer, 'toneMapping', {
No: THREE.NoToneMapping,
Linear: THREE.LinearToneMapping,
Reinhard: THREE.ReinhardToneMapping,
Cineon: THREE.CineonToneMapping,
ACESFilmic: THREE.ACESFilmicToneMapping
})
gui.add(renderer, 'toneMappingExposure', 0, 20, 0.01)
let mousePosition = new THREE.Vector3()
let cursor = {}
window.addEventListener('mousemove', (event) => {
cursor.x = (event.clientX / window.innerWidth) * 2 - 1
cursor.y = (event.clientY / window.innerHeight) * 2 - 1
// Make the sphere follow the mouse
let vector = new THREE.Vector3(cursor.x, cursor.y, 0.5)
vector.unproject(camera)
let dir = vector.sub(camera.position).normalize()
let distance = -camera.position.z / dir.z
let pos = camera.position.clone().add(dir.multiplyScalar(distance))
mousePosition = pos
})
//Animate
let previousTime = 0
const clock = new THREE.Clock();
const tick = () => {
const elapsedTime = clock.getElapsedTime()
let deltaTime = elapsedTime - previousTime
previousTime = elapsedTime
//Update controls
controls.update()
if(architecture){
architecture.rotation.x = mousePosition.y < 0 ? architecture.rotation.lerp() : -0.2
}
//Easing
pointLight.position.x = mousePosition.x
pointLight.position.z = mousePosition.y
//mouseMesh.position.copy(pos)
//pointLight.position.copy(new THREE.Vector3(mousePosition.x , 1, mousePosition.y))
//Render
renderer.render(scene, camera)
window.requestAnimationFrame(tick)
}
tick()