当前位置:   article > 正文

Three模型换装项目,引入FBX模型并且修改渲染器大小和模型居中_three.js 换衣

three.js 换衣

      项目内嵌到uniapp开发的h5页面中,

        第一步先引入three.js

npm install three

        然后代码里面引入,我的模型是FBX文件,引入的fbxloader加载器,如果出现找不到指定的版本号错误则检查引入模型文件的时候地址,需要放在公共文件夹

  1. import * as THREE from 'three'
  2. import { FBXLoader } from 'three/examples/jsm/loaders/FBXLoader';
  3. import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'

        html页面是这样的,代码里面的<tabbar>标签是我项目需要的导航栏,如果借用的话请删除,不然会引起报错。

  1. <template>
  2. <view>
  3. <button @click="changeClothes()">缩小</button>
  4. <button @click="hoMing()">归位</button>
  5. <button @click="changeOld('裤子','../../static/fbx/2.fbx')">切换裤子</button>
  6. <button @click="changeOld('卫衣','../../static/fbx/weiyi.fbx')">切换卫衣</button>
  7. <view id="canvas">
  8. </view>
  9. <!-- <view id="loading">Loading...</view> -->
  10. <!-- <image src="../../static/images/No.2.png"></image> -->
  11. <tabbar></tabbar>
  12. </view>
  13. </template>

       下面是JS部分

  1. <script>
  2. import * as THREE from 'three'
  3. import {
  4. FBXLoader
  5. } from 'three/examples/jsm/loaders/FBXLoader';
  6. import {
  7. OrbitControls
  8. } from 'three/examples/jsm/controls/OrbitControls'
  9. export default {
  10. name: 'HelloWorld',
  11. data() {
  12. return {
  13. scene: null,
  14. camera: null,
  15. renderer: null,
  16. model: null,
  17. materials: [],
  18. controls: null,
  19. mixer: null,
  20. meshes: [],
  21. newMaterial: null, //新模型的材质
  22. pants: null,
  23. manager: null,
  24. aspectOld: null, //旧的等宽比
  25. heightOld: null, //初始设备的高度
  26. widthOld: null, //初始设备的宽度
  27. Newaspect: null, //新的宽比
  28. }
  29. },
  30. methods: {
  31. /**
  32. * 初始化场景、相机、灯光、模型等
  33. */
  34. init() {
  35. this.aler()
  36. //记录设备初始宽高
  37. this.scene = new THREE.Scene()
  38. console.log(this.camera)
  39. this.aspectOld = this.camera.aspect //记录设备初始等宽比!!!
  40. console.log(window.innerWidth)
  41. // this.camera.aspect = window.innerWidth / window.innerHeight;
  42. // this.camera.updateProjectionMatrix();
  43. document.getElementById('canvas').appendChild(this.renderer.domElement)
  44. this.controls = new OrbitControls(this.camera, this.renderer.domElement)
  45. // this.renderer.domElement.style.height='390px'
  46. // this.renderer.domElement.style.width='50%'
  47. this.renderer.setClearColor(0xffffff); // 设置背景颜色为白色
  48. // this.loading()
  49. this.load('../../static/fbx/boy.fbx')
  50. this.render()
  51. },
  52. /**
  53. * 加载 FBX 模型
  54. */
  55. load(modelUrl) {
  56. let that = this
  57. // const loader = new FBXLoader(that.manager);
  58. const loader = new FBXLoader();
  59. loader.load(modelUrl, (fbx) => {
  60. console.log(fbx)
  61. fbx.scale.set(0.3, 0.3, 0.3)
  62. this.camera.lookAt(0, 0, 0)
  63. //模型居中
  64. const box = new THREE.Box3().setFromObject(fbx);
  65. const center = new THREE.Vector3();
  66. box.getCenter(center);
  67. fbx.position.sub(center);
  68. fbx.traverse((child) => {
  69. if (child.name == "Camera") {
  70. this.camera.position.set(child.position.x, child.position.y, child.position.z)
  71. }
  72. if (child.type == "PointLight") {
  73. child.intensity = 1
  74. }
  75. if (child.type == 'DirectionalLight') {
  76. child.intensity = 1
  77. }
  78. })
  79. //有动画的模型默认加载第一个动画
  80. if (fbx.animations.length != 0) {
  81. that.mixer = new THREE.AnimationMixer(fbx)
  82. const action = that.mixer.clipAction(fbx.animations[0])
  83. action.play()
  84. that.methse(fbx)
  85. }
  86. that.model = fbx
  87. that.scene.add(fbx);
  88. })
  89. },
  90. // 对象数据保存到数组中
  91. methse(fbx) {
  92. fbx.traverse((child) => {
  93. if (child instanceof THREE.Mesh) {
  94. this.meshes.push(child)
  95. }
  96. })
  97. },
  98. changeOld(num, path) {
  99. let that = this
  100. this.switching_model(num, path)
  101. },
  102. //页面回到默认
  103. hoMing() {
  104. this.camera.aspect = this.aspectOld;
  105. this.renderer.setSize(window.innerWidth, window.innerHeight)
  106. this.camera.updateProjectionMatrix(); //更新相机!!!
  107. },
  108. aler() {
  109. this.heightOld = window.innerHeight
  110. this.widthOld = window.innerWidth
  111. this.Newaspect = window.innerWidth / window.innerWidth
  112. this.camera = new THREE.PerspectiveCamera(20, window.innerWidth / window.innerHeight, 0.1, 10000);
  113. this.renderer = new THREE.WebGLRenderer({
  114. alpha: true,
  115. antialias: true
  116. })
  117. this.renderer.setSize(window.innerWidth, window.innerHeight)
  118. },
  119. changeClothes() {
  120. let that = this
  121. this.camera.aspect = this.Newaspect;
  122. this.renderer.setSize(this.widthOld, this.widthOld)
  123. this.camera.updateProjectionMatrix(); //更新相机!!!
  124. console.log(this.camera)
  125. // that.render()
  126. },
  127. //切换衣服
  128. switching_model(num, path) {
  129. const loader = new FBXLoader();
  130. let that = this
  131. loader.load(path, (newPants) => {
  132. that.model.traverse((child) => {
  133. if (child.name == num) {
  134. let newPantsGroup = null
  135. newPants.traverse((newchild) => {
  136. console.log(newchild.name)
  137. if (newchild.name == num) {
  138. newPantsGroup = newchild;
  139. }
  140. })
  141. console.log(newPantsGroup)
  142. child.material = newPantsGroup.material
  143. child.material.needsUpdate = true;
  144. }
  145. })
  146. that.renderer.render(that.scene, that.camera);
  147. }), undefined,
  148. function(error) {
  149. console.error(error);
  150. }, undefined,
  151. function(error) {
  152. console.error(error);
  153. }
  154. },
  155. /**
  156. * 执行渲染操作
  157. */
  158. render() {
  159. this.renderer.render(this.scene, this.camera)
  160. this.controls.update()
  161. requestAnimationFrame(this.render.bind(this))
  162. //更新动画
  163. if (this.mixer) {
  164. this.mixer.update(0.01)
  165. }
  166. }
  167. },
  168. mounted() {
  169. this.init()
  170. }
  171. }
  172. </script>

样式代码

  1. <style lang="scss">
  2. #canvas {
  3. text-align: center;
  4. height: 100vh;
  5. display: flex;
  6. align-items: center;
  7. justify-content: center;
  8. }
  9. </style>

        完整代码如下,FBX我引入的是本地的,大家如果有需要可以去网上找一些开源的文件

     

  1. <template>
  2. <view>
  3. <button @click="changeClothes()">缩小</button>
  4. <button @click="hoMing()">归位</button>
  5. <button @click="changeOld('裤子','../../static/fbx/2.fbx')">切换裤子</button>
  6. <button @click="changeOld('卫衣','../../static/fbx/weiyi.fbx')">切换卫衣</button>
  7. <view id="canvas">
  8. </view>
  9. <tabbar></tabbar>
  10. </view>
  11. </template>
  12. <script>
  13. import * as THREE from 'three'
  14. import {
  15. FBXLoader
  16. } from 'three/examples/jsm/loaders/FBXLoader';
  17. import {
  18. OrbitControls
  19. } from 'three/examples/jsm/controls/OrbitControls'
  20. export default {
  21. name: 'HelloWorld',
  22. data() {
  23. return {
  24. scene: null,
  25. camera: null,
  26. renderer: null,
  27. model: null,
  28. materials: [],
  29. controls: null,
  30. mixer: null,
  31. meshes: [],
  32. newMaterial: null, //新模型的材质
  33. pants: null,
  34. manager: null,
  35. aspectOld: null, //旧的等宽比
  36. heightOld: null, //初始设备的高度
  37. widthOld: null, //初始设备的宽度
  38. Newaspect: null, //新的宽比
  39. }
  40. },
  41. methods: {
  42. /**
  43. * 初始化场景、相机、灯光、模型等
  44. */
  45. init() {
  46. this.aler()
  47. //记录设备初始宽高
  48. this.scene = new THREE.Scene()
  49. console.log(this.camera)
  50. this.aspectOld = this.camera.aspect //记录设备初始等宽比!!!
  51. console.log(window.innerWidth)
  52. // this.camera.aspect = window.innerWidth / window.innerHeight;
  53. // this.camera.updateProjectionMatrix();
  54. document.getElementById('canvas').appendChild(this.renderer.domElement)
  55. this.controls = new OrbitControls(this.camera, this.renderer.domElement)
  56. // this.renderer.domElement.style.height='390px'
  57. // this.renderer.domElement.style.width='50%'
  58. this.renderer.setClearColor(0xffffff); // 设置背景颜色为白色
  59. // this.loading()
  60. this.load('../../static/fbx/boy.fbx')
  61. this.render()
  62. },
  63. /**
  64. * 加载 FBX 模型
  65. */
  66. load(modelUrl) {
  67. let that = this
  68. // const loader = new FBXLoader(that.manager);
  69. const loader = new FBXLoader();
  70. loader.load(modelUrl, (fbx) => {
  71. console.log(fbx)
  72. fbx.scale.set(0.3, 0.3, 0.3)
  73. this.camera.lookAt(0, 0, 0)
  74. //模型居中
  75. const box = new THREE.Box3().setFromObject(fbx);
  76. const center = new THREE.Vector3();
  77. box.getCenter(center);
  78. fbx.position.sub(center);
  79. fbx.traverse((child) => {
  80. if (child.name == "Camera") {
  81. this.camera.position.set(child.position.x, child.position.y, child.position.z)
  82. }
  83. if (child.type == "PointLight") {
  84. child.intensity = 1
  85. }
  86. if (child.type == 'DirectionalLight') {
  87. child.intensity = 1
  88. }
  89. })
  90. //有动画的模型默认加载第一个动画
  91. if (fbx.animations.length != 0) {
  92. that.mixer = new THREE.AnimationMixer(fbx)
  93. const action = that.mixer.clipAction(fbx.animations[0])
  94. action.play()
  95. that.methse(fbx)
  96. }
  97. that.model = fbx
  98. that.scene.add(fbx);
  99. })
  100. },
  101. // 对象数据保存到数组中
  102. methse(fbx) {
  103. fbx.traverse((child) => {
  104. if (child instanceof THREE.Mesh) {
  105. this.meshes.push(child)
  106. }
  107. })
  108. },
  109. changeOld(num, path) {
  110. let that = this
  111. this.switching_model(num, path)
  112. },
  113. //页面回到默认
  114. hoMing() {
  115. this.camera.aspect = this.aspectOld;
  116. this.renderer.setSize(window.innerWidth, window.innerHeight)
  117. this.camera.updateProjectionMatrix(); //更新相机!!!
  118. },
  119. aler() {
  120. this.heightOld = window.innerHeight
  121. this.widthOld = window.innerWidth
  122. this.Newaspect = window.innerWidth / window.innerWidth
  123. this.camera = new THREE.PerspectiveCamera(20, window.innerWidth / window.innerHeight, 0.1, 10000);
  124. this.renderer = new THREE.WebGLRenderer({
  125. alpha: true,
  126. antialias: true
  127. })
  128. this.renderer.setSize(window.innerWidth, window.innerHeight)
  129. },
  130. changeClothes() {
  131. let that = this
  132. this.camera.aspect = this.Newaspect;
  133. this.renderer.setSize(this.widthOld, this.widthOld)
  134. this.camera.updateProjectionMatrix(); //更新相机!!!
  135. console.log(this.camera)
  136. // that.render()
  137. },
  138. //切换衣服
  139. switching_model(num, path) {
  140. const loader = new FBXLoader();
  141. let that = this
  142. loader.load(path, (newPants) => {
  143. that.model.traverse((child) => {
  144. if (child.name == num) {
  145. let newPantsGroup = null
  146. newPants.traverse((newchild) => {
  147. console.log(newchild.name)
  148. if (newchild.name == num) {
  149. newPantsGroup = newchild;
  150. }
  151. })
  152. console.log(newPantsGroup)
  153. child.material = newPantsGroup.material
  154. child.material.needsUpdate = true;
  155. }
  156. })
  157. that.renderer.render(that.scene, that.camera);
  158. }), undefined,
  159. function(error) {
  160. console.error(error);
  161. }, undefined,
  162. function(error) {
  163. console.error(error);
  164. }
  165. },
  166. /**
  167. * 执行渲染操作
  168. */
  169. render() {
  170. this.renderer.render(this.scene, this.camera)
  171. this.controls.update()
  172. requestAnimationFrame(this.render.bind(this))
  173. //更新动画
  174. if (this.mixer) {
  175. this.mixer.update(0.01)
  176. }
  177. }
  178. },
  179. mounted() {
  180. this.init()
  181. }
  182. }
  183. </script>
  184. <style lang="scss">
  185. #canvas {
  186. text-align: center;
  187. height: 100vh;
  188. display: flex;
  189. align-items: center;
  190. justify-content: center;
  191. }
  192. </style>

效果如下:

个人思路: 

—————————————————————————————————————————

因为以后要换不同款式的衣服,有可能是裙子,短裤等,所以我替换的是模型的材质,如果只是固定的服装,只是颜色图案不同的话可以用最简单的替换贴图来解决。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Cpp五条/article/detail/95910
推荐阅读
相关标签
  

闽ICP备14008679号