+
+```
+
#### Mask
diff --git a/src/core/RenderCubeTexture.tsx b/src/core/RenderCubeTexture.tsx
new file mode 100644
index 000000000..0021ac868
--- /dev/null
+++ b/src/core/RenderCubeTexture.tsx
@@ -0,0 +1,149 @@
+import * as THREE from 'three'
+import * as React from 'react'
+import { ReactThreeFiber, createPortal, useFrame, useThree } from '@react-three/fiber'
+import { ForwardRefComponent } from '../helpers/ts-utils'
+
+export type RenderCubeTextureProps = Omit & {
+ /** Optional stencil buffer, defaults to false */
+ stencilBuffer?: boolean
+ /** Optional depth buffer, defaults to true */
+ depthBuffer?: boolean
+ /** Optional generate mipmaps, defaults to false */
+ generateMipmaps?: boolean
+ /** Optional render priority, defaults to 0 */
+ renderPriority?: number
+ /** Optional event priority, defaults to 0 */
+ eventPriority?: number
+ /** Optional frame count, defaults to Infinity. If you set it to 1, it would only render a single frame, etc */
+ frames?: number
+ /** Optional event compute, defaults to undefined */
+ compute?: (event: any, state: any, previous: any) => false | undefined
+ /** Flip cubemap, see https://github.com/mrdoob/three.js/blob/master/src/renderers/WebGLCubeRenderTarget.js */
+ flip?: boolean
+ /** Cubemap resolution (for each of the 6 takes), null === full screen resolution, default: 896 */
+ resolution?: number
+ /** Children will be rendered into a portal */
+ children: React.ReactNode
+ near?: number
+ far?: number
+ position?: ReactThreeFiber.Vector3
+ rotation?: ReactThreeFiber.Euler
+ scale?: ReactThreeFiber.Vector3
+ quaternion?: ReactThreeFiber.Quaternion
+ matrix?: ReactThreeFiber.Matrix4
+ matrixAutoUpdate?: boolean
+}
+
+export type RenderCubeTextureApi = {
+ scene: THREE.Scene
+ fbo: THREE.WebGLCubeRenderTarget
+ camera: THREE.CubeCamera
+}
+
+export const RenderCubeTexture: ForwardRefComponent = React.forwardRef(
+ (
+ {
+ children,
+ compute,
+ renderPriority = -1,
+ eventPriority = 0,
+ frames = Infinity,
+ stencilBuffer = false,
+ depthBuffer = true,
+ generateMipmaps = false,
+ resolution = 896,
+ near = 0.1,
+ far = 1000,
+ flip = false,
+ position,
+ rotation,
+ scale,
+ quaternion,
+ matrix,
+ matrixAutoUpdate,
+ ...props
+ },
+ forwardRef
+ ) => {
+ const { size, viewport } = useThree()
+
+ const camera = React.useRef(null!)
+ const fbo = React.useMemo(() => {
+ const fbo = new THREE.WebGLCubeRenderTarget(
+ Math.max((resolution || size.width) * viewport.dpr, (resolution || size.height) * viewport.dpr),
+ {
+ stencilBuffer,
+ depthBuffer,
+ generateMipmaps,
+ }
+ )
+ fbo.texture.isRenderTargetTexture = !flip
+ fbo.texture.flipY = true
+ fbo.texture.type = THREE.HalfFloatType
+ return fbo
+ }, [resolution, flip])
+
+ React.useEffect(() => {
+ return () => fbo.dispose()
+ }, [fbo])
+
+ const [vScene] = React.useState(() => new THREE.Scene())
+ const uvCompute = React.useCallback((event, state, previous) => {
+ // https://github.com/pmndrs/react-three-fiber/pull/782
+ // Events trigger outside of canvas when moved, use offsetX/Y by default and allow overrides
+ state.pointer.set((event.offsetX / state.size.width) * 2 - 1, -(event.offsetY / state.size.height) * 2 + 1)
+ state.raycaster.setFromCamera(state.pointer, state.camera)
+ }, [])
+
+ React.useImperativeHandle(forwardRef, () => ({ scene: vScene, fbo, camera: camera.current }), [fbo])
+
+ return (
+ <>
+ {createPortal(
+
+ {children}
+ {/* Without an element that receives pointer events state.pointer will always be 0/0 */}
+ null} />
+ ,
+ vScene,
+ { events: { compute: compute || uvCompute, priority: eventPriority } }
+ )}
+
+
+ >
+ )
+ }
+)
+
+// The container component has to be separate, it can not be inlined because "useFrame(state" when run inside createPortal will return
+// the portals own state which includes user-land overrides (custom cameras etc), but if it is executed in 's render function
+// it would return the default state.
+function Container({
+ frames,
+ renderPriority,
+ children,
+ camera,
+}: {
+ frames: number
+ renderPriority: number
+ children: React.ReactNode
+ camera: React.MutableRefObject
+}) {
+ let count = 0
+ useFrame((state) => {
+ if (frames === Infinity || count < frames) {
+ camera.current.update(state.gl, state.scene)
+ count++
+ }
+ }, renderPriority)
+ return <>{children}>
+}
diff --git a/src/core/index.ts b/src/core/index.ts
index b34aa0df6..15ab3c14a 100644
--- a/src/core/index.ts
+++ b/src/core/index.ts
@@ -138,6 +138,7 @@ export * from './PerformanceMonitor'
// Portals
export * from './RenderTexture'
+export * from './RenderCubeTexture'
export * from './Mask'
export * from './Hud'
export * from './MeshPortalMaterial'