diff --git a/example/RGBD2QuiltsExample.png.png b/example/RGBD2QuiltsExample.png.png new file mode 100644 index 0000000..57d3b56 Binary files /dev/null and b/example/RGBD2QuiltsExample.png.png differ diff --git a/example/UI.png b/example/UI.png index 2fb793d..821142a 100644 Binary files a/example/UI.png and b/example/UI.png differ diff --git a/web/config.js b/web/config.js index 249ce10..0a1d08c 100644 --- a/web/config.js +++ b/web/config.js @@ -51,13 +51,14 @@ export const config = { // Initial values for parameter 参数初始值 defaults: { - depthStrength: 1.0, - dofStrength: 0.0, - focusDistance: 0.0, + depthStrength: 1.5, + dofStrength: 0.5, + focusDistance: 0.95, zOffset: 0.0, - quiltsNum: 2, + quiltsNum: 4, quiltsAngleRange: 14, - screenshotSize: 0 // 0 代表 512 * 2^0 = 512 + screenshotSize: 1, // 0 代表 512 * 2^0 = 512 + cameraFOV: 7, }, // Slider control settings 滑块控制设置 @@ -77,7 +78,7 @@ export const config = { focusDistance: { min: 0, max: 1, - step: 0.1, + step: 0.01, label: '对焦点' }, zOffset: { @@ -86,6 +87,12 @@ export const config = { step: 0.1, label: '轴心偏移' }, + cameraFOV: { + min: 5, // 修改最小值为5度 + max: 120, + step: 1, + label: '相机FOV角' + }, quiltsNum: { min: 1, max: 48, diff --git a/web/html/threeVisualizer.html b/web/html/threeVisualizer.html index 038bec7..229efc7 100644 --- a/web/html/threeVisualizer.html +++ b/web/html/threeVisualizer.html @@ -30,23 +30,6 @@ max-height: 100% !important; object-fit: contain; } - #progress-dialog { - background-color: rgba(0, 0, 0, 0.8); - color: #ffffff; - border: 1px solid #ffffff; - border-radius: 8px; - padding: 20px; - } - #progress-dialog label { - color: #ffffff; - font-size: 16px; - margin-bottom: 10px; - display: block; - } - #progress-indicator { - width: 100%; - height: 20px; - } diff --git a/web/js/threeVisualizer.js b/web/js/threeVisualizer.js index c218a08..3d597ce 100644 --- a/web/js/threeVisualizer.js +++ b/web/js/threeVisualizer.js @@ -3,12 +3,12 @@ import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js'; let apiURL = ''; -let currentDepthStrength = 1.0; +let currentDepthStrength = 1.5; let currentDofStrength = 0.0; let currentFocusDistance = 0.0; let currentZOffset = 0.0; // 添加Z轴偏移变量 let currentDepthMesh = null; -const BASE_CAMERA_DISTANCE = 13.5; // 修改基础相机距离为13.5 +const BASE_CAMERA_DISTANCE = 5.0; // 修改基础相机距离为5(fov90时) let currentScreenshotSize = 1024; // 默认截图尺寸 // 添加Quilts相关变量 @@ -75,6 +75,10 @@ window.addEventListener('message', function(event) { const finalZOffset = currentZOffset - (currentDepthStrength * 5 / 2); currentDepthMesh.position.z = finalZOffset; } + } else if (event.data.type === 'updateCameraFOV') { + // 更新相机FOV + camera.fov = event.data.value; + camera.updateProjectionMatrix(); } }, false); @@ -167,7 +171,7 @@ scene.add(axesHelper); const ambientLight = new THREE.AmbientLight(0xffffff); -const camera = new THREE.PerspectiveCamera(40, 1, 0.1, 1000); +const camera = new THREE.PerspectiveCamera(7, 1, 0.1, 1000); // 设置默认FOV为7度 camera.position.set(0, 0, 10); const pointLight = new THREE.PointLight(0xffffff, 15); camera.add(pointLight); @@ -242,7 +246,13 @@ const onError = function (e) { // 修改自适应回正函数 function resetView(depthStrength) { const maxDisplacement = 2.5 * depthStrength; - const cameraDistance = BASE_CAMERA_DISTANCE + maxDisplacement + currentZOffset; + + // 计算基于FOV的相机回正距离补偿 + const fovRadians = THREE.MathUtils.degToRad(camera.fov); + const fovCompensation = 1 / Math.tan(fovRadians / 2); + + // 基础距离 * FOV补偿系数 + const cameraDistance = (BASE_CAMERA_DISTANCE* fovCompensation) + (maxDisplacement * 0.5) + currentZOffset ; // 重置相机位置和旋转 camera.position.set(0, 0, cameraDistance); @@ -367,13 +377,21 @@ async function main(referenceImageParams, depthMapParams) { vec4 color = vec4(0.0); float total = 0.0; - // 使用更高效的采样模式 - float weights[5] = float[5](0.227027, 0.316216, 0.070270, 0.008491, 0.000000); - float offsets[5] = float[5](0.0, 1.0, 2.0, 3.0, 4.0); + // 使用高斯函数计算权重 + float sigma = 4.0; // 控制高斯分布的宽度 + float weights[8]; + float offsets[8]; + + // 计算权重和偏移 + for(int i = 0; i < 8; i++) { + offsets[i] = float(i); + // 高斯函数: exp(-(x^2)/(2*sigma^2)) + weights[i] =0.005 + exp(-(float(i) * float(i)) / (2.0 * sigma * sigma)); + } // 水平方向 - for(int i = 0; i < 5; i++) { - float offset = offsets[i] * blurFactor * 0.015; + for(int i = 0; i < 8; i++) { + float offset = offsets[i] * blurFactor * 0.005; color += texture2D(tex, uv + vec2(offset, 0.0)) * weights[i]; color += texture2D(tex, uv - vec2(offset, 0.0)) * weights[i]; total += weights[i] * 2.0; @@ -381,8 +399,8 @@ async function main(referenceImageParams, depthMapParams) { // 垂直方向 vec4 color2 = vec4(0.0); - for(int i = 0; i < 5; i++) { - float offset = offsets[i] * blurFactor * 0.015; + for(int i = 0; i < 8; i++) { + float offset = offsets[i] * blurFactor * 0.005; color2 += texture2D(tex, uv + vec2(0.0, offset)) * weights[i]; color2 += texture2D(tex, uv - vec2(0.0, offset)) * weights[i]; total += weights[i] * 2.0; @@ -395,13 +413,14 @@ async function main(referenceImageParams, depthMapParams) { vec4 referenceColor = texture2D(referenceTexture, vUv); // 计算景深模糊 - float blurFactor = abs(vDepth - focusDistance) * dofStrength; + float depthDiff = abs(vDepth - focusDistance); + float blurFactor = pow(depthDiff, (0.25 - (0.05 * dofStrength)) ) * dofStrength; // 应用优化后的高斯模糊 vec4 blurredColor = gaussianBlur(referenceTexture, vUv, blurFactor); // 调整亮度 - blurredColor.rgb *= 0.95; // 略微降低亮度 + blurredColor.rgb *= 0.99; // 略微降低亮度 // 混合原始颜色和模糊颜色 gl_FragColor = mix(referenceColor, blurredColor, blurFactor); @@ -409,7 +428,7 @@ async function main(referenceImageParams, depthMapParams) { ` }); - const planeGeometry = new THREE.PlaneGeometry(imageWidth, imageHeight, 200, 200); + const planeGeometry = new THREE.PlaneGeometry(imageWidth, imageHeight, 360, 360); currentDepthMesh = new THREE.Mesh(planeGeometry, depthMaterial); // 设置初始Z轴偏移时也要考虑深度强度 const finalZOffset = currentZOffset - (currentDepthStrength * 5 / 2); diff --git a/web/visualization.js b/web/visualization.js index 6a67594..0f69a32 100644 --- a/web/visualization.js +++ b/web/visualization.js @@ -1,145 +1,252 @@ import { app } from "../../scripts/app.js" import { api } from '../../scripts/api.js' -import { config } from './config.js' -// 等待 config 加载完成 -let configLoaded = false; -const waitForConfig = () => { - if (config && config.defaults) { - configLoaded = true; - return true; - } - return false; -} - -// 初始化函数 -const initializeVisualizer = () => { - if (!waitForConfig()) { - console.log('[Visualizer] Waiting for config to load...'); - setTimeout(initializeVisualizer, 100); - return; - } - - console.log('[Visualizer] Config loaded, initializing...'); - - app.registerExtension({ - name: config.extensionName, - async beforeRegisterNodeDef(nodeType, nodeData, app) { - registerVisualizer(nodeType, nodeData, config.node.name, 'threeVisualizer') +// 硬编码配置 +const config = { + paths: { + extension: '/extensions/ComfyUI-Depth-Visualization-advanced', + html: '/extensions/ComfyUI-Depth-Visualization-advanced/html' + }, + extensionName: 'ORANGESILVER.DepthViewerAndQuilts', + node: { + name: 'DepthViewerAndQuilts', + defaultSize: [600, 500], + minSize: { + width: 600, + height: 500 + } + }, + controlPanel: { + width: 200, + background: 'rgba(0, 0, 0, 0.75)', + padding: 10, + borderRadius: 5, + zIndex: 1000, + defaultScale: 0.01, + scales: { + large: { + minDimension: 800, + scale: 1.25 + }, + medium: { + minDimension: 600, + scale: 1.0 + }, + small: { + minDimension: 400, + scale: 0.6 + }, + tiny: { + minDimension: 300, + scale: 0.5 + } + } + }, + defaults: { + depthStrength: 1.5, + dofStrength: 0.5, + focusDistance: 0.95, + zOffset: 0.0, + quiltsNum: 4, + quiltsAngleRange: 14, + screenshotSize: 1, + cameraFOV: 7, + }, + sliders: { + depthStrength: { + min: 0, + max: 2, + step: 0.1, + label: '深度强度' }, - nodeCreated(node, app) { - console.log('[DepthViewerAndQuilts] Node created:', node.id) - - setTimeout(() => { - let widget = node.widgets?.filter(w => w.name == 'previewRGBD')[0] - let framesWidget = node.widgets?.filter(w => w.name == 'frames')[0] + dofStrength: { + min: 0, + max: 1, + step: 0.1, + label: '景深强度' + }, + focusDistance: { + min: 0, + max: 1, + step: 0.01, + label: '对焦点' + }, + zOffset: { + min: -5, + max: 5, + step: 0.1, + label: '轴心偏移' + }, + cameraFOV: { + min: 5, + max: 120, + step: 1, + label: '相机FOV角' + }, + quiltsNum: { + min: 1, + max: 48, + step: 1, + label: 'Quilts数量' + }, + quiltsAngleRange: { + min: 2, + max: 180, + step: 1, + label: 'Quilt角度差' + }, + screenshotSize: { + min: 0, + max: 3, + step: 1, + label: '截图分辨率', + getValue: (index) => 512 * Math.pow(2, index) + } + }, + buttons: { + resetView: { + text: '回正视图', + style: { + padding: '8px', + background: 'rgba(255, 255, 255, 0.1)', + color: 'white', + border: '1px solid rgba(255, 255, 255, 0.2)', + borderRadius: '4px', + cursor: 'pointer', + width: '100%', + marginBottom: '10px' + } + }, + quiltsCapture: { + text: '开始QuiltsCapture', + style: { + padding: '8px', + background: 'rgba(255, 255, 255, 0.1)', + color: 'white', + border: '1px solid rgba(255, 255, 255, 0.2)', + borderRadius: '4px', + cursor: 'pointer', + width: '100%', + marginTop: '10px' + } + } + } +}; + +// 删除config加载检查相关代码 +app.registerExtension({ + name: config.extensionName, + async beforeRegisterNodeDef(nodeType, nodeData, app) { + registerVisualizer(nodeType, nodeData, config.node.name, 'threeVisualizer') + }, + nodeCreated(node, app) { + console.log('[DepthViewerAndQuilts] Node created:', node.id) - console.log('[DepthViewerAndQuilts] Widgets found:', { - preview3d: !!widget, - frames: !!framesWidget - }) - - if (node.type === 'DepthViewerAndQuilts' && widget) { - let nodeId = node.id - console.log('[DepthViewerAndQuilts] Setting up iframe for node:', nodeId) - - //延迟才能获得this.id - widget.visualizer.querySelector('iframe').src += '?id=' + nodeId - console.log('[DepthViewerAndQuilts] iframe src updated:', widget.visualizer.querySelector('iframe').src) - - // 添加消息监听器 - window.addEventListener('message', async event => { - console.log('[DepthViewerAndQuilts] Message received:', { - source: event.source, - origin: event.origin, - data: event.data - }) - - try { - // 检查消息的来源,确保消息来自可信的源 - if (!event.data || !event.data.id || !event.data.imgs) { - console.error('[DepthViewerAndQuilts] Invalid message format:', event.data) - return - } - - const { id, imgs } = event.data - console.log('[DepthViewerAndQuilts] Processing message:', { - receivedId: id, - nodeId: nodeId, - imagesCount: imgs?.length - }) - - // 验证节点ID - 转换为相同类型进行比较 - if (String(id) !== String(nodeId)) { - console.log('[DepthViewerAndQuilts] Message ID mismatch:', { - received: id, - expected: nodeId, - receivedType: typeof id, - expectedType: typeof nodeId - }) - return - } - - // 初始化frames控件 - framesWidget.value = { images: [] } - console.log('[DepthViewerAndQuilts] Frames widget initialized') - - // 处理每张图片 - for (let i = 0; i < imgs.length; i++) { - const base64Image = imgs[i] - try { - console.log(`[DepthViewerAndQuilts] Processing image ${i + 1}/${imgs.length}`) - - // 验证base64数据 - if (!base64Image || typeof base64Image !== 'string') { - console.error(`[DepthViewerAndQuilts] Invalid base64 data for image ${i + 1}`) - continue - } - - // 上传图片并获取URL - console.log(`[DepthViewerAndQuilts] Uploading image ${i + 1}`) - const file = await uploadBase64ToFile(base64Image) - - // 验证返回的文件数据 - if (!file || !file.name) { - console.error(`[DepthViewerAndQuilts] Invalid file data for image ${i + 1}:`, file) - continue - } - - console.log(`[DepthViewerAndQuilts] Image ${i + 1} uploaded successfully:`, file.name) - - // 添加到frames控件 - framesWidget.value.images.push(file) - } catch (error) { - console.error(`[DepthViewerAndQuilts] Error processing image ${i + 1}:`, error) - } - } - - // 触发更新 - framesWidget.value._seed = Math.random() - node.title = 'Input #' + framesWidget.value.images.length - console.log('[DepthViewerAndQuilts] Update completed:', { - imagesCount: framesWidget.value.images.length, - newTitle: node.title - }) - - // 触发节点更新 - node.setDirtyCanvas(true, true) - console.log('[DepthViewerAndQuilts] Canvas marked as dirty') - } catch (error) { - console.error('[DepthViewerAndQuilts] Error in message handler:', error) - } + setTimeout(() => { + let widget = node.widgets?.filter(w => w.name == 'previewRGBD')[0] + let framesWidget = node.widgets?.filter(w => w.name == 'frames')[0] + + console.log('[DepthViewerAndQuilts] Widgets found:', { + preview3d: !!widget, + frames: !!framesWidget + }) + + if (node.type === 'DepthViewerAndQuilts' && widget) { + let nodeId = node.id + console.log('[DepthViewerAndQuilts] Setting up iframe for node:', nodeId) + + widget.visualizer.querySelector('iframe').src += '?id=' + nodeId + console.log('[DepthViewerAndQuilts] iframe src updated:', widget.visualizer.querySelector('iframe').src) + + // 添加消息监听器 + window.addEventListener('message', async event => { + console.log('[DepthViewerAndQuilts] Message received:', { + source: event.source, + origin: event.origin, + data: event.data }) - console.log('[DepthViewerAndQuilts] Message listener setup completed') - } - }, 1000) - } - }); -} - -// 启动初始化 -initializeVisualizer(); + try { + // 检查消息的来源,确保消息来自可信的源 + if (!event.data || !event.data.id || !event.data.imgs) { + console.error('[DepthViewerAndQuilts] Invalid message format:', event.data) + return + } + + const { id, imgs } = event.data + console.log('[DepthViewerAndQuilts] Processing message:', { + receivedId: id, + nodeId: nodeId, + imagesCount: imgs?.length + }) + + // 验证节点ID - 转换为相同类型进行比较 + if (String(id) !== String(nodeId)) { + console.log('[DepthViewerAndQuilts] Message ID mismatch:', { + received: id, + expected: nodeId, + receivedType: typeof id, + expectedType: typeof nodeId + }) + return + } + + // 初始化frames控件 + framesWidget.value = { images: [] } + console.log('[DepthViewerAndQuilts] Frames widget initialized') + + // 处理每张图片 + for (let i = 0; i < imgs.length; i++) { + const base64Image = imgs[i] + try { + console.log(`[DepthViewerAndQuilts] Processing image ${i + 1}/${imgs.length}`) + + // 验证base64数据 + if (!base64Image || typeof base64Image !== 'string') { + console.error(`[DepthViewerAndQuilts] Invalid base64 data for image ${i + 1}`) + continue + } + + // 上传图片并获取URL + console.log(`[DepthViewerAndQuilts] Uploading image ${i + 1}`) + const file = await uploadBase64ToFile(base64Image) + + // 验证返回的文件数据 + if (!file || !file.name) { + console.error(`[DepthViewerAndQuilts] Invalid file data for image ${i + 1}:`, file) + continue + } + + console.log(`[DepthViewerAndQuilts] Image ${i + 1} uploaded successfully:`, file.name) + + // 添加到frames控件 + framesWidget.value.images.push(file) + } catch (error) { + console.error(`[DepthViewerAndQuilts] Error processing image ${i + 1}:`, error) + } + } + + // 触发更新 + framesWidget.value._seed = Math.random() + node.title = 'Input #' + framesWidget.value.images.length + console.log('[DepthViewerAndQuilts] Update completed:', { + imagesCount: framesWidget.value.images.length, + newTitle: node.title + }) + + // 触发节点更新 + node.setDirtyCanvas(true, true) + console.log('[DepthViewerAndQuilts] Canvas marked as dirty') + } catch (error) { + console.error('[DepthViewerAndQuilts] Error in message handler:', error) + } + }) + + console.log('[DepthViewerAndQuilts] Message listener setup completed') + } + }, 1000) + } +}); function base64ToBlobFromURL (base64URL, contentType) { return fetch(base64URL).then(response => response.blob()) @@ -192,6 +299,7 @@ class Visualizer { this.depthStrength = config.defaults.depthStrength; this.dofStrength = config.defaults.dofStrength; this.focusDistance = config.defaults.focusDistance; + this.cameraFOV = config.defaults.cameraFOV; // 创建控制面板容器 this.controlPanel = document.createElement('div') @@ -299,10 +407,24 @@ class Visualizer { } ); + // 添加相机FOV控制 + const fovControl = this.createSliderControl( + 'cameraFOV', + config.sliders.cameraFOV, + (value) => { + this.cameraFOV = value; + this.iframe.contentWindow.postMessage({ + type: 'updateCameraFOV', + value: value + }, '*'); + } + ); + this.controlPanel.appendChild(depthControl) this.controlPanel.appendChild(dofControl) this.controlPanel.appendChild(focusControl) this.controlPanel.appendChild(zOffsetControl) + this.controlPanel.appendChild(fovControl) // 添加Quilts控制 const quiltsControl = document.createElement('div')