index.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423
  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('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 = 10
  82. while (i-- > 0) {
  83. this.addCard(true)
  84. }
  85. let add = () => {
  86. if (document.hidden) return
  87. this.addCard()
  88. setTimeout(add, 40000 * Math.random() * this.getDensity()) //当前视野中密度越小 添加越频繁
  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(cardNames[cardIndex].img, map => {
  110. let card = new THREE.Mesh(
  111. planeGeo,
  112. new THREE.ShaderMaterial({
  113. uniforms: {
  114. map: { value: map },
  115. resolution: {
  116. value: new THREE.Vector2(
  117. this.renderer.domElement.width,
  118. this.renderer.domElement.height
  119. )
  120. },
  121. blurRadius: { value: 0 }, //像素
  122. opacity: { value: 0 }
  123. },
  124. vertexShader: BlurShader.vertexShader,
  125. fragmentShader: BlurShader.fragmentShader,
  126. transparent: true,
  127. side: 2
  128. })
  129. )
  130. Object.defineProperty(card.material, 'opacity', {
  131. get: function () {
  132. return card.material.uniforms.opacity.value
  133. },
  134. set: function (e) {
  135. card.material.uniforms.opacity.value = e
  136. card.material.uniforms.blurRadius.value = math.linearClamp(e, [0, 0.4], [40, 0])
  137. }
  138. })
  139. let direction,
  140. far = setting.cards.far
  141. if (around) {
  142. //在四周所有方向都可生成,在一开始时需要
  143. let n = 0.4 //范围0-1 越大越可能接近相机
  144. far = far * (1 - n * Math.random()) //靠近一点
  145. direction = new THREE.Vector3(0, 0, -1).applyEuler(
  146. new THREE.Euler(0, Math.PI * 2 * Math.random(), 0)
  147. )
  148. } else {
  149. //仅在相机前方生成,因为相机往这个方向移动,最前方空缺
  150. direction = new THREE.Vector3(0, 0, -1)
  151. .applyQuaternion(this.camera.quaternion)
  152. .applyEuler(new THREE.Euler(0, this.camera.hfov * (Math.random() - 0.5), 0))
  153. }
  154. let h = (Math.random() * 2 - 1) * setting.cards.highest * 0.8 // *0.8是因为靠近后就会飞出视线
  155. card.position
  156. .copy(this.camera.position)
  157. .add(direction.add(new THREE.Vector3(0, h, 0)).multiplyScalar(far))
  158. card.scale.set(map.image.width / 500, map.image.height / 500, 1)
  159. this.cardGroup.add(card)
  160. card.transition = transitions.start(
  161. lerp.property(card.material, 'opacity', 1, e => {
  162. //console.log(e, card.uuid)
  163. }),
  164. setting.cards.fadeInDur
  165. )
  166. })
  167. }
  168. Viewer.prototype.removeCards = function () {
  169. //移除超过bound的卡
  170. let needRemove = this.cardGroup.children.filter(card => {
  171. if (card.disToCam > setting.cards.far) {
  172. card.material.dispose()
  173. transitions.cancel(card.transition)
  174. //console.log('remove一张卡')
  175. return true
  176. }
  177. })
  178. needRemove.forEach(card => card.parent.remove(card))
  179. //needRemove.length>0 && console.log('当前存在卡数', this.cardGroup.children.length)
  180. }
  181. Viewer.prototype.update = function (deltaTime) {
  182. //绘制的时候同时更新
  183. this.setSize()
  184. this.control.update(deltaTime)
  185. transitions.update(deltaTime)
  186. if (this.autoMove) {
  187. let direction = new THREE.Vector3(0, 0, -1).applyQuaternion(this.camera.quaternion)
  188. let moveSpeed = 0.8
  189. this.camera.position.add(direction.multiplyScalar(deltaTime * moveSpeed))
  190. }
  191. this.cardGroup.children.forEach(card => {
  192. card.quaternion.copy(this.camera.quaternion)
  193. let dis = card.position.clone().setY(0).distanceTo(this.camera.position.clone().setY(0))
  194. if (!card.transition.running) {
  195. card.material.opacity = math.linearClamp(
  196. dis,
  197. [setting.cards.near, setting.cards.beginFadeNear],
  198. [0, 1]
  199. )
  200. }
  201. card.disToCam = dis
  202. })
  203. this.removeCards()
  204. var needsUpdate = 1
  205. if (needsUpdate) {
  206. this.renderer.autoClear = true
  207. this.renderer.render(this.scene, this.camera)
  208. }
  209. }
  210. Viewer.prototype.bindEvents = function () {
  211. this.renderer.domElement.addEventListener('pointermove', this.onPointerMove.bind(this), false)
  212. this.renderer.domElement.addEventListener('pointerdown', this.onPointerDown.bind(this), false)
  213. this.renderer.domElement.addEventListener('pointerup', this.onPointerUp.bind(this), false)
  214. }
  215. Viewer.prototype.setRenderer = function () {
  216. try {
  217. ;((this.renderer = new THREE.WebGLRenderer({
  218. canvas: $(this.dom).find('canvas')[0],
  219. antialias: true
  220. })),
  221. this.renderer.setPixelRatio(window.devicePixelRatio ? window.devicePixelRatio : 1),
  222. (this.renderer.autoClear = false))
  223. this.renderer.setClearColor(0xffffff, 1)
  224. console.log('ContextCreated')
  225. //this.emit(Events.ContextCreated)
  226. } catch (e) {
  227. console.error('Unable to create a WebGL rendering context')
  228. }
  229. }
  230. Viewer.prototype.hasChanged = function () {
  231. //判断画面是否改变了,改变后需要更新一些东西
  232. var copy = function () {
  233. this.previousState = {
  234. projectionMatrix: this.camera.projectionMatrix.clone(), //worldMatrix在control时归零了所以不用了吧,用position和qua也一样
  235. position: this.camera.position.clone(),
  236. quaternion: this.camera.quaternion.clone(),
  237. //mouse: this.mouse.clone(),
  238. fov: this.camera.fov
  239. }
  240. }.bind(this)
  241. if (!this.previousState) {
  242. copy()
  243. return { cameraChanged: !0, changed: !0 }
  244. }
  245. var cameraChanged =
  246. !this.camera.projectionMatrix.equals(this.previousState.projectionMatrix) ||
  247. !this.camera.position.equals(this.previousState.position) ||
  248. !this.camera.quaternion.equals(this.previousState.quaternion)
  249. var changed = cameraChanged //|| !this.mouse.equals(this.previousState.mouse)
  250. copy()
  251. return { cameraChanged, changed }
  252. }
  253. Viewer.prototype.setSize = (function () {
  254. var w, h, pixelRatio
  255. return function () {
  256. if (
  257. w != this.dom.clientWidth ||
  258. h != this.dom.clientHeight ||
  259. pixelRatio != window.devicePixelRatio
  260. ) {
  261. w = this.dom.clientWidth
  262. h = this.dom.clientHeight
  263. pixelRatio = window.devicePixelRatio
  264. this.camera.aspect = w / h
  265. this.camera.updateProjectionMatrix()
  266. this.renderer.setSize(w, h, false, pixelRatio)
  267. this.camera.hfov = cameraLight.getHFOVForCamera(this.camera, true)
  268. this.cardGroup.children.forEach(card => card.material.uniforms.resolution.value.set(w, h))
  269. }
  270. }
  271. })()
  272. ;((Viewer.prototype.animate = function () {
  273. var deltaTime = Math.min(1, this.updateClock.getDelta())
  274. this.update(deltaTime)
  275. //bus.emit('player/position/change', {x:this.position.x, y:this.position.z, lon: this.cameraControls.controls.panorama.lon})
  276. window.requestAnimationFrame(this.animate.bind(this))
  277. }),
  278. (Viewer.prototype.onPointerMove = function (event) {
  279. if (event.isPrimary === false) return
  280. mouse.x = (event.clientX / window.innerWidth) * 2 - 1
  281. mouse.y = -(event.clientY / window.innerHeight) * 2 + 1
  282. if (!this.pointerDownPos) this.checkIntersection()
  283. }))
  284. Viewer.prototype.onPointerDown = function (event) {
  285. if (event.isPrimary === false) return
  286. mouse.x = (event.clientX / window.innerWidth) * 2 - 1
  287. mouse.y = -(event.clientY / window.innerHeight) * 2 + 1
  288. this.pointerDownPos = mouse.clone()
  289. this.pointerDownTime = Date.now()
  290. }
  291. Viewer.prototype.onPointerUp = function (event) {
  292. if (event.isPrimary === false) return
  293. mouse.x = (event.clientX / window.innerWidth) * 2 - 1
  294. mouse.y = -(event.clientY / window.innerHeight) * 2 + 1
  295. let now = Date.now()
  296. if (mouse.distanceTo(this.pointerDownPos) < 0.006 && now - this.pointerDownTime < 1000) {
  297. //click
  298. //doubleClick
  299. /* var time = new Date().getTime();
  300. if(time - this.clickTime < 300){
  301. if(this.intersects.length){
  302. console.log('doubleClick');
  303. transitions.cancelById(0)
  304. transitions.start(lerp.vector(this.control.target, this.intersects[0].point), 600, null, 0 , easing.easeInOutQuad, null, Transitions.doubleClick);
  305. }
  306. } */
  307. if (this.hoveredObject) {
  308. this.dispatchEvent({
  309. type: 'clickObject',
  310. imgName: this.hoveredObject.material.uniforms.map.value.image.src.split('/').pop()
  311. })
  312. }
  313. }
  314. this.pointerDownPos = null
  315. }
  316. Viewer.prototype.checkIntersection = function () {
  317. raycaster.setFromCamera(mouse, this.camera)
  318. raycaster.near = 2
  319. const intersects = raycaster.intersectObject(this.cardGroup, true)
  320. var recover = () => {
  321. if (this.hoveredObject) {
  322. }
  323. }
  324. if (intersects.length > 0) {
  325. const hoveredObject = intersects[0].object
  326. if (this.hoveredObject != hoveredObject) {
  327. recover()
  328. this.dispatchEvent({
  329. type: 'hoverObject',
  330. imgName: hoveredObject.material.uniforms.map.value.image.src.split('/').pop()
  331. })
  332. this.hoveredObject = hoveredObject
  333. this.dom.style.cursor = 'pointer'
  334. }
  335. } else {
  336. recover()
  337. this.dispatchEvent({ type: 'mouseoutObject' })
  338. this.dom.style.cursor = ''
  339. this.hoveredObject = null
  340. }
  341. this.intersects = intersects
  342. }
  343. Viewer.prototype.setAutoMove = function (state) {
  344. //设置相机飞行状态
  345. this.autoMove = !!state
  346. }
  347. Object.assign(Viewer.prototype, THREE.EventDispatcher.prototype)
  348. //============
  349. var startTime = new Date().getTime()
  350. var viewer = new Viewer(0, $('#player')[0])