index.js 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. import getBehavior from './behavior'
  2. import yuvBehavior from './yuvBehavior'
  3. import { getCachedImage, hash } from '../../utils/index'
  4. import { MARKER_LIST } from './constants'
  5. const NEAR = 0.01
  6. const FAR = 1000
  7. Component({
  8. behaviors: [getBehavior, yuvBehavior],
  9. data: {
  10. widthScale: 1, // canvas宽度缩放值
  11. heightScale: 1, // canvas高度缩放值
  12. markerImgList: [], // 使用的 marker 列表
  13. chooseImgList: [], // 使用的 图片 列表
  14. hintBoxList: [], // 显示提示盒子列表
  15. markerMap: {},
  16. forzen: false,
  17. showVideo: false,
  18. markerIndex: 1, // 使用的 marker 索引
  19. },
  20. hintInfo: undefined, // 提示框信息
  21. pageLifetimes: {
  22. show() {
  23. setTimeout(() => {
  24. this.setData({
  25. forzen: false
  26. })
  27. }, 1000)
  28. }
  29. },
  30. methods: {
  31. handleEnded() {
  32. wx.navigateTo({
  33. url: '/pages/webview/index?url=' + encodeURIComponent(`https://sit-kelamayi.4dage.com/zuan/#/info/${this.data.markerIndex}`),
  34. })
  35. this.setData({
  36. showVideo: false
  37. })
  38. },
  39. // 对应案例的初始化逻辑,由统一的 behavior 触发
  40. init() {
  41. // 初始化 Three.js,用于模型相关的渲染
  42. this.initTHREE()
  43. // 初始化 GL,基于 Three.js 的 Context,用于相机YUV渲染
  44. this.initYUV()
  45. // 初始化VK
  46. // start完毕后,进行更新渲染循环
  47. this.initVK()
  48. // 添加 识别包围盒子
  49. // this.add3DBox()
  50. },
  51. initVK() {
  52. // VKSession 配置
  53. const session = this.session = wx.createVKSession({
  54. track: {
  55. plane: {
  56. mode: 1
  57. },
  58. marker: true,
  59. },
  60. version: 'v1',
  61. gl: this.gl
  62. })
  63. session.start(async err => {
  64. if (err) return console.error('VK error: ', err)
  65. console.log('@@@@@@@@ VKSession.version', session.version)
  66. try {
  67. const promises = MARKER_LIST.map(async url => {
  68. const cacheKey = 'image_marker_' + hash(url)
  69. const sysPath = await getCachedImage(url, cacheKey)
  70. return [sysPath, url]
  71. })
  72. wx.showLoading({
  73. title: '资源加载中...',
  74. })
  75. await Promise.all(promises).then(arr => {
  76. for (const [path, url] of arr) {
  77. console.log('marker添加成功:', path)
  78. const id = session.addMarker(path)
  79. console.log('markerId:', id)
  80. this.data.markerMap[id] = url
  81. }
  82. })
  83. } catch(err) {
  84. console.log(err)
  85. } finally {
  86. wx.hideLoading()
  87. }
  88. // VKSession EVENT resize
  89. session.on('resize', () => {
  90. this.calcCanvasSize()
  91. })
  92. // VKSession EVENT addAnchors
  93. // session.on('addAnchors', anchors => {
  94. // this.left.visible = true
  95. // this.right.visible = true
  96. // this.top.visible = true
  97. // this.bottom.visible = true
  98. // })
  99. // VKSession EVENT updateAnchors
  100. session.on('updateAnchors', anchors => {
  101. // marker 模式下,目前仅有一个识别目标,可以直接取
  102. const anchor = anchors[0]
  103. const markerId = anchor.id
  104. const size = anchor.size
  105. this.hintInfo = {
  106. markerId,
  107. size
  108. }
  109. const url = this.data.markerMap[markerId]
  110. if (url) {
  111. const match = url.match(/\/kelamayi\/(\d+)/);
  112. const id = match ? match[1] : null;
  113. console.log('识别到的图片:', url, id)
  114. if (id && !this.data.forzen) {
  115. this.setData({
  116. forzen: true,
  117. markerIndex: Number(id),
  118. showVideo: true
  119. })
  120. }
  121. }
  122. })
  123. // VKSession removeAnchors
  124. // 识别目标丢失时,会触发一次
  125. session.on('removeAnchors', anchors => {
  126. // this.left.visible = false
  127. // this.right.visible = false
  128. // this.top.visible = false
  129. // this.bottom.visible = false
  130. if (this.data.hintBoxList && this.data.hintBoxList.length > 0) {
  131. // 清理信息
  132. this.hintInfo = undefined
  133. // 存在列表的情况,去除remove
  134. this.setData({
  135. hintBoxList: []
  136. })
  137. }
  138. })
  139. console.log('ready to initloop')
  140. // start 初始化完毕后,进行更新渲染循环
  141. this.initLoop()
  142. })
  143. },
  144. loop() {
  145. // console.log('loop')
  146. // 获取 VKFrame
  147. const frame = this.session.getVKFrame(this.canvas.width, this.canvas.height)
  148. // 成功获取 VKFrame 才进行
  149. if (!frame) { return }
  150. // 更新相机 YUV 数据
  151. this.renderYUV(frame)
  152. // 获取 VKCamera
  153. const VKCamera = frame.camera
  154. // 相机
  155. if (VKCamera) {
  156. // 接管 ThreeJs 相机矩阵更新,Marker模式下,主要由视图和投影矩阵改变渲染效果
  157. this.camera.matrixAutoUpdate = false
  158. // 视图矩阵
  159. this.camera.matrixWorldInverse.fromArray(VKCamera.viewMatrix)
  160. this.camera.matrixWorld.getInverse(this.camera.matrixWorldInverse)
  161. // 投影矩阵
  162. const projectionMatrix = VKCamera.getProjectionMatrix(NEAR, FAR)
  163. this.camera.projectionMatrix.fromArray(projectionMatrix)
  164. this.camera.projectionMatrixInverse.getInverse(this.camera.projectionMatrix)
  165. }
  166. // 绘制而为提示框的逻辑
  167. if (this.hintInfo) {
  168. // 存在提示信息,则更新
  169. const THREE = this.THREE
  170. // 原点偏移矩阵,VK情况下,marker 点对应就是 0 0 0,世界矩阵可以认为是一个单位矩阵
  171. // marker 右侧点可以理解是 0.5 0 0
  172. const center = new THREE.Vector3()
  173. const right = new THREE.Vector3(0.5, 0, 0)
  174. // 获取设备空间坐标
  175. const devicePos = center.clone().project(this.camera)
  176. // 转换坐标系,从 (-1, 1) 转到 (0, 100),同时移到左上角 0 0,右下角 1 1
  177. const screenPos = new THREE.Vector3(0, 0, 0)
  178. screenPos.x = devicePos.x * 50 + 50
  179. screenPos.y = 50 - devicePos.y * 50
  180. // 获取右侧点信息
  181. const deviceRightPos = right.clone().project(this.camera)
  182. const screenRightPos = new THREE.Vector3(0, 0, 0)
  183. screenRightPos.x = deviceRightPos.x * 50 + 50
  184. const markerHalfWidth = screenRightPos.x - screenPos.x
  185. this.setData({
  186. hintBoxList: [
  187. {
  188. markerId: this.hintInfo.markerId,
  189. left: screenPos.x - markerHalfWidth,
  190. top: screenPos.y - markerHalfWidth,
  191. width: markerHalfWidth * this.data.domWidth * 2 / 100,
  192. height: markerHalfWidth * this.data.domWidth * 2 / 100,
  193. }
  194. ]
  195. })
  196. }
  197. this.renderer.autoClearColor = false
  198. this.renderer.state.setCullFace(this.THREE.CullFaceBack)
  199. this.renderer.render(this.scene, this.camera)
  200. this.renderer.state.setCullFace(this.THREE.CullFaceNone)
  201. },
  202. add3DBox() {
  203. // 添加marker需要的 三维包围框
  204. const THREE = this.THREE
  205. const scene = this.scene
  206. const material = new THREE.MeshPhysicalMaterial({
  207. metalness: 0.0,
  208. roughness: 0.1,
  209. color: 0x64f573,
  210. })
  211. const geometry = new THREE.BoxGeometry(1, 1, 1)
  212. const borderSize = 0.1
  213. const left = new THREE.Mesh(geometry, material)
  214. left.position.set(-0.5, 0, 0)
  215. left.rotation.set(-Math.PI / 2, 0, 0)
  216. left.scale.set(borderSize, 1.1, borderSize)
  217. scene.add(left)
  218. left.visible = false
  219. this.left = left
  220. const right = new THREE.Mesh(geometry, material)
  221. right.position.set(0.5, 0, 0)
  222. right.rotation.set(-Math.PI / 2, 0, 0)
  223. right.scale.set(borderSize, 1.1, borderSize)
  224. scene.add(right)
  225. right.visible = false
  226. this.right = right
  227. const top = new THREE.Mesh(geometry, material)
  228. top.position.set(0, 0, 0.5)
  229. top.rotation.set(0, 0, 0)
  230. top.scale.set(1.1, borderSize, borderSize)
  231. scene.add(top)
  232. top.visible = false
  233. this.top = top
  234. const bottom = new THREE.Mesh(geometry, material)
  235. bottom.position.set(0, 0, -0.5)
  236. bottom.rotation.set(0, 0, 0)
  237. bottom.scale.set(1.1, borderSize, borderSize)
  238. scene.add(bottom)
  239. bottom.visible = false
  240. this.bottom = bottom
  241. console.log('add3DBox is finish')
  242. },
  243. },
  244. })