├── .eslintrc.json ├── .gitignore ├── .gitignore copy ├── .prettierrc ├── LICENSE ├── README.md ├── examples.jpg ├── package.json ├── rollup.config.js ├── src └── index.tsx ├── tsconfig.json └── yarn.lock /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es6": true, 5 | "node": true 6 | }, 7 | "extends": [ 8 | "prettier", 9 | "prettier/react", 10 | "prettier/@typescript-eslint", 11 | "plugin:prettier/recommended", 12 | "plugin:react-hooks/recommended", 13 | "plugin:import/errors", 14 | "plugin:import/warnings", 15 | "prettier", 16 | "prettier/react", 17 | "prettier/@typescript-eslint" 18 | ], 19 | "plugins": ["@typescript-eslint", "react", "react-hooks", "import", "jest", "prettier"], 20 | "parser": "@typescript-eslint/parser", 21 | "parserOptions": { 22 | "ecmaFeatures": { 23 | "jsx": true 24 | }, 25 | "ecmaVersion": 2018, 26 | "sourceType": "module", 27 | "rules": { 28 | "curly": ["warn", "multi-line", "consistent"], 29 | "no-console": "off", 30 | "no-empty-pattern": "warn", 31 | "no-duplicate-imports": "error", 32 | "import/no-unresolved": "off", 33 | "import/export": "error", 34 | // https://github.com/typescript-eslint/typescript-eslint/blob/master/docs/getting-started/linting/FAQ.md#eslint-plugin-import 35 | // We recommend you do not use the following import/* rules, as TypeScript provides the same checks as part of standard type checking: 36 | "import/named": "off", 37 | "import/namespace": "off", 38 | "import/default": "off", 39 | "no-unused-vars": ["warn", { "argsIgnorePattern": "^_", "varsIgnorePattern": "^_" }], 40 | "@typescript-eslint/no-unused-vars": ["warn", { "argsIgnorePattern": "^_", "varsIgnorePattern": "^_" }], 41 | "@typescript-eslint/no-use-before-define": "off", 42 | "@typescript-eslint/no-empty-function": "off", 43 | "@typescript-eslint/no-empty-interface": "off", 44 | "@typescript-eslint/no-explicit-any": "off", 45 | "jest/consistent-test-it": ["error", { "fn": "it", "withinDescribe": "it" }] 46 | } 47 | }, 48 | "settings": { 49 | "react": { 50 | "version": "detect" 51 | }, 52 | "import/extensions": [".js", ".jsx", ".ts", ".tsx"], 53 | "import/parsers": { 54 | "@typescript-eslint/parser": [".js", ".jsx", ".ts", ".tsx"] 55 | }, 56 | "import/resolver": { 57 | "node": { 58 | "extensions": [".js", ".jsx", ".ts", ".tsx", ".json"], 59 | "paths": ["src"] 60 | }, 61 | "alias": { 62 | "extensions": [".js", ".jsx", ".ts", ".tsx", ".json"], 63 | "map": [["react-three-fiber", "./src/targets/web.tsx"]] 64 | } 65 | } 66 | }, 67 | "overrides": [ 68 | { 69 | "files": ["src"], 70 | "parserOptions": { 71 | "project": "./tsconfig.json" 72 | } 73 | } 74 | ] 75 | } 76 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | -------------------------------------------------------------------------------- /.gitignore copy: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "trailingComma": "es5", 4 | "singleQuote": true, 5 | "tabWidth": 2, 6 | "printWidth": 120 7 | } 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT license statement included in lglTracer: 2 | 3 | GLSL-PathTracer: 4 | Copyright 2019-2021 Asif Ali. MIT License 5 | 6 | ray-tracing-renderer: 7 | Copyright 2019 HOVER. MIT License 8 | 9 | lglTracer is not open source. you are free to use this library except for projects for commercial purposes. 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![](/examples.jpg) 2 | 3 | A React(-[three-fiber](https://github.com/pmndrs/react-three-fiber)) abstraction for the [LGL-Raytracer](http://lgltracer.com/). 4 | 5 | It does its best to remove all unwanted complexity, you can build your scenes as you always would. This is mostly for photorealistic still-images that can take a while to process but will look absolutely stunning. It is side-effect free, when you unmount it goes back to the default WebGLRenderer. 6 | 7 | Demos: [[sandbox](https://codesandbox.io/s/basic-demo-forked-rnuve)], [[studio-setup](https://codesandbox.io/s/lgl-raytracer-forked-8yfnd)] 8 | 9 | ```shell 10 | npm install @react-three/lgl 11 | ``` 12 | 13 | ```jsx 14 | import { Canvas } from '@react-three/fiber' 15 | import { Raytracer } from '@react-three/lgl' 16 | 17 | function App() { 18 | return ( 19 | 20 | 21 | 22 | 23 | 24 | 25 | ... 26 | 27 | 28 | ) 29 | } 30 | ``` 31 | 32 | ### Options 33 | 34 | - `samples`, How many frames it takes to complete a still-image, `64` by default. Set this to something higher if you want to wait for high-quality images, or `Infinity` if you want it to go on forever, but keep in mind that raytracing is very expensive! 35 | 36 | Otherwise `` takes all the LGL raytracer's options: https://lgltracer.com/docs/index.html#/api/LGLTracerRenderer 37 | 38 | ### Lights 39 | 40 | LGL ignores threejs lights, it wants to use its own. But in three-fiber you can simply extend, and now the JSX natives will refer to LGL. 41 | 42 | ```jsx 43 | import { extend } from '@react-three/fiber' 44 | import { PointLight, RectAreaLight } from 'lgl-tracer' 45 | 46 | extend({ PointLight, RectAreaLight }) 47 | 48 | 49 | 50 | ``` 51 | 52 | If you plan to switch between renderers you can make lights opt in. 53 | 54 | ```jsx 55 | extend({ LglPointLight: PointLight, LglRectAreaLight: RectAreaLight }) 56 | 57 | 58 | 59 | ``` 60 | 61 | ### Environmental lighting 62 | 63 | Simply drop the `` component from drei into your scene, it knows how to work with that ootb, just make sure both the raytracer and the environment are under the same suspense boundary so that they are in sync. 64 | 65 | ```jsx 66 | import { Environment } from '@react-three/drei' 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | ``` 75 | 76 | ### Movement 77 | 78 | Your scene has to be static, it will ignore moving parts. This will never be fast enough for runtime usage but you can get away with some camera movement by lowering your resolution (and your expectations). Do not forget to mark your controls as `makeDefault` so that the raycaster can react to it. Try something like this for example: 79 | 80 | ```jsx 81 | import { OrbitControls } from '@react-three/drei' 82 | 83 | 84 | 90 | ... 91 | 92 | 93 | ``` 94 | 95 | ### Screenshots 96 | 97 | In order to obtain a screenshot the drawing-buffer has to be preserved, this is a setting in Threejs. 98 | 99 | ```jsx 100 | import { Canvas, useThree } from '@react-three/fiber' 101 | 102 | 103 | 104 | 105 | 106 | ... 107 | const gl = useThree(state => state.gl) 108 | ... 109 | const link = document.createElement('a') 110 | link.setAttribute('download', 'canvas.png') 111 | link.setAttribute('href', gl.domElement.toDataURL('image/png').replace('image/png', 'image/octet-stream')) 112 | link.click() 113 | ``` 114 | -------------------------------------------------------------------------------- /examples.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pmndrs/react-three-lgl/ed137a7d960b15a67d00ac166ed20ce5018a81b6/examples.jpg -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@react-three/lgl", 3 | "version": "0.0.4", 4 | "description": "A React abstraction for the LGL Raytracer", 5 | "main": "dist/index.cjs", 6 | "module": "dist/index.js", 7 | "types": "dist/index.d.ts", 8 | "sideEffects": false, 9 | "scripts": { 10 | "build": "rollup -c", 11 | "postbuild": "tsc --emitDeclarationOnly", 12 | "prepublishOnly": "npm run build", 13 | "test": "echo no tests yet" 14 | }, 15 | "husky": { 16 | "hooks": { 17 | "pre-commit": "lint-staged" 18 | } 19 | }, 20 | "lint-staged": { 21 | "*.{js,jsx,ts,tsx}": [ 22 | "eslint --fix" 23 | ] 24 | }, 25 | "repository": { 26 | "type": "git", 27 | "url": "git+https://github.com/pmndrs/react-three-lgl.git" 28 | }, 29 | "keywords": [ 30 | "react", 31 | "webgl", 32 | "threejs", 33 | "react-three-fiber", 34 | "lgl", 35 | "raytracer" 36 | ], 37 | "author": "Paul Henschel", 38 | "license": "MIT", 39 | "bugs": { 40 | "url": "https://github.com/pmndrs/react-three-lgl/issues" 41 | }, 42 | "homepage": "https://github.com/pmndrs/react-three-lgl#readme", 43 | "devDependencies": { 44 | "@babel/core": "7.16.0", 45 | "@babel/plugin-proposal-class-properties": "^7.16.0", 46 | "@babel/plugin-transform-modules-commonjs": "7.16.0", 47 | "@babel/plugin-transform-parameters": "7.16.0", 48 | "@babel/plugin-transform-runtime": "7.16.0", 49 | "@babel/plugin-transform-template-literals": "7.16.0", 50 | "@babel/preset-env": "7.16.0", 51 | "@babel/preset-react": "7.16.0", 52 | "@babel/preset-typescript": "^7.16.0", 53 | "@react-three/fiber": "^7.0.25", 54 | "@rollup/plugin-babel": "^5.3.0", 55 | "@rollup/plugin-node-resolve": "^13.0.6", 56 | "@types/jest": "^27.0.2", 57 | "@types/node": "^16.11.6", 58 | "@types/react": "^17.0.33", 59 | "@types/react-dom": "^17.0.10", 60 | "@types/react-test-renderer": "^17.0.1", 61 | "@types/three": "^0.136.1", 62 | "@typescript-eslint/eslint-plugin": "^5.3.0", 63 | "@typescript-eslint/parser": "^5.3.0", 64 | "eslint": "^8.1.0", 65 | "eslint-config-prettier": "^8.3.0", 66 | "eslint-import-resolver-alias": "^1.1.2", 67 | "eslint-plugin-import": "^2.25.2", 68 | "eslint-plugin-jest": "^25.2.2", 69 | "eslint-plugin-prettier": "^4.0.0", 70 | "eslint-plugin-react": "^7.26.1", 71 | "eslint-plugin-react-hooks": "^4.2.0", 72 | "husky": "^7.0.4", 73 | "lint-staged": "^11.2.6", 74 | "prettier": "^2.4.1", 75 | "react": "^17.0.1", 76 | "rollup": "^2.59.0", 77 | "rollup-plugin-size-snapshot": "^0.12.0", 78 | "rollup-plugin-terser": "^7.0.2", 79 | "three": "^0.136.0", 80 | "typescript": "^4.4.4" 81 | }, 82 | "dependencies": { 83 | "lgl-tracer": "^1.0.6" 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import path from 'path' 2 | import babel from '@rollup/plugin-babel' 3 | import resolve from '@rollup/plugin-node-resolve' 4 | 5 | const root = process.platform === 'win32' ? path.resolve('/') : '/' 6 | const external = (id) => !id.startsWith('.') && !id.startsWith(root) 7 | const extensions = ['.js', '.jsx', '.ts', '.tsx', '.json'] 8 | 9 | const getBabelOptions = ({ useESModules }) => ({ 10 | babelrc: false, 11 | extensions, 12 | exclude: '**/node_modules/**', 13 | babelHelpers: 'runtime', 14 | presets: [ 15 | [ 16 | '@babel/preset-env', 17 | { 18 | include: [ 19 | '@babel/plugin-proposal-optional-chaining', 20 | '@babel/plugin-proposal-nullish-coalescing-operator', 21 | '@babel/plugin-proposal-numeric-separator', 22 | '@babel/plugin-proposal-logical-assignment-operators', 23 | ], 24 | bugfixes: true, 25 | loose: true, 26 | modules: false, 27 | targets: '> 1%, not dead, not ie 11, not op_mini all', 28 | }, 29 | ], 30 | '@babel/preset-react', 31 | '@babel/preset-typescript', 32 | ], 33 | plugins: [['@babel/transform-runtime', { regenerator: false, useESModules }]], 34 | }) 35 | 36 | export default [ 37 | { 38 | input: `./src/index.tsx`, 39 | output: { file: `dist/index.js`, format: 'esm' }, 40 | external, 41 | plugins: [ 42 | babel(getBabelOptions({ useESModules: true })), 43 | resolve({ extensions }), 44 | ], 45 | }, 46 | { 47 | input: `./src/index.tsx`, 48 | output: { file: `dist/index.cjs.js`, format: 'cjs' }, 49 | external, 50 | plugins: [babel(getBabelOptions({ useESModules: false })), resolve({ extensions })], 51 | }, 52 | ] 53 | -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | // @ts-ignore 3 | import { LGLTracerRenderer } from 'lgl-tracer' 4 | import { useThree, useFrame, ThreeEvent } from '@react-three/fiber' 5 | 6 | export type RaytracerProps = { 7 | children: React.ReactNode 8 | /** Number of samples until rendering stops. Default: 64 */ 9 | samples?: number 10 | /** useFrame render index. Default: 1 */ 11 | renderIndex?: number 12 | /** The number of times the light bounces per path, at least 2 times and at most 8 times. Default: 2 */ 13 | bounces?: number 14 | /** Set the intensity of the environment lighting. Default: 1 */ 15 | envMapIntensity?: number 16 | /** Whether to display the enviroment background (does not affect the lighting of scene objects). Default: true */ 17 | enviromentVisible?: boolean 18 | /** Whether to enable the SVGF(Spatiotemporal Variance-Guided Filter) denoise pass. SVGF pass include temporal and spatial part, controlled by two independent switches. Default: false */ 19 | enableDenoise?: boolean 20 | /** Whether to enable the temporal part denoise. temporal denoise will turn on the reprojection pass, reuse effective information in historical pixels for denoise, and pass the variance factor to spatial denoise. Default: true */ 21 | enableTemporalDenoise?: boolean 22 | /** Whether to enable the spatial part denoise. spatial denoise will turn on the A-Tours filter pass. Default: true */ 23 | enableSpatialDenoise?: boolean 24 | /** The callback method after the complete sampling of a frame is rendered, for example, to ensure that the screenshot from the canvas is successful. */ 25 | fullSampleCallback?: () => void 26 | /** Whether to downsample during camera movement (to maintain frame rate). Default: false */ 27 | movingDownsampling?: boolean 28 | /** Whether to keep sampling when canvas element off focus. Default: true */ 29 | renderWhenOffFocus?: boolean 30 | /** Set the method used in ToneMapping pass. including several methods built-in support in threejs. Default: THREE.LinearToneMapping */ 31 | toneMapping?: number 32 | /** Whether to use tile rendering, if enabled, the screen space will be divided according to the frame rate and then rendered block by block. Default: false */ 33 | useTileRender?: boolean 34 | /** Set svgf reproject pass's color blend factor, the factor is used to determine the weight of mixing the current frame and the history frame. Default: 0.2 */ 35 | denoiseColorBlendFactor?: number 36 | /** Set svgf reproject pass's moment variance blend factor, the factor is used to determine the weight of mixing the current frame and the history frame. Default: 0.2 */ 37 | denoiseMomentBlendFactor?: number 38 | /** Set svgf a-tours filter pass's color threshold value. Default: 0.5 */ 39 | denoiseColorFactor?: number 40 | /** Set svgf a-tours filter pass's position threshold value. Default: 0.35 */ 41 | denoisePositionFactor?: number 42 | } 43 | 44 | const Raytracer = React.forwardRef( 45 | ({ children, renderIndex = 1, samples = 64, ...props }: RaytracerProps, forwardRef) => { 46 | const ref = React.useRef(null!) 47 | const renderer = React.useRef() 48 | const { scene, controls, camera, gl, size, viewport } = useThree() 49 | 50 | // Set up the renderer 51 | React.useEffect(() => { 52 | renderer.current = new LGLTracerRenderer({ 53 | // Fake a canvas to trick LGL into using the existing WebGL context 54 | canvas: { getContext: gl.getContext, getExtension: (gl as any).getExtension }, 55 | // Silence console logs 56 | loadingCallback: { onProgress: () => null, onComplete: () => null }, 57 | }) 58 | // Use the existing canvas and WebGL context 59 | renderer.current.canvas = gl.domElement 60 | renderer.current.gl = gl.getContext() 61 | // Reuse the default scenes background 62 | renderer.current.enviromentVisible = !!scene.background 63 | ref.current.environment = scene.environment 64 | return () => { 65 | renderer.current.canvas = null 66 | renderer.current.gl = null 67 | renderer.current.pipeline = null 68 | } 69 | }, []) 70 | 71 | // Update function for refreshing the renderer 72 | const update = React.useCallback(() => { 73 | if (renderer.current) { 74 | renderer.current.needsUpdate = true 75 | if (renderer.current.pipeline) renderer.current.render(ref.current, camera) 76 | } 77 | }, []) 78 | 79 | // Update when props are assigned to the renderer 80 | React.useEffect(() => { 81 | const { 82 | denoiseColorBlendFactor, 83 | denoiseMomentBlendFactor, 84 | denoiseColorFactor, 85 | denoisePositionFactor, 86 | ...parameters 87 | } = props 88 | Object.assign(renderer.current, parameters) 89 | if (denoiseColorBlendFactor !== undefined) renderer.current.setDenoiseColorBlendFactor(denoiseColorBlendFactor) 90 | if (denoiseMomentBlendFactor !== undefined) renderer.current.setDenoiseMomentBlendFactor(denoiseMomentBlendFactor) 91 | if (denoiseColorFactor !== undefined) renderer.current.setDenoiseColorFactor(denoiseColorFactor) 92 | if (denoisePositionFactor !== undefined) renderer.current.setDenoisePositionFactor(denoisePositionFactor) 93 | update() 94 | }, [props]) 95 | 96 | // Update when the camera is being dragged/controlled 97 | React.useEffect(() => { 98 | if (controls) { 99 | controls.addEventListener('start', update) 100 | return () => controls.removeEventListener('start', update) 101 | } 102 | }, [controls]) 103 | 104 | // Update on size and pixel-ratio changes 105 | React.useEffect(() => { 106 | renderer.current.setSize(size.width, size.height) 107 | renderer.current.setPixelRatio(viewport.dpr) 108 | update() 109 | }, [size, viewport]) 110 | 111 | // Let it build the scene 112 | React.useEffect(() => void renderer.current.buildScene(ref.current, camera).then(update), []) 113 | 114 | // Allow user land to access the renderer as a ref 115 | React.useImperativeHandle(forwardRef, () => renderer.current, []) 116 | 117 | // Render out, as long as the total number of samples is not reached 118 | useFrame(() => { 119 | if (renderer.current && renderer.current.pipeline && renderer.current.getTotalSamples() < samples) 120 | renderer.current.render(ref.current, camera) 121 | }, renderIndex) 122 | 123 | return {children} 124 | } 125 | ) 126 | 127 | export { Raytracer } 128 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "esnext", 4 | "target": "es2018", 5 | "allowSyntheticDefaultImports": true, 6 | "jsx": "react", 7 | "strict": true, 8 | "preserveSymlinks": true, 9 | "moduleResolution": "Node", 10 | "esModuleInterop": true, 11 | "declaration": true, 12 | "declarationDir": "dist", 13 | "skipLibCheck": true, 14 | "removeComments": false, 15 | "baseUrl": "." 16 | }, 17 | "include": ["src"] 18 | } 19 | --------------------------------------------------------------------------------