three.vue 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. <template>
  2. <div>
  3. <div id="container"></div>
  4. <canvas id="canvas"></canvas>
  5. <div class="top">
  6. <el-select v-model="value" size="mini" placeholder="请选择">
  7. <el-option
  8. v-for="item in options"
  9. :key="item.value"
  10. :label="item.label"
  11. :value="item.value"
  12. >
  13. </el-option>
  14. </el-select>
  15. <el-select v-model="value" size="mini" placeholder="请选择">
  16. <el-option
  17. v-for="item in options"
  18. :key="item.value"
  19. :label="item.label"
  20. :value="item.value"
  21. >
  22. </el-option>
  23. </el-select>
  24. </div>
  25. <div class="rt">
  26. <el-select v-model="value" size="mini" placeholder="请选择">
  27. <el-option
  28. v-for="item in options"
  29. :key="item.value"
  30. :label="item.label"
  31. :value="item.value"
  32. >
  33. </el-option>
  34. </el-select>
  35. </div>
  36. <div class="menu">
  37. <el-button
  38. v-if="playStatu"
  39. icon="el-icon-video-play"
  40. @click="paly"
  41. circle
  42. ></el-button>
  43. <el-button
  44. v-else
  45. icon="el-icon-video-pause"
  46. @click="paly"
  47. circle
  48. ></el-button>
  49. </div>
  50. </div>
  51. </template>
  52. <script>
  53. import { FBXLoader } from "three/examples/jsm/loaders/FBXLoader";
  54. import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
  55. import * as Three from "three";
  56. import { SwitchSprite } from "../../public/static/three_sprite_utils";
  57. import { GridHelper, Raycaster, Vector2 } from "three";
  58. var camera = null;
  59. var scene = null;
  60. var renderer = null;
  61. var clock = null;
  62. var orbitControls = null;
  63. var animationMixer = null;
  64. var walkAction = null;
  65. var sprite = null;
  66. var group = null;
  67. export default {
  68. data() {
  69. return {
  70. publicPath: process.env.BASE_URL,
  71. playStatu: true,
  72. options: [{
  73. value: '选项1',
  74. label: '黄金糕'
  75. }, {
  76. value: '选项2',
  77. label: '双皮奶'
  78. }, {
  79. value: '选项3',
  80. label: '蚵仔煎'
  81. }, {
  82. value: '选项4',
  83. label: '龙须面'
  84. }, {
  85. value: '选项5',
  86. label: '北京烤鸭'
  87. }],
  88. value: ''
  89. };
  90. },
  91. mounted() {
  92. this.element = document.getElementById("container");
  93. // this.animate();
  94. this.init();
  95. // this.spriteText()
  96. },
  97. methods: {
  98. paly() {
  99. this.playStatu == true ? walkAction.play() : walkAction.stop();
  100. this.playStatu = !this.playStatu;
  101. },
  102. //初始化
  103. init() {
  104. scene = new Three.Scene();
  105. clock = new Three.Clock();
  106. camera = new Three.PerspectiveCamera(
  107. 70,
  108. window.innerWidth / window.innerHeight,
  109. 1,
  110. 1000
  111. );
  112. camera.position.set(5, 21, -1);
  113. renderer = new Three.WebGLRenderer({ antialias: true });
  114. orbitControls = new OrbitControls(camera, renderer.domElement);
  115. orbitControls.target = new Three.Vector3(0, 19, 0);
  116. animationMixer = new Three.AnimationMixer(scene);
  117. const container = document.getElementById("container");
  118. renderer.setSize(window.innerWidth, window.innerHeight);
  119. container.appendChild(renderer.domElement);
  120. window.addEventListener("resize", () => this.onWindowResize());
  121. // PolarGridHelper( radius:标网格的半径, radials:径向线的数量, circles:圆圈数,
  122. // divisions:每个圆圈使用的线段数, color1:用于网格元素的第一种颜色, color2:用于网格元素的第一种颜色 )
  123. var radius = 20;
  124. var radials = 15;
  125. var circles = 15;
  126. var divisions = 100;
  127. var helper02 = new Three.PolarGridHelper(
  128. radius,
  129. radials,
  130. circles,
  131. divisions,
  132. "#6adcff",
  133. "#6adcff"
  134. );
  135. // helper02.position.set(0, -20, 0)
  136. scene.add(helper02);
  137. let that = this;
  138. var loader = new FBXLoader();
  139. loader.load(`${that.publicPath}static/fan1.fbx`, async (fbx3d) => {
  140. await console.log(fbx3d);
  141. fbx3d.position.set(0, 12.4, -0.6);
  142. fbx3d.scale.set(0.2, 0.2, 0.2);
  143. fbx3d.children[9].material = fbx3d.children[6].material.clone();
  144. fbx3d.children[6].material = fbx3d.children[6].material.clone();
  145. fbx3d.children[9].material.transparent = true;
  146. fbx3d.children[9].material.opacity = 0.3;
  147. // fbx3d.children[9].material.wireframe = true;
  148. // fbx3d.children[9].material.wireframeLinewidth = 20
  149. // fbx3d.children[6].material.wireframe = true;
  150. // fbx3d.children[9].material.color.set("#00bfff");
  151. // fbx3d.children[6].material.color.set("#00bfff");
  152. fbx3d.children[2].materialnew = new Three.MeshPhysicalMaterial({
  153. color: 0xff0000,
  154. // 材质像金属的程度. 非金属材料,如木材或石材,使用0.0,金属使用1.0,中间没有(通常).
  155. // 默认 0.5. 0.0到1.0之间的值可用于生锈的金属外观
  156. metalness: 1.0,
  157. // 材料的粗糙程度. 0.0表示平滑的镜面反射,1.0表示完全漫反射. 默认 0.5
  158. roughness: 0.6,
  159. // 设置环境贴图
  160. // 反射程度, 从 0.0 到1.0.默认0.5.
  161. // 这模拟了非金属材料的反射率。 当metalness为1.0时无效
  162. // reflectivity: 0.5,
  163. });
  164. scene.add(fbx3d);
  165. scene.add(new Three.AmbientLight(0xffffff, 1.5));
  166. const animationClip = fbx3d.animations.find(
  167. (animationClip) => animationClip.name === "Take 001"
  168. );
  169. walkAction = animationMixer.clipAction(animationClip);
  170. });
  171. //射线
  172. renderer.domElement.addEventListener("click", (event) => {
  173. const { offsetX, offsetY } = event;
  174. const x = (offsetX / window.innerWidth) * 2 - 1;
  175. const y = -(offsetY / window.innerHeight) * 2 + 1;
  176. const mousePoint = new Vector2(x, y);
  177. const raycaster = new Raycaster();
  178. raycaster.params = { sprite: {} };
  179. raycaster.setFromCamera(mousePoint, camera);
  180. let intersects = raycaster.intersectObjects(scene.children, true);
  181. intersects = intersects.filter(
  182. (intersect) => !(intersect.object instanceof GridHelper)
  183. );
  184. console.log(intersects)
  185. var selectedMesh = intersects[0].object;
  186. var newSprite = SwitchSprite(
  187. selectedMesh.name || "aa",
  188. selectedMesh.position,
  189. selectedMesh
  190. );
  191. if (newSprite != null) scene.add(newSprite);
  192. });
  193. this.render();
  194. },
  195. render() {
  196. // 更新动画
  197. animationMixer.update(clock.getDelta());
  198. renderer.render(scene, camera);
  199. orbitControls.update();
  200. window.requestAnimationFrame(() => this.render());
  201. },
  202. onWindowResize() {
  203. renderer.setSize(window.innerWidth, window.innerHeight);
  204. camera.aspect = window.innerWidth / window.innerHeight;
  205. camera.updateProjectionMatrix();
  206. },
  207. },
  208. beforeDestroy() {
  209. camera = null;
  210. scene = null;
  211. renderer = null;
  212. clock = null;
  213. orbitControls = null;
  214. animationMixer = null;
  215. walkAction = null;
  216. },
  217. };
  218. </script>
  219. <style lang="scss" scoped>
  220. #container {
  221. width: 1920px;
  222. height: 100vh;
  223. }
  224. .menu {
  225. position: fixed;
  226. left: 20px;
  227. top: 20px;
  228. background-color: #fff;
  229. border: 1px solid #fff;
  230. border-radius: 20px;
  231. display: flex;
  232. justify-content: space-around;
  233. align-items: center;
  234. }
  235. .top{
  236. width: 60%;
  237. display: flex;
  238. position: fixed;
  239. justify-content: space-around;
  240. top: 20px;
  241. left: 10%;
  242. }
  243. .rt {
  244. width: 200px;
  245. min-height: 200px;
  246. position: fixed;
  247. right: 20px;
  248. top: 20px;
  249. // border: 1px solid #fff;
  250. // border-radius: 20px;
  251. }
  252. </style>