{"id":158,"date":"2025-12-11T16:28:55","date_gmt":"2025-12-11T08:28:55","guid":{"rendered":"https:\/\/www.thebugmuxi.xyz\/?p=158"},"modified":"2025-12-11T16:28:57","modified_gmt":"2025-12-11T08:28:57","slug":"three-js3d%e7%b2%92%e5%ad%90%e5%ae%9e%e6%97%b6%e4%ba%a4%e4%ba%92%ef%bc%88%e9%99%84%e6%8f%90%e7%a4%ba%e8%af%8d%e5%92%8c%e4%bb%a3%e7%a0%81%ef%bc%89-gemini-3","status":"publish","type":"post","link":"https:\/\/www.thebugmuxi.xyz\/index.php\/2025\/12\/11\/three-js3d%e7%b2%92%e5%ad%90%e5%ae%9e%e6%97%b6%e4%ba%a4%e4%ba%92%ef%bc%88%e9%99%84%e6%8f%90%e7%a4%ba%e8%af%8d%e5%92%8c%e4%bb%a3%e7%a0%81%ef%bc%89-gemini-3\/","title":{"rendered":"Three.js3D\u7c92\u5b50\u5b9e\u65f6\u4ea4\u4e92\uff08\u9644\u63d0\u793a\u8bcd\u548c\u4ee3\u7801\uff09\u2014\u2014Gemini 3"},"content":{"rendered":"\n<p>\u8fd9\u4e24\u5929\u5237\u6296\u97f3\uff0c\u4f60\u4e00\u5b9a\u89c1\u8fc7\u90a3\u4e2a\u706b\u7206\u7684\u2014\u2014&nbsp;<strong>\u53ea\u9700\u8981\u4e00\u4e2a\u6444\u50cf\u5934\uff0c\u5355\u624b\u5f20\u5408\uff0c\u5c31\u80fd\u8ba9 3D \u7c92\u5b50\u5b9e\u65f6\u7f29\u653e\u3001\u6269\u6563\u3001\u53d8\u5f62<\/strong>\u2026\u2026\u751a\u81f3\u53ef\u4ee5\u53d8\u6210\u7231\u5fc3\u3001\u571f\u661f\u3001\u70df\u82b1\u7b49\u8d85\u70ab\u6a21\u578b\u3002<\/p>\n\n\n\n<p>\u5f88\u591a\u535a\u4e3b\u8bf4\u662f&nbsp;<a href=\"https:\/\/zhida.zhihu.com\/search?content_id=267267821&amp;content_type=Article&amp;match_order=1&amp;q=Gemini+3&amp;zd_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ6aGlkYV9zZXJ2ZXIiLCJleHAiOjE3NjU2MTM3NDYsInEiOiJHZW1pbmkgMyIsInpoaWRhX3NvdXJjZSI6ImVudGl0eSIsImNvbnRlbnRfaWQiOjI2NzI2NzgyMSwiY29udGVudF90eXBlIjoiQXJ0aWNsZSIsIm1hdGNoX29yZGVyIjoxLCJ6ZF90b2tlbiI6bnVsbH0.CAsJuettpEADFOaki2yCJA3WYKzxv_8yRRel8u08Ky0&amp;zhida_source=entity\" target=\"_blank\" rel=\"noreferrer noopener\">Gemini 3<\/a>&nbsp;\u7684\u6548\u679c\uff0c\u5176\u5b9e\u80cc\u540e\u771f\u6b63\u7684\u6838\u5fc3\u662f Three.js +&nbsp;<a href=\"https:\/\/zhida.zhihu.com\/search?content_id=267267821&amp;content_type=Article&amp;match_order=1&amp;q=MediaPipe&amp;zd_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ6aGlkYV9zZXJ2ZXIiLCJleHAiOjE3NjU2MTM3NDYsInEiOiJNZWRpYVBpcGUiLCJ6aGlkYV9zb3VyY2UiOiJlbnRpdHkiLCJjb250ZW50X2lkIjoyNjcyNjc4MjEsImNvbnRlbnRfdHlwZSI6IkFydGljbGUiLCJtYXRjaF9vcmRlciI6MSwiemRfdG9rZW4iOm51bGx9.kA_N-VBFXvy7kEgZDDjmSfYjqPN-UvMp-mgVec3WxuQ&amp;zhida_source=entity\" target=\"_blank\" rel=\"noreferrer noopener\">MediaPipe<\/a>&nbsp;\u624b\u52bf\u8bc6\u522b\uff0cGemini 3 \u53ea\u662f\u751f\u6210\u4ee3\u7801\u7684\u201c\u52a0\u901f\u5668\u201d\u3002<\/p>\n\n\n\n<p>\u65e2\u7136\u8fd9\u4e48\u706b\uff0c\u90a3\u54b1\u4eec\u5f53\u7136\u4e5f\u8981\u73a9\u4e00\u628a\uff0c\u800c\u4e14\u8981\u505a\u5f97\u66f4\u5b8c\u6574\u3001\u66f4\u53ef\u63a7\u3001\u66f4\u9002\u5408\u96c6\u6210\u5230\u4f60\u81ea\u5df1\u7684\u9879\u76ee\u91cc\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">\ud83d\ude80 \u4f7f\u7528\u7f51\u7ad9\uff08\u5168\u90e8\u6ee1\u901f\u3001\u65e0\u9700\u9b54\u6cd5\uff09<\/h3>\n\n\n\n<p>\u4f60\u53ef\u4ee5\u76f4\u63a5\u7528\u4e0b\u9762\u4efb\u4e00\u7ad9\u70b9\u751f\u6210\u6574\u4e2a\u9879\u76ee\u7684\u4ee3\u7801\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">https:\/\/share.zhangsan.cool<br>https:\/\/share-hk.zhangsan.cool<br>https:\/\/share.searchknowledge.cloud<br>https:\/\/hello.aiforme.cloud<\/pre>\n\n\n\n<p>\u5168\u90e8\u652f\u6301&nbsp;<strong><a href=\"https:\/\/zhida.zhihu.com\/search?content_id=267267821&amp;content_type=Article&amp;match_order=1&amp;q=GPT-5.1&amp;zd_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ6aGlkYV9zZXJ2ZXIiLCJleHAiOjE3NjU2MTM3NDYsInEiOiJHUFQtNS4xIiwiemhpZGFfc291cmNlIjoiZW50aXR5IiwiY29udGVudF9pZCI6MjY3MjY3ODIxLCJjb250ZW50X3R5cGUiOiJBcnRpY2xlIiwibWF0Y2hfb3JkZXIiOjEsInpkX3Rva2VuIjpudWxsfQ.W2-7-6Sma17w6zsbFqBiAMnbV2toiAi8jmGAoTwhIiE&amp;zhida_source=entity\" target=\"_blank\" rel=\"noreferrer noopener\">GPT-5.1<\/a>&nbsp;\/ Gemini 3 \/&nbsp;<a href=\"https:\/\/zhida.zhihu.com\/search?content_id=267267821&amp;content_type=Article&amp;match_order=1&amp;q=Grok+4.1&amp;zd_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ6aGlkYV9zZXJ2ZXIiLCJleHAiOjE3NjU2MTM3NDYsInEiOiJHcm9rIDQuMSIsInpoaWRhX3NvdXJjZSI6ImVudGl0eSIsImNvbnRlbnRfaWQiOjI2NzI2NzgyMSwiY29udGVudF90eXBlIjoiQXJ0aWNsZSIsIm1hdGNoX29yZGVyIjoxLCJ6ZF90b2tlbiI6bnVsbH0.oM5JIFfxwgj1y76q0CRuMhrTWFmA4i2DiJRuIeURZHo&amp;zhida_source=entity\" target=\"_blank\" rel=\"noreferrer noopener\">Grok 4.1<\/a>&nbsp;\/ Banana 2<\/strong>&nbsp;\u7b49\u591a\u6a21\u578b AI \u56fd\u5185\u8bbf\u95ee\u7a33\u5b9a\u3001\u901f\u5ea6\u5feb\u3001\u975e\u5e38\u9002\u5408\u5f00\u53d1\u8005\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">\ud83e\udde0 \u53ea\u9700\u4e00\u6bb5\u63d0\u793a\u8bcd\uff0c\u751f\u6210\u5b8c\u6574 3D \u4ea4\u4e92\u7cfb\u7edf<\/h3>\n\n\n\n<p>\u4f60\u53ef\u4ee5\u628a\u4e0b\u9762\u8fd9\u6bb5\u63d0\u793a\u8bcd\u539f\u5c01\u4e0d\u52a8\u4e22\u7ed9\u4e0a\u8ff0\u7f51\u7ad9\u7684Gemini-3-pro\u6a21\u578b\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">\u8981\u6c42: \u7528Three.js\u521b\u5efa\u4e00\u4e2a\u5b9e\u65f6\u4ea4\u4e92\u76843D\u7c92\u5b50\u7cfb\u7edf\u3002<br><br>1. \u901a\u8fc7\u6444\u50cf\u5934\u68c0\u6d4b\u5355\u624b\u5f20\u5408\u63a7\u5236\u7c92\u5b50\u7fa4\u7684\u7f29\u653e\u4e0e\u6269\u6563\uff08\u5355\u624b\u5f20\u5f00\u65f6\u7c92\u5b50\u6269\u6563\uff0c\u5355\u624b\u5408\u62e2\u65f6\u7c92\u5b50\u6536\u7f29\uff0c\u5f20\u5408\u5e45\u5ea6\u4e0e\u7f29\u653e\/\u6269\u6563\u7a0b\u5ea6\u6210\u6b63\u6bd4\uff0c\u5e45\u5ea6\u8db3\u591f\u5927\uff09\uff1b<br><br>2. \u63d0\u4f9bUI\u9762\u677f\u53ef\u9009\u62e9\u7231\u5fc3\/\u82b1\u6735\/\u571f\u661f\/\u4f5b\u50cf\/\u70df\u82b1\u7b49\u6a21\u578b\uff08\u7c92\u5b50\u81ea\u52a8\u7ec4\u6210\u9009\u4e2d\u7684\u6a21\u578b\u8f6e\u5ed3\uff0c\u5207\u6362\u6a21\u578b\u65f6\u7c92\u5b50\u5e73\u6ed1\u8fc7\u6e21\uff09\uff1b<br><br>3. \u652f\u6301\u989c\u8272\u9009\u62e9\u5668\u8c03\u6574\u7c92\u5b50\u989c\u8272\uff08\u53ef\u9009\u62e9\u5355\u8272\uff0c\u652f\u6301\u5b9e\u65f6\u9884\u89c8\u989c\u8272\u53d8\u5316\uff09\uff1b<br><br>4. \u7c92\u5b50\u9700\u5b9e\u65f6\u54cd\u5e94\u624b\u52bf\u53d8\u5316\uff08\u5ef6\u8fdf\u2264100ms\uff0c\u65e0\u5361\u987f\uff09\u3002<br><br>5. \u754c\u9762\u7b80\u6d01\u73b0\u4ee3\uff0c\u5305\u542b\u5168\u5c4f\u63a7\u5236\u6309\u94ae\uff08UI\u9762\u677f\u56fa\u5b9a\u5728\u9875\u9762\u53f3\u4fa7\uff0c\u4e0d\u906e\u63213D\u7c92\u5b50\u533a\u57df\uff0c\u6309\u94ae\u6837\u5f0f\u7edf\u4e00\u3001\u6241\u5e73\u5316\uff09\uff1b<br><br>6. \u65e0\u9700\u989d\u5916\u4f9d\u8d56\u540e\u7aef\uff0c\u6240\u6709\u529f\u80fd\u524d\u7aef\u5b9e\u73b0\uff0c\u751f\u6210\u5b8c\u6574HTML\u6587\u4ef6\uff0c\u53ef\u76f4\u63a5\u5728\u6d4f\u89c8\u5668\u6253\u5f00\u9884\u89c8\u3002<\/pre>\n\n\n\n<p>\u4efb\u4f55\u4e3b\u6d41\u5927\u6a21\u578b\u90fd\u80fd\u6309\u8981\u6c42\u751f\u6210\u5b8c\u6574\u9879\u76ee\uff0c<strong>\u4f46 Gemini 3 \u5728 Web \u4ea4\u4e92\u4ee3\u7801\u751f\u6210\u65b9\u9762\u5c24\u4e3a\u5f3a\u52b2<\/strong>\uff0c\u63a8\u8350\u4f7f\u7528\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">\ud83e\udde9 \u751f\u6210\u7684\u5b8c\u6574\u4ee3\u7801\uff08\u53ef\u76f4\u63a5\u8fd0\u884c\uff09<\/h3>\n\n\n\n<p>\u4e0b\u9762\u662f AI \u751f\u6210\u7684\u5b8c\u6574 HTML \u6587\u4ef6\uff0c\u53ea\u9700\u590d\u5236\u5230 index.html\uff0c\u5373\u53ef\u76f4\u63a5\u6d4f\u89c8\u5668\u6253\u5f00\u8fd0\u884c\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;!DOCTYPE html>\n&lt;html lang=\"zh-CN\">\n&lt;head>\n    &lt;meta charset=\"UTF-8\">\n    &lt;meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    &lt;title>3D\u624b\u52bf\u7c92\u5b50\u4ea4\u4e92\u7cfb\u7edf&lt;\/title>\n    &lt;style>\n        body { margin: 0; overflow: hidden; background-color: #050505; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; }\n        canvas { display: block; }\n\n        \/* \u9690\u85cf\u7684\u89c6\u9891\u5143\u7d20\uff0c\u7528\u4e8eMediaPipe\u5904\u7406 *\/\n        #input_video { position: absolute; top: -1000px; left: -1000px; transform: scaleX(-1); }\n\n        \/* UI \u9762\u677f *\/\n        #ui-container {\n            position: absolute;\n            top: 20px;\n            right: 20px;\n            width: 260px;\n            background: rgba(30, 30, 30, 0.85);\n            backdrop-filter: blur(10px);\n            padding: 20px;\n            border-radius: 12px;\n            border: 1px solid rgba(255, 255, 255, 0.1);\n            color: #fff;\n            box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.37);\n            transition: all 0.3s ease;\n            z-index: 10;\n        }\n\n        h2 { margin: 0 0 15px 0; font-size: 18px; font-weight: 500; letter-spacing: 1px; border-bottom: 1px solid rgba(255,255,255,0.1); padding-bottom: 10px; }\n\n        .control-group { margin-bottom: 20px; }\n        label { display: block; margin-bottom: 8px; font-size: 12px; color: #aaa; text-transform: uppercase; }\n\n        \/* \u6309\u94ae\u6837\u5f0f *\/\n        .shape-btn {\n            display: inline-block;\n            width: 48%;\n            padding: 8px 0;\n            margin: 0 1% 8px 0;\n            background: rgba(255, 255, 255, 0.1);\n            border: 1px solid rgba(255, 255, 255, 0.05);\n            color: #ddd;\n            border-radius: 6px;\n            cursor: pointer;\n            font-size: 13px;\n            transition: all 0.2s;\n            text-align: center;\n        }\n        .shape-btn:hover { background: rgba(255, 255, 255, 0.2); }\n        .shape-btn.active { background: #00d2ff; color: #000; font-weight: bold; border-color: #00d2ff; }\n\n        \/* \u989c\u8272\u9009\u62e9\u5668 *\/\n        input&#91;type=\"color\"] {\n            -webkit-appearance: none;\n            border: none;\n            width: 100%;\n            height: 35px;\n            border-radius: 6px;\n            cursor: pointer;\n            background: none;\n        }\n        input&#91;type=\"color\"]::-webkit-color-swatch-wrapper { padding: 0; }\n        input&#91;type=\"color\"]::-webkit-color-swatch { border: 1px solid rgba(255,255,255,0.2); border-radius: 6px; }\n\n        \/* \u5168\u5c4f\u6309\u94ae *\/\n        #fullscreen-btn {\n            width: 100%;\n            padding: 10px;\n            background: transparent;\n            border: 1px solid rgba(255, 255, 255, 0.2);\n            color: #fff;\n            border-radius: 6px;\n            cursor: pointer;\n            margin-top: 10px;\n            transition: 0.2s;\n        }\n        #fullscreen-btn:hover { background: rgba(255, 255, 255, 0.1); }\n\n        \/* \u72b6\u6001\u6307\u793a\u5668 *\/\n        #status {\n            position: absolute;\n            top: 20px;\n            left: 20px;\n            color: #00d2ff;\n            font-size: 14px;\n            background: rgba(0,0,0,0.6);\n            padding: 8px 15px;\n            border-radius: 20px;\n            pointer-events: none;\n        }\n\n        \/* \u6444\u50cf\u5934\u9884\u89c8\u5c0f\u7a97 (\u53ef\u9009\uff0c\u4fbf\u4e8e\u8c03\u8bd5) *\/\n        #webcam-preview {\n            position: absolute;\n            bottom: 20px;\n            left: 20px;\n            width: 160px;\n            height: 120px;\n            background: #000;\n            border-radius: 8px;\n            overflow: hidden;\n            border: 2px solid rgba(255,255,255,0.1);\n            opacity: 0.7;\n            transform: scaleX(-1); \/* \u955c\u50cf *\/\n        }\n    &lt;\/style>\n\n    &lt;!-- \u5f15\u5165 Three.js -->\n    &lt;script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/three.js\/r128\/three.min.js\">&lt;\/script>\n    &lt;!-- \u5f15\u5165 MediaPipe Hands -->\n    &lt;script src=\"https:\/\/cdn.jsdelivr.net\/npm\/@mediapipe\/camera_utils\/camera_utils.js\" crossorigin=\"anonymous\">&lt;\/script>\n    &lt;script src=\"https:\/\/cdn.jsdelivr.net\/npm\/@mediapipe\/control_utils\/control_utils.js\" crossorigin=\"anonymous\">&lt;\/script>\n    &lt;script src=\"https:\/\/cdn.jsdelivr.net\/npm\/@mediapipe\/drawing_utils\/drawing_utils.js\" crossorigin=\"anonymous\">&lt;\/script>\n    &lt;script src=\"https:\/\/cdn.jsdelivr.net\/npm\/@mediapipe\/hands\/hands.js\" crossorigin=\"anonymous\">&lt;\/script>\n&lt;\/head>\n&lt;body>\n\n    &lt;div id=\"status\">\u6b63\u5728\u521d\u59cb\u5316\u6444\u50cf\u5934\u4e0e\u6a21\u578b...&lt;\/div>\n    &lt;video id=\"input_video\">&lt;\/video>\n    &lt;canvas id=\"webcam-preview\">&lt;\/canvas>\n\n    &lt;div id=\"ui-container\">\n        &lt;h2>\u7c92\u5b50\u63a7\u5236\u53f0&lt;\/h2>\n\n        &lt;div class=\"control-group\">\n            &lt;label>\u9009\u62e9\u6a21\u578b&lt;\/label>\n            &lt;div id=\"shape-buttons\">\n                &lt;div class=\"shape-btn active\" data-shape=\"heart\">\u7231\u5fc3&lt;\/div>\n                &lt;div class=\"shape-btn\" data-shape=\"flower\">\u82b1\u6735&lt;\/div>\n                &lt;div class=\"shape-btn\" data-shape=\"saturn\">\u571f\u661f&lt;\/div>\n                &lt;div class=\"shape-btn\" data-shape=\"buddha\">\u4f5b\u50cf(\u7985)&lt;\/div>\n                &lt;div class=\"shape-btn\" data-shape=\"firework\">\u70df\u82b1\u7403&lt;\/div>\n            &lt;\/div>\n        &lt;\/div>\n\n        &lt;div class=\"control-group\">\n            &lt;label>\u7c92\u5b50\u989c\u8272&lt;\/label>\n            &lt;input type=\"color\" id=\"color-picker\" value=\"#00d2ff\">\n        &lt;\/div>\n\n        &lt;button id=\"fullscreen-btn\">\u26f6 \u5168\u5c4f\u6a21\u5f0f&lt;\/button>\n\n        &lt;div class=\"control-group\" style=\"margin-top:20px; border-top: 1px solid rgba(255,255,255,0.1); padding-top:10px;\">\n            &lt;label>\u624b\u52bf\u6570\u636e&lt;\/label>\n            &lt;div style=\"font-size:12px; color:#ddd; display:flex; justify-content:space-between;\">\n                &lt;span>\u72b6\u6001: &lt;span id=\"hand-state\" style=\"color:#aaa;\">\u672a\u68c0\u6d4b&lt;\/span>&lt;\/span>\n                &lt;span>\u7cfb\u6570: &lt;span id=\"scale-val\" style=\"color:#00d2ff;\">1.0&lt;\/span>&lt;\/span>\n            &lt;\/div>\n        &lt;\/div>\n    &lt;\/div>\n\n&lt;script>\n    \/\/ ================= \u914d\u7f6e\u53c2\u6570 =================\n    const PARTICLE_COUNT = 25000;\n    const PARTICLE_SIZE = 0.08;\n    const TRANSITION_SPEED = 0.08; \/\/ \u5f62\u72b6\u53d8\u6362\u5e73\u6ed1\u5ea6\n    const HAND_SENSITIVITY = 0.15; \/\/ \u624b\u52bf\u54cd\u5e94\u5e73\u6ed1\u5ea6\n\n    \/\/ ================= \u5168\u5c40\u53d8\u91cf =================\n    let scene, camera, renderer, particles, geometry, material;\n    let currentShape = 'heart';\n    let targetPositions = &#91;]; \/\/ \u5b58\u50a8\u76ee\u6807\u5f62\u72b6\u7684\u5750\u6807\n    let basePositions = &#91;];   \/\/ \u5b58\u50a8\u5f53\u524d\u8ba1\u7b97\u51fa\u7684\u57fa\u7840\u5f62\u72b6\u5750\u6807\uff08\u65e0\u7f29\u653e\uff09\n    let currentColor = new THREE.Color(0x00d2ff);\n\n    \/\/ \u624b\u52bf\u63a7\u5236\u53d8\u91cf\n    let handScaleFactor = 1.0;  \/\/ \u5f53\u524d\u5e94\u7528\u7684\u7f29\u653e\u56e0\u5b50\n    let targetHandScale = 1.0;  \/\/ \u76ee\u6807\u7f29\u653e\u56e0\u5b50\uff08\u6765\u81eaMediaPipe\uff09\n    let isHandDetected = false;\n\n    \/\/ UI \u5143\u7d20\n    const statusEl = document.getElementById('status');\n    const handStateEl = document.getElementById('hand-state');\n    const scaleValEl = document.getElementById('scale-val');\n    const previewCanvas = document.getElementById('webcam-preview');\n    const previewCtx = previewCanvas.getContext('2d');\n\n    \/\/ ================= Three.js \u521d\u59cb\u5316 =================\n    function initThree() {\n        scene = new THREE.Scene();\n        \/\/ \u6dfb\u52a0\u4e00\u70b9\u96fe\u6548\u679c\u589e\u52a0\u6df1\u5ea6\u611f\n        scene.fog = new THREE.FogExp2(0x050505, 0.02);\n\n        camera = new THREE.PerspectiveCamera(75, window.innerWidth \/ window.innerHeight, 0.1, 1000);\n        camera.position.z = 25;\n        camera.position.y = 2;\n\n        renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });\n        renderer.setSize(window.innerWidth, window.innerHeight);\n        renderer.setPixelRatio(window.devicePixelRatio);\n        document.body.appendChild(renderer.domElement);\n\n        \/\/ \u521b\u5efa\u7c92\u5b50\u7cfb\u7edf\n        geometry = new THREE.BufferGeometry();\n        const positions = new Float32Array(PARTICLE_COUNT * 3);\n\n        \/\/ \u521d\u59cb\u4f4d\u7f6e\u968f\u673a\u5206\u5e03\n        for (let i = 0; i &lt; PARTICLE_COUNT * 3; i++) {\n            positions&#91;i] = (Math.random() - 0.5) * 50;\n        }\n\n        geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));\n\n        material = new THREE.PointsMaterial({\n            color: currentColor,\n            size: PARTICLE_SIZE,\n            sizeAttenuation: true,\n            blending: THREE.AdditiveBlending,\n            depthWrite: false,\n            transparent: true,\n            opacity: 0.8\n        });\n\n        particles = new THREE.Points(geometry, material);\n        scene.add(particles);\n\n        \/\/ \u521d\u59cb\u5316\u76ee\u6807\u4f4d\u7f6e\u6570\u7ec4\n        targetPositions = new Float32Array(PARTICLE_COUNT * 3);\n        basePositions = new Float32Array(PARTICLE_COUNT * 3);\n\n        \/\/ \u9ed8\u8ba4\u751f\u6210\u7231\u5fc3\n        generateShape('heart');\n\n        \/\/ \u76d1\u542c\u7a97\u53e3\u5927\u5c0f\u53d8\u5316\n        window.addEventListener('resize', onWindowResize, false);\n    }\n\n    function onWindowResize() {\n        camera.aspect = window.innerWidth \/ window.innerHeight;\n        camera.updateProjectionMatrix();\n        renderer.setSize(window.innerWidth, window.innerHeight);\n    }\n\n    \/\/ ================= \u5f62\u72b6\u751f\u6210\u7b97\u6cd5 =================\n    \/\/ \u4f7f\u7528\u6570\u5b66\u516c\u5f0f\u751f\u6210\u4e0d\u540c\u5f62\u72b6\u7684\u70b9\u4e91\n    function getPointOnShape(type, idx, total) {\n        const u = Math.random();\n        const v = Math.random();\n        const theta = Math.random() * Math.PI * 2;\n        const phi = Math.acos(2 * Math.random() - 1);\n\n        let x, y, z;\n\n        switch (type) {\n            case 'heart':\n                \/\/ 3D Heart equation\n                \/\/ \\&#91; x = 16\\sin^3(t) \\]\n                \/\/ \\&#91; y = 13\\cos(t) - 5\\cos(2t) - 2\\cos(3t) - \\cos(4t) \\]\n                \/\/ \u6269\u5c55\u52303D: \u7ed3\u5408\u5c42\u72b6\u5206\u5e03\u6216\u65cb\u8f6c\u4f53\u79ef\n                {\n                    const t = theta; \/\/ 0 to 2PI\n                    \/\/ \u7a0d\u5fae\u968f\u673a\u5316\u5c42\u539a\u5ea6\n                    const r = 0.5 + Math.random() * 0.5;\n                    \/\/ \u7231\u5fc3\u53c2\u6570\u65b9\u7a0b\n                    x = 16 * Math.pow(Math.sin(t), 3);\n                    y = 13 * Math.cos(t) - 5 * Math.cos(2*t) - 2 * Math.cos(3*t) - Math.cos(4*t);\n                    z = (Math.random() - 0.5) * 8; \/\/ \u539a\u5ea6\n\n                    \/\/ \u8ba9\u7c92\u5b50\u586b\u5145\u5185\u90e8\n                    const scale = Math.cbrt(Math.random());\n                    x *= scale; y *= scale; z *= scale;\n\n                    \/\/ \u8c03\u6574\u6bd4\u4f8b\n                    x *= 0.5; y *= 0.5; z *= 0.5;\n                }\n                break;\n\n            case 'flower':\n                \/\/ 3D Rose \/ Flower shape\n                {\n                    const k = 3; \/\/ \u82b1\u74e3\u6570\n                    const r = 10 * Math.cos(k * theta) + 5; \/\/ \u6781\u5750\u6807\u534a\u5f84\n                    const r_vol = r * Math.random(); \/\/ \u586b\u5145\u5185\u90e8\n\n                    x = r_vol * Math.cos(theta);\n                    y = r_vol * Math.sin(theta);\n                    z = (Math.random() - 0.5) * 5 * (1 - r_vol\/15); \/\/ \u4e2d\u5fc3\u539a\uff0c\u8fb9\u7f18\u8584\n\n                    \/\/ \u7a0d\u5fae\u5f2f\u66f2\u82b1\u74e3\n                    z += Math.cos(theta * k) * 2;\n                }\n                break;\n\n            case 'saturn':\n                \/\/ Sphere + Ring\n                if (Math.random() > 0.4) {\n                    \/\/ Planet Body (Sphere)\n                    const r = 6 * Math.cbrt(Math.random());\n                    x = r * Math.sin(phi) * Math.cos(theta);\n                    y = r * Math.sin(phi) * Math.sin(theta);\n                    z = r * Math.cos(phi);\n                } else {\n                    \/\/ Ring (Flat disc with hole)\n                    const rInner = 8;\n                    const rOuter = 14;\n                    const r = Math.sqrt(Math.random() * (rOuter*rOuter - rInner*rInner) + rInner*rInner);\n                    x = r * Math.cos(theta);\n                    z = r * Math.sin(theta); \/\/ \u73af\u5728\u6c34\u5e73\u9762(\u8fd9\u91cc\u8ba9\u5b83\u503e\u659c\u4e00\u70b9)\n                    y = (Math.random() - 0.5) * 0.5; \/\/ \u8584\u73af\n\n                    \/\/ \u503e\u659c\u73af\n                    const tilt = 0.4; \/\/ \u5f27\u5ea6\n                    const yt = y * Math.cos(tilt) - z * Math.sin(tilt);\n                    const zt = y * Math.sin(tilt) + z * Math.cos(tilt);\n                    y = yt; z = zt;\n                }\n                break;\n\n            case 'buddha':\n                \/\/ \u7b80\u5316\u7684\u7985\u4fee\u4eba\u50cf (Procedural Composition)\n                \/\/ \u5934\u90e8(\u7403) + \u8eab\u4f53(\u692d\u7403) + \u76d8\u817f(\u5706\u73af\/\u692d\u5706)\n                {\n                    const dice = Math.random();\n                    if (dice &lt; 0.15) {\n                        \/\/ Head\n                        const r = 2.5 * Math.cbrt(Math.random());\n                        x = r * Math.sin(phi) * Math.cos(theta);\n                        y = r * Math.sin(phi) * Math.sin(theta) + 6; \/\/ Lift up\n                        z = r * Math.cos(phi);\n                    } else if (dice &lt; 0.55) {\n                        \/\/ Body\n                        const r = 4.0 * Math.cbrt(Math.random());\n                        x = r * Math.sin(phi) * Math.cos(theta);\n                        y = (r * Math.sin(phi) * Math.sin(theta)) * 1.2; \/\/ Stretch Y slightly\n                        z = r * Math.cos(phi) * 0.8; \/\/ Flatten Z\n                    } else {\n                        \/\/ Legs\/Base\n                        const r = 5.5 * Math.sqrt(Math.random());\n                        x = r * Math.cos(theta);\n                        z = r * Math.sin(theta) * 0.7;\n                        y = (Math.random() - 1) * 2 - 3; \/\/ Bottom area\n                    }\n                }\n                break;\n\n            case 'firework':\n                \/\/ \u7206\u70b8\u5f62\u6001 (Burst)\n                {\n                    const r = 15 * Math.cbrt(Math.random());\n                    \/\/ \u96c6\u4e2d\u5728\u4e2d\u5fc3\uff0c\u4e5f\u6709\u8fb9\u7f18\u6563\u843d\n                    x = r * Math.sin(phi) * Math.cos(theta);\n                    y = r * Math.sin(phi) * Math.sin(theta);\n                    z = r * Math.cos(phi);\n\n                    \/\/ \u589e\u52a0\u4e00\u4e9b\u5c04\u7ebf\u6548\u679c\n                    if (Math.random() > 0.9) {\n                        x *= 1.5; y *= 1.5; z *= 1.5;\n                    }\n                }\n                break;\n\n            default:\n                x = y = z = 0;\n        }\n\n        return { x, y, z };\n    }\n\n    function generateShape(shapeType) {\n        currentShape = shapeType;\n\n        for (let i = 0; i &lt; PARTICLE_COUNT; i++) {\n            const p = getPointOnShape(shapeType, i, PARTICLE_COUNT);\n            basePositions&#91;i * 3] = p.x;\n            basePositions&#91;i * 3 + 1] = p.y;\n            basePositions&#91;i * 3 + 2] = p.z;\n        }\n\n        \/\/ \u5f53\u5207\u6362\u6a21\u578b\u65f6\uff0c\u6211\u4eec\u91cd\u7f6e\u4e00\u4e0b\u57fa\u7840\u4f4d\u7f6e\uff0c\u4f46\u5728\u52a8\u753b\u5faa\u73af\u4e2d\u4f1a\u5e94\u7528\u624b\u52bf\u7f29\u653e\n        \/\/ \u76ee\u6807\u4f4d\u7f6e\u7684\u66f4\u65b0\u903b\u8f91\u4e3b\u8981\u5728 animate \u51fd\u6570\u4e2d\u7ed3\u5408\u624b\u52bf\u7cfb\u6570\u5b9e\u65f6\u8ba1\u7b97\n    }\n\n    \/\/ ================= MediaPipe Hands \u8bbe\u7f6e =================\n    const videoElement = document.getElementById('input_video');\n\n    function onHandsResults(results) {\n        \/\/ \u66f4\u65b0\u9884\u89c8 Canvas\n        previewCanvas.width = videoElement.videoWidth;\n        previewCanvas.height = videoElement.videoHeight;\n        previewCtx.save();\n        previewCtx.clearRect(0, 0, previewCanvas.width, previewCanvas.height);\n        previewCtx.drawImage(results.image, 0, 0, previewCanvas.width, previewCanvas.height);\n\n        if (results.multiHandLandmarks &amp;&amp; results.multiHandLandmarks.length > 0) {\n            isHandDetected = true;\n            statusEl.innerText = \"\u5df2\u68c0\u6d4b\u5230\u624b\u52bf - \u5c1d\u8bd5\u5f20\u5408\u624b\u638c\";\n            statusEl.style.color = \"#00ff00\";\n\n            \/\/ \u7ed8\u5236\u9aa8\u67b6\uff08\u53ef\u9009\uff0c\u4e3a\u4e86\u6027\u80fd\u53ea\u7ed8\u5236\u7b2c\u4e00\u53ea\u624b\uff09\n            drawConnectors(previewCtx, results.multiHandLandmarks&#91;0], HAND_CONNECTIONS, {color: '#00FF00', lineWidth: 2});\n\n            \/\/ --- \u8ba1\u7b97\u624b\u52bf\u5f20\u5408\u5ea6 ---\n            const landmarks = results.multiHandLandmarks&#91;0];\n\n            \/\/ \u5173\u952e\u70b9: 0(\u624b\u8155), 4(\u62c7\u6307\u5c16), 8(\u98df\u6307\u5c16)\n            const wrist = landmarks&#91;0];\n            const thumbTip = landmarks&#91;4];\n            const indexTip = landmarks&#91;8];\n            const indexBase = landmarks&#91;5]; \/\/ \u98df\u6307\u6839\u90e8\n\n            \/\/ \u8ba1\u7b97\u53c2\u8003\u957f\u5ea6\uff08\u624b\u638c\u5927\u5c0f\uff09\uff1a\u624b\u8155\u5230\u98df\u6307\u6839\u90e8\u7684\u8ddd\u79bb\n            \/\/ \u7528\u4e8e\u5f52\u4e00\u5316\uff0c\u89e3\u51b3\u624b\u79bb\u6444\u50cf\u5934\u8fdc\u8fd1\u5bfc\u81f4\u7684\u8ddd\u79bb\u53d8\u5316\n            const palmSize = Math.sqrt(\n                Math.pow(indexBase.x - wrist.x, 2) +\n                Math.pow(indexBase.y - wrist.y, 2)\n            );\n\n            \/\/ \u8ba1\u7b97\u62c7\u6307\u5c16\u5230\u98df\u6307\u5c16\u7684\u8ddd\u79bb\n            const pinchDist = Math.sqrt(\n                Math.pow(thumbTip.x - indexTip.x, 2) +\n                Math.pow(thumbTip.y - indexTip.y, 2)\n            );\n\n            \/\/ \u5f52\u4e00\u5316\u5f20\u5408\u7a0b\u5ea6 (Ratio)\n            \/\/ \u901a\u5e38\u63e1\u62f3\u65f6 Ratio &lt; 0.2\uff0c\u5f20\u5f00\u65f6 Ratio > 1.0\n            let openRatio = pinchDist \/ (palmSize || 0.1); \/\/ \u9632\u6b62\u9664\u96f6\n\n            \/\/ \u6620\u5c04\u5230\u7f29\u653e\u56e0\u5b50\n            \/\/ \u6211\u4eec\u8bbe\u5b9a\uff1a\u63e1\u62f3(0.2) -> \u7f29\u653e 0.4\u500d | \u5f20\u5f00(1.2) -> \u7f29\u653e 2.5\u500d\n            const minRatio = 0.2;\n            const maxRatio = 1.2;\n            const minScale = 0.3; \/\/ \u6700\u5c0f\u6536\u7f29\n            const maxScale = 2.5; \/\/ \u6700\u5927\u6269\u6563\n\n            \/\/ Clamp and Map\n            let normalized = (openRatio - minRatio) \/ (maxRatio - minRatio);\n            if (normalized &lt; 0) normalized = 0;\n            if (normalized > 1) normalized = 1;\n\n            \/\/ \u7ebf\u6027\u63d2\u503c\u5f97\u5230\u76ee\u6807\u7f29\u653e\n            targetHandScale = minScale + normalized * (maxScale - minScale);\n\n            \/\/ UI Update\n            handStateEl.innerText = normalized &lt; 0.2 ? \"\u5408\u62e2\" : (normalized > 0.8 ? \"\u5f20\u5f00\" : \"\u8fd0\u52a8\u4e2d\");\n\n        } else {\n            isHandDetected = false;\n            statusEl.innerText = \"\u672a\u68c0\u6d4b\u5230\u624b\u52bf\uff0c\u8bf7\u5728\u6444\u50cf\u5934\u524d\u4e3e\u8d77\u5355\u624b\";\n            statusEl.style.color = \"#00d2ff\";\n            handStateEl.innerText = \"\u672a\u68c0\u6d4b\";\n            \/\/ \u5982\u679c\u6ca1\u68c0\u6d4b\u5230\u624b\uff0c\u7f13\u6162\u6062\u590d\u5230\u9ed8\u8ba4\u5927\u5c0f\n            targetHandScale = 1.0;\n        }\n        previewCtx.restore();\n    }\n\n    const hands = new Hands({locateFile: (file) => {\n        return `https:\/\/cdn.jsdelivr.net\/npm\/@mediapipe\/hands\/${file}`;\n    }});\n\n    hands.setOptions({\n        maxNumHands: 1,\n        modelComplexity: 1, \/\/ 1 is Lite, faster.\n        minDetectionConfidence: 0.5,\n        minTrackingConfidence: 0.5\n    });\n\n    hands.onResults(onHandsResults);\n\n    \/\/ \u5f00\u542f\u6444\u50cf\u5934\n    const cameraUtils = new Camera(videoElement, {\n        onFrame: async () => {\n            await hands.send({image: videoElement});\n        },\n        width: 320, \/\/ \u4f4e\u5206\u8fa8\u7387\u5904\u7406\u4ee5\u63d0\u9ad8\u6027\u80fd\n        height: 240\n    });\n\n    \/\/ \u542f\u52a8\u5904\u7406\uff08\u9700\u5728\u6b64\u4e4b\u540e\u521d\u59cb\u5316\uff09\n    cameraUtils.start().catch(err => {\n        statusEl.innerText = \"\u6444\u50cf\u5934\u8bbf\u95ee\u5931\u8d25\uff0c\u8bf7\u68c0\u67e5\u6743\u9650\u3002\";\n        statusEl.style.color = \"red\";\n    });\n\n    \/\/ ================= UI \u4ea4\u4e92\u903b\u8f91 =================\n    \/\/ \u5f62\u72b6\u5207\u6362\n    document.getElementById('shape-buttons').addEventListener('click', (e) => {\n        if (e.target.classList.contains('shape-btn')) {\n            \/\/ UI \u66f4\u65b0\n            document.querySelectorAll('.shape-btn').forEach(b => b.classList.remove('active'));\n            e.target.classList.add('active');\n\n            \/\/ \u903b\u8f91\u66f4\u65b0\n            const shape = e.target.getAttribute('data-shape');\n            generateShape(shape);\n        }\n    });\n\n    \/\/ \u989c\u8272\u5207\u6362\n    const colorPicker = document.getElementById('color-picker');\n    colorPicker.addEventListener('input', (e) => {\n        currentColor.set(e.target.value);\n        if (material) material.color = currentColor;\n    });\n\n    \/\/ \u5168\u5c4f\u5207\u6362\n    document.getElementById('fullscreen-btn').addEventListener('click', () => {\n        if (!document.fullscreenElement) {\n            document.documentElement.requestFullscreen();\n        } else {\n            if (document.exitFullscreen) {\n                document.exitFullscreen();\n            }\n        }\n    });\n\n    \/\/ ================= \u52a8\u753b\u5faa\u73af =================\n    function animate() {\n        requestAnimationFrame(animate);\n\n        \/\/ 1. \u5e73\u6ed1\u624b\u52bf\u7f29\u653e\u7cfb\u6570 (Lerp)\n        \/\/ \u5ef6\u8fdf &lt;= 100ms \u610f\u5473\u7740\u6211\u4eec\u9700\u8981\u8f83\u5feb\u7684\u54cd\u5e94\u901f\u5ea6\n        \/\/ simple lerp: current += (target - current) * speed\n        handScaleFactor += (targetHandScale - handScaleFactor) * HAND_SENSITIVITY;\n\n        \/\/ \u66f4\u65b0UI\u663e\u793a\u6570\u503c\n        scaleValEl.innerText = handScaleFactor.toFixed(2);\n\n        \/\/ 2. \u66f4\u65b0\u7c92\u5b50\u4f4d\u7f6e\n        const positions = particles.geometry.attributes.position.array;\n\n        \/\/ \u7f13\u6162\u65cb\u8f6c\u6574\u4f53\n        particles.rotation.y += 0.002;\n        \/\/ \u5982\u679c\u662f\u571f\u661f\uff0c\u7a0d\u5fae\u503e\u659c\u4e00\u70b9\u65cb\u8f6c\n        if (currentShape === 'saturn') {\n            particles.rotation.z = 0.3;\n        } else {\n            particles.rotation.z += (0 - particles.rotation.z) * 0.05; \/\/ \u590d\u4f4d\n        }\n\n        for (let i = 0; i &lt; PARTICLE_COUNT; i++) {\n            const ix = i * 3;\n            const iy = i * 3 + 1;\n            const iz = i * 3 + 2;\n\n            \/\/ \u83b7\u53d6\u57fa\u7840\u5f62\u72b6\u7684\u76ee\u6807\u4f4d\u7f6e\n            const bx = basePositions&#91;ix];\n            const by = basePositions&#91;iy];\n            const bz = basePositions&#91;iz];\n\n            \/\/ \u5e94\u7528\u624b\u52bf\u7f29\u653e\n            \/\/ \u6269\u6563\u6548\u679c\uff1a\u4f4d\u7f6e * \u7f29\u653e\u7cfb\u6570\n            const tx = bx * handScaleFactor;\n            const ty = by * handScaleFactor;\n            const tz = bz * handScaleFactor;\n\n            \/\/ \u7c92\u5b50\u63d2\u503c\u79fb\u52a8 (Morphing transition)\n            \/\/ \u8fd9\u91cc\u540c\u65f6\u5904\u7406\u4e86 \"\u5f62\u72b6\u5207\u6362\u7684\u8fc7\u6e21\" \u548c \"\u624b\u52bf\u7f29\u653e\u7684\u5b9e\u65f6\u6027\"\n            \/\/ positions&#91;ix] \u662f\u5f53\u524d\u6e32\u67d3\u4f4d\u7f6e\n            positions&#91;ix] += (tx - positions&#91;ix]) * TRANSITION_SPEED;\n            positions&#91;iy] += (ty - positions&#91;iy]) * TRANSITION_SPEED;\n            positions&#91;iz] += (tz - positions&#91;iz]) * TRANSITION_SPEED;\n        }\n\n        particles.geometry.attributes.position.needsUpdate = true;\n\n        renderer.render(scene, camera);\n    }\n\n    \/\/ \u542f\u52a8\n    initThree();\n    animate();\n\n&lt;\/script>\n&lt;\/body>\n&lt;\/html>\n<\/code><\/pre>\n\n\n\n<p>\ud83d\udc49 \u4f60\u4e0a\u9762\u8d34\u51fa\u7684\u4ee3\u7801\u5c31\u662f\u6700\u7ec8\u53ef\u8fd0\u884c\u7248\u672c<\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u8fd9\u4e24\u5929\u5237\u6296\u97f3\uff0c\u4f60\u4e00\u5b9a\u89c1\u8fc7\u90a3\u4e2a\u706b\u7206\u7684\u2014\u2014&nbsp;\u53ea\u9700\u8981\u4e00\u4e2a\u6444\u50cf\u5934\uff0c\u5355\u624b\u5f20\u5408\uff0c\u5c31\u80fd\u8ba9 3D \u7c92\u5b50\u5b9e\u65f6\u7f29\u653e\u3001\u6269\u6563 [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":159,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[36,10,1],"tags":[37,8,12],"class_list":["post-158","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-ai","category-10","category-leam","tag-ai","tag-8","tag-12"],"_links":{"self":[{"href":"https:\/\/www.thebugmuxi.xyz\/index.php\/wp-json\/wp\/v2\/posts\/158","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.thebugmuxi.xyz\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.thebugmuxi.xyz\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.thebugmuxi.xyz\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.thebugmuxi.xyz\/index.php\/wp-json\/wp\/v2\/comments?post=158"}],"version-history":[{"count":1,"href":"https:\/\/www.thebugmuxi.xyz\/index.php\/wp-json\/wp\/v2\/posts\/158\/revisions"}],"predecessor-version":[{"id":160,"href":"https:\/\/www.thebugmuxi.xyz\/index.php\/wp-json\/wp\/v2\/posts\/158\/revisions\/160"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.thebugmuxi.xyz\/index.php\/wp-json\/wp\/v2\/media\/159"}],"wp:attachment":[{"href":"https:\/\/www.thebugmuxi.xyz\/index.php\/wp-json\/wp\/v2\/media?parent=158"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.thebugmuxi.xyz\/index.php\/wp-json\/wp\/v2\/categories?post=158"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.thebugmuxi.xyz\/index.php\/wp-json\/wp\/v2\/tags?post=158"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}