Files
2024-09-18 18:34:55 +08:00

351 lines
10 KiB
JavaScript

import * as THREE from './three/three.module.js'
// import { api } from '../../../scripts/api.js'
import { OrbitControls } from './three/OrbitControls.js'
import { RoomEnvironment } from './three/RoomEnvironment.js'
const api = {
apiURL: url => {
console.log(url)
return url
}
}
const visualizer = document.getElementById('visualizer')
const container = document.getElementById('container')
const progressDialog = document.getElementById('progress-dialog')
const progressIndicator = document.getElementById('progress-indicator')
const renderer = new THREE.WebGLRenderer({
antialias: true,
extensions: {
derivatives: true
}
})
renderer.setPixelRatio(window.devicePixelRatio)
renderer.setSize(window.innerWidth, window.innerHeight)
if (container) container.appendChild(renderer.domElement)
const pmremGenerator = new THREE.PMREMGenerator(renderer)
// scene
const scene = new THREE.Scene()
scene.background = new THREE.Color(0x000000)
scene.environment = pmremGenerator.fromScene(
new RoomEnvironment(renderer),
0.04
).texture
const ambientLight = new THREE.AmbientLight(0xffffff)
const camera = new THREE.PerspectiveCamera(
40,
window.innerWidth / window.innerHeight,
0.1,
1000
)
camera.position.set(0, 0, 10)
const pointLight = new THREE.PointLight(0xffffff, 15)
camera.add(pointLight)
const controls = new OrbitControls(camera, renderer.domElement)
controls.target.set(0, 0, 0)
controls.update()
controls.enablePan = true
controls.enableDamping = true
// Handle window resize event
window.onresize = function () {
camera.aspect = window.innerWidth / window.innerHeight
camera.updateProjectionMatrix()
renderer.setSize(window.innerWidth, window.innerHeight)
}
var lastReferenceImage = ''
var lastDepthMap = ''
var needUpdate = false
function frameUpdate () {
var referenceImage = visualizer?.getAttribute('reference_image')
var depthMap = visualizer?.getAttribute('depth_map')
if (referenceImage == lastReferenceImage && depthMap == lastDepthMap) {
if (needUpdate) {
controls.update()
renderer.render(scene, camera)
}
requestAnimationFrame(frameUpdate)
} else {
needUpdate = false
scene.clear()
if (progressDialog) progressDialog.open = true
lastReferenceImage = referenceImage
lastDepthMap = depthMap
if (lastReferenceImage && lastReferenceImage != 'undefined') {
// console.log('lastReferenceImage',typeof(lastReferenceImage),lastDepthMap)
main(JSON.parse(lastReferenceImage), JSON.parse(lastDepthMap))
}
}
}
const onProgress = function (xhr) {
if (xhr.lengthComputable) {
progressIndicator.value = (xhr.loaded / xhr.total) * 100
}
}
const onError = function (e) {
console.error(e)
}
async function main (referenceImageParams, depthMapParams) {
let referenceTexture, depthTexture
let imageWidth = 10 // Default width
let imageHeight = 10 // Default height, will be updated based on the image's aspect ratio
// console.log('#referenceImageParams', referenceImageParams)
if (referenceImageParams?.filename) {
console.log('referenceImageParams', referenceImageParams)
const referenceImageUrl = api
.apiURL('/view?' + new URLSearchParams(referenceImageParams))
.replace(/extensions.*\//, '')
const referenceImageExt = referenceImageParams.filename.slice(
referenceImageParams.filename.lastIndexOf('.') + 1
)
if (
referenceImageExt === 'png' ||
referenceImageExt === 'jpg' ||
referenceImageExt === 'jpeg'
) {
const referenceImageLoader = new THREE.TextureLoader()
referenceTexture = await new Promise((resolve, reject) => {
referenceImageLoader.load(
referenceImageUrl,
texture => {
// Once the image is loaded, update the width and height based on the image's aspect ratio
imageWidth = 10 // Keep the width as 10
imageHeight = texture.image.height / (texture.image.width / 10)
resolve(texture)
},
undefined,
reject
)
})
}
}
if (depthMapParams?.filename) {
const depthMapUrl = api
.apiURL('/view?' + new URLSearchParams(depthMapParams))
.replace(/extensions.*\//, '')
const depthMapExt = depthMapParams.filename.slice(
depthMapParams.filename.lastIndexOf('.') + 1
)
if (
depthMapExt === 'png' ||
depthMapExt === 'jpg' ||
depthMapExt === 'jpeg'
) {
const depthMapLoader = new THREE.TextureLoader()
depthTexture = await depthMapLoader.loadAsync(depthMapUrl)
}
}
if (referenceTexture && depthTexture) {
const depthMaterial = new THREE.ShaderMaterial({
uniforms: {
referenceTexture: { value: referenceTexture },
depthTexture: { value: depthTexture },
depthScale: { value: 5.0 },
ambientLightColor: { value: new THREE.Color(0.2, 0.2, 0.2) },
lightPosition: { value: new THREE.Vector3(2, 2, 2) },
lightColor: { value: new THREE.Color(1, 1, 1) },
lightIntensity: { value: 1.0 },
shininess: { value: 30 }
},
vertexShader: `
uniform sampler2D depthTexture;
uniform float depthScale;
varying vec2 vUv;
varying float vDepth;
varying vec3 vNormal;
varying vec3 vViewPosition;
void main() {
vUv = uv;
float depth = texture2D(depthTexture, uv).r;
vec3 displacement = normal * depth * depthScale;
vec3 displacedPosition = position + displacement;
vec4 worldPosition = modelMatrix * vec4(displacedPosition, 1.0);
vNormal = normalize(normalMatrix * normal);
vViewPosition = (viewMatrix * worldPosition).xyz;
gl_Position = projectionMatrix * viewMatrix * worldPosition;
vDepth = depth;
}
`,
fragmentShader: `
uniform sampler2D referenceTexture;
varying vec2 vUv;
varying float vDepth;
void main() {
vec4 referenceColor = texture2D(referenceTexture, vUv);
// Directly use reference color without fog
gl_FragColor = referenceColor;
}
`
})
const planeGeometry = new THREE.PlaneGeometry(
imageWidth,
imageHeight,
200,
200
)
const depthMesh = new THREE.Mesh(planeGeometry, depthMaterial)
scene.add(depthMesh)
}
needUpdate = true
scene.add(ambientLight)
scene.add(camera)
progressDialog?.close()
frameUpdate()
}
document
.getElementById('screenshotButton')
?.addEventListener('click', takeScreenshot)
const sleep = (t = 1000) => {
return new Promise((res, rej) => {
setTimeout(() => res(1), t)
})
}
// 方法:旋转摄像机并拍摄图片 // 每次旋转的角度增量,转换为弧度
async function captureImages (
totalFrames = 20,
angleIncrement = 1.5,
scaleFactor = 1 // 添加放大倍数参数,默认为1
) {
angleIncrement = THREE.MathUtils.degToRad(angleIncrement)
// 计算场景中所有物体的中心点
const box = new THREE.Box3().setFromObject(scene)
const center = new THREE.Vector3()
box.getCenter(center)
// 计算当前相机距离中心点的半径
const radius = camera.position.distanceTo(center)
// 存储图片的数组
let images = []
// 记录初始相机位置和朝向
const initialPosition = camera.position.clone()
const initialTarget = center.clone()
// 计算当前相机的初始角度
const initialAngle = Math.atan2(
camera.position.z - center.z,
camera.position.x - center.x
)
// 起始角度为从当前角度往左旋转 20 度的位置
const startAngle = initialAngle
// - (angleIncrement * totalFrames) / 2
// 保存原始尺寸
const originalWidth = renderer.domElement.width
const originalHeight = renderer.domElement.height
// 调整渲染器尺寸
renderer.setSize(
originalWidth * scaleFactor,
originalHeight * scaleFactor,
false
)
// 调整相机的视图矩阵(如果需要)
camera.aspect = (originalWidth * scaleFactor) / (originalHeight * scaleFactor)
camera.updateProjectionMatrix()
for (let i = 0; i < totalFrames; i++) {
const angle = startAngle + i * angleIncrement
// 计算相机的位置
camera.position.x = center.x + radius * Math.cos(angle)
camera.position.z = center.z + radius * Math.sin(angle)
camera.position.y = initialPosition.y // 保持相机高度不变
camera.lookAt(center) // 相机看向中心点
// 渲染当前帧
renderer.render(scene, camera)
// 将当前帧保存为图片
const imgData = renderer.domElement.toDataURL('image/png')
images.push(imgData)
// 等待一段时间
await new Promise(resolve => setTimeout(resolve, 500))
}
// 恢复渲染器尺寸
renderer.setSize(originalWidth, originalHeight, false)
// 恢复相机的视图矩阵
camera.aspect = originalWidth / originalHeight
camera.updateProjectionMatrix()
// 恢复相机到初始位置和朝向
camera.position.copy(initialPosition)
camera.lookAt(initialTarget)
return images
}
async function takeScreenshot () {
// 更新相机的矩阵,以确保其世界矩阵是最新的
camera.updateMatrixWorld()
const imgs = await captureImages(12,3,4)
// 获取当前网页的 URL
const currentUrl = window.location.href
// 创建一个 URL 对象
const url = new URL(currentUrl)
// 使用 URLSearchParams 获取参数
const params = new URLSearchParams(url.search)
// 获取参数 'id' 的值
const id = params.get('id')
window.parent.postMessage({ imgs, id }, '*')
}
main()
window.addEventListener('message', event => {
// 这里可以添加对来源的验证,以确保安全
// console.log('Message received from parent page:', event.data)
let { reference_image, depth_map } = event.data
if (reference_image && depth_map) {
visualizer?.setAttribute('reference_image', JSON.stringify(reference_image))
visualizer?.setAttribute('depth_map', JSON.stringify(depth_map))
frameUpdate()
}
})