renderer.shadowMap.enabled = true
const light = new THREE.PointLight(0xffffff, 40000)
light.castShadow = true
light.shadow.camera.near = 1
light.shadow.camera.far = 160
scene.add(light)
scene.add(new THREE.CameraHelper(light.shadow.camera))
const box = new THREE.Mesh(
new THREE.BoxGeometry(40, 40, 40),
new THREE.MeshNormalMaterial()
)
box.castShadow = true
scene.add(box)
const plane = new THREE.Mesh(
new THREE.PlaneGeometry(300, 300),
new THREE.MeshPhysicalMaterial({ color: 'white', side: THREE.DoubleSide })
)
plane.receiveShadow = true
scene.add(plane)
const world = new CANNON.World()
world.gravity.set(0, -200, 0)
const plane = new THREE.Mesh(
new THREE.PlaneGeometry(300, 300),
new THREE.MeshPhysicalMaterial({ color: 'white', side: THREE.DoubleSide })
)
plane.rotateX(-Math.PI / 2)
scene.add(plane)
const planeCannonMaterial = new CANNON.Material()
const planeCannonBody = new CANNON.Body({
mass: 0,
material: planeCannonMaterial,
shape: new CANNON.Plane(),
})
planeCannonBody.position.set(0, 0, 0)
planeCannonBody.quaternion.setFromAxisAngle(new CANNON.Vec3(1, 0, 0), -Math.PI / 2)
world.addBody(planeCannonBody)
const sphere = new THREE.Mesh(
new THREE.SphereGeometry(16, 16, 16),
new THREE.MeshNormalMaterial()
)
sphere.position.set(0, 100, 0)
scene.add(sphere)
const sphereCannonMaterial = new CANNON.Material()
const sphereCannonBody = new CANNON.Body({
mass: 1,
shape: new CANNON.Sphere(16),
material: sphereCannonMaterial,
})
sphereCannonBody.position.set(0, 100, 0)
world.addBody(sphereCannonBody)
world.addContactMaterial(
new CANNON.ContactMaterial(planeCannonMaterial, sphereCannonMaterial, {
friction: 0.2,
restitution: 0.66,
})
)
const xSphere = new THREE.Mesh(
new THREE.SphereGeometry(16, 5),
new THREE.MeshNormalMaterial()
)
xSphere.position.set(0, 100, 40)
scene.add(xSphere)
const vertices: CANNON.Vec3[] = []
const faces: number[][] = []
const ps = xSphere.geometry.attributes.position
for (let i = 0; i < ps.count; i++) {
vertices.push(new CANNON.Vec3(ps.getX(i), ps.getY(i), ps.getZ(i)))
}
const index = xSphere.geometry.index! as unknown as number[]
for (let i = 0; i < index.length; i++) {
faces.push([index[i], index[i + 1], index[i + 2]])
}
const xSphereCannonMaterial = new CANNON.Material()
const xSphereCannonBody = new CANNON.Body({
mass: 1,
shape: new CANNON.ConvexPolyhedron({ vertices, faces }),
material: xSphereCannonMaterial,
})
xSphereCannonBody.position.set(0, 100, 40)
world.addBody(xSphereCannonBody)
world.addContactMaterial(
new CANNON.ContactMaterial(planeCannonMaterial, xSphereCannonMaterial, {
friction: 0.2,
restitution: 0.66,
})
)
function r() {
world.fixedStep()
if (sphere) {
sphere.position.copy(sphereCannonBody.position)
sphere.position.copy(sphereCannonBody.position)
sphere.quaternion.copy(sphereCannonBody.quaternion)
}
if (xSphere) {
xSphere.position.copy(xSphereCannonBody.position)
xSphere.position.copy(xSphereCannonBody.position)
xSphere.quaternion.copy(xSphereCannonBody.quaternion)
}
renderer.render(scene, camera)
requestAnimationFrame(r)
}
requestAnimationFrame(r)
const downloadBlob = (blob, filename) => {
const url = URL.createObjectURL(blob)
const a = document.createElement('a')
a.href = url
a.download = filename
document.body.appendChild(a)
a.click()
document.body.removeChild(a)
URL.revokeObjectURL(url)
}
const renderer = new THREE.WebGLRenderer({
antialias: true,
preserveDrawingBuffer: true,
})
const stats = new Stats()
stats.dom.style.position = 'absolute'
c.appendChild(stats.dom)
const orbitControls = new OrbitControls(camera, renderer.domElement)
const x = {
downloadImage() {
renderer.domElement.toBlob((blob) => {
if (blob) {
downloadBlob(blob, 'browser.png')
}
}, 'image/png')
},
onDownloadVideo() {
const stream = renderer.domElement.captureStream(60)
const mediaRecorder = new MediaRecorder(stream, { mimeType: 'video/webm' })
orbitControls.autoRotate = true
orbitControls.autoRotateSpeed = 8
mediaRecorder.start()
mediaRecorder.ondataavailable = (event) => {
if (event.data.size > 0) {
downloadBlob(event.data, 'browser.webm')
}
}
setTimeout(() => {
orbitControls.autoRotate = false
orbitControls.reset()
mediaRecorder.stop()
}, 5000)
},
}
const gui = new GUI()
gui.add(x, 'downloadImage').name('截屏')
gui.add(x, 'onDownloadVideo').name('录屏')
gui.domElement.style.position = 'absolute'
c.appendChild(gui.domElement)
function r() {
stats.update()
orbitControls.update()
renderer.render(scene, camera)
requestAnimationFrame(r)
}
requestAnimationFrame(r)