{"id":2232,"date":"2026-06-11T15:41:09","date_gmt":"2026-06-11T07:41:09","guid":{"rendered":"https:\/\/hueil.top\/?p=2232"},"modified":"2026-06-11T15:45:30","modified_gmt":"2026-06-11T07:45:30","slug":"%f0%9f%8d%b3-%e5%b0%8f%e5%8e%a8%e6%9a%96%e5%85%89-%e9%a1%b9%e7%9b%ae%e4%bb%8b%e7%bb%8d%e4%b8%8e%e6%8a%80%e6%9c%af%e8%af%a6%e8%a7%a3","status":"publish","type":"post","link":"https:\/\/hueil.top\/?p=2232","title":{"rendered":"\ud83c\udf73 \u5c0f\u53a8\u6696\u5149 \u2014 \u9879\u76ee\u4ecb\u7ecd\u4e0e\u6280\u672f\u8be6\u89e3"},"content":{"rendered":"\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">\u96c6\u83dc\u8c31\u5206\u4eab\u3001\u793e\u4ea4\u4e92\u52a8\u3001\u7535\u5546\u4ea4\u6613\u3001\u4f1a\u5458\u4f53\u7cfb\u3001AI \u667a\u80fd\u5ba2\u670d\u3001Three.js 3D \u89c6\u89c9\u4f53\u9a8c\u4e8e\u4e00\u4f53\u7684\u5168\u6808\u7f8e\u98df\u793e\u533a\u5e73\u53f0\u3002<\/p>\n<\/blockquote>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83d\udcd1 \u76ee\u5f55<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"#1-\u9879\u76ee\u6982\u8ff0\">1. \u9879\u76ee\u6982\u8ff0<\/a><\/li>\n\n\n\n<li><a href=\"#2-\u6280\u672f\u6808\u8be6\u89e3\">2. \u6280\u672f\u6808\u8be6\u89e3<\/a><\/li>\n\n\n\n<li><a href=\"#3-threejs-3d-\u6548\u679c\u5b9e\u73b0\u539f\u7406\">3. Three.js 3D \u6548\u679c\u5b9e\u73b0\u539f\u7406<\/a><\/li>\n\n\n\n<li><a href=\"#31-\u67b6\u6784\u8bbe\u8ba1\">3.1 \u67b6\u6784\u8bbe\u8ba1<\/a><\/li>\n\n\n\n<li><a href=\"#32-\u573a\u666f\u521d\u59cb\u5316\">3.2 \u573a\u666f\u521d\u59cb\u5316<\/a><\/li>\n\n\n\n<li><a href=\"#33-\u5149\u7167\u7cfb\u7edf\">3.3 \u5149\u7167\u7cfb\u7edf<\/a><\/li>\n\n\n\n<li><a href=\"#34-\u76f8\u673a\u4e0e\u4ea4\u4e92\u63a7\u5236\">3.4 \u76f8\u673a\u4e0e\u4ea4\u4e92\u63a7\u5236<\/a><\/li>\n\n\n\n<li><a href=\"#35-3d-\u6a21\u578b\u6784\u5efa\">3.5 3D \u6a21\u578b\u6784\u5efa<\/a><\/li>\n\n\n\n<li><a href=\"#36-\u52a8\u753b\u7cfb\u7edf\">3.6 \u52a8\u753b\u7cfb\u7edf<\/a><\/li>\n\n\n\n<li><a href=\"#37-\u7c92\u5b50\u7279\u6548\">3.7 \u7c92\u5b50\u7279\u6548<\/a><\/li>\n\n\n\n<li><a href=\"#38-\u8d44\u6e90\u7ba1\u7406\u4e0e\u6027\u80fd\u4f18\u5316\">3.8 \u8d44\u6e90\u7ba1\u7406\u4e0e\u6027\u80fd\u4f18\u5316<\/a><\/li>\n\n\n\n<li><a href=\"#39-glb-\u6a21\u578b\u52a0\u8f7d\">3.9 GLB \u6a21\u578b\u52a0\u8f7d<\/a><\/li>\n\n\n\n<li><a href=\"#310-vue-\u7ec4\u4ef6\u96c6\u6210\">3.10 Vue \u7ec4\u4ef6\u96c6\u6210<\/a><\/li>\n\n\n\n<li><a href=\"#4-\u603b\u7ed3\">4. \u603b\u7ed3<\/a><\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">1. \u9879\u76ee\u6982\u8ff0<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\u5c0f\u53a8\u6696\u5149 (Yummly)<\/strong> \u662f\u4e00\u5957\u9762\u5411\u7f8e\u98df\u7231\u597d\u8005\u7684\u5168\u6808\u793e\u533a\u5e73\u53f0\uff0c\u91c7\u7528\u524d\u540e\u7aef\u5206\u79bb\u67b6\u6784\u3002\u9879\u76ee\u540d\u79f0\u53d6\u610f&#8221;\u5c0f\u5c0f\u53a8\u623f\u91cc\u6e29\u6696\u7684\u5149&#8221;\uff0c\u5e0c\u671b\u4e3a\u6bcf\u4e00\u4e2a\u70ed\u7231\u70f9\u996a\u7684\u4eba\u6253\u9020\u4e00\u4e2a\u5206\u4eab\u4e0e\u4ea4\u6d41\u7684\u7ebf\u4e0a\u5bb6\u56ed\u3002<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><div class='fancybox-wrapper' data-fancybox='post-images' href='https:\/\/hueil.top\/wp-content\/uploads\/2026\/06\/image-1024x533.png'><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"533\" src=\"https:\/\/hueil.top\/wp-content\/uploads\/2026\/06\/image-1024x533.png\" alt=\"\" class=\"wp-image-2235\" srcset=\"https:\/\/hueil.top\/wp-content\/uploads\/2026\/06\/image-1024x533.png 1024w, https:\/\/hueil.top\/wp-content\/uploads\/2026\/06\/image-300x156.png 300w, https:\/\/hueil.top\/wp-content\/uploads\/2026\/06\/image-768x400.png 768w, https:\/\/hueil.top\/wp-content\/uploads\/2026\/06\/image-1536x800.png 1536w, https:\/\/hueil.top\/wp-content\/uploads\/2026\/06\/image.png 1920w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/div><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">\u6838\u5fc3\u80fd\u529b\u4e00\u89c8<\/h3>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>\u6a21\u5757<\/th><th>\u529f\u80fd\u63cf\u8ff0<\/th><\/tr><\/thead><tbody><tr><td>\ud83c\udf72 <strong>\u83dc\u8c31\u7cfb\u7edf<\/strong><\/td><td>\u53d1\u5e03\/\u6d4f\u89c8\/\u641c\u7d22\/\u6536\u85cf\u83dc\u8c31\uff0c\u652f\u6301\u5c01\u9762\u56fe\u3001\u98df\u6750\u6e05\u5355\u3001\u5206\u6b65\u6559\u7a0b\u3001SKU \u591a\u89c4\u683c<\/td><\/tr><tr><td>\ud83d\udcac <strong>\u793e\u4ea4\u4e92\u52a8<\/strong><\/td><td>\u8bc4\u8bba\u3001\u70b9\u8d5e\u3001\u6536\u85cf\u3001\u5173\u6ce8\u3001\u597d\u53cb\u79c1\u4fe1\u3001\u7f8e\u98df\u5708\u52a8\u6001 Feed<\/td><\/tr><tr><td>\ud83d\uded2 <strong>\u7535\u5546\u4ea4\u6613<\/strong><\/td><td>\u8d2d\u7269\u8f66\u3001\u4e0b\u5355\u652f\u4ed8\u3001\u8ba2\u5355\u6d41\u8f6c\u3001\u914d\u9001\u8ddf\u8e2a\u3001\u94b1\u5305\u652f\u4ed8<\/td><\/tr><tr><td>\ud83d\udc51 <strong>\u4f1a\u5458\u4f53\u7cfb<\/strong><\/td><td>\u9752\u94dc\/\u767d\u94f6\/\u9ec4\u91d1\/\u94bb\u77f3\u56db\u6863\uff0c\u7b7e\u5230\u3001\u79ef\u5206\u3001\u6210\u957f\u503c\u3001\u6392\u884c<\/td><\/tr><tr><td>\ud83c\udfab <strong>\u8425\u9500\u5de5\u5177<\/strong><\/td><td>\u4f18\u60e0\u5238\u3001\u79d2\u6740\u3001\u62fc\u56e2\u3001\u79ef\u5206\u5546\u57ce\u3001\u62bd\u5956\u8f6c\u76d8<\/td><\/tr><tr><td>\ud83c\udfa8 <strong>3D \u89c6\u89c9<\/strong><\/td><td>Three.js \u98df\u7269\u7c92\u5b50\u52a8\u753b\u80cc\u666f\uff0c\u6bcf\u9875\u4e0d\u540c\u573a\u666f\uff0c\u9f20\u6807\u8ddf\u968f\u4ea4\u4e92<\/td><\/tr><tr><td>\ud83e\udd16 <strong>AI \u5ba2\u670d<\/strong><\/td><td>\u57fa\u4e8e DeepSeek \u7684\u667a\u80fd\u95ee\u7b54\uff0c\u652f\u6301\u6587\u5b57\u5bf9\u8bdd + \u56fe\u7247\u8bc6\u522b + \u83dc\u8c31\u63a8\u8350<\/td><\/tr><tr><td>\ud83d\udda5\ufe0f <strong>\u7ba1\u7406\u540e\u53f0<\/strong><\/td><td>\u7528\u6237\/\u83dc\u8c31\/\u8ba2\u5355\/\u5e93\u5b58\/\u914d\u9001\/\u8425\u9500\/\u7edf\u8ba1\/\u65e5\u5fd7\u5168\u9762\u7ba1\u7406<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">\u9879\u76ee\u89c4\u6a21<\/h3>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>\u6307\u6807<\/th><th>\u6570\u91cf<\/th><\/tr><\/thead><tbody><tr><td>Java \u6e90\u6587\u4ef6<\/td><td>95<\/td><\/tr><tr><td>Vue\/JS \u6587\u4ef6<\/td><td>130<\/td><\/tr><tr><td>\u6570\u636e\u8868<\/td><td>42 \u5f20<\/td><\/tr><tr><td>API \u7aef\u70b9<\/td><td>90+<\/td><\/tr><tr><td>\u524d\u7aef\u9875\u9762<\/td><td>42<\/td><\/tr><tr><td>3D \u573a\u666f\u7c7b\u578b<\/td><td>7 \u79cd<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">2. \u6280\u672f\u6808\u8be6\u89e3<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">\u6574\u4f53\u67b6\u6784<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code> \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n \u2502                        \u6d4f\u89c8\u5668 \/ \u79fb\u52a8\u7aef                         \u2502\n \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n \u2502  \u524d\u7aef (Vue 2 + Element UI + Three.js)                        \u2502\n \u2502  \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510  \u2502\n \u2502  \u2502 Vue CLI  \u2502  Router  \u2502   Vuex   \u2502  Axios   \u2502 Three.js  \u2502  \u2502\n \u2502  \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518  \u2502\n \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n \u2502  \u540e\u7aef (Spring Boot 2.5.14)                                    \u2502\n \u2502  \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510  \u2502\n \u2502  \u2502 Security \u2502   JWT    \u2502 MyBatis+ \u2502  Hutool  \u2502  Knife4j  \u2502  \u2502\n \u2502  \u2502  + CORS  \u2502  Token   \u2502   \u5206\u9875   \u2502  FastJSON\u2502  Swagger  \u2502  \u2502\n \u2502  \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518  \u2502\n \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n \u2502  \u6570\u636e\u5c42                                                       \u2502\n \u2502  \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510  \u2502\n \u2502  \u2502   MySQL 8.0        \u2502   \u6587\u4ef6\u5b58\u50a8 (\u672c\u5730 upload\/)         \u2502  \u2502\n \u2502  \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518  \u2502\n \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">\u524d\u7aef\u6280\u672f\u6808<\/h3>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>\u6280\u672f<\/th><th>\u7248\u672c<\/th><th>\u7528\u9014<\/th><\/tr><\/thead><tbody><tr><td><strong>Vue 2<\/strong><\/td><td>2.6.14<\/td><td>\u6e10\u8fdb\u5f0f\u524d\u7aef\u6846\u67b6\uff0c\u7ec4\u4ef6\u5316\u6784\u5efa UI<\/td><\/tr><tr><td><strong>Vue Router<\/strong><\/td><td>3.5.3<\/td><td>SPA \u8def\u7531\u7ba1\u7406\uff0c\u542b\u5bfc\u822a\u5b88\u536b\u5b9e\u73b0\u6743\u9650\u63a7\u5236<\/td><\/tr><tr><td><strong>Vuex<\/strong><\/td><td>3.6.2<\/td><td>\u96c6\u4e2d\u5f0f\u72b6\u6001\u7ba1\u7406\uff0c\u7ba1\u7406\u7528\u6237\u72b6\u6001\u3001\u6743\u9650\u3001\u5e94\u7528\u914d\u7f6e<\/td><\/tr><tr><td><strong>Element UI<\/strong><\/td><td>2.15.7<\/td><td>\u4f01\u4e1a\u7ea7 UI \u7ec4\u4ef6\u5e93\uff0c\u63d0\u4f9b\u8868\u683c\u3001\u8868\u5355\u3001\u5f39\u7a97\u7b49 60+ \u7ec4\u4ef6<\/td><\/tr><tr><td><strong>Axios<\/strong><\/td><td>0.24.0<\/td><td>HTTP \u5ba2\u6237\u7aef\uff0c\u5c01\u88c5\u8bf7\u6c42\/\u54cd\u5e94\u62e6\u622a\u5668\u5904\u7406 JWT \u8ba4\u8bc1<\/td><\/tr><tr><td><strong>Three.js<\/strong><\/td><td>0.184<\/td><td>WebGL 3D \u6e32\u67d3\u5f15\u64ce\uff0c\u5b9e\u73b0\u98df\u7269\u7c92\u5b50\u52a8\u753b\u80cc\u666f<\/td><\/tr><tr><td><strong>QRCode<\/strong><\/td><td>1.5.4<\/td><td>\u4e8c\u7ef4\u7801\u751f\u6210\uff0c\u7528\u4e8e\u83dc\u8c31\u5206\u4eab<\/td><\/tr><tr><td><strong>Vue Quill Editor<\/strong><\/td><td>3.0.6<\/td><td>\u5bcc\u6587\u672c\u7f16\u8f91\u5668\uff0c\u83dc\u8c31\u6b65\u9aa4\u7f16\u8f91<\/td><\/tr><tr><td><strong>NProgress<\/strong><\/td><td>0.2.0<\/td><td>\u9875\u9762\u52a0\u8f7d\u8fdb\u5ea6\u6761<\/td><\/tr><tr><td><strong>SCSS<\/strong><\/td><td>\u2014<\/td><td>CSS \u9884\u5904\u7406\u5668\uff0c\u7f16\u5199\u53ef\u7ef4\u62a4\u7684\u6837\u5f0f<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">\u540e\u7aef\u6280\u672f\u6808<\/h3>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>\u6280\u672f<\/th><th>\u7248\u672c<\/th><th>\u7528\u9014<\/th><\/tr><\/thead><tbody><tr><td><strong>Spring Boot<\/strong><\/td><td>2.5.14<\/td><td>Java \u4f01\u4e1a\u7ea7\u540e\u7aef\u6846\u67b6\uff0c\u81ea\u52a8\u914d\u7f6e + \u8d77\u6b65\u4f9d\u8d56<\/td><\/tr><tr><td><strong>Spring Security<\/strong><\/td><td>\u2014<\/td><td>\u8ba4\u8bc1\u6388\u6743\u6846\u67b6\uff0c\u8fc7\u6ee4\u5668\u94fe\u5904\u7406 JWT + CORS<\/td><\/tr><tr><td><strong>MyBatis Plus<\/strong><\/td><td>3.4.3.4<\/td><td>ORM \u589e\u5f3a\u5de5\u5177\uff0c\u5185\u7f6e\u5206\u9875\u3001\u4ee3\u7801\u751f\u6210\u3001\u6761\u4ef6\u6784\u9020\u5668<\/td><\/tr><tr><td><strong>JWT (jjwt)<\/strong><\/td><td>0.9.1<\/td><td>\u65e0\u72b6\u6001 Token \u8ba4\u8bc1\uff0c\u7528\u6237\u767b\u5f55\u72b6\u6001\u7ba1\u7406<\/td><\/tr><tr><td><strong>Knife4j<\/strong><\/td><td>3.0.3<\/td><td>Swagger \u589e\u5f3a UI\uff0c\u81ea\u52a8\u751f\u6210 API \u6587\u6863<\/td><\/tr><tr><td><strong>Hutool<\/strong><\/td><td>5.7.22<\/td><td>Java \u5de5\u5177\u96c6\uff0c\u7b80\u5316\u6587\u4ef6\u3001\u52a0\u5bc6\u3001\u65e5\u671f\u7b49\u64cd\u4f5c<\/td><\/tr><tr><td><strong>FastJSON<\/strong><\/td><td>1.2.83<\/td><td>\u963f\u91cc\u5df4\u5df4 JSON \u5e8f\u5217\u5316\u5e93<\/td><\/tr><tr><td><strong>MySQL<\/strong><\/td><td>8.0<\/td><td>\u5173\u7cfb\u578b\u6570\u636e\u5e93\uff0cutf8mb4 \u5b57\u7b26\u96c6<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">\u540e\u7aef\u6a21\u5757\u7ed3\u6784<\/h3>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>\u6a21\u5757<\/th><th>\u804c\u8d23<\/th><\/tr><\/thead><tbody><tr><td><code>yummly-common<\/code><\/td><td>\u516c\u5171\u6a21\u5757\uff1aAjaxResult \u7edf\u4e00\u54cd\u5e94\u3001BaseEntity \u57fa\u7c7b\u3001\u5168\u5c40\u5f02\u5e38\u5904\u7406\u3001\u5e38\u91cf\u5b9a\u4e49<\/td><\/tr><tr><td><code>yummly-framework<\/code><\/td><td>\u6846\u67b6\u6a21\u5757\uff1aSpring Security \u914d\u7f6e\u3001JWT \u8fc7\u6ee4\u5668\u94fe\u3001MyBatis Plus \u5206\u9875\u63d2\u4ef6\u3001WebMvc \u8de8\u57df<\/td><\/tr><tr><td><code>yummly-system<\/code><\/td><td>\u4e1a\u52a1\u6a21\u5757\uff1a30+ \u5b9e\u4f53\u7c7b\u300120+ Mapper\u3001Service \u5c42\u300115+ REST \u63a7\u5236\u5668\uff08\u542b\u7ba1\u7406\u7aef\uff09<\/td><\/tr><tr><td><code>yummly-admin<\/code><\/td><td>\u542f\u52a8\u6a21\u5757\uff1aSpring Boot \u5165\u53e3\u3001\u591a\u73af\u5883\u914d\u7f6e\u6587\u4ef6\u3001SQL \u521d\u59cb\u5316\u811a\u672c<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">\u90e8\u7f72\u67b6\u6784<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502              Docker Compose              \u2502\n\u2502                                          \u2502\n\u2502  \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510  \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510  \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u2502\n\u2502  \u2502 Frontend \u2502  \u2502 Backend  \u2502  \u2502 MySQL \u2502 \u2502\n\u2502  \u2502 (Nginx)  \u2502  \u2502 (:8088)  \u2502  \u2502 (:3306)\u2502 \u2502\n\u2502  \u2502  :80     \u2502  \u2502          \u2502  \u2502       \u2502 \u2502\n\u2502  \u2514\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2518  \u2514\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2518  \u2514\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2518 \u2502\n\u2502       \u2502             \u2502            \u2502      \u2502\n\u2502       \u2514\u2500\u2500\u2500 proxy \u2500\u2500\u2500\u2518            \u2502      \u2502\n\u2502                    \u2502\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518      \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u524d\u7aef\u4f7f\u7528 <strong>Nginx Alpine<\/strong> \u90e8\u7f72\uff0c\u914d\u7f6e SPA \u8def\u7531\u56de\u9000 + API \u53cd\u5411\u4ee3\u7406<\/li>\n\n\n\n<li>\u540e\u7aef\u4f7f\u7528 <strong>\u591a\u9636\u6bb5 Docker \u6784\u5efa<\/strong>\uff08Maven \u7f16\u8bd1 \u2192 JRE \u8fd0\u884c\u955c\u50cf\uff09<\/li>\n\n\n\n<li>\u524d\u7aef\u901a\u8fc7 <code>vue.config.js<\/code> devServer proxy \u5728\u5f00\u53d1\u73af\u5883\u4ee3\u7406\u5230\u540e\u7aef 8088<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">3. Three.js 3D \u6548\u679c\u5b9e\u73b0\u539f\u7406<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\u5c0f\u53a8\u6696\u5149<\/strong> \u7684 3D \u89c6\u89c9\u7cfb\u7edf\u662f\u6574\u4e2a\u9879\u76ee\u524d\u7aef\u7684\u4e00\u5927\u4eae\u70b9\u3002\u5b83\u5e76\u975e\u7b80\u5355\u7684\u7b2c\u4e09\u65b9\u7ec4\u4ef6\u5957\u7528\uff0c\u800c\u662f\u4e00\u5957\u5b8c\u5168\u4ece\u96f6\u6784\u5efa\u7684\u3001\u9762\u5411\u7f8e\u98df\u4e3b\u9898\u7684\u7a0b\u5e8f\u5316 3D \u573a\u666f\u7cfb\u7edf\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">\u8bbe\u8ba1\u7406\u5ff5<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u8fd9\u5957 3D \u7cfb\u7edf\u7684\u6838\u5fc3\u8bbe\u8ba1\u539f\u5219\u662f\uff1a<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u96f6\u5916\u90e8\u8d44\u6e90\u4f9d\u8d56<\/strong> \u2014 \u6240\u6709\u852c\u679c\u6a21\u578b\uff08\u82f9\u679c\u3001\u6a59\u5b50\u3001\u756a\u8304\u3001\u5357\u74dc\u7b49\uff09\u5747\u901a\u8fc7 Three.js \u539f\u751f\u51e0\u4f55\u4f53<strong>\u7a0b\u5e8f\u5316\u751f\u6210<\/strong>\uff0c\u4e0d\u4f9d\u8d56\u4efb\u4f55\u5916\u90e8 3D \u6a21\u578b\u6587\u4ef6\uff08\u9664 plant \u573a\u666f\u7684 GLB \u6a21\u578b\u5916\uff09<\/li>\n\n\n\n<li><strong>\u573a\u666f-\u8def\u7531\u6620\u5c04<\/strong> \u2014 7 \u79cd\u4e0d\u540c\u573a\u666f\u5bf9\u5e94\u4e0d\u540c\u9875\u9762\u8def\u7531\uff0c\u6bcf\u4e2a\u9875\u9762\u7684 3D \u80cc\u666f\u5404\u5177\u7279\u8272<\/li>\n\n\n\n<li><strong>\u6027\u80fd\u4f18\u5148<\/strong> \u2014 \u6750\u8d28\u7f13\u5b58\u3001\u6309\u9700\u6e32\u67d3\u3001GPU \u8d44\u6e90\u81ea\u52a8\u56de\u6536<\/li>\n\n\n\n<li><strong>\u4ea4\u4e92\u81ea\u7136<\/strong> \u2014 OrbitControls \u63d0\u4f9b\u62d6\u62fd\/\u7f29\u653e\/\u5e73\u79fb\uff0c\u9f20\u6807\u60ac\u505c\u6fc0\u6d3b\u63a7\u5236<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">3.1 \u67b6\u6784\u8bbe\u8ba1<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u6574\u4e2a 3D \u7cfb\u7edf\u5c01\u88c5\u5728\u4e00\u4e2a Vue \u5355\u6587\u4ef6\u7ec4\u4ef6 <code>ThreeBackground\/index.vue<\/code> \u4e2d\uff0c\u901a\u8fc7 <code>scene<\/code> prop \u5207\u6362\u573a\u666f\u7c7b\u578b\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code> ThreeBackground \u7ec4\u4ef6 (Vue SFC)\n\u251c\u2500\u2500 &lt;template&gt;        \u2014 DOM \u5bb9\u5668 + loading\/hint \u6d6e\u5c42\n\u251c\u2500\u2500 &lt;script&gt;          \u2014 3D \u6838\u5fc3\u903b\u8f91 (\u7ea6 1500 \u884c)\n\u2502   \u251c\u2500\u2500 \u573a\u666f\u914d\u7f6e      \u2014 SCENE_COLORS \u989c\u8272\u6620\u5c04\n\u2502   \u251c\u2500\u2500 \u6750\u8d28\u7f13\u5b58      \u2014 mat() \u5de5\u5382\u51fd\u6570\n\u2502   \u251c\u2500\u2500 \u6a21\u578b\u5de5\u5382      \u2014 16 \u79cd\u6c34\u679c\/\u852c\u83dc\u7a0b\u5e8f\u5316\u5efa\u6a21\n\u2502   \u251c\u2500\u2500 \u573a\u666f\u6784\u5efa      \u2014 7 \u4e2a buildXxx() \u65b9\u6cd5\n\u2502   \u251c\u2500\u2500 \u7c92\u5b50\u7cfb\u7edf      \u2014 addParticles() \u6cdb\u7528\u7c92\u5b50\n\u2502   \u251c\u2500\u2500 \u52a8\u753b\u5faa\u73af      \u2014 requestAnimationFrame + Clock\n\u2502   \u251c\u2500\u2500 \u4ea4\u4e92\u7ba1\u7406      \u2014 OrbitControls + \u9f20\u6807\u4e8b\u4ef6\n\u2502   \u2514\u2500\u2500 \u8d44\u6e90\u6e05\u7406      \u2014 beforeDestroy GPU \u56de\u6536\n\u2514\u2500\u2500 &lt;style scoped&gt;    \u2014 \u5bb9\u5668\/\u63d0\u793a\/hint \u6837\u5f0f<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">3.2 \u573a\u666f\u521d\u59cb\u5316<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u573a\u666f\u521d\u59cb\u5316\u662f\u6574\u4e2a 3D \u7cfb\u7edf\u7684\u5165\u53e3\uff0c\u5728 Vue \u7684 <code>mounted()<\/code> \u94a9\u5b50\u4e2d\u8c03\u7528\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ 1. \u521b\u5efa WebGL \u6e32\u67d3\u5668\nthis.renderer = new THREE.WebGLRenderer({\n  alpha: true,        \/\/ \u900f\u660e\u80cc\u666f\uff0c\u4e0e\u9875\u9762 CSS \u80cc\u666f\u878d\u5408\n  antialias: true     \/\/ \u6297\u952f\u9f7f\uff0c\u8fb9\u7f18\u66f4\u5e73\u6ed1\n})\nthis.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)) \/\/ \u9650\u5236\u50cf\u7d20\u6bd4 \u22642\nthis.renderer.shadowMap.enabled = true           \/\/ \u542f\u7528\u9634\u5f71\nthis.renderer.shadowMap.type = THREE.PCFSoftShadowMap  \/\/ \u67d4\u548c\u9634\u5f71\n\n\/\/ 2. \u521b\u5efa\u573a\u666f + \u80cc\u666f\u8272 + \u96fe\u6548\nthis.scene3d = new THREE.Scene()\nconst sc = SCENE_COLORS&#91;this.scene]  \/\/ \u6839\u636e\u573a\u666f\u83b7\u53d6\u914d\u8272\nthis.scene3d.background = new THREE.Color(sc.light)  \/\/ \u6e10\u53d8\u80cc\u666f\nthis.scene3d.fog = new THREE.Fog(sc.light, 8, 28)    \/\/ \u8fdc\u8ddd\u79bb\u96fe\u5316\n\n\/\/ 3. \u521b\u5efa\u900f\u89c6\u76f8\u673a\nthis.camera = new THREE.PerspectiveCamera(\n  50,           \/\/ FOV \u89c6\u573a\u89d2\n  w \/ h,        \/\/ \u5bbd\u9ad8\u6bd4\n  0.1,          \/\/ \u8fd1\u88c1\u526a\u9762\n  100           \/\/ \u8fdc\u88c1\u526a\u9762\n)\nthis.camera.position.set(0, 1.2, 8.5)  \/\/ \u76f8\u673a\u4f4d\u7f6e\nthis.camera.lookAt(0, -0.1, 0)         \/\/ \u6ce8\u89c6\u76ee\u6807<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">\u5173\u952e\u8bbe\u8ba1\u70b9<\/h4>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>alpha: true<\/strong> \u2014 \u6e32\u67d3\u5668\u80cc\u666f\u900f\u660e\uff0c3D \u573a\u666f\u4e0e\u9875\u9762\u7684 CSS \u80cc\u666f\u65e0\u7f1d\u878d\u5408<\/li>\n\n\n\n<li><strong>Fog \u96fe\u6548<\/strong> \u2014 \u8ddd\u79bb 8 \u5355\u4f4d\u5f00\u59cb\u51fa\u73b0\uff0c28 \u5355\u4f4d\u5b8c\u5168\u6d88\u5931\uff0c\u8425\u9020\u7a7a\u95f4\u6df1\u5ea6\u611f<\/li>\n\n\n\n<li><strong>pixelRatio \u9650\u5236<\/strong> \u2014 \u9650\u5236\u8bbe\u5907\u50cf\u7d20\u6bd4\u4e0d\u8d85\u8fc7 2\uff0c\u5728\u9ad8 DPI \u5c4f\u5e55\u4e0a\u907f\u514d\u6027\u80fd\u6d6a\u8d39<\/li>\n\n\n\n<li><strong>\u6bcf\u9875\u4e0d\u540c\u914d\u8272<\/strong> \u2014 \u901a\u8fc7 <code>SCENE_COLORS<\/code> \u6620\u5c04\uff0c7 \u79cd\u573a\u666f\u5404\u6709\u72ec\u7279\u7684\u4e3b\u8272\u548c\u5f3a\u8c03\u8272 const SCENE_COLORS = {<br>garden: { light: 0xFFF5EE, accent: 0x228B22 }, \/\/ \u83dc\u56ed\u7eff<br>kitchen: { light: 0xFFF5EE, accent: 0xFF6347 }, \/\/ \u53a8\u623f\u7ea2<br>dessert: { light: 0xFFFACD, accent: 0xFF69B4 }, \/\/ \u751c\u70b9\u7c89<br>market: { light: 0xFFFFF0, accent: 0xFFD700 }, \/\/ \u5e02\u96c6\u91d1<br>cookbook: { light: 0xFFF8DC, accent: 0xD2691E }, \/\/ \u83dc\u8c31\u68d5<br>gathering: { light: 0xFFF5EE, accent: 0x4ECDC4 }, \/\/ \u805a\u4f1a\u9752<br>error: { light: 0xDDDDDD, accent: 0x999999 }, \/\/ \u9519\u8bef\u7070<br>}<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">3.3 \u5149\u7167\u7cfb\u7edf<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u4e00\u4e2a\u903c\u771f\u7684 3D \u573a\u666f\u79bb\u4e0d\u5f00\u7cbe\u5fc3\u5e03\u7f6e\u7684\u5149\u7167\u3002\u672c\u7cfb\u7edf\u91c7\u7528<strong>\u4e94\u70b9\u5149\u7167\u65b9\u6848<\/strong>\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ 1. \u73af\u5883\u5149 \u2014 \u63d0\u4f9b\u57fa\u7840\u7167\u660e\uff0c\u6d88\u9664\u7eaf\u9ed1\u9634\u5f71\nthis.scene3d.add(new THREE.AmbientLight(sc.light, 0.55))\n\n\/\/ 2. \u4e3b\u5149\u6e90 (Key Light) \u2014 \u5e26\u9634\u5f71\u7684\u65b9\u5411\u5149\nconst key = new THREE.DirectionalLight(0xFFF8F0, 0.9)\nkey.position.set(6, 8, 4)      \/\/ \u53f3\u4e0a\u65b9\u7167\u5c04\nkey.castShadow = true\nkey.shadow.mapSize.set(512, 512)  \/\/ \u9634\u5f71\u8d34\u56fe\u5206\u8fa8\u7387\nkey.shadow.camera.near = 0.5\nkey.shadow.camera.far = 50\nkey.shadow.camera.left = -10       \/\/ \u9634\u5f71\u76f8\u673a\u89c6\u9525\nkey.shadow.camera.right = 10\nkey.shadow.camera.top = 10\nkey.shadow.camera.bottom = -10\nkey.shadow.bias = -0.0001          \/\/ \u9632\u6b62\u9634\u5f71\u6761\u7eb9\n\n\/\/ 3. \u8f85\u52a9\u5149 (Fill Light) \u2014 \u6697\u90e8\u8865\u5149\uff0c\u7528\u573a\u666f\u5f3a\u8c03\u8272\nconst fill = new THREE.DirectionalLight(sc.accent, 0.35)\nfill.position.set(-4, 2, -4)      \/\/ \u5de6\u540e\u65b9\n\n\/\/ 4. \u8f6e\u5ed3\u5149 (Rim Light) \u2014 \u7269\u4f53\u8fb9\u7f18\u9ad8\u5149\nconst rim = new THREE.DirectionalLight(0xFFFFFF, 0.25)\nrim.position.set(0, -1, 3)        \/\/ \u4e0b\u65b9\u524d\u65b9\n\n\/\/ 5. \u534a\u7403\u5149 (Hemisphere Light) \u2014 \u5929\u7a7a-\u5730\u9762\u6e10\u53d8\u5149\nconst hemi = new THREE.HemisphereLight(0xFFFFFF, sc.accent, 0.2)<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">\u5149\u7167\u8bbe\u8ba1\u9075\u5faa<strong>\u4e09\u70b9\u5e03\u5149\u6cd5<\/strong>\uff08Three-point Lighting\uff09\u7684\u6269\u5c55\uff1a<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Key Light<\/strong> \u4f5c\u4e3a\u4e3b\u5149\uff0c\u643a\u5e26\u9634\u5f71\u8d34\u56fe\uff0c\u8d4b\u4e88\u573a\u666f\u7acb\u4f53\u611f<\/li>\n\n\n\n<li><strong>Fill Light<\/strong> \u4f7f\u7528\u573a\u666f\u5f3a\u8c03\u8272\uff0c\u6d88\u9664\u4e3b\u5149\u9020\u6210\u7684\u6697\u90e8\u8fc7\u9ed1\uff0c\u540c\u65f6\u4e3a\u6697\u9762\u6ce8\u5165\u8272\u5f69<\/li>\n\n\n\n<li><strong>Rim Light<\/strong> \u4ece\u4e0b\u65b9\u7167\u5c04\uff0c\u5206\u79bb\u7269\u4f53\u4e0e\u80cc\u666f<\/li>\n\n\n\n<li><strong>Hemisphere Light<\/strong> \u6a21\u62df\u5929\u7a7a\uff08\u767d\uff09\u5230\u5730\u9762\uff08accent\u8272\uff09\u7684\u6f2b\u53cd\u5c04\u8fc7\u6e21<\/li>\n<\/ul>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">\ud83d\udca1 <strong>\u6269\u5c55\u9605\u8bfb<\/strong>\uff1a\u4e09\u70b9\u5e03\u5149\u662f\u5f71\u89c6\u548c 3D \u6e32\u67d3\u4e2d\u6700\u7ecf\u5178\u7684\u5149\u7167\u6280\u672f\uff0c\u6e90\u81ea\u597d\u83b1\u575e\u7535\u5f71\u6444\u5f71\u3002Three.js \u901a\u8fc7\u4e0d\u540c\u7c7b\u578b\u7684 Light \u5bf9\u8c61\u8ba9\u6211\u4eec\u80fd\u5728 Web \u4e2d\u590d\u73b0\u8fd9\u4e00\u6280\u672f\u3002<\/p>\n<\/blockquote>\n\n\n\n<h3 class=\"wp-block-heading\">3.4 \u76f8\u673a\u4e0e\u4ea4\u4e92\u63a7\u5236<\/h3>\n\n\n\n<h4 class=\"wp-block-heading\">PerspectiveCamera \u2014 \u900f\u89c6\u76f8\u673a<\/h4>\n\n\n\n<pre class=\"wp-block-code\"><code>this.camera = new THREE.PerspectiveCamera(50, w \/ h, 0.1, 100)\nthis.camera.position.set(0, 1.2, 8.5)<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>FOV = 50\u00b0<\/strong> \u2014 \u7565\u5927\u4e8e\u4eba\u773c\u81ea\u7136\u89c6\u89d2\uff08\u7ea6 40-60\u00b0\uff09\uff0c\u753b\u9762\u4e0d\u8fc7\u5206\u5938\u5f20<\/li>\n\n\n\n<li><strong>Z = 8.5<\/strong> \u2014 \u76f8\u673a\u8ddd\u573a\u666f\u4e2d\u5fc3\u7ea6 8.5 \u7c73\uff0c\u7ed9\u852c\u679c\u6a21\u578b\u7559\u6709\u5145\u8db3\u7a7a\u95f4<\/li>\n<\/ul>\n\n\n\n<h4 class=\"wp-block-heading\">OrbitControls \u2014 \u8f68\u9053\u63a7\u5236\u5668<\/h4>\n\n\n\n<pre class=\"wp-block-code\"><code>import { OrbitControls } from 'three\/examples\/jsm\/controls\/OrbitControls.js'\n\nthis.controls = new OrbitControls(this.camera, this.renderer.domElement)\nthis.controls.enableDamping = true        \/\/ \u963b\u5c3c\u60ef\u6027\nthis.controls.dampingFactor = 0.12        \/\/ \u963b\u5c3c\u7cfb\u6570\nthis.controls.rotateSpeed = 1.2           \/\/ \u65cb\u8f6c\u7075\u654f\u5ea6\nthis.controls.zoomSpeed = 1.8             \/\/ \u7f29\u653e\u7075\u654f\u5ea6\nthis.controls.minDistance = 3.5           \/\/ \u6700\u8fd1\u7f29\u653e\u8ddd\u79bb\nthis.controls.maxDistance = 18            \/\/ \u6700\u8fdc\u7f29\u653e\u8ddd\u79bb\nthis.controls.maxPolarAngle = Math.PI * 0.68  \/\/ \u9650\u5236\u4fef\u4ef0\u89d2\nthis.controls.autoRotate = true           \/\/ \u81ea\u52a8\u65cb\u8f6c\nthis.controls.autoRotateSpeed = 0.25      \/\/ \u81ea\u52a8\u65cb\u8f6c\u901f\u5ea6\nthis.controls.target.set(0, -0.2, 0)      \/\/ \u8f68\u9053\u4e2d\u5fc3<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>\u53c2\u6570<\/th><th>\u4f5c\u7528<\/th><th>\u8bbe\u8ba1\u8003\u91cf<\/th><\/tr><\/thead><tbody><tr><td><code>enableDamping<\/code><\/td><td>\u9f20\u6807\u677e\u5f00\u540e\u6709\u60ef\u6027\u6ed1\u52a8<\/td><td>\u6a21\u62df\u7269\u7406\u52a8\u91cf\uff0c\u624b\u611f\u66f4\u81ea\u7136<\/td><\/tr><tr><td><code>minDistance=3.5<\/code><\/td><td>\u9650\u5236\u6700\u8fd1\u8ddd\u79bb<\/td><td>\u9632\u6b62\u76f8\u673a\u7a7f\u900f\u7269\u4f53<\/td><\/tr><tr><td><code>maxDistance=18<\/code><\/td><td>\u9650\u5236\u6700\u8fdc\u8ddd\u79bb<\/td><td>\u9632\u6b62\u573a\u666f\u6d88\u5931\u5728\u96fe\u4e2d<\/td><\/tr><tr><td><code>maxPolarAngle<\/code><\/td><td>\u9650\u5236\u4fef\u4ef0\u89d2\u4e0a\u9650<\/td><td>\u9632\u6b62\u7ffb\u5230\u5e95\u90e8\u770b\u5230\u7a7a\u767d<\/td><\/tr><tr><td><code>autoRotate<\/code><\/td><td>\u573a\u666f\u7f13\u6162\u81ea\u8f6c<\/td><td>\u65e0\u64cd\u4f5c\u65f6\u573a\u666f\u4e5f\u6709\u52a8\u6001\u611f<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h4 class=\"wp-block-heading\">\u9f20\u6807\u60ac\u505c\u4ea4\u4e92<\/h4>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ \u9f20\u6807\u8fdb\u5165\u5bb9\u5668 \u2192 \u542f\u7528\u63a7\u5236\u5668 + \u663e\u793a\u63d0\u793a\nactivateControl() {\n  this.controls.enabled = true\n  this.hintVisible = true\n}\n\n\/\/ \u9f20\u6807\u79bb\u5f00\u5bb9\u5668 \u2192 \u7981\u7528\u63a7\u5236\u5668 + \u6de1\u51fa\u63d0\u793a\ndeactivateControl() {\n  this.controls.enabled = false\n  \/\/ 800ms \u540e\u5f00\u59cb\u6de1\u51fa\uff0c\u518d 600ms \u540e\u5b8c\u5168\u9690\u85cf\n  setTimeout(() =&gt; { this.hintFading = true }, 800)\n  setTimeout(() =&gt; { this.hintVisible = false }, 1400)\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">\u8fd9\u6837\u8bbe\u8ba1\u7684\u597d\u5904\u662f\uff1a\u7528\u6237\u5c06\u9f20\u6807\u79fb\u52a8\u5230 3D \u80cc\u666f\u533a\u57df\u65f6\u81ea\u52a8\u6fc0\u6d3b\u4ea4\u4e92\uff0c\u79fb\u5f00\u65f6\u81ea\u52a8\u5173\u95ed\uff0c<strong>\u907f\u514d\u4e0e\u5176\u4ed6\u9875\u9762\u5143\u7d20\u7684\u6eda\u52a8\/\u70b9\u51fb\u4ea7\u751f\u51b2\u7a81<\/strong>\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">3.5 3D \u6a21\u578b\u6784\u5efa<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u8fd9\u662f\u6574\u4e2a 3D \u7cfb\u7edf\u6700\u7cbe\u5f69\u7684\u90e8\u5206\u3002\u6240\u6709\u852c\u83dc\u548c\u6c34\u679c\u6a21\u578b<strong>\u5b8c\u5168\u901a\u8fc7 Three.js \u539f\u751f\u51e0\u4f55\u4f53\u7ec4\u5408\u800c\u6210<\/strong>\uff0c\u4e0d\u4f9d\u8d56\u4efb\u4f55\u5916\u90e8\u6a21\u578b\u6587\u4ef6\u3002<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">\u6750\u8d28\u7f13\u5b58\u7b56\u7565<\/h4>\n\n\n\n<pre class=\"wp-block-code\"><code>const mats = {}\nfunction mat(color, opts = {}) {\n  const k = `${color}_${JSON.stringify(opts)}`\n  if (!mats&#91;k]) mats&#91;k] = new THREE.MeshStandardMaterial({\n    color,\n    roughness: 0.35,   \/\/ \u9ed8\u8ba4\u7c97\u7cd9\u5ea6\n    ...opts\n  })\n  return mats&#91;k]\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">\u901a\u8fc7 key \u5bf9\u6750\u8d28\u5bf9\u8c61\u8fdb\u884c\u7f13\u5b58\uff0c\u76f8\u540c\u7684 <code>(color, opts)<\/code> \u7ec4\u5408\u53ea\u521b\u5efa\u4e00\u6b21\u6750\u8d28\uff0c\u591a\u4e2a\u51e0\u4f55\u4f53<strong>\u5171\u4eab\u540c\u4e00\u6750\u8d28\u5b9e\u4f8b<\/strong>\uff0c\u51cf\u5c11 GPU \u5185\u5b58\u5360\u7528\u3002<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">\u57fa\u7840\u51e0\u4f55\u4f53\u5de5\u5177\u5e93<\/h4>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Three.js \u51e0\u4f55\u4f53<\/th><th>\u7528\u9014<\/th><\/tr><\/thead><tbody><tr><td><code>SphereGeometry<\/code><\/td><td>\u756a\u8304\u3001\u6a59\u5b50\u3001\u8461\u8404\u7c92\u3001\u6a31\u6843\u3001\u6843\u5b50\u3001\u8611\u83c7\u83cc\u76d6<\/td><\/tr><tr><td><code>CylinderGeometry<\/code><\/td><td>\u679c\u67c4\u3001\u830e\u5e72\u3001\u7389\u7c73\u4e3b\u4f53\u3001\u8721\u70db<\/td><\/tr><tr><td><code>ConeGeometry<\/code><\/td><td>\u80e1\u841d\u535c\u4e3b\u4f53\u3001\u82b1\u843c\u53f6\u7247<\/td><\/tr><tr><td><code>LatheGeometry<\/code><\/td><td>\u82f9\u679c\u8f6e\u5ed3\u3001\u68a8\u8f6e\u5ed3\u3001\u8349\u8393\u8f6e\u5ed3\u3001\u8304\u5b50\u8f6e\u5ed3<\/td><\/tr><tr><td><code>TorusGeometry<\/code><\/td><td>\u897f\u74dc\u6761\u7eb9\u3001\u5357\u74dc\u68f1\u7eb9\u3001\u9762\u5305\u5708<\/td><\/tr><tr><td><code>BoxGeometry<\/code><\/td><td>\u7827\u677f\u3001\u4e66\u9875\u3001\u6728\u7bb1\u3001\u5f69\u6912\u68f1<\/td><\/tr><tr><td><code>TubeGeometry<\/code><\/td><td>\u9999\u8549\u5f2f\u66f2\u4f53\u3001\u8461\u8404\u830e\u66f2\u7ebf\u3001\u6a31\u6843\u830e\u66f2\u7ebf<\/td><\/tr><tr><td><code>IcosahedronGeometry<\/code><\/td><td>\u6a59\u5b50\u4e3b\u4f53\uff08\u6bd4\u7403\u4f53\u66f4\u6709\u673a\uff09<\/td><\/tr><tr><td><code>PlaneGeometry<\/code><\/td><td>\u5730\u9762\u3001\u82b1\u74e3\u3001\u7389\u7c73\u5916\u58f3<\/td><\/tr><tr><td><code>CircleGeometry<\/code><\/td><td>\u8611\u83c7\u83cc\u6591<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h4 class=\"wp-block-heading\">\u6848\u4f8b\u4e00\uff1a\u82f9\u679c <code>createApple()<\/code><\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">\u82f9\u679c\u7684\u5efa\u6a21\u5c55\u793a\u4e86 <strong>LatheGeometry\uff08\u65cb\u8f6c\u6210\u578b\uff09<\/strong> \u7684\u7ecf\u5178\u7528\u6cd5\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>function createApple(x, y, z, variant = 'red') {\n  const pts = &#91;]\n  \/\/ \u901a\u8fc7 41 \u4e2a\u91c7\u6837\u70b9\u5b9a\u4e49\u82f9\u679c\u7684\u7eb5\u5411\u8f6e\u5ed3\u66f2\u7ebf\n  for (let i = 0; i &lt;= 40; i++) {\n    const t = (i \/ 40) * Math.PI         \/\/ \u4ece 0 \u5230 \u03c0\n    let r = Math.sin(t) * 0.28            \/\/ \u57fa\u7840\u7403\u5f62\u8f6e\u5ed3\n    if (t &lt; 0.35) r *= 0.65 + 0.35 * (t \/ 0.35)     \/\/ \u5e95\u90e8\u6536\u7a84\uff08\u82f9\u679c\u7a9d\uff09\n    if (t &gt; Math.PI - 0.25) r *= 0.78 + 0.22 * ...  \/\/ \u9876\u90e8\u6536\u7a84\uff08\u82f9\u679c\u7a9d\uff09\n    pts.push(new THREE.Vector2(r, Math.cos(t) * 0.3 + 0.02))\n  }\n  \/\/ LatheGeometry \u5c06 2D \u8f6e\u5ed3\u7ed5 Y \u8f74\u65cb\u8f6c\u6210 3D \u6a21\u578b\n  const body = new THREE.Mesh(\n    new THREE.LatheGeometry(pts, 32),  \/\/ 32 \u6bb5\u65cb\u8f6c\u7ec6\u5206\n    mat(clr, { roughness: 0.25 })      \/\/ \u4f4e\u7c97\u7cd9\u5ea6\u6a21\u62df\u5149\u6ed1\u679c\u76ae\n  )\n  \/\/ ... \u6dfb\u52a0\u679c\u67c4\uff08CylinderGeometry\uff09+ \u5c0f\u53f6\u5b50\uff08\u538b\u6241\u7684 SphereGeometry\uff09\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>LatheGeometry \u539f\u7406\u56fe\u89e3<\/strong>\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>     Y\u8f74\n     \u2191\n     \u2502   \u25cf \u2190 \u9876\u90e8\u82f9\u679c\u7a9d\uff08\u534a\u5f84\u6536\u7a84\uff09\n     \u2502  \u2571 \u2572\n     \u2502 \u25cf   \u25cf \u2190 \u6700\u5bbd\u5904\n     \u2502  \u2572 \u2571\n     \u2502   \u25cf \u2190 \u5e95\u90e8\u82f9\u679c\u7a9d\n     \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2192 X\u8f74\uff08\u534a\u5f84\uff09\n\n\u5c06\u4e0a\u65b9\u7684 2D \u8f6e\u5ed3\u66f2\u7ebf\u7ed5 Y \u8f74\u65cb\u8f6c 360\u00b0\uff0c\u751f\u6210 3D \u82f9\u679c\u6a21\u578b<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">\u8fd9\u79cd\u6280\u672f\u7684\u4f18\u52bf\u5728\u4e8e\uff1a<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u7528\u6781\u5c11\u7684\u6570\u636e\uff0841 \u4e2a\u70b9\uff09\u5b9a\u4e49\u51fa\u590d\u6742\u7684\u6709\u673a\u5f62\u72b6<\/li>\n\n\n\n<li>\u65cb\u8f6c\u7ec6\u5206\uff0832 \u6bb5\uff09\u63d0\u4f9b\u8db3\u591f\u7684\u5706\u6ed1\u5ea6<\/li>\n\n\n\n<li>\u53ef\u901a\u8fc7\u4fee\u6539\u8f6e\u5ed3\u66f2\u7ebf\u7684\u6570\u5b66\u516c\u5f0f\u4ea7\u751f\u4e0d\u540c\u54c1\u79cd<\/li>\n<\/ul>\n\n\n\n<h4 class=\"wp-block-heading\">\u6848\u4f8b\u4e8c\uff1a\u9999\u8549 <code>createBanana()<\/code><\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">\u9999\u8549\u5c55\u793a\u7684\u662f <strong>TubeGeometry\uff08\u7ba1\u72b6\u51e0\u4f55\u4f53\uff09<\/strong> \u6cbf 3D \u66f2\u7ebf\u5ef6\u4f38\u7684\u80fd\u529b\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ 1. \u5b9a\u4e49\u9999\u8549\u7684 3D \u5f2f\u66f2\u8def\u5f84\nconst curve = new THREE.CatmullRomCurve3(&#91;\n  new THREE.Vector3(0, -0.55, 0),\n  new THREE.Vector3(0.12, -0.2, 0.06),\n  new THREE.Vector3(0.18, 0.15, 0.03),\n  new THREE.Vector3(0.1, 0.42, -0.04),\n  new THREE.Vector3(0, 0.58, 0)\n])\n\/\/ 2. \u6cbf\u66f2\u7ebf\u751f\u6210\u7ba1\u9053\uff0824 \u6bb5\u7ec6\u5206\uff0c\u534a\u5f84 0.065\uff0c8 \u8fb9\u622a\u9762\uff09\nconst tubeGeo = new THREE.TubeGeometry(curve, 24, 0.065, 8, false)\n\/\/ 3. Z \u8f74\u538b\u6241 0.7 \u500d\uff0c\u6a21\u62df\u9999\u8549\u7684\u6241\u5f62\u622a\u9762\nbody.scale.set(1, 1, 0.7)<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><code>CatmullRomCurve3<\/code> \u901a\u8fc7 5 \u4e2a\u63a7\u5236\u70b9\u5b9a\u4e49\u4e86\u4e00\u6761\u5149\u6ed1\u7684 3D \u6837\u6761\u66f2\u7ebf\uff0c<code>TubeGeometry<\/code> \u5c06\u4e00\u4e2a 8 \u8fb9\u5f62\u622a\u9762\u6cbf\u6b64\u66f2\u7ebf\u6324\u51fa\uff0c\u5929\u7136\u5f62\u6210\u4e86\u9999\u8549\u7684\u6708\u7259\u5f62\u5f2f\u66f2\u3002<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">\u6848\u4f8b\u4e09\uff1a\u8461\u8404\u4e32 <code>createGrapes()<\/code><\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">\u8461\u8404\u4e32\u5c55\u793a\u4e86<strong>\u7ec4\u5408\u6a21\u5f0f + \u7b97\u6cd5\u5316\u6392\u5217<\/strong>\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>function createGrapes(x, y, z, color = 0x7B2D8B) {\n  const rows = &#91;1, 3, 4, 5, 5, 4, 3]  \/\/ \u6bcf\u884c\u9897\u7c92\u6570\uff08\u5012\u4e09\u89d2\u6392\u5217\uff09\n  const grapeRadius = 0.055\n\n  for (let row = 0; row &lt; rows.length; row++) {\n    const n = rows&#91;row]\n    for (let i = 0; i &lt; n; i++) {\n      const offsetX = (i - (n - 1) \/ 2) * grapeRadius * 2.2  \/\/ \u6c34\u5e73\u5747\u5300\u5206\u5e03\n      const offsetZ = (Math.random() - 0.5) * 0.1              \/\/ \u524d\u540e\u968f\u673a\u5fae\u79fb\n      const grape = new THREE.Mesh(\n        new THREE.SphereGeometry(grapeRadius, 8, 8),\n        mat(color, { roughness: 0.15 })  \/\/ \u4f4e\u7c97\u7cd9\u5ea6 = \u5149\u6ed1\u679c\u76ae\n      )\n      grape.position.set(offsetX, row * grapeRadius * 1.8, offsetZ)\n      g.add(grape)\n    }\n  }\n  \/\/ \u518d\u7528 TubeGeometry \u6dfb\u52a0\u5f2f\u66f2\u7684\u830e\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">\u901a\u8fc7 <code>rows<\/code> \u6570\u7ec4\u63a7\u5236\u6bcf\u884c\u9897\u7c92\u6570\u91cf\uff0c\u5f62\u6210\u81ea\u7136\u7684\u5012\u4e09\u89d2\uff08\u4e0a\u5bbd\u4e0b\u7a84\uff09\u8461\u8404\u4e32\u5f62\u3002<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">\u90e8\u5206\u6a21\u578b\u5de5\u5382\u4e00\u89c8<\/h4>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>\u51fd\u6570<\/th><th>\u6838\u5fc3\u51e0\u4f55\u4f53<\/th><th>\u7279\u6b8a\u6280\u5de7<\/th><\/tr><\/thead><tbody><tr><td><code>createApple<\/code><\/td><td>LatheGeometry<\/td><td>\u4e0a\u4e0b\u6536\u7a84\u6a21\u62df\u82f9\u679c\u7a9d<\/td><\/tr><tr><td><code>createOrange<\/code><\/td><td>IcosahedronGeometry<\/td><td>\u4e8c\u5341\u9762\u4f53\u6bd4\u7403\u4f53\u66f4&#8221;\u6709\u673a&#8221;<\/td><\/tr><tr><td><code>createBanana<\/code><\/td><td>TubeGeometry<\/td><td>CatmullRom \u66f2\u7ebf + Z\u8f74\u538b\u6241<\/td><\/tr><tr><td><code>createPear<\/code><\/td><td>LatheGeometry<\/td><td>\u5206\u6bb5\u534a\u5f84\u516c\u5f0f\uff08\u7a84\u2192\u5bbd\u2192\u7a84\uff09<\/td><\/tr><tr><td><code>createWatermelon<\/code><\/td><td>SphereGeometry<\/td><td>Torus \u8d34\u7403\u9762\u505a\u6761\u7eb9<\/td><\/tr><tr><td><code>createGrapes<\/code><\/td><td>SphereGeometry \u00d7 N<\/td><td>\u5012\u4e09\u89d2\u884c\u5217\u6392\u5217<\/td><\/tr><tr><td><code>createStrawberry<\/code><\/td><td>LatheGeometry<\/td><td>\u5206\u6bb5\u534a\u5f84 + \u79cd\u5b50\u70b9\u6492\u70b9<\/td><\/tr><tr><td><code>createLemon<\/code><\/td><td>SphereGeometry<\/td><td>Y\u8f74\u62c9\u4f38 + \u4e24\u7aef\u51f8\u8d77<\/td><\/tr><tr><td><code>createCarrot<\/code><\/td><td>ConeGeometry<\/td><td>Torus \u73af\u7eb9 + \u9876\u90e8\u53f6\u5b50\u7c07<\/td><\/tr><tr><td><code>createTomato<\/code><\/td><td>SphereGeometry<\/td><td>Y\u8f74\u538b\u6241 + \u661f\u5f62\u82b1\u843c<\/td><\/tr><tr><td><code>createBellPepper<\/code><\/td><td>SphereGeometry \u00d7 3<\/td><td>\u4e09\u74e3\u4ea4\u53e0 + \u830e\u8482<\/td><\/tr><tr><td><code>createBroccoli<\/code><\/td><td>SphereGeometry \u00d7 50<\/td><td>\u534a\u7403\u968f\u673a\u6492\u70b9\u6a21\u62df\u82b1\u7403<\/td><\/tr><tr><td><code>createPumpkin<\/code><\/td><td>SphereGeometry<\/td><td>Y\u8f74\u5f3a\u70c8\u538b\u6241 + Box \u68f1\u7eb9<\/td><\/tr><tr><td><code>createEggplant<\/code><\/td><td>LatheGeometry<\/td><td>\u4e0b\u534a\u90e8\u5267\u70c8\u81a8\u80c0\u7684\u8f6e\u5ed3<\/td><\/tr><tr><td><code>createMushroom<\/code><\/td><td>CylinderGeometry + SphereGeometry<\/td><td>\u4e0a\u534a\u7403\u622a\u65ad\u505a\u83cc\u76d6<\/td><\/tr><tr><td><code>createCorn<\/code><\/td><td>CylinderGeometry<\/td><td>\u884c\u5217\u5c0f\u7403\u6a21\u62df\u7389\u7c73\u7c92<\/td><\/tr><tr><td><code>createCherries<\/code><\/td><td>SphereGeometry \u00d7 2<\/td><td>\u53cc\u7403 + \u53cc\u66f2\u7ebf\u830e<\/td><\/tr><tr><td><code>createPeach<\/code><\/td><td>SphereGeometry<\/td><td>Y\u8f74\u62c9\u4f38 + Z\u8f74\u538b\u6241 + \u6c9f\u7f1d + \u7ea2\u6655\u53e0\u5c42<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">3.6 \u52a8\u753b\u7cfb\u7edf<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u52a8\u753b\u5faa\u73af\u4f7f\u7528\u7ecf\u5178\u7684 <code>requestAnimationFrame<\/code> + <code>THREE.Clock<\/code> \u65b9\u6848\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>animate() {\n  this.animationId = requestAnimationFrame(this.animate)  \/\/ \u9012\u5f52\u8c03\u7528\n  const t = this.clock.getElapsedTime()  \/\/ \u81ea\u573a\u666f\u521b\u5efa\u4ee5\u6765\u7684\u79d2\u6570\n\n  this.objects.forEach(o =&gt; {\n    \/\/ 1. \u81ea\u8f6c\u52a8\u753b\n    if (o.rs) o.m.rotation.y += o.rs    \/\/ \u7269\u4f53\u7ed5 Y \u8f74\u7f13\u6162\u65cb\u8f6c\n\n    \/\/ 2. \u6d6e\u52a8\u52a8\u753b\uff08\u975e\u7c92\u5b50\u7269\u4f53\uff09\n    if (o.fs &amp;&amp; !o.isPts) {\n      const amp = o.m.userData?.floatAmp || o.fa   \/\/ \u6d6e\u52a8\u5e45\u5ea6\n      const spd = o.m.userData?.floatSpd || o.fs   \/\/ \u6d6e\u52a8\u901f\u5ea6\n      o.m.position.y = o.by + Math.sin(t * spd) * amp  \/\/ sin \u6ce2\u4e0a\u4e0b\u6d6e\u52a8\n    }\n\n    \/\/ 3. \u7c92\u5b50\u65cb\u8f6c\n    if (o.isPts) o.m.rotation.y += 0.0008\n  })\n\n  this.controls.update()                          \/\/ \u66f4\u65b0\u963b\u5c3c\n  this.renderer.render(this.scene3d, this.camera) \/\/ \u6e32\u67d3\u4e00\u5e27\n}<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">\u52a8\u753b\u5bf9\u8c61\u6570\u636e\u7ed3\u6784<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">\u6bcf\u4e2a\u52a0\u5165\u573a\u666f\u7684\u7269\u4f53\u90fd\u643a\u5e26\u4e00\u4efd\u52a8\u753b\u5143\u6570\u636e\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>function addObj(ctx, mesh, opts = {}) {\n  ctx.objects.push({\n    m: mesh,      \/\/ Three.js \u5bf9\u8c61\u5f15\u7528\n    rs: 0.003,    \/\/ rotation speed \u2014 \u81ea\u8f6c\u901f\u5ea6\n    fs: 0.5,      \/\/ float speed \u2014 \u6d6e\u52a8\u901f\u5ea6\n    fa: 0.06,     \/\/ float amplitude \u2014 \u6d6e\u52a8\u5e45\u5ea6\n    by: y,        \/\/ base Y \u2014 \u6d6e\u52a8\u57fa\u51c6 Y \u5750\u6807\n    isPts: false  \/\/ \u662f\u5426\u4e3a\u7c92\u5b50\u7cfb\u7edf\n  })\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">\u6bcf\u4e2a\u88ab\u521b\u5efa\u7684\u852c\u679c\u6a21\u578b\u4e5f\u5185\u5d4c\u4e86 <code>userData<\/code> \u7528\u4e8e\u4e2a\u6027\u5316\u7684\u52a8\u753b\u53c2\u6570\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>g.userData = {\n  floatAmp: 0.04 + Math.random() * 0.06,  \/\/ 0.04~0.10 \u968f\u673a\u6d6e\u52a8\u5e45\u5ea6\n  floatSpd: 0.5 + Math.random() * 0.5      \/\/ 0.5~1.0  \u968f\u673a\u6d6e\u52a8\u901f\u5ea6\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\u968f\u673a\u5316\u53c2\u6570\u7684\u610f\u4e49<\/strong>\uff1a\u5982\u679c\u6240\u6709\u7269\u4f53\u4ee5\u76f8\u540c\u7684\u901f\u5ea6\/\u5e45\u5ea6\u6d6e\u52a8\uff0c\u770b\u8d77\u6765\u50cf\u673a\u68b0\u7684\u519b\u961f\u3002\u901a\u8fc7\u4e3a\u6bcf\u4e2a\u7269\u4f53\u8d4b\u4e88\u7565\u5fae\u4e0d\u540c\u7684 <code>floatAmp<\/code> \u548c <code>floatSpd<\/code>\uff0c\u4ea7\u751f\u81ea\u7136\u7684\u3001<strong>\u53bb\u540c\u6b65\u5316<\/strong>\u7684\u6d6e\u52a8\u6548\u679c\uff0c\u6a21\u62df\u6c34\u4e2d\u6216\u7a7a\u6c14\u4e2d\u7684\u6f02\u6d6e\u611f\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">3.7 \u7c92\u5b50\u7279\u6548<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u6bcf\u4e2a\u573a\u666f\u90fd\u5e03\u6709\u6f02\u6d6e\u7c92\u5b50\uff0c\u589e\u52a0\u6c1b\u56f4\u611f\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>addParticles(color, count, sx, sy, sz) {\n  const pos = &#91;]\n  \/\/ \u5728\u6307\u5b9a\u7a7a\u95f4\u8303\u56f4\u5185\u968f\u673a\u5206\u5e03\n  for (let i = 0; i &lt; count; i++) {\n    pos.push(\n      (Math.random() - 0.5) * sx,     \/\/ X: \u00b1sx\/2\n      Math.random() * sy - sy \/ 2,     \/\/ Y: \u00b1sy\/2\n      (Math.random() - 0.5) * sz      \/\/ Z: \u00b1sz\/2\n    )\n  }\n  const geo = new THREE.BufferGeometry()\n  geo.setAttribute('position', new THREE.Float32BufferAttribute(pos, 3))\n\n  const mat = new THREE.PointsMaterial({\n    color,\n    size: 0.04,\n    transparent: true,\n    opacity: 0.55,\n    blending: THREE.AdditiveBlending,  \/\/ \u53e0\u52a0\u6df7\u5408 \u2014 \u4eae\u8272\u53e0\u52a0\u66f4\u4eae\n    depthWrite: false                  \/\/ \u4e0d\u5199\u6df1\u5ea6\u7f13\u51b2\uff0c\u907f\u514d\u906e\u6321\u95ee\u9898\n  })\n\n  const pts = new THREE.Points(geo, mat)\n  this.scene3d.add(pts)\n  this.objects.push({ m: pts, rs: 0.0015, fs: 0.7, fa: 0.02, by: 0, isPts: true })\n}<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>\u53c2\u6570<\/th><th>\u4f5c\u7528<\/th><\/tr><\/thead><tbody><tr><td><code>AdditiveBlending<\/code><\/td><td>\u53e0\u52a0\u6df7\u5408\u6a21\u5f0f\u2014\u2014\u7c92\u5b50\u91cd\u53e0\u5904\u53d8\u5f97\u66f4\u4eae\uff0c\u6a21\u62df\u5149\u70b9\/\u8424\u706b\u866b\u6548\u679c<\/td><\/tr><tr><td><code>depthWrite: false<\/code><\/td><td>\u4e0d\u5199\u5165\u6df1\u5ea6\u7f13\u51b2\uff0c\u7c92\u5b50\u4e4b\u95f4\u4e0d\u4f1a\u4e92\u76f8\u906e\u6321<\/td><\/tr><tr><td><code>transparent + opacity: 0.55<\/code><\/td><td>\u534a\u900f\u660e\uff0c\u4e0d\u55a7\u5bbe\u593a\u4e3b<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">3.8 \u8d44\u6e90\u7ba1\u7406\u4e0e\u6027\u80fd\u4f18\u5316<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">3D \u6e32\u67d3\u662f GPU \u5bc6\u96c6\u578b\u64cd\u4f5c\u3002\u5f53 Vue \u7ec4\u4ef6\u88ab\u9500\u6bc1\uff08\u8def\u7531\u5207\u6362\uff09\u65f6\uff0c\u5982\u679c\u4e0d\u624b\u52a8\u91ca\u653e WebGL \u8d44\u6e90\uff0c\u4f1a\u5bfc\u81f4<strong>\u663e\u5b58\u6cc4\u6f0f<\/strong>\u2014\u2014\u6bcf\u6b21\u8fdb\u5165\u9875\u9762\u90fd\u65b0\u589e GPU \u5185\u5b58\u5360\u7528\u800c\u4e0d\u91ca\u653e\uff0c\u6700\u7ec8\u5bfc\u81f4\u9875\u9762\u5361\u987f\u751a\u81f3\u5d29\u6e83\u3002<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>beforeDestroy() {\n  window.removeEventListener('resize', this.onResize)\n  if (this.controls) this.controls.dispose()  \/\/ \u9500\u6bc1\u63a7\u5236\u5668\n\n  cancelAnimationFrame(this.animationId)  \/\/ \u505c\u6b62\u52a8\u753b\u5faa\u73af\n\n  \/\/ \u9010\u4e2a\u6e05\u7406\u6240\u6709\u573a\u666f\u7269\u4f53\n  this.objects.forEach(obj =&gt; {\n    const mesh = obj.m || obj.mesh\n    if (mesh) {\n      mesh.traverse((node) =&gt; {\n        \/\/ \u6e05\u7406\u51e0\u4f55\u4f53\uff08\u91ca\u653e GPU \u9876\u70b9\u7f13\u51b2\uff09\n        if (node.geometry) node.geometry.dispose()\n        \/\/ \u6e05\u7406\u6750\u8d28\uff08\u91ca\u653e GPU \u7eb9\u7406\/\u7740\u8272\u5668\uff09\n        if (node.material) {\n          const mats = Array.isArray(node.material) ? node.material : &#91;node.material]\n          mats.forEach(m =&gt; {\n            Object.keys(m).forEach(key =&gt; {\n              const val = m&#91;key]\n              if (val &amp;&amp; val.isTexture) val.dispose() \/\/ \u6e05\u7406\u7eb9\u7406\n            })\n            m.dispose()\n          })\n        }\n      })\n    }\n  })\n\n  \/\/ \u6e05\u7406\u6750\u8d28\u7f13\u5b58\n  Object.values(mats).forEach(m =&gt; m.dispose())\n\n  \/\/ \u9500\u6bc1\u6e32\u67d3\u5668\n  if (this.renderer) {\n    this.renderer.dispose()\n    this.renderer.forceContextLoss()  \/\/ \u5f3a\u5236\u4e22\u5931 WebGL \u4e0a\u4e0b\u6587\n  }\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">\u5b8c\u6574\u7684\u8d44\u6e90\u6e05\u7406\u94fe\u8def\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\u7ec4\u4ef6\u9500\u6bc1\n  \u251c\u2500\u2500 OrbitControls.dispose()       \u2014 \u79fb\u9664\u4e8b\u4ef6\u76d1\u542c\n  \u251c\u2500\u2500 cancelAnimationFrame()        \u2014 \u505c\u6b62\u6e32\u67d3\u5faa\u73af\n  \u251c\u2500\u2500 geometry.dispose() \u00d7 N        \u2014 \u91ca\u653e GPU \u9876\u70b9\/\u7d22\u5f15\u7f13\u51b2\n  \u251c\u2500\u2500 material.dispose() \u00d7 M        \u2014 \u91ca\u653e GPU \u7740\u8272\u5668\u7a0b\u5e8f\n  \u251c\u2500\u2500 texture.dispose() \u00d7 K         \u2014 \u91ca\u653e GPU \u7eb9\u7406\u5185\u5b58\n  \u2514\u2500\u2500 renderer.dispose()\n      \u2514\u2500\u2500 .forceContextLoss()       \u2014 \u91ca\u653e\u6574\u4e2a WebGL \u4e0a\u4e0b\u6587<\/code><\/pre>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">\u26a0\ufe0f \u5982\u679c\u7f3a\u5c11\u8fd9\u4e9b\u6e05\u7406\u4ee3\u7801\uff0c\u5728 SPA \u4e2d\u9891\u7e41\u5207\u6362\u9875\u9762\uff08\u8fdb\u5165\u2192\u79bb\u5f00\u2192\u8fdb\u5165\uff09\u5c06\u5bfc\u81f4\u663e\u5b58\u6301\u7eed\u589e\u957f\u3002\u8fd9\u662f Three.js \u5728\u5355\u9875\u5e94\u7528\u4e2d<strong>\u6700\u5e38\u89c1\u7684\u6027\u80fd\u9677\u9631<\/strong>\u3002<\/p>\n<\/blockquote>\n\n\n\n<h3 class=\"wp-block-heading\">3.9 GLB \u6a21\u578b\u52a0\u8f7d<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u9664\u4e86\u7a0b\u5e8f\u5316\u5efa\u6a21\u5916\uff0c\u7cfb\u7edf\u8fd8\u652f\u6301\u52a0\u8f7d GLB \u683c\u5f0f\u7684\u5916\u90e8 3D \u6a21\u578b\uff08Plant \u573a\u666f\uff09\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import { GLTFLoader } from 'three\/examples\/jsm\/loaders\/GLTFLoader.js'\nimport { DRACOLoader } from 'three\/examples\/jsm\/loaders\/DRACOLoader.js'\n\n\/\/ 1. \u914d\u7f6e DRACO \u89e3\u538b\u5668\uff08\u5927\u5e45\u51cf\u5c0f\u6a21\u578b\u6587\u4ef6\u4f53\u79ef\uff09\nconst dracoLoader = new DRACOLoader()\ndracoLoader.setDecoderPath('https:\/\/www.gstatic.com\/draco\/versioned\/decoders\/1.5.7\/')\ndracoLoader.setDecoderConfig({ type: 'js' })\n\n\/\/ 2. \u52a0\u8f7d GLB \u6a21\u578b\nconst loader = new GLTFLoader()\nloader.setDRACOLoader(dracoLoader)\n\nloader.load(\n  '\/models\/strelitzia.glb',\n  (gltf) =&gt; {\n    \/\/ 3. \u81ea\u52a8\u7f29\u653e\u9002\u914d\u573a\u666f\n    const box = new THREE.Box3().setFromObject(gltf.scene)\n    const size = box.getSize(new THREE.Vector3())\n    const maxDim = Math.max(size.x, size.y, size.z)\n    gltf.scene.scale.setScalar(3.5 \/ maxDim)  \/\/ \u7b49\u6bd4\u7f29\u653e\u5230 3.5 \u5355\u4f4d\n    \/\/ 4. \u5c45\u4e2d\u5b9a\u4f4d\n    gltf.scene.position.set(...)\n    this.scene3d.add(gltf.scene)\n  },\n  (progress) =&gt; { \/* \u52a0\u8f7d\u8fdb\u5ea6\u56de\u8c03 *\/ },\n  (error) =&gt; {\n    \/\/ 5. \u52a0\u8f7d\u5931\u8d25 \u2192 \u964d\u7ea7\u5230\u7a0b\u5e8f\u5316\u5efa\u6a21\n    this.buildFallbackPlant(sc)\n  }\n)<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>\u6280\u672f<\/th><th>\u8bf4\u660e<\/th><\/tr><\/thead><tbody><tr><td><strong>GLB \u683c\u5f0f<\/strong><\/td><td>glTF 2.0 \u4e8c\u8fdb\u5236\u683c\u5f0f\uff0cThree.js \u539f\u751f\u652f\u6301\uff0c\u5305\u542b\u51e0\u4f55\u4f53+\u6750\u8d28+\u52a8\u753b<\/td><\/tr><tr><td><strong>DRACO \u538b\u7f29<\/strong><\/td><td>Google \u5f00\u53d1\u7684 3D \u7f51\u683c\u538b\u7f29\u7b97\u6cd5\uff0c\u53ef\u5c06\u6a21\u578b\u6587\u4ef6\u7f29\u5c0f 10-25 \u500d<\/td><\/tr><tr><td><strong>Box3 \u81ea\u52a8\u7f29\u653e<\/strong><\/td><td>\u8bfb\u53d6\u6a21\u578b\u7684\u5305\u56f4\u76d2\uff0c\u81ea\u52a8\u7f29\u653e\u5230\u9002\u914d\u573a\u666f\u7684\u5c3a\u5bf8<\/td><\/tr><tr><td><strong>\u964d\u7ea7\u7b56\u7565<\/strong><\/td><td>\u6a21\u578b\u52a0\u8f7d\u5931\u8d25\u65f6\u81ea\u52a8\u56de\u9000\u5230\u7a0b\u5e8f\u5316\u5efa\u6a21\uff0c\u786e\u4fdd\u573a\u666f\u59cb\u7ec8\u53ef\u7528<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">3.10 Vue \u7ec4\u4ef6\u96c6\u6210<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u5728\u9875\u9762\u4e2d\u4f7f\u7528 3D \u80cc\u666f\u975e\u5e38\u7b80\u5355\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;template&gt;\n  &lt;div class=\"home-page\"&gt;\n    &lt;!-- 3D \u80cc\u666f\u5c42 --&gt;\n    &lt;ThreeBackground scene=\"garden\" color=\"#FF6B35\" \/&gt;\n\n    &lt;!-- \u9875\u9762\u5185\u5bb9\u5c42\uff08\u66f4\u9ad8\u7684 z-index\uff09 --&gt;\n    &lt;div class=\"page-content\"&gt;\n      &lt;h1&gt;\u5c0f\u53a8\u6696\u5149&lt;\/h1&gt;\n      &lt;!-- ... \u8f6e\u64ad\u56fe\u3001\u83dc\u8c31\u5361\u7247\u7b49 ... --&gt;\n    &lt;\/div&gt;\n  &lt;\/div&gt;\n&lt;\/template&gt;\n\n&lt;script&gt;\nimport ThreeBackground from '@\/components\/ThreeBackground'\n\nexport default {\n  components: { ThreeBackground }\n}\n&lt;\/script&gt;\n\n&lt;style scoped&gt;\n.home-page {\n  position: relative;\n  min-height: 100vh;\n}\n.page-content {\n  position: relative;\n  z-index: 1;  \/* \u5185\u5bb9\u5728 3D \u80cc\u666f\u4e4b\u4e0a *\/\n}\n&lt;\/style&gt;<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><code>ThreeBackground<\/code> \u7ec4\u4ef6\u6e32\u67d3\u4e00\u4e2a <code>position: absolute<\/code> \u7684\u5168\u5c4f canvas\uff0c<code>z-index: 0<\/code> \u786e\u4fdd\u5b83\u4f4d\u4e8e\u5185\u5bb9\u5c42\u4e4b\u4e0b\u3002\u9875\u9762\u5185\u5bb9\u53ea\u9700\u8981\u8bbe\u7f6e <code>z-index: 1<\/code> \u5373\u53ef\u8986\u76d6\u5176\u4e0a\u3002<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">7 \u79cd\u573a\u666f-\u8def\u7531\u5bf9\u5e94\u5173\u7cfb<\/h4>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>\u573a\u666f\u6807\u8bc6<\/th><th>\u9875\u9762<\/th><th>3D \u5185\u5bb9<\/th><\/tr><\/thead><tbody><tr><td><code>garden<\/code><\/td><td>\u9996\u9875 <code>\/<\/code><\/td><td>\u80e1\u841d\u535c\u3001\u756a\u8304\u3001\u5357\u74dc\u3001\u897f\u5170\u82b1\u3001\u7389\u7c73 + \u83dc\u56ed\u5730\u9762<\/td><\/tr><tr><td><code>kitchen<\/code><\/td><td>\u767b\u5f55 <code>\/login<\/code>\u3001\u6ce8\u518c <code>\/register<\/code><\/td><td>\u7827\u677f\u4e0a\u7684\u82f9\u679c\u3001\u6a59\u5b50\u3001\u67e0\u6aac\u3001\u756a\u8304\u3001\u8304\u5b50\u3001\u8611\u83c7\u3001\u8461\u8404<\/td><\/tr><tr><td><code>dessert<\/code><\/td><td>\u5c0f\u5403\u4e13\u533a <code>\/snacks<\/code><\/td><td>\u5927\u7bee\u5b50\u76db\u6ee1\u6c34\u679c\uff0c\u6563\u843d\u7684\u82f9\u679c\u3001\u6a59\u5b50\u3001\u8461\u8404<\/td><\/tr><tr><td><code>market<\/code><\/td><td>\u79d2\u6740 <code>\/seckill<\/code><\/td><td>\u4e09\u4e2a\u6728\u7bb1\u644a\u4f4d + \u897f\u74dc\u3001\u5357\u74dc\u3001\u7389\u7c73\u3001\u756a\u8304<\/td><\/tr><tr><td><code>cookbook<\/code><\/td><td>\u505a\u6cd5\u5927\u5168 <code>\/encyclopedia<\/code><\/td><td>\u6253\u5f00\u7684\u4e66\u672c + \u73af\u7ed5\u6c34\u679c + \u8721\u70db<\/td><\/tr><tr><td><code>gathering<\/code><\/td><td>\u7f8e\u98df\u5708 <code>\/community\/feed<\/code><\/td><td>\u5706\u5f62\u5730\u53f0 + \u73af\u5f62\u6392\u5217\u852c\u679c + \u4e2d\u5fc3\u8461\u8404<\/td><\/tr><tr><td><code>plant<\/code><\/td><td>\u76c6\u683d\u5c55\u793a<\/td><td>GLB \u6a21\u578b\u52a0\u8f7d\uff08\u542b DRACO \u89e3\u538b + \u964d\u7ea7\u65b9\u6848\uff09<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">4. \u603b\u7ed3<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\u5c0f\u53a8\u6696\u5149<\/strong> \u5c55\u793a\u4e86\u5982\u4f55\u5c06 Three.js \u6df1\u5ea6\u96c6\u6210\u5230 Vue \u5355\u9875\u5e94\u7528\u4e2d\uff1a<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">\u6280\u672f\u4eae\u70b9\u56de\u987e<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u96f6\u5916\u90e8\u6a21\u578b\u4f9d\u8d56<\/strong> \u2014 16 \u79cd\u852c\u679c\u6a21\u578b\u5168\u90e8\u901a\u8fc7 Three.js \u539f\u751f\u51e0\u4f55\u4f53\u7a0b\u5e8f\u5316\u751f\u6210\uff0c\u4f7f\u7528 LatheGeometry \u65cb\u8f6c\u6210\u578b\u3001TubeGeometry \u66f2\u7ebf\u6324\u51fa\u3001SphereGeometry \u7ec4\u5408\u7b49\u7eaf\u6570\u5b66\u5efa\u6a21\u6280\u672f<\/li>\n\n\n\n<li><strong>\u4e94\u70b9\u5149\u7167\u7cfb\u7edf<\/strong> \u2014 \u73af\u5883\u5149 + \u4e3b\u5149\uff08\u5e26\u9634\u5f71\uff09+ \u8f85\u52a9\u5149 + \u8f6e\u5ed3\u5149 + \u534a\u7403\u5149\uff0c\u590d\u523b\u5f71\u89c6\u7ea7\u4e09\u70b9\u5e03\u5149\u6cd5<\/li>\n\n\n\n<li><strong>OrbitControls \u667a\u80fd\u4ea4\u4e92<\/strong> \u2014 \u9f20\u6807\u60ac\u505c\u6fc0\u6d3b\/\u79bb\u5f00\u505c\u7528\uff0c\u907f\u514d\u4e0e\u9875\u9762\u6eda\u52a8\u51b2\u7a81\uff1b\u963b\u5c3c\u60ef\u6027\u5e26\u6765\u81ea\u7136\u624b\u611f<\/li>\n\n\n\n<li><strong>\u53bb\u540c\u6b65\u6d6e\u52a8\u52a8\u753b<\/strong> \u2014 \u6bcf\u4e2a\u7269\u4f53\u6709\u72ec\u7acb\u7684\u968f\u673a\u6d6e\u52a8\u53c2\u6570\uff0c<code>Math.sin(t * spd) * amp<\/code> \u4ea7\u751f\u81ea\u7136\u7684\u4e0d\u89c4\u5219\u6f02\u6d6e\u611f<\/li>\n\n\n\n<li><strong>GPU \u8d44\u6e90\u5168\u751f\u547d\u5468\u671f\u7ba1\u7406<\/strong> \u2014 \u7ec4\u4ef6\u9500\u6bc1\u65f6\u5b8c\u6574\u91ca\u653e geometry\/material\/texture\/renderer\uff0c\u675c\u7edd SPA \u663e\u5b58\u6cc4\u6f0f<\/li>\n\n\n\n<li><strong>GLB + DRACO \u652f\u6301<\/strong> \u2014 \u5916\u90e8\u6a21\u578b\u52a0\u8f7d + Google Draco \u538b\u7f29 + \u52a0\u8f7d\u5931\u8d25\u964d\u7ea7\u65b9\u6848<\/li>\n\n\n\n<li><strong>\u6750\u8d28\u7f13\u5b58\u7b56\u7565<\/strong> \u2014 \u76f8\u540c\u989c\u8272\u548c\u914d\u7f6e\u7684\u6750\u8d28\u53ea\u521b\u5efa\u4e00\u6b21\uff0c\u591a\u4e2a\u51e0\u4f55\u4f53\u5171\u4eab<\/li>\n\n\n\n<li><strong>AdditiveBlending \u7c92\u5b50<\/strong> \u2014 \u53e0\u52a0\u6df7\u5408\u6a21\u5f0f\u4ea7\u751f\u5149\u70b9\/\u8424\u706b\u866b\u822c\u7684\u534a\u900f\u660e\u7c92\u5b50\u6c1b\u56f4<\/li>\n\n\n\n<li><strong>Fog \u96fe\u6548 + \u573a\u666f\u914d\u8272<\/strong> \u2014 \u6bcf\u9875\u72ec\u7acb\u7684\u80cc\u666f\u8272\u3001\u5f3a\u8c03\u8272\u548c\u96fe\u6548\uff0c\u8425\u9020\u5404\u5177\u7279\u8272\u7684\u89c6\u89c9\u6c1b\u56f4<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">\u6280\u672f\u6808\u603b\u7ed3<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>Vue 2.6 (\u7ec4\u4ef6\u5316) + Three.js 0.184 (WebGL \u6e32\u67d3)\n    \u2195 RESTful API (Axios + JWT)\nSpring Boot 2.5 (\u4e1a\u52a1\u903b\u8f91) + MyBatis Plus 3.4 (\u6570\u636e\u8bbf\u95ee)\n    \u2195\nMySQL 8.0 (\u6301\u4e45\u5316) + \u672c\u5730\u6587\u4ef6\u5b58\u50a8 (upload\/)\n    \u2195\nDocker Compose (\u4e00\u952e\u90e8\u7f72)<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p class=\"wp-block-paragraph\"><sub>Made with \u2764\ufe0f by Yummly Team | \u5c0f\u53a8\u6696\u5149\uff0c\u8ba9\u7f8e\u98df\u6e29\u6696\u751f\u6d3b<\/sub><\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u96c6\u83dc\u8c31\u5206\u4eab\u3001\u793e\u4ea4\u4e92\u52a8\u3001\u7535\u5546\u4ea4\u6613\u3001\u4f1a\u5458\u4f53\u7cfb\u3001AI \u667a\u80fd\u5ba2\u670d\u3001Three.js 3D \u89c6\u89c9\u4f53\u9a8c\u4e8e\u4e00\u4f53\u7684\u5168\u6808\u7f8e\u98df\u793e\u533a [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-2232","post","type-post","status-publish","format-standard","hentry","category-learn"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v26.0 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>\ud83c\udf73 \u5c0f\u53a8\u6696\u5149 \u2014 \u9879\u76ee\u4ecb\u7ecd\u4e0e\u6280\u672f\u8be6\u89e3 - Hueil\u306e\u5c0f\u7a9d<\/title>\n<meta name=\"description\" content=\"\u8ba1\u7b97\u673a\u5e94\u7528\uff0cPython\uff0cJava\uff0c\u4fe1\u606f\u5b89\u5168\uff0c\u7f51\u7ad9\u7f8e\u5316,\u7269\u8054\u7f51\uff0cC\/C++\u7b97\u6cd5\u5206\u6790,\u7f51\u7ad9\u5b9e\u6218\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/hueil.top\/?p=2232\" \/>\n<meta property=\"og:locale\" content=\"zh_CN\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"\ud83c\udf73 \u5c0f\u53a8\u6696\u5149 \u2014 \u9879\u76ee\u4ecb\u7ecd\u4e0e\u6280\u672f\u8be6\u89e3 - Hueil\u306e\u5c0f\u7a9d\" \/>\n<meta property=\"og:description\" content=\"\u8ba1\u7b97\u673a\u5e94\u7528\uff0cPython\uff0cJava\uff0c\u4fe1\u606f\u5b89\u5168\uff0c\u7f51\u7ad9\u7f8e\u5316,\u7269\u8054\u7f51\uff0cC\/C++\u7b97\u6cd5\u5206\u6790,\u7f51\u7ad9\u5b9e\u6218\" \/>\n<meta property=\"og:url\" content=\"https:\/\/hueil.top\/?p=2232\" \/>\n<meta property=\"og:site_name\" content=\"Hueil\u306e\u5c0f\u7a9d\" \/>\n<meta property=\"article:published_time\" content=\"2026-06-11T07:41:09+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-06-11T07:45:30+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/hueil.top\/wp-content\/uploads\/2026\/06\/image.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1920\" \/>\n\t<meta property=\"og:image:height\" content=\"1000\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"\u5c0f\u5f20\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"\u4f5c\u8005\" \/>\n\t<meta name=\"twitter:data1\" content=\"\u5c0f\u5f20\" \/>\n\t<meta name=\"twitter:label2\" content=\"\u9884\u8ba1\u9605\u8bfb\u65f6\u95f4\" \/>\n\t<meta name=\"twitter:data2\" content=\"5 \u5206\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/hueil.top\/?p=2232#article\",\"isPartOf\":{\"@id\":\"https:\/\/hueil.top\/?p=2232\"},\"author\":{\"name\":\"\u5c0f\u5f20\",\"@id\":\"https:\/\/hueil.top\/#\/schema\/person\/b3ed2232564570b957865ad1cefe7b88\"},\"headline\":\"\ud83c\udf73 \u5c0f\u53a8\u6696\u5149 \u2014 \u9879\u76ee\u4ecb\u7ecd\u4e0e\u6280\u672f\u8be6\u89e3\",\"datePublished\":\"2026-06-11T07:41:09+00:00\",\"dateModified\":\"2026-06-11T07:45:30+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/hueil.top\/?p=2232\"},\"wordCount\":287,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/hueil.top\/#\/schema\/person\/b3ed2232564570b957865ad1cefe7b88\"},\"image\":{\"@id\":\"https:\/\/hueil.top\/?p=2232#primaryimage\"},\"thumbnailUrl\":\"https:\/\/hueil.top\/wp-content\/uploads\/2026\/06\/image-1024x533.png\",\"articleSection\":[\"\u6587\u7ae0\"],\"inLanguage\":\"zh-Hans\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/hueil.top\/?p=2232#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/hueil.top\/?p=2232\",\"url\":\"https:\/\/hueil.top\/?p=2232\",\"name\":\"\ud83c\udf73 \u5c0f\u53a8\u6696\u5149 \u2014 \u9879\u76ee\u4ecb\u7ecd\u4e0e\u6280\u672f\u8be6\u89e3 - Hueil\u306e\u5c0f\u7a9d\",\"isPartOf\":{\"@id\":\"https:\/\/hueil.top\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/hueil.top\/?p=2232#primaryimage\"},\"image\":{\"@id\":\"https:\/\/hueil.top\/?p=2232#primaryimage\"},\"thumbnailUrl\":\"https:\/\/hueil.top\/wp-content\/uploads\/2026\/06\/image-1024x533.png\",\"datePublished\":\"2026-06-11T07:41:09+00:00\",\"dateModified\":\"2026-06-11T07:45:30+00:00\",\"description\":\"\u8ba1\u7b97\u673a\u5e94\u7528\uff0cPython\uff0cJava\uff0c\u4fe1\u606f\u5b89\u5168\uff0c\u7f51\u7ad9\u7f8e\u5316,\u7269\u8054\u7f51\uff0cC\/C++\u7b97\u6cd5\u5206\u6790,\u7f51\u7ad9\u5b9e\u6218\",\"breadcrumb\":{\"@id\":\"https:\/\/hueil.top\/?p=2232#breadcrumb\"},\"inLanguage\":\"zh-Hans\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/hueil.top\/?p=2232\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"zh-Hans\",\"@id\":\"https:\/\/hueil.top\/?p=2232#primaryimage\",\"url\":\"https:\/\/hueil.top\/wp-content\/uploads\/2026\/06\/image.png\",\"contentUrl\":\"https:\/\/hueil.top\/wp-content\/uploads\/2026\/06\/image.png\",\"width\":1920,\"height\":1000},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/hueil.top\/?p=2232#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"\u9996\u9875\",\"item\":\"https:\/\/hueil.top\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"\ud83c\udf73 \u5c0f\u53a8\u6696\u5149 \u2014 \u9879\u76ee\u4ecb\u7ecd\u4e0e\u6280\u672f\u8be6\u89e3\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/hueil.top\/#website\",\"url\":\"https:\/\/hueil.top\/\",\"name\":\"Hueil\u306e\u5c0f\u7a9d\",\"description\":\"Hueil\u7684\u5c0f\u7834\u7ad9\",\"publisher\":{\"@id\":\"https:\/\/hueil.top\/#\/schema\/person\/b3ed2232564570b957865ad1cefe7b88\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/hueil.top\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"zh-Hans\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\/\/hueil.top\/#\/schema\/person\/b3ed2232564570b957865ad1cefe7b88\",\"name\":\"\u5c0f\u5f20\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"zh-Hans\",\"@id\":\"https:\/\/hueil.top\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/hueil.top\/wp-content\/uploads\/2025\/08\/Hueil.jpg\",\"contentUrl\":\"https:\/\/hueil.top\/wp-content\/uploads\/2025\/08\/Hueil.jpg\",\"width\":940,\"height\":940,\"caption\":\"\u5c0f\u5f20\"},\"logo\":{\"@id\":\"https:\/\/hueil.top\/#\/schema\/person\/image\/\"},\"sameAs\":[\"https:\/\/hueil.top\"],\"url\":\"https:\/\/hueil.top\/?author=1\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"\ud83c\udf73 \u5c0f\u53a8\u6696\u5149 \u2014 \u9879\u76ee\u4ecb\u7ecd\u4e0e\u6280\u672f\u8be6\u89e3 - Hueil\u306e\u5c0f\u7a9d","description":"\u8ba1\u7b97\u673a\u5e94\u7528\uff0cPython\uff0cJava\uff0c\u4fe1\u606f\u5b89\u5168\uff0c\u7f51\u7ad9\u7f8e\u5316,\u7269\u8054\u7f51\uff0cC\/C++\u7b97\u6cd5\u5206\u6790,\u7f51\u7ad9\u5b9e\u6218","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/hueil.top\/?p=2232","og_locale":"zh_CN","og_type":"article","og_title":"\ud83c\udf73 \u5c0f\u53a8\u6696\u5149 \u2014 \u9879\u76ee\u4ecb\u7ecd\u4e0e\u6280\u672f\u8be6\u89e3 - Hueil\u306e\u5c0f\u7a9d","og_description":"\u8ba1\u7b97\u673a\u5e94\u7528\uff0cPython\uff0cJava\uff0c\u4fe1\u606f\u5b89\u5168\uff0c\u7f51\u7ad9\u7f8e\u5316,\u7269\u8054\u7f51\uff0cC\/C++\u7b97\u6cd5\u5206\u6790,\u7f51\u7ad9\u5b9e\u6218","og_url":"https:\/\/hueil.top\/?p=2232","og_site_name":"Hueil\u306e\u5c0f\u7a9d","article_published_time":"2026-06-11T07:41:09+00:00","article_modified_time":"2026-06-11T07:45:30+00:00","og_image":[{"width":1920,"height":1000,"url":"https:\/\/hueil.top\/wp-content\/uploads\/2026\/06\/image.png","type":"image\/png"}],"author":"\u5c0f\u5f20","twitter_card":"summary_large_image","twitter_misc":{"\u4f5c\u8005":"\u5c0f\u5f20","\u9884\u8ba1\u9605\u8bfb\u65f6\u95f4":"5 \u5206"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/hueil.top\/?p=2232#article","isPartOf":{"@id":"https:\/\/hueil.top\/?p=2232"},"author":{"name":"\u5c0f\u5f20","@id":"https:\/\/hueil.top\/#\/schema\/person\/b3ed2232564570b957865ad1cefe7b88"},"headline":"\ud83c\udf73 \u5c0f\u53a8\u6696\u5149 \u2014 \u9879\u76ee\u4ecb\u7ecd\u4e0e\u6280\u672f\u8be6\u89e3","datePublished":"2026-06-11T07:41:09+00:00","dateModified":"2026-06-11T07:45:30+00:00","mainEntityOfPage":{"@id":"https:\/\/hueil.top\/?p=2232"},"wordCount":287,"commentCount":0,"publisher":{"@id":"https:\/\/hueil.top\/#\/schema\/person\/b3ed2232564570b957865ad1cefe7b88"},"image":{"@id":"https:\/\/hueil.top\/?p=2232#primaryimage"},"thumbnailUrl":"https:\/\/hueil.top\/wp-content\/uploads\/2026\/06\/image-1024x533.png","articleSection":["\u6587\u7ae0"],"inLanguage":"zh-Hans","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/hueil.top\/?p=2232#respond"]}]},{"@type":"WebPage","@id":"https:\/\/hueil.top\/?p=2232","url":"https:\/\/hueil.top\/?p=2232","name":"\ud83c\udf73 \u5c0f\u53a8\u6696\u5149 \u2014 \u9879\u76ee\u4ecb\u7ecd\u4e0e\u6280\u672f\u8be6\u89e3 - Hueil\u306e\u5c0f\u7a9d","isPartOf":{"@id":"https:\/\/hueil.top\/#website"},"primaryImageOfPage":{"@id":"https:\/\/hueil.top\/?p=2232#primaryimage"},"image":{"@id":"https:\/\/hueil.top\/?p=2232#primaryimage"},"thumbnailUrl":"https:\/\/hueil.top\/wp-content\/uploads\/2026\/06\/image-1024x533.png","datePublished":"2026-06-11T07:41:09+00:00","dateModified":"2026-06-11T07:45:30+00:00","description":"\u8ba1\u7b97\u673a\u5e94\u7528\uff0cPython\uff0cJava\uff0c\u4fe1\u606f\u5b89\u5168\uff0c\u7f51\u7ad9\u7f8e\u5316,\u7269\u8054\u7f51\uff0cC\/C++\u7b97\u6cd5\u5206\u6790,\u7f51\u7ad9\u5b9e\u6218","breadcrumb":{"@id":"https:\/\/hueil.top\/?p=2232#breadcrumb"},"inLanguage":"zh-Hans","potentialAction":[{"@type":"ReadAction","target":["https:\/\/hueil.top\/?p=2232"]}]},{"@type":"ImageObject","inLanguage":"zh-Hans","@id":"https:\/\/hueil.top\/?p=2232#primaryimage","url":"https:\/\/hueil.top\/wp-content\/uploads\/2026\/06\/image.png","contentUrl":"https:\/\/hueil.top\/wp-content\/uploads\/2026\/06\/image.png","width":1920,"height":1000},{"@type":"BreadcrumbList","@id":"https:\/\/hueil.top\/?p=2232#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"\u9996\u9875","item":"https:\/\/hueil.top\/"},{"@type":"ListItem","position":2,"name":"\ud83c\udf73 \u5c0f\u53a8\u6696\u5149 \u2014 \u9879\u76ee\u4ecb\u7ecd\u4e0e\u6280\u672f\u8be6\u89e3"}]},{"@type":"WebSite","@id":"https:\/\/hueil.top\/#website","url":"https:\/\/hueil.top\/","name":"Hueil\u306e\u5c0f\u7a9d","description":"Hueil\u7684\u5c0f\u7834\u7ad9","publisher":{"@id":"https:\/\/hueil.top\/#\/schema\/person\/b3ed2232564570b957865ad1cefe7b88"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/hueil.top\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"zh-Hans"},{"@type":["Person","Organization"],"@id":"https:\/\/hueil.top\/#\/schema\/person\/b3ed2232564570b957865ad1cefe7b88","name":"\u5c0f\u5f20","image":{"@type":"ImageObject","inLanguage":"zh-Hans","@id":"https:\/\/hueil.top\/#\/schema\/person\/image\/","url":"https:\/\/hueil.top\/wp-content\/uploads\/2025\/08\/Hueil.jpg","contentUrl":"https:\/\/hueil.top\/wp-content\/uploads\/2025\/08\/Hueil.jpg","width":940,"height":940,"caption":"\u5c0f\u5f20"},"logo":{"@id":"https:\/\/hueil.top\/#\/schema\/person\/image\/"},"sameAs":["https:\/\/hueil.top"],"url":"https:\/\/hueil.top\/?author=1"}]}},"_links":{"self":[{"href":"https:\/\/hueil.top\/index.php?rest_route=\/wp\/v2\/posts\/2232","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/hueil.top\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/hueil.top\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/hueil.top\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/hueil.top\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=2232"}],"version-history":[{"count":2,"href":"https:\/\/hueil.top\/index.php?rest_route=\/wp\/v2\/posts\/2232\/revisions"}],"predecessor-version":[{"id":2236,"href":"https:\/\/hueil.top\/index.php?rest_route=\/wp\/v2\/posts\/2232\/revisions\/2236"}],"wp:attachment":[{"href":"https:\/\/hueil.top\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=2232"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/hueil.top\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=2232"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/hueil.top\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=2232"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}