123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264 |
- /* eslint-disable max-len */
- import { watch, ref, shallowRef, computed, toRefs } from 'vue'
- import {
- MaybeComputedRef,
- MaybeElementRef,
- resolveUnref,
- unrefElement,
- useDevicePixelRatio,
- useElementSize,
- useWindowSize,
- } from '@vueuse/core'
- import {
- WebGLRendererParameters,
- NoToneMapping,
- LinearEncoding,
- WebGLRenderer,
- ShadowMapType,
- PCFShadowMap,
- Clock,
- } from 'three'
- import type { TextureEncoding, ToneMapping } from 'three'
- import { useRenderLoop, useTres } from '/@/core/'
- import { normalizeColor } from '/@/utils/normalize'
- import { TresColor } from '/@/types'
- import { rendererPresets, RendererPresetsType } from './const'
- import { merge } from '/@/utils'
- import { useLogger } from '/@/composables/useLogger'
- export interface UseRendererOptions extends WebGLRendererParameters {
- /**
- * Enable shadows in the Renderer
- *
- * @default false
- */
- shadows?: MaybeComputedRef<boolean>
- /**
- * Set the shadow map type
- * Can be PCFShadowMap, PCFSoftShadowMap, BasicShadowMap, VSMShadowMap
- * [see](https://threejs.org/docs/?q=we#api/en/constants/Renderer)
- *
- * @default PCFSoftShadowMap
- */
- shadowMapType?: MaybeComputedRef<ShadowMapType>
- /**
- * Whether to use physically correct lighting mode.
- * See the [lights / physical example](https://threejs.org/examples/#webgl_lights_physical).
- *
- * @default false
- * @deprecated Use {@link WebGLRenderer.useLegacyLights useLegacyLights} instead.
- */
- physicallyCorrectLights?: MaybeComputedRef<boolean>
- /**
- * Whether to use legacy lighting mode.
- *
- * @type {MaybeComputedRef<boolean>}
- * @memberof UseRendererOptions
- */
- useLegacyLights?: MaybeComputedRef<boolean>
- /**
- * Defines the output encoding of the renderer.
- * Can be LinearEncoding, sRGBEncoding
- *
- * @default LinearEncoding
- */
- outputEncoding?: MaybeComputedRef<TextureEncoding>
- /**
- * Defines the tone mapping used by the renderer.
- * Can be NoToneMapping, LinearToneMapping, ReinhardToneMapping, Uncharted2ToneMapping, CineonToneMapping, ACESFilmicToneMapping, CustomToneMapping
- *
- * @default NoToneMapping
- */
- toneMapping?: MaybeComputedRef<ToneMapping>
- /**
- * Defines the tone mapping exposure used by the renderer.
- *
- * @default 1
- */
- toneMappingExposure?: MaybeComputedRef<number>
- /**
- * The context used by the renderer.
- *
- * @default undefined
- */
- context?: WebGLRenderingContext | undefined
- /**
- * Provides a hint to the user agent indicating what configuration of GPU is suitable for this WebGL context.
- * Can be "high-performance", "low-power" or "default".
- *
- * @default "default"
- */
- powerPreference?: 'high-performance' | 'low-power' | 'default'
- /**
- * Whether to preserve the buffers until manually cleared or overwritten.
- *
- * @default false
- */
- preserveDrawingBuffer?: boolean
- /**
- * The color value to use when clearing the canvas.
- *
- * @default 0x000000
- */
- clearColor?: MaybeComputedRef<TresColor>
- windowSize?: MaybeComputedRef<boolean>
- preset?: RendererPresetsType
- }
- const renderer = shallowRef<WebGLRenderer>()
- const isReady = ref(false)
- /**
- * Reactive Three.js WebGLRenderer instance
- *
- * @param canvas
- * @param container
- * @param {UseRendererOptions} [options]
- */
- export function useRenderer(canvas: MaybeElementRef, container: MaybeElementRef, options: UseRendererOptions) {
- // Defaults
- const {
- alpha = true,
- antialias = true,
- depth,
- logarithmicDepthBuffer,
- failIfMajorPerformanceCaveat,
- precision,
- premultipliedAlpha,
- stencil,
- shadows = false,
- shadowMapType = PCFShadowMap,
- physicallyCorrectLights = false,
- useLegacyLights = false,
- outputEncoding = LinearEncoding,
- toneMapping = NoToneMapping,
- toneMappingExposure = 1,
- context = undefined,
- powerPreference = 'default',
- preserveDrawingBuffer = false,
- clearColor,
- windowSize = false,
- preset = undefined,
- } = toRefs(options)
- const { setState } = useTres()
- const { width, height } = resolveUnref(windowSize) ? useWindowSize() : useElementSize(container)
- const { logError } = useLogger()
- const { pixelRatio } = useDevicePixelRatio()
- const { pause, resume } = useRenderLoop()
- const aspectRatio = computed(() => width.value / height.value)
- const updateRendererSize = () => {
- if (!renderer.value) {
- return
- }
- renderer.value.setSize(width.value, height.value)
- renderer.value.setPixelRatio(Math.min(pixelRatio.value, 2))
- }
- const updateRendererOptions = () => {
- if (!renderer.value) {
- return
- }
- const rendererPreset = resolveUnref(preset)
- if (rendererPreset) {
- if (!(rendererPreset in rendererPresets))
- logError('Renderer Preset must be one of these: ' + Object.keys(rendererPresets).join(', '))
- merge(renderer.value, rendererPresets[rendererPreset])
- return
- }
- renderer.value.shadowMap.enabled = resolveUnref(shadows) as boolean
- renderer.value.shadowMap.type = resolveUnref(shadowMapType) as ShadowMapType
- renderer.value.toneMapping = (resolveUnref(toneMapping) as ToneMapping) || NoToneMapping
- renderer.value.toneMappingExposure = resolveUnref(toneMappingExposure) as number
- renderer.value.outputEncoding = (resolveUnref(outputEncoding) as TextureEncoding) || LinearEncoding
- if (clearColor?.value) renderer.value.setClearColor(normalizeColor(resolveUnref(clearColor) as TresColor))
- /* renderer.value.physicallyCorrectLights = resolveUnref(physicallyCorrectLights) as boolean */
- renderer.value.useLegacyLights = resolveUnref(useLegacyLights) as boolean
- }
- const init = () => {
- const _canvas = unrefElement(canvas)
- if (renderer.value || !_canvas) {
- return
- }
- renderer.value = new WebGLRenderer({
- canvas: _canvas,
- alpha: resolveUnref(alpha),
- antialias: resolveUnref(antialias),
- context: resolveUnref(context),
- depth: resolveUnref(depth),
- failIfMajorPerformanceCaveat: resolveUnref(failIfMajorPerformanceCaveat),
- logarithmicDepthBuffer: resolveUnref(logarithmicDepthBuffer),
- powerPreference: resolveUnref(powerPreference),
- precision: resolveUnref(precision),
- stencil: resolveUnref(stencil),
- preserveDrawingBuffer: resolveUnref(preserveDrawingBuffer),
- premultipliedAlpha: resolveUnref(premultipliedAlpha),
- })
- setState('renderer', renderer.value)
- setState('clock', new Clock())
- setState('aspectRatio', aspectRatio)
- updateRendererOptions()
- updateRendererSize()
- resume()
- isReady.value = true
- }
- const dispose = () => {
- if (!renderer.value) {
- return
- }
- renderer.value.dispose()
- renderer.value = undefined
- isReady.value = false
- pause()
- }
- watch([aspectRatio, pixelRatio], updateRendererSize)
- watch(
- [shadows, shadowMapType, outputEncoding, useLegacyLights, toneMapping, toneMappingExposure, clearColor],
- updateRendererOptions,
- )
- watch(
- () => [canvas, container],
- () => {
- if (unrefElement(canvas) && unrefElement(container)) {
- init()
- }
- },
- { immediate: true, deep: true },
- )
- return {
- renderer,
- isReady,
- dispose,
- aspectRatio,
- }
- }
- export type UseRendererReturn = ReturnType<typeof useRenderer>
|