index.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426
  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 = 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('./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 / 500, map.image.height / 500, 1)
  163. this.cardGroup.add(card)
  164. card.transition = transitions.start(
  165. lerp.property(card.material, 'opacity', 1, e => {
  166. //console.log(e, card.uuid)
  167. }),
  168. setting.cards.fadeInDur
  169. )
  170. })
  171. }
  172. Viewer.prototype.removeCards = function () {
  173. //移除超过bound的卡
  174. let needRemove = this.cardGroup.children.filter(card => {
  175. if (card.disToCam > setting.cards.far) {
  176. card.material.dispose()
  177. transitions.cancel(card.transition)
  178. //console.log('remove一张卡')
  179. return true
  180. }
  181. })
  182. needRemove.forEach(card => card.parent.remove(card))
  183. //needRemove.length>0 && console.log('当前存在卡数', this.cardGroup.children.length)
  184. }
  185. Viewer.prototype.update = function (deltaTime) {
  186. //绘制的时候同时更新
  187. this.setSize()
  188. this.control.update(deltaTime)
  189. transitions.update(deltaTime)
  190. if (this.autoMove) {
  191. let direction = new THREE.Vector3(0, 0, -1).applyQuaternion(this.camera.quaternion)
  192. let moveSpeed = 0.8
  193. this.camera.position.add(direction.multiplyScalar(deltaTime * moveSpeed))
  194. }
  195. this.cardGroup.children.forEach(card => {
  196. card.quaternion.copy(this.camera.quaternion)
  197. let dis = card.position.clone().setY(0).distanceTo(this.camera.position.clone().setY(0))
  198. if (!card.transition.running) {
  199. card.material.opacity = math.linearClamp(
  200. dis,
  201. [setting.cards.near, setting.cards.beginFadeNear],
  202. [0, 1]
  203. )
  204. }
  205. card.disToCam = dis
  206. })
  207. this.removeCards()
  208. var needsUpdate = 1
  209. if (needsUpdate) {
  210. this.renderer.autoClear = true
  211. this.renderer.render(this.scene, this.camera)
  212. }
  213. }
  214. Viewer.prototype.bindEvents = function () {
  215. this.renderer.domElement.addEventListener('pointermove', this.onPointerMove.bind(this), false)
  216. this.renderer.domElement.addEventListener('pointerdown', this.onPointerDown.bind(this), false)
  217. this.renderer.domElement.addEventListener('pointerup', this.onPointerUp.bind(this), false)
  218. }
  219. Viewer.prototype.setRenderer = function () {
  220. try {
  221. ;((this.renderer = new THREE.WebGLRenderer({
  222. canvas: $(this.dom).find('canvas')[0],
  223. antialias: true
  224. })),
  225. this.renderer.setPixelRatio(window.devicePixelRatio ? window.devicePixelRatio : 1),
  226. (this.renderer.autoClear = false))
  227. this.renderer.setClearColor(0xffffff, 1)
  228. console.log('ContextCreated')
  229. //this.emit(Events.ContextCreated)
  230. } catch (e) {
  231. console.error('Unable to create a WebGL rendering context')
  232. }
  233. }
  234. Viewer.prototype.hasChanged = function () {
  235. //判断画面是否改变了,改变后需要更新一些东西
  236. var copy = function () {
  237. this.previousState = {
  238. projectionMatrix: this.camera.projectionMatrix.clone(), //worldMatrix在control时归零了所以不用了吧,用position和qua也一样
  239. position: this.camera.position.clone(),
  240. quaternion: this.camera.quaternion.clone(),
  241. //mouse: this.mouse.clone(),
  242. fov: this.camera.fov
  243. }
  244. }.bind(this)
  245. if (!this.previousState) {
  246. copy()
  247. return { cameraChanged: !0, changed: !0 }
  248. }
  249. var cameraChanged =
  250. !this.camera.projectionMatrix.equals(this.previousState.projectionMatrix) ||
  251. !this.camera.position.equals(this.previousState.position) ||
  252. !this.camera.quaternion.equals(this.previousState.quaternion)
  253. var changed = cameraChanged //|| !this.mouse.equals(this.previousState.mouse)
  254. copy()
  255. return { cameraChanged, changed }
  256. }
  257. Viewer.prototype.setSize = (function () {
  258. var w, h, pixelRatio
  259. return function () {
  260. if (
  261. w != this.dom.clientWidth ||
  262. h != this.dom.clientHeight ||
  263. pixelRatio != window.devicePixelRatio
  264. ) {
  265. w = this.dom.clientWidth
  266. h = this.dom.clientHeight
  267. pixelRatio = window.devicePixelRatio
  268. this.camera.aspect = w / h
  269. this.camera.updateProjectionMatrix()
  270. this.renderer.setSize(w, h, false, pixelRatio)
  271. this.camera.hfov = cameraLight.getHFOVForCamera(this.camera, true)
  272. this.cardGroup.children.forEach(card => card.material.uniforms.resolution.value.set(w, h))
  273. }
  274. }
  275. })()
  276. ;((Viewer.prototype.animate = function () {
  277. var deltaTime = Math.min(1, this.updateClock.getDelta())
  278. this.update(deltaTime)
  279. //bus.emit('player/position/change', {x:this.position.x, y:this.position.z, lon: this.cameraControls.controls.panorama.lon})
  280. window.requestAnimationFrame(this.animate.bind(this))
  281. }),
  282. (Viewer.prototype.onPointerMove = function (event) {
  283. if (event.isPrimary === false) return
  284. mouse.x = (event.clientX / window.innerWidth) * 2 - 1
  285. mouse.y = -(event.clientY / window.innerHeight) * 2 + 1
  286. if (!this.pointerDownPos) this.checkIntersection()
  287. }))
  288. Viewer.prototype.onPointerDown = function (event) {
  289. if (event.isPrimary === false) return
  290. mouse.x = (event.clientX / window.innerWidth) * 2 - 1
  291. mouse.y = -(event.clientY / window.innerHeight) * 2 + 1
  292. this.pointerDownPos = mouse.clone()
  293. this.pointerDownTime = Date.now()
  294. }
  295. Viewer.prototype.onPointerUp = function (event) {
  296. if (event.isPrimary === false) return
  297. mouse.x = (event.clientX / window.innerWidth) * 2 - 1
  298. mouse.y = -(event.clientY / window.innerHeight) * 2 + 1
  299. let now = Date.now()
  300. if (mouse.distanceTo(this.pointerDownPos) < 0.006 && now - this.pointerDownTime < 1000) {
  301. //click
  302. //doubleClick
  303. /* var time = new Date().getTime();
  304. if(time - this.clickTime < 300){
  305. if(this.intersects.length){
  306. console.log('doubleClick');
  307. transitions.cancelById(0)
  308. transitions.start(lerp.vector(this.control.target, this.intersects[0].point), 600, null, 0 , easing.easeInOutQuad, null, Transitions.doubleClick);
  309. }
  310. } */
  311. if (this.hoveredObject) {
  312. this.dispatchEvent({
  313. type: 'clickObject',
  314. id: this.hoveredObject.material.uniforms.map.id
  315. })
  316. }
  317. }
  318. this.pointerDownPos = null
  319. }
  320. Viewer.prototype.checkIntersection = function () {
  321. raycaster.setFromCamera(mouse, this.camera)
  322. raycaster.near = 2
  323. const intersects = raycaster.intersectObject(this.cardGroup, true)
  324. var recover = () => {
  325. if (this.hoveredObject) {
  326. }
  327. }
  328. if (intersects.length > 0) {
  329. const hoveredObject = intersects[0].object
  330. if (this.hoveredObject != hoveredObject) {
  331. recover()
  332. this.dispatchEvent({
  333. type: 'hoverObject',
  334. name: hoveredObject.material.uniforms.map.name
  335. })
  336. this.hoveredObject = hoveredObject
  337. this.dom.style.cursor = 'pointer'
  338. }
  339. } else {
  340. recover()
  341. this.dispatchEvent({ type: 'mouseoutObject' })
  342. this.dom.style.cursor = ''
  343. this.hoveredObject = null
  344. }
  345. this.intersects = intersects
  346. }
  347. Viewer.prototype.setAutoMove = function (state) {
  348. //设置相机飞行状态
  349. this.autoMove = !!state
  350. }
  351. Object.assign(Viewer.prototype, THREE.EventDispatcher.prototype)
  352. //============
  353. var startTime = new Date().getTime()
  354. var viewer = new Viewer(0, $('#player')[0])