index.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436
  1. let camera, scene, renderer
  2. const shadowHasAlpha = false //阴影是否考虑光透过透明材质
  3. const planeGeo = new THREE.PlaneBufferGeometry(1, 1)
  4. const raycaster = new THREE.Raycaster()
  5. raycaster.linePrecision = 0 //不检测boxHelper
  6. const mouse = new THREE.Vector2()
  7. let needUpdateShadow, needsUpdateScene
  8. var BlurShader = {
  9. uniforms: {
  10. map: { value: null },
  11. blurRadius: { value: new THREE.Vector2(1.0 / 512.0, 1.0 / 512.0) },
  12. opacity: { value: 0 }
  13. },
  14. vertexShader: [
  15. 'varying vec2 vUv;',
  16. 'void main() {',
  17. ' vUv = uv;',
  18. ' gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );',
  19. '}'
  20. ].join('\n'),
  21. fragmentShader: [
  22. 'uniform sampler2D map;',
  23. 'uniform vec2 resolution;',
  24. 'uniform float blurRadius;',
  25. 'uniform float opacity;',
  26. 'varying vec2 vUv;',
  27. 'void main() {',
  28. ' vec2 offset = blurRadius / resolution; ',
  29. ' vec4 sum = vec4( 0.0 );',
  30. ' sum += texture2D( map, vec2( vUv.x - 4.0 * offset.x, vUv.y ) ) * 0.051;',
  31. ' sum += texture2D( map, vec2( vUv.x - 3.0 * offset.x, vUv.y ) ) * 0.0918;',
  32. ' sum += texture2D( map, vec2( vUv.x - 2.0 * offset.x, vUv.y ) ) * 0.12245;',
  33. ' sum += texture2D( map, vec2( vUv.x - 1.0 * offset.x, vUv.y ) ) * 0.1531;',
  34. ' sum += texture2D( map, vec2( vUv.x, vUv.y ) ) * 0.1633;',
  35. ' sum += texture2D( map, vec2( vUv.x + 1.0 * offset.x, vUv.y ) ) * 0.1531;',
  36. ' sum += texture2D( map, vec2( vUv.x + 2.0 * offset.x, vUv.y ) ) * 0.12245;',
  37. ' sum += texture2D( map, vec2( vUv.x + 3.0 * offset.x, vUv.y ) ) * 0.0918;',
  38. ' sum += texture2D( map, vec2( vUv.x + 4.0 * offset.x, vUv.y ) ) * 0.051;',
  39. ' sum += texture2D( map, vec2( vUv.x , vUv.y - 4.0 * offset.y ) ) * 0.051;',
  40. ' sum += texture2D( map, vec2( vUv.x , vUv.y - 3.0 * offset.y) ) * 0.0918;',
  41. ' sum += texture2D( map, vec2( vUv.x , vUv.y - 2.0 * offset.y) ) * 0.12245;',
  42. ' sum += texture2D( map, vec2( vUv.x , vUv.y - 1.0 * offset.y ) ) * 0.1531;',
  43. ' sum += texture2D( map, vec2( vUv.x , vUv.y ) ) * 0.1633;',
  44. ' sum += texture2D( map, vec2( vUv.x , vUv.y + 1.0 * offset.y ) ) * 0.1531;',
  45. ' sum += texture2D( map, vec2( vUv.x , vUv.y + 2.0 * offset.y ) ) * 0.12245;',
  46. ' sum += texture2D( map, vec2( vUv.x , vUv.y + 3.0 * offset.y ) ) * 0.0918;',
  47. ' sum += texture2D( map, vec2( vUv.x , vUv.y + 4.0 * offset.y ) ) * 0.051;',
  48. ' gl_FragColor = sum / 2.0 ;',
  49. ' gl_FragColor.a *= opacity;',
  50. '}'
  51. ].join('\n')
  52. }
  53. var Viewer = function (index, dom) {
  54. this.index = index
  55. this.dom = dom
  56. this.camera = new THREE.PerspectiveCamera(setting.vfov)
  57. this.camera.position.set(0, 1, -1.5)
  58. this.control = new PanoramaControls(this.camera, this.dom)
  59. this.control.latMin = this.control.latMax = 0
  60. //this.control.target.set(0,-1,0)
  61. this.setRenderer()
  62. this.scene = new THREE.Scene()
  63. this.scene.background = common.loadTexture('./assets/background.jpg')
  64. this.pointerDownPos
  65. this.active = false
  66. this.antialias = true
  67. this.clickTime = new Date().getTime()
  68. this.updateClock = new THREE.Clock()
  69. this.cardGroup = new THREE.Object3D()
  70. this.scene.add(this.cardGroup)
  71. this.cardGroup.name = 'cardGroup'
  72. this.autoMove = true
  73. this.bindEvents()
  74. this.preLoadCards()
  75. this.animate()
  76. }
  77. /*
  78. 同一时间,视线范围不能同时出现两张卡。也就是分布要根据视角范围变化,但是如果浏览器变宽导致的出现两张卡不算。
  79. */
  80. Viewer.prototype.preLoadCards = function () {
  81. let i = 5 // 减少初始卡片数量,让画面更稀疏
  82. while (i-- > 0) {
  83. this.addCard(true)
  84. }
  85. let add = () => {
  86. if (document.hidden) return
  87. this.addCard()
  88. setTimeout(add, 60000 * Math.random() * this.getDensity()) // 增加添加间隔时间(从40000改为60000)
  89. }
  90. add()
  91. document.addEventListener('visibilitychange', e => {
  92. if (!document.hidden) add()
  93. console.log('document.hidden', document.hidden)
  94. })
  95. }
  96. Viewer.prototype.getDensity = function () {
  97. let frustumMatrix = new THREE.Matrix4()
  98. frustumMatrix.multiplyMatrices(this.camera.projectionMatrix, this.camera.matrixWorldInverse)
  99. let frustum = new THREE.Frustum()
  100. frustum.setFromProjectionMatrix(frustumMatrix)
  101. let count = this.cardGroup.children.filter(card => {
  102. return frustum.containsPoint(card.position)
  103. }).length
  104. let density = (count / (this.renderer.domElement.width * this.renderer.domElement.height)) * 1000
  105. return density
  106. }
  107. Viewer.prototype.addCard = function (around) {
  108. let cardIndex = Math.floor(cardNames.length * Math.random())
  109. common.loadTexture('./assets/out/' + cardNames[cardIndex].img, map => {
  110. /* let card = new THREE.Mesh(planeGeo, new THREE.MeshBasicMaterial({
  111. map,
  112. transparent:true, opacity:0,side:2
  113. })) */
  114. let card = new THREE.Mesh(
  115. planeGeo,
  116. new THREE.ShaderMaterial({
  117. uniforms: {
  118. map: { value: map, id: cardNames[cardIndex].id, name: cardNames[cardIndex].name },
  119. resolution: {
  120. value: new THREE.Vector2(
  121. this.renderer.domElement.width,
  122. this.renderer.domElement.height
  123. )
  124. },
  125. blurRadius: { value: 0 }, //像素
  126. opacity: { value: 0 }
  127. },
  128. vertexShader: BlurShader.vertexShader,
  129. fragmentShader: BlurShader.fragmentShader,
  130. transparent: true,
  131. side: 2
  132. })
  133. )
  134. Object.defineProperty(card.material, 'opacity', {
  135. get: function () {
  136. return card.material.uniforms.opacity.value
  137. },
  138. set: function (e) {
  139. card.material.uniforms.opacity.value = e
  140. card.material.uniforms.blurRadius.value = math.linearClamp(e, [0, 0.4], [40, 0])
  141. }
  142. })
  143. let direction,
  144. far = setting.cards.far
  145. if (around) {
  146. //在四周所有方向都可生成,在一开始时需要
  147. let n = 0.6 //范围0-1 越大越可能接近相机
  148. far = far * (1 - n * Math.random()) //靠近一点
  149. direction = new THREE.Vector3(0, 0, -1).applyEuler(
  150. new THREE.Euler(0, Math.PI * 2 * Math.random(), 0)
  151. )
  152. } else {
  153. //仅在相机前方生成,因为相机往这个方向移动,最前方空缺
  154. direction = new THREE.Vector3(0, 0, -1)
  155. .applyQuaternion(this.camera.quaternion)
  156. .applyEuler(new THREE.Euler(0, this.camera.hfov * (Math.random() - 0.5), 0))
  157. }
  158. let h = (Math.random() * 2 - 1) * setting.cards.highest * 0.8 // *0.8是因为靠近后就会飞出视线
  159. card.position
  160. .copy(this.camera.position)
  161. .add(direction.add(new THREE.Vector3(0, h, 0)).multiplyScalar(far))
  162. card.scale.set(map.image.width / 300, map.image.height / 300, 1) // 图片放大(分母从500改为300)
  163. // 检查是否与现有卡片重叠,如果重叠则移除
  164. let isOverlapping = false
  165. this.cardGroup.children.forEach(existingCard => {
  166. let distance = card.position.distanceTo(existingCard.position)
  167. if (distance < 3) { // 如果距离小于3,认为重叠
  168. isOverlapping = true
  169. }
  170. })
  171. if (!isOverlapping) {
  172. this.cardGroup.add(card)
  173. card.transition = transitions.start(
  174. lerp.property(card.material, 'opacity', 1, e => {
  175. //console.log(e, card.uuid)
  176. }),
  177. setting.cards.fadeInDur
  178. )
  179. }
  180. })
  181. }
  182. Viewer.prototype.removeCards = function () {
  183. //移除超过bound的卡
  184. let needRemove = this.cardGroup.children.filter(card => {
  185. if (card.disToCam > setting.cards.far) {
  186. card.material.dispose()
  187. transitions.cancel(card.transition)
  188. //console.log('remove一张卡')
  189. return true
  190. }
  191. })
  192. needRemove.forEach(card => card.parent.remove(card))
  193. //needRemove.length>0 && console.log('当前存在卡数', this.cardGroup.children.length)
  194. }
  195. Viewer.prototype.update = function (deltaTime) {
  196. //绘制的时候同时更新
  197. this.setSize()
  198. this.control.update(deltaTime)
  199. transitions.update(deltaTime)
  200. if (this.autoMove) {
  201. let direction = new THREE.Vector3(0, 0, -1).applyQuaternion(this.camera.quaternion)
  202. let moveSpeed = 0.3 // 减慢速度(从0.8改为0.3)
  203. this.camera.position.add(direction.multiplyScalar(deltaTime * moveSpeed))
  204. }
  205. this.cardGroup.children.forEach(card => {
  206. card.quaternion.copy(this.camera.quaternion)
  207. let dis = card.position.clone().setY(0).distanceTo(this.camera.position.clone().setY(0))
  208. if (!card.transition.running) {
  209. card.material.opacity = math.linearClamp(
  210. dis,
  211. [setting.cards.near, setting.cards.beginFadeNear],
  212. [0, 1]
  213. )
  214. }
  215. card.disToCam = dis
  216. })
  217. this.removeCards()
  218. var needsUpdate = 1
  219. if (needsUpdate) {
  220. this.renderer.autoClear = true
  221. this.renderer.render(this.scene, this.camera)
  222. }
  223. }
  224. Viewer.prototype.bindEvents = function () {
  225. this.renderer.domElement.addEventListener('pointermove', this.onPointerMove.bind(this), false)
  226. this.renderer.domElement.addEventListener('pointerdown', this.onPointerDown.bind(this), false)
  227. this.renderer.domElement.addEventListener('pointerup', this.onPointerUp.bind(this), false)
  228. }
  229. Viewer.prototype.setRenderer = function () {
  230. try {
  231. ;((this.renderer = new THREE.WebGLRenderer({
  232. canvas: $(this.dom).find('canvas')[0],
  233. antialias: true
  234. })),
  235. this.renderer.setPixelRatio(window.devicePixelRatio ? window.devicePixelRatio : 1),
  236. (this.renderer.autoClear = false))
  237. this.renderer.setClearColor(0xffffff, 1)
  238. console.log('ContextCreated')
  239. //this.emit(Events.ContextCreated)
  240. } catch (e) {
  241. console.error('Unable to create a WebGL rendering context')
  242. }
  243. }
  244. Viewer.prototype.hasChanged = function () {
  245. //判断画面是否改变了,改变后需要更新一些东西
  246. var copy = function () {
  247. this.previousState = {
  248. projectionMatrix: this.camera.projectionMatrix.clone(), //worldMatrix在control时归零了所以不用了吧,用position和qua也一样
  249. position: this.camera.position.clone(),
  250. quaternion: this.camera.quaternion.clone(),
  251. //mouse: this.mouse.clone(),
  252. fov: this.camera.fov
  253. }
  254. }.bind(this)
  255. if (!this.previousState) {
  256. copy()
  257. return { cameraChanged: !0, changed: !0 }
  258. }
  259. var cameraChanged =
  260. !this.camera.projectionMatrix.equals(this.previousState.projectionMatrix) ||
  261. !this.camera.position.equals(this.previousState.position) ||
  262. !this.camera.quaternion.equals(this.previousState.quaternion)
  263. var changed = cameraChanged //|| !this.mouse.equals(this.previousState.mouse)
  264. copy()
  265. return { cameraChanged, changed }
  266. }
  267. Viewer.prototype.setSize = (function () {
  268. var w, h, pixelRatio
  269. return function () {
  270. if (
  271. w != this.dom.clientWidth ||
  272. h != this.dom.clientHeight ||
  273. pixelRatio != window.devicePixelRatio
  274. ) {
  275. w = this.dom.clientWidth
  276. h = this.dom.clientHeight
  277. pixelRatio = window.devicePixelRatio
  278. this.camera.aspect = w / h
  279. this.camera.updateProjectionMatrix()
  280. this.renderer.setSize(w, h, false, pixelRatio)
  281. this.camera.hfov = cameraLight.getHFOVForCamera(this.camera, true)
  282. this.cardGroup.children.forEach(card => card.material.uniforms.resolution.value.set(w, h))
  283. }
  284. }
  285. })()
  286. ;((Viewer.prototype.animate = function () {
  287. var deltaTime = Math.min(1, this.updateClock.getDelta())
  288. this.update(deltaTime)
  289. //bus.emit('player/position/change', {x:this.position.x, y:this.position.z, lon: this.cameraControls.controls.panorama.lon})
  290. window.requestAnimationFrame(this.animate.bind(this))
  291. }),
  292. (Viewer.prototype.onPointerMove = function (event) {
  293. if (event.isPrimary === false) return
  294. mouse.x = (event.clientX / window.innerWidth) * 2 - 1
  295. mouse.y = -(event.clientY / window.innerHeight) * 2 + 1
  296. if (!this.pointerDownPos) this.checkIntersection()
  297. }))
  298. Viewer.prototype.onPointerDown = function (event) {
  299. if (event.isPrimary === false) return
  300. mouse.x = (event.clientX / window.innerWidth) * 2 - 1
  301. mouse.y = -(event.clientY / window.innerHeight) * 2 + 1
  302. this.pointerDownPos = mouse.clone()
  303. this.pointerDownTime = Date.now()
  304. }
  305. Viewer.prototype.onPointerUp = function (event) {
  306. if (event.isPrimary === false) return
  307. mouse.x = (event.clientX / window.innerWidth) * 2 - 1
  308. mouse.y = -(event.clientY / window.innerHeight) * 2 + 1
  309. let now = Date.now()
  310. if (mouse.distanceTo(this.pointerDownPos) < 0.006 && now - this.pointerDownTime < 1000) {
  311. //click
  312. //doubleClick
  313. /* var time = new Date().getTime();
  314. if(time - this.clickTime < 300){
  315. if(this.intersects.length){
  316. console.log('doubleClick');
  317. transitions.cancelById(0)
  318. transitions.start(lerp.vector(this.control.target, this.intersects[0].point), 600, null, 0 , easing.easeInOutQuad, null, Transitions.doubleClick);
  319. }
  320. } */
  321. if (this.hoveredObject) {
  322. this.dispatchEvent({
  323. type: 'clickObject',
  324. id: this.hoveredObject.material.uniforms.map.id
  325. })
  326. }
  327. }
  328. this.pointerDownPos = null
  329. }
  330. Viewer.prototype.checkIntersection = function () {
  331. raycaster.setFromCamera(mouse, this.camera)
  332. raycaster.near = 2
  333. const intersects = raycaster.intersectObject(this.cardGroup, true)
  334. var recover = () => {
  335. if (this.hoveredObject) {
  336. }
  337. }
  338. if (intersects.length > 0) {
  339. const hoveredObject = intersects[0].object
  340. if (this.hoveredObject != hoveredObject) {
  341. recover()
  342. this.dispatchEvent({
  343. type: 'hoverObject',
  344. name: hoveredObject.material.uniforms.map.name
  345. })
  346. this.hoveredObject = hoveredObject
  347. this.dom.style.cursor = 'pointer'
  348. }
  349. } else {
  350. recover()
  351. this.dispatchEvent({ type: 'mouseoutObject' })
  352. this.dom.style.cursor = ''
  353. this.hoveredObject = null
  354. }
  355. this.intersects = intersects
  356. }
  357. Viewer.prototype.setAutoMove = function (state) {
  358. //设置相机飞行状态
  359. this.autoMove = !!state
  360. }
  361. Object.assign(Viewer.prototype, THREE.EventDispatcher.prototype)
  362. //============
  363. var startTime = new Date().getTime()
  364. var viewer = new Viewer(0, $('#player')[0])