index.ts 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. /* eslint-disable max-len */
  2. import { watch, ref, shallowRef, computed, toRefs } from 'vue'
  3. import {
  4. MaybeComputedRef,
  5. MaybeElementRef,
  6. resolveUnref,
  7. unrefElement,
  8. useDevicePixelRatio,
  9. useElementSize,
  10. useWindowSize,
  11. } from '@vueuse/core'
  12. import {
  13. WebGLRendererParameters,
  14. NoToneMapping,
  15. LinearEncoding,
  16. WebGLRenderer,
  17. ShadowMapType,
  18. PCFShadowMap,
  19. Clock,
  20. } from 'three'
  21. import type { TextureEncoding, ToneMapping } from 'three'
  22. import { useRenderLoop, useTres } from '/@/core/'
  23. import { normalizeColor } from '/@/utils/normalize'
  24. import { TresColor } from '/@/types'
  25. import { rendererPresets, RendererPresetsType } from './const'
  26. import { merge } from '/@/utils'
  27. import { useLogger } from '/@/composables/useLogger'
  28. export interface UseRendererOptions extends WebGLRendererParameters {
  29. /**
  30. * Enable shadows in the Renderer
  31. *
  32. * @default false
  33. */
  34. shadows?: MaybeComputedRef<boolean>
  35. /**
  36. * Set the shadow map type
  37. * Can be PCFShadowMap, PCFSoftShadowMap, BasicShadowMap, VSMShadowMap
  38. * [see](https://threejs.org/docs/?q=we#api/en/constants/Renderer)
  39. *
  40. * @default PCFSoftShadowMap
  41. */
  42. shadowMapType?: MaybeComputedRef<ShadowMapType>
  43. /**
  44. * Whether to use physically correct lighting mode.
  45. * See the [lights / physical example](https://threejs.org/examples/#webgl_lights_physical).
  46. *
  47. * @default false
  48. * @deprecated Use {@link WebGLRenderer.useLegacyLights useLegacyLights} instead.
  49. */
  50. physicallyCorrectLights?: MaybeComputedRef<boolean>
  51. /**
  52. * Whether to use legacy lighting mode.
  53. *
  54. * @type {MaybeComputedRef<boolean>}
  55. * @memberof UseRendererOptions
  56. */
  57. useLegacyLights?: MaybeComputedRef<boolean>
  58. /**
  59. * Defines the output encoding of the renderer.
  60. * Can be LinearEncoding, sRGBEncoding
  61. *
  62. * @default LinearEncoding
  63. */
  64. outputEncoding?: MaybeComputedRef<TextureEncoding>
  65. /**
  66. * Defines the tone mapping used by the renderer.
  67. * Can be NoToneMapping, LinearToneMapping, ReinhardToneMapping, Uncharted2ToneMapping, CineonToneMapping, ACESFilmicToneMapping, CustomToneMapping
  68. *
  69. * @default NoToneMapping
  70. */
  71. toneMapping?: MaybeComputedRef<ToneMapping>
  72. /**
  73. * Defines the tone mapping exposure used by the renderer.
  74. *
  75. * @default 1
  76. */
  77. toneMappingExposure?: MaybeComputedRef<number>
  78. /**
  79. * The context used by the renderer.
  80. *
  81. * @default undefined
  82. */
  83. context?: WebGLRenderingContext | undefined
  84. /**
  85. * Provides a hint to the user agent indicating what configuration of GPU is suitable for this WebGL context.
  86. * Can be "high-performance", "low-power" or "default".
  87. *
  88. * @default "default"
  89. */
  90. powerPreference?: 'high-performance' | 'low-power' | 'default'
  91. /**
  92. * Whether to preserve the buffers until manually cleared or overwritten.
  93. *
  94. * @default false
  95. */
  96. preserveDrawingBuffer?: boolean
  97. /**
  98. * The color value to use when clearing the canvas.
  99. *
  100. * @default 0x000000
  101. */
  102. clearColor?: MaybeComputedRef<TresColor>
  103. windowSize?: MaybeComputedRef<boolean>
  104. preset?: RendererPresetsType
  105. }
  106. const renderer = shallowRef<WebGLRenderer>()
  107. const isReady = ref(false)
  108. /**
  109. * Reactive Three.js WebGLRenderer instance
  110. *
  111. * @param canvas
  112. * @param container
  113. * @param {UseRendererOptions} [options]
  114. */
  115. export function useRenderer(canvas: MaybeElementRef, container: MaybeElementRef, options: UseRendererOptions) {
  116. // Defaults
  117. const {
  118. alpha = true,
  119. antialias = true,
  120. depth,
  121. logarithmicDepthBuffer,
  122. failIfMajorPerformanceCaveat,
  123. precision,
  124. premultipliedAlpha,
  125. stencil,
  126. shadows = false,
  127. shadowMapType = PCFShadowMap,
  128. physicallyCorrectLights = false,
  129. useLegacyLights = false,
  130. outputEncoding = LinearEncoding,
  131. toneMapping = NoToneMapping,
  132. toneMappingExposure = 1,
  133. context = undefined,
  134. powerPreference = 'default',
  135. preserveDrawingBuffer = false,
  136. clearColor,
  137. windowSize = false,
  138. preset = undefined,
  139. } = toRefs(options)
  140. const { setState } = useTres()
  141. const { width, height } = resolveUnref(windowSize) ? useWindowSize() : useElementSize(container)
  142. const { logError } = useLogger()
  143. const { pixelRatio } = useDevicePixelRatio()
  144. const { pause, resume } = useRenderLoop()
  145. const aspectRatio = computed(() => width.value / height.value)
  146. const updateRendererSize = () => {
  147. if (!renderer.value) {
  148. return
  149. }
  150. renderer.value.setSize(width.value, height.value)
  151. renderer.value.setPixelRatio(Math.min(pixelRatio.value, 2))
  152. }
  153. const updateRendererOptions = () => {
  154. if (!renderer.value) {
  155. return
  156. }
  157. const rendererPreset = resolveUnref(preset)
  158. if (rendererPreset) {
  159. if (!(rendererPreset in rendererPresets))
  160. logError('Renderer Preset must be one of these: ' + Object.keys(rendererPresets).join(', '))
  161. merge(renderer.value, rendererPresets[rendererPreset])
  162. return
  163. }
  164. renderer.value.shadowMap.enabled = resolveUnref(shadows) as boolean
  165. renderer.value.shadowMap.type = resolveUnref(shadowMapType) as ShadowMapType
  166. renderer.value.toneMapping = (resolveUnref(toneMapping) as ToneMapping) || NoToneMapping
  167. renderer.value.toneMappingExposure = resolveUnref(toneMappingExposure) as number
  168. renderer.value.outputEncoding = (resolveUnref(outputEncoding) as TextureEncoding) || LinearEncoding
  169. if (clearColor?.value) renderer.value.setClearColor(normalizeColor(resolveUnref(clearColor) as TresColor))
  170. /* renderer.value.physicallyCorrectLights = resolveUnref(physicallyCorrectLights) as boolean */
  171. renderer.value.useLegacyLights = resolveUnref(useLegacyLights) as boolean
  172. }
  173. const init = () => {
  174. const _canvas = unrefElement(canvas)
  175. if (renderer.value || !_canvas) {
  176. return
  177. }
  178. renderer.value = new WebGLRenderer({
  179. canvas: _canvas,
  180. alpha: resolveUnref(alpha),
  181. antialias: resolveUnref(antialias),
  182. context: resolveUnref(context),
  183. depth: resolveUnref(depth),
  184. failIfMajorPerformanceCaveat: resolveUnref(failIfMajorPerformanceCaveat),
  185. logarithmicDepthBuffer: resolveUnref(logarithmicDepthBuffer),
  186. powerPreference: resolveUnref(powerPreference),
  187. precision: resolveUnref(precision),
  188. stencil: resolveUnref(stencil),
  189. preserveDrawingBuffer: resolveUnref(preserveDrawingBuffer),
  190. premultipliedAlpha: resolveUnref(premultipliedAlpha),
  191. })
  192. setState('renderer', renderer.value)
  193. setState('clock', new Clock())
  194. setState('aspectRatio', aspectRatio)
  195. updateRendererOptions()
  196. updateRendererSize()
  197. resume()
  198. isReady.value = true
  199. }
  200. const dispose = () => {
  201. if (!renderer.value) {
  202. return
  203. }
  204. renderer.value.dispose()
  205. renderer.value = undefined
  206. isReady.value = false
  207. pause()
  208. }
  209. watch([aspectRatio, pixelRatio], updateRendererSize)
  210. watch(
  211. [shadows, shadowMapType, outputEncoding, useLegacyLights, toneMapping, toneMappingExposure, clearColor],
  212. updateRendererOptions,
  213. )
  214. watch(
  215. () => [canvas, container],
  216. () => {
  217. if (unrefElement(canvas) && unrefElement(container)) {
  218. init()
  219. }
  220. },
  221. { immediate: true, deep: true },
  222. )
  223. return {
  224. renderer,
  225. isReady,
  226. dispose,
  227. aspectRatio,
  228. }
  229. }
  230. export type UseRendererReturn = ReturnType<typeof useRenderer>