Repository: pmndrs/react-three-gpu-pathtracer
Branch: main
Commit: 6301a31d8bdd
Files: 34
Total size: 34.9 KB
Directory structure:
gitextract_mschw82f/
├── .gitignore
├── .prettierrc
├── .yarnrc.yml
├── LICENSE.md
├── README.md
├── example/
│ ├── .codesandbox/
│ │ └── tasks.json
│ ├── README.md
│ ├── csb.vite.config.js
│ ├── index.html
│ ├── package.json
│ ├── public/
│ │ └── game_boy_classic.glb
│ ├── src/
│ │ ├── App.tsx
│ │ ├── Controls.js
│ │ ├── Model.jsx
│ │ ├── Tag.jsx
│ │ ├── index.css
│ │ ├── index.js
│ │ ├── main.tsx
│ │ └── vite-env.d.ts
│ ├── tsconfig.json
│ ├── tsconfig.tsbuildinfo
│ └── vite.config.ts
├── package/
│ ├── package.json
│ ├── src/
│ │ ├── FogVolumeMaterial.tsx
│ │ ├── Pass.ts
│ │ ├── Pathtracer.tsx
│ │ ├── PhysicalCamera.tsx
│ │ ├── PhysicalSpotLight.tsx
│ │ ├── ShapedAreaLight.tsx
│ │ └── index.ts
│ ├── tsconfig.json
│ └── vite.config.ts
├── package.json
└── tsconfig.json
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
.yarn
================================================
FILE: .prettierrc
================================================
{
"trailingComma": "none",
"tabWidth": 2,
"semi": true,
"singleQuote": false
}
================================================
FILE: .yarnrc.yml
================================================
nodeLinker: node-modules
================================================
FILE: LICENSE.md
================================================
MIT License
Copyright (c) 2024 Poimandres
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
<br />
<h1 align="center">react-three-gpu-pathtracer</h1>
<h3 align="center">⚡️ A React abstraction for the popular <a href="https://github.com/gkjohnson/three-gpu-pathtracer">three-gpu-pathtracer</a></h3>
<br />
<p align="center">
<a href="https://codesandbox.io/embed/github/pmndrs/react-three-gpu-pathtracer/tree/main/example" target="_blank"><img src="https://github.com/pmndrs/react-three-gpu-pathtracer/blob/main/assets/hero-screenshot.png?raw=true"/></a>
</p>
<p align="middle">
<i>This demo is real, you can click it! It contains full code, too. 📦</i>
</p>
<p align="middle">
GameBoy model by
<a href="https://sketchfab.com/kleingeo">(@kleingeo)</a>
<a hef="https://sketchfab.com/3d-models/game-boy-classic-0ae80019e6f046168923286d7e628f6f">
on Sketchfab
</a>
. GameBoy Cartridge by
<a href="https://sketchfab.com/kleingeo">(@MeBob)</a>
<a hef="https://sketchfab.com/3d-models/gameboy-cartridge-lowpoly-8b9728eab16c4056ac2636ae7f0f038f">
on Sketchfab
</a>
.
</p>
<br>
<p align="center">
<a href="https://www.npmjs.com/package/@react-three/gpu-pathtracer" target="_blank">
<img src="https://img.shields.io/npm/v/@react-three/gpu-pathtracer.svg?style=flat&colorA=000000&colorB=000000" />
</a>
<a href="https://www.npmjs.com/package/@react-three/gpu-pathtracer" target="_blank">
<img src="https://img.shields.io/npm/dm/@react-three/gpu-pathtracer.svg?style=flat&colorA=000000&colorB=000000" />
</a>
<a href="https://twitter.com/pmndrs" target="_blank">
<img src="https://img.shields.io/twitter/follow/pmndrs?label=%40pmndrs&style=flat&colorA=000000&colorB=000000&logo=twitter&logoColor=000000" alt="Chat on Twitter">
</a>
<a href="https://discord.gg/ZZjjNvJ" target="_blank">
<img src="https://img.shields.io/discord/740090768164651008?style=flat&colorA=000000&colorB=000000&label=discord&logo=discord&logoColor=000000" alt="Chat on Twitter">
</a>
</p>
<br />
`react-three-gpu-pathtracer` lets you render your `react-three-fiber` scenes using Path Tracing! It is as simple as
```jsx
import { Pathtracer } from "@react-three/gpu-pathtracer";
function GradientSphere() {
return (
<Canvas>
<Pathtracer>{/* Your scene */}</Pathtracer>
</Canvas>
);
}
```
The `<Pathtracer />` component wraps your scene. The scene is then rendered using Path Tracing.
#### Props
| Prop | Type | Default | Description |
| ------------ | ---------------------------------------------------------------------- | ---------- | -------------------------------------------------------------------------------------------------------------------- |
| `minSamples` | `number` | `1` | Default: 5. Min number of samples before blending the base scene with the pathtraced one. |
| `samples` | `number` | `1` | Max number of samples before the pathtracer stops. |
| `frames` | `number` | `Infinity` | Number of frames to path trace. Will pause rendering once this number is reached. |
| `tiles` | `[number, number] / THREE.Vector2 / { x: number; y: number } / number` | `2` | Number of tiles. Can be used to improve the responsiveness of a page while still rendering a high resolution target. |
| `bounces` | `number` | `1` | The number of ray bounces to test. Higher is better quality but slower performance. |
| `enabled` | `boolean` | `true` | Wether to enable pathtracing. |
### Env maps
Env maps can be added using [Drei's `<Environment />`](https://github.com/pmndrs/drei#environment) component just like in a regular scene.
```jsx
<Pathtracer>
<Environment
preset="..."
background // Optional, set as scene background
backgroundBlurriness={0.5}
backgroundIntensity={1}
/>
</Pathtracer>
```
### `usePathtracer`
This hook provides access to useful functions in the internal renderer. Can only be used within the `<Pathtracer />` component.
```ts
const { renderer, update, reset } = usePathtracer();
```
| Return value | Type | Description |
| -------------- | ----------------- | ------------------------------------------------------------------------------------------- |
| `pathtracer` | `WebGLPathTracer` | Internal renderer. Can be used to access/edit internal properties |
| ~~`renderer`~~ | `WebGLPathTracer` | DEPRECIATED: use `pathtracer` to not get confused with raster renderer |
| `reset` | `() => void` | Flushes the rendered scene and resets the samples count. |
| `update` | `() => void` | Tells the pathtracer that the scene has been updated. Everything is managed internally now. |
### Note on controls
When you set controls be sure to use `makeDefault` and it's best to import the `OrbitControls` [from drei](https://drei.docs.pmnd.rs/controls/introduction)
```jsx
<OrbitControls makeDefault>
// ...
```
### Development
#### Dev
```bash
cd project-root
yarn
yarn dev
```
#### Build
```bash
yarn build
```
#### Publish
```bash
cd package
npm run release
```
================================================
FILE: example/.codesandbox/tasks.json
================================================
{
// These tasks will run in order when initializing your CodeSandbox project.
"setupTasks": [
{
"name": "Installing Dependencies",
"command": "yarn install"
}
],
// These tasks can be run from CodeSandbox. Running one will open a log in the app.
"tasks": {
"dev": {
"name": "dev",
"command": "rm -f vite.config.ts && cp csb.vite.config.js vite.config.ts && yarn add @react-three/gpu-pathtracer && yarn dev",
"runAtStart": true,
"preview": {
"port": 5173
}
},
"build": {
"name": "build",
"command": "yarn build",
"runAtStart": false
},
"preview": {
"name": "preview",
"command": "yarn preview",
"runAtStart": false
}
}
}
================================================
FILE: example/README.md
================================================
<br />
<h3 align="center">react-three-gpu-pathtracer</h3>
<h3 align="center">⚡️ A React abstraction for the popular <a href="https://github.com/gkjohnson/three-gpu-pathtracer">three-gpu-pathtracer</a></h3>
---
<h3 align="center">Example</h3>
================================================
FILE: example/csb.vite.config.js
================================================
import react from '@vitejs/plugin-react'
import { defineConfig } from 'vite'
// https://vitejs.dev/config/
export default defineConfig({
resolve: {
plugins: [react()],
},
})
================================================
FILE: example/index.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link
rel="icon"
type="image/svg+xml"
href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>⚡️</text></svg>"
/>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>@react-three/gpu-pathtracer | Example</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
================================================
FILE: example/package.json
================================================
{
"name": "@react-three/gpu-pathtracer-example",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"preview": "vite preview"
},
"dependencies": {
"@pmndrs/branding": "^0.0.8",
"@react-three/drei": "^10.3.0",
"@react-three/fiber": "^9.1.2",
"@react-three/gpu-pathtracer": "^0.1.1",
"leva": "^0.9.35",
"react": "^19.1.0",
"react-dom": "^19.1.0",
"react-icons": "^5.3.0",
"three": "^0.169.0"
},
"devDependencies": {
"@types/node": "^22.5.5",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@vitejs/plugin-react": "^4.3.1",
"globals": "^15.9.0",
"typescript": "^5.5.3",
"vite": "^5.4.1"
}
}
================================================
FILE: example/src/App.tsx
================================================
import {
Bounds,
Center,
Circle,
Environment,
OrbitControls,
PerspectiveCamera,
Stats,
useTexture
} from "@react-three/drei";
import { Canvas, useFrame } from "@react-three/fiber";
import { Pathtracer, usePathtracer } from "@react-three/gpu-pathtracer";
import { Leva } from "leva";
import { useEffect, useRef } from "react";
import Controls from "./Controls";
import { ACESFilmicToneMapping, MathUtils } from "three";
import Model from "./Model";
import Tag from "./Tag";
const baseURL = import.meta.env.BASE_URL;
function Floor() {
const [aoMap, diffMap, norMap, roughMap] = useTexture([
baseURL + "/textures/wood_cabinet_worn_long_ao_2k.jpg",
baseURL + "/textures/wood_cabinet_worn_long_diff_2k.jpg",
baseURL + "/textures/wood_cabinet_worn_long_nor_gl_2k.jpg",
baseURL + "/textures/wood_cabinet_worn_long_rough_2k.jpg"
]);
return (
<>
<Circle args={[4, 128]} position={[0, -1, 0]} rotation-x={-Math.PI / 2}>
<meshPhysicalMaterial map={diffMap} aoMap={aoMap} roughness={0.2} />
</Circle>
</>
);
}
function UI({ infoRef }) {
const { pathtracer } = usePathtracer();
const opts = Controls();
useFrame(() => {
if (pathtracer && infoRef.current) {
infoRef.current.children[0].textContent = `Samples: ${Math.ceil(
// @ts-ignore
pathtracer.samples
)}/${opts.Rendering_Samples}`;
// @ts-ignore
infoRef.current.children[1].textContent = pathtracer.isCompiling
? `Initializing...`
: "";
}
});
return null;
}
function Thing() {
const { reset, update } = usePathtracer();
const opts = Controls();
// Trigger updates when envmap stuff changes
useEffect(() => {
update();
}, [
opts.Environment_Visible,
opts.Environment_Preset,
opts.Environment_Intensity,
opts.Environment_Blur
]);
return (
<>
<group rotation-y={MathUtils.degToRad(-180)}>
<Bounds fit clip observe margin={1.3}>
<Center>
<group position={[-1, -1.2, 0]}>
<Model rotation-y={Math.PI} position={[-0.3, 0, 0]} scale={15} />
</group>
</Center>
</Bounds>
<Floor />
</group>
</>
);
}
export default function App() {
const infoRef = useRef();
const opts = Controls();
return (
<>
<Leva
collapsed
titleBar={{
title: "Options"
}}
/>
<Canvas
gl={{
toneMapping: ACESFilmicToneMapping
}}
>
<PerspectiveCamera makeDefault position={[4, 2, -1]} fov={40} />
<OrbitControls makeDefault />
<Pathtracer
samples={opts.Rendering_Samples}
bounces={opts.Rendering_Bounces}
resolutionFactor={opts.Rendering_Factor}
tiles={[opts.Rendering_Tiles.x, opts.Rendering_Tiles.y]}
enabled={opts.Rendering_Enabled}
>
{opts.Environment_Visible ? (
<Environment
preset={opts.Environment_Preset as any}
// background
environmentIntensity={opts.Environment_Intensity}
backgroundBlurriness={opts.Environment_Blur}
/>
) : (
<directionalLight intensity={5} position={[5, 5, 5]} />
)}
<Thing />
<UI infoRef={infoRef} />
</Pathtracer>
</Canvas>
<Stats />
<div className="info" ref={infoRef}>
<p>Samples: 0</p>
<p>Initializing...</p>
</div>
<Tag />
</>
);
}
================================================
FILE: example/src/Controls.js
================================================
import { presetsObj } from "@react-three/drei/helpers/environment-assets";
import { folder, useControls } from "leva";
export default function Controls() {
return useControls({
Rendering: folder({
Rendering_Factor: {
value: 1,
max: 1,
min: 0,
step: 0.01,
label: "Resolution Factor"
},
Rendering_Samples: {
value: 128,
max: 512,
min: 1,
step: 1,
label: "Samples"
},
Rendering_Bounces: {
value: 8,
max: 20,
min: 1,
step: 1,
label: "Bounces"
},
Rendering_Tiles: {
value: {
x: 2,
y: 2
},
max: 20,
min: 1,
step: 1,
label: "Tiles",
joystick: false
},
Rendering_Enabled: {
value: true,
label: "Enabled"
}
}),
Environment: folder({
Environment_Visible: {
value: true,
label: "Enabled"
},
Environment_Preset: {
options: Object.keys(presetsObj),
value: "apartment",
label: "Preset"
},
Environment_Intensity: {
value: 1.2,
max: 10,
min: 0,
step: 0.01,
label: "Intensity"
},
Environment_Blur: {
value: 0.3,
max: 1,
min: 0,
step: 0.01,
label: "Blur"
}
})
});
}
================================================
FILE: example/src/Model.jsx
================================================
/*
Auto-generated by: https://github.com/pmndrs/gltfjsx
*/
import { useGLTF } from "@react-three/drei";
import { useLayoutEffect } from "react";
import { MeshPhysicalMaterial } from "three";
export default function Model(props) {
const { scene } = useGLTF(import.meta.env.BASE_URL + "game_boy_classic.glb");
useLayoutEffect(() => {
convertOpacityToTransmission(scene);
}, []);
return <primitive {...props} object={scene} />;
}
function convertOpacityToTransmission(model) {
model.traverse((c) => {
if (c.material) {
const material = c.material;
if (material.opacity < 0.65 && material.opacity > 0.2) {
const newMaterial = new MeshPhysicalMaterial();
for (const key in material) {
if (key in material) {
if (material[key] === null) {
continue;
}
if (material[key].isTexture) {
newMaterial[key] = material[key];
} else if (
material[key].copy &&
material[key].constructor === newMaterial[key].constructor
) {
newMaterial[key].copy(material[key]);
} else if (typeof material[key] === "number") {
newMaterial[key] = material[key];
}
}
}
newMaterial.opacity = 1.0;
newMaterial.transmission = 1.0;
c.material = newMaterial;
}
}
});
}
================================================
FILE: example/src/Tag.jsx
================================================
import { Logo } from "@pmndrs/branding";
import { FaGithub } from "react-icons/fa";
export default function Tag() {
return (
<div className="copy">
<a
style={{
fontSize: "1.5rem",
color: "white",
textDecoration: "underline"
}}
target="_blank"
rel="noopener"
href="https://github.com/pmndrs/react-three-gpu-pathtracer"
>
@react-three/gpu-pathtracer
</a>{" "}
<div>
<span>
<a target="_blank" rel="noopener" href="https://github.com/pmndrs">
<Logo />
</a>
</span>
<span
style={{
fontSize: "0.75rem"
}}
>
<div
style={{
display: "flex",
flexDirection: "column",
justifyContent: "center",
alignItems: "center",
gap: "1rem"
}}
>
<span>
<a
target="_blank"
rel="noopener"
href="https://github.com/pmndrs/react-three-gpu-pathtracer"
>
@react-three/gpu-pathtracer
</a>{" "}
is a React port of{" "}
<a
target="_blank"
rel="noopener"
href="https://github.com/gkjohnson/three-gpu-pathtracer"
>
three-gpu-pathtracer
</a>{" "}
by{" "}
<a
target="_blank"
rel="noopener"
href="https://twitter.com/garrettkjohnson"
>
Garrett Johnson
</a>
</span>
<span>
GameBoy model by{" "}
<a href="https://sketchfab.com/kleingeo">(@kleingeo)</a>{" "}
<a hef="https://sketchfab.com/3d-models/game-boy-classic-0ae80019e6f046168923286d7e628f6f">
on Sketchfab
</a>
. GameBoy Cartridge by{" "}
<a href="https://sketchfab.com/kleingeo">(@MeBob)</a>{" "}
<a hef="https://sketchfab.com/3d-models/gameboy-cartridge-lowpoly-8b9728eab16c4056ac2636ae7f0f038f">
on Sketchfab
</a>
.
</span>
</div>
</span>
<span>
<a
target="_blank"
rel="noopener"
href="https://github.com/pmndrs/react-three-gpu-pathtracer"
>
<FaGithub size={40} />
</a>
</span>
</div>
</div>
);
}
================================================
FILE: example/src/index.css
================================================
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
"Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
monospace;
}
#root,
body,
canvas {
width: 100vw;
height: 100vh;
overflow: hidden;
margin: 0;
background: linear-gradient(180deg, #2a1303 0%, #000000 100%);
}
canvas {
cursor: grab;
cursor: -moz-grab;
cursor: -webkit-grab;
}
canvas:active {
cursor: grabbing;
cursor: -moz-grabbing;
cursor: -webkit-grabbing;
}
.copy {
position: absolute;
bottom: 0;
right: 0;
width: 100%;
box-sizing: border-box;
padding: 32px;
color: white;
text-align: center;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 0.25rem;
pointer-events: none;
background: linear-gradient(180deg, rgba(0, 0, 0, 0) 0%, #000000 80%);
}
.copy > div {
width: 100%;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
gap: 1rem;
}
.copy svg {
width: 1.5rem;
fill: white;
}
a {
font-style: italic;
color: #ffbe33;
font-weight: bold;
text-decoration: none;
pointer-events: all;
}
/* .copy span a svg {
margin-right: 16px;
} */
.info {
position: absolute;
top: 50px;
left: 0;
padding: 0.25rem;
color: white;
font-family: monospace;
}
.info p {
margin: 0;
}
================================================
FILE: example/src/index.js
================================================
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
================================================
FILE: example/src/main.tsx
================================================
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import App from "./App.tsx";
import "./index.css";
createRoot(document.getElementById("root")!).render(
<StrictMode>
<App />
</StrictMode>
);
================================================
FILE: example/src/vite-env.d.ts
================================================
/// <reference types="vite/client" />
================================================
FILE: example/tsconfig.json
================================================
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,
"jsx": "react-jsx",
/* Linting */
"strict": false,
"allowJs": true,
"preserveSymlinks": true,
"paths": {
"@react-three/gpu-pathtracer": ["../package/src"]
}
},
"include": ["src"]
}
================================================
FILE: example/tsconfig.tsbuildinfo
================================================
{"root":["./src/app.tsx","./src/controls.js","./src/model.jsx","./src/tag.jsx","./src/index.js","./src/main.tsx","./src/vite-env.d.ts"],"version":"5.6.2"}
================================================
FILE: example/vite.config.ts
================================================
import react from "@vitejs/plugin-react";
import path from "path";
import { defineConfig } from "vite";
// https://vitejs.dev/config/
export default defineConfig({
base: "/react-three-gpu-pathtracer/",
plugins: [react()],
resolve: {
alias: {
"@react-three/gpu-pathtracer": path.resolve(
__dirname,
"../package/src/index.ts"
)
}
}
});
================================================
FILE: package/package.json
================================================
{
"name": "@react-three/gpu-pathtracer",
"version": "0.3.2",
"description": "⚡️ A React abstraction for the popular three-gpu-pathtracer.",
"type": "module",
"files": [
"./**/*"
],
"repository": {
"type": "git",
"url": "git+https://github.com/pmndrs/react-three-gpu-pathtracer"
},
"bugs": {
"url": "https://github.com/pmndrs/react-three-gpu-pathtracer/issues"
},
"author": "Faraz Shaikh <farazzshaikh@gmail.com> (https://twitter.com/CantBeFaraz)",
"contributors": [
"Dennis Smolek <Dennis@smolek.dev> (https://twitter.com/DennisSmolek)"
],
"license": "MIT",
"homepage": "https://github.com/pmndrs/react-three-gpu-pathtracer",
"keywords": [
"react",
"webgl",
"three",
"path",
"tracing",
"pathtracing",
"raytracing"
],
"main": "react-three-gpu-pathtracer.cjs.js",
"module": "react-three-gpu-pathtracer.es.js",
"types": "index.d.ts",
"scripts": {
"build": "tsc && vite build",
"release": "yarn build && cp ./package.json ./dist/package.json && cd ./dist && npm publish"
},
"devDependencies": {
"@types/node": "^22.7.4",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@types/three": "^0.177.0",
"@vitejs/plugin-react": "^4.3.1",
"globals": "^15.9.0",
"rollup-plugin-exclude-dependencies-from-bundle": "^1.1.24",
"typescript": "^5.5.3",
"typescript-eslint": "^8.0.1",
"vite": "^5.4.1",
"vite-plugin-dts": "^4.2.1"
},
"peerDependencies": {
"@react-three/fiber": ">=8.0",
"react": ">=18.0",
"three": ">=0.149"
},
"dependencies": {
"three-gpu-pathtracer": "^0.0.23"
}
}
================================================
FILE: package/src/FogVolumeMaterial.tsx
================================================
import { extend, ThreeElement } from "@react-three/fiber";
import { useEffect } from "react";
import { FogVolumeMaterial as FogVolumeMaterialImpl } from "three-gpu-pathtracer";
import { usePathtracer } from "./Pathtracer";
export function FogVolumeMaterial(
props: ThreeElement<typeof FogVolumeMaterialImpl>
) {
extend({ FogVolumeMaterial: FogVolumeMaterialImpl });
const { pathtracer } = usePathtracer();
useEffect(() => void pathtracer.updateMaterials());
// @ts-expect-error
return <fogVolumeMaterial {...props} />;
}
export { FogVolumeMaterialImpl };
================================================
FILE: package/src/Pass.ts
================================================
import {
Material,
Mesh,
OrthographicCamera,
PlaneGeometry,
WebGLRenderer,
WebGLRenderTarget
} from "three";
class Pass {
// if set to true, the pass is processed by the composer
public enabled = true;
// if set to true, the pass indicates to swap read and write buffer after rendering
public needsSwap = true;
// if set to true, the pass clears its buffer before rendering
public clear = false;
// if set to true, the result of the pass is rendered to screen. This is set automatically by EffectComposer.
public renderToScreen = false;
public setSize(
/* eslint-disable @typescript-eslint/no-unused-vars */
/* eslint-disable no-unused-vars */
width: number,
height: number
): void {}
public render(
/* eslint-disable @typescript-eslint/no-unused-vars */
/* eslint-disable no-unused-vars */
renderer: WebGLRenderer,
writeBuffer: WebGLRenderTarget,
readBuffer: WebGLRenderTarget,
deltaTime: number,
maskActive?: unknown
): void {
console.error("THREE.Pass: .render() must be implemented in derived pass.");
}
}
// Helper for passes that need to fill the viewport with a single quad.
class FullScreenQuad<TMaterial extends Material = Material> {
public camera = new OrthographicCamera(-1, 1, 1, -1, 0, 1);
public geometry = new PlaneGeometry(2, 2);
private mesh: Mesh<PlaneGeometry, TMaterial>;
constructor(material: TMaterial) {
this.mesh = new Mesh(this.geometry, material);
}
public get material(): TMaterial {
return this.mesh.material;
}
public set material(value: TMaterial) {
this.mesh.material = value;
}
public dispose(): void {
this.mesh.geometry.dispose();
}
public render(renderer: WebGLRenderer): void {
renderer.render(this.mesh, this.camera);
}
}
export { FullScreenQuad, Pass };
================================================
FILE: package/src/Pathtracer.tsx
================================================
import {
applyProps,
ElementProps,
useFrame,
useThree,
Vector2
} from "@react-three/fiber";
import React, { useMemo } from "react";
import * as THREE from "three";
import { WebGLPathTracer } from "three-gpu-pathtracer";
type PathtracerProps = ElementProps<typeof WebGLPathTracer> & {
minSamples?: number;
samples?: number;
tiles?: Vector2;
bounces?: number;
enabled?: boolean;
resolutionFactor?: number;
renderPriority?: number;
filteredGlossyFactor?: number;
};
interface PathtracerAPI {
update: () => void;
reset: () => void;
renderer: WebGLPathTracer;
pathtracer: WebGLPathTracer;
}
const context = React.createContext<PathtracerAPI>(null as any);
//* Helper Function to convert TilesType to [number, number]
function fiberVec2ToArr(vec: Vector2): [number, number] {
if (Array.isArray(vec)) return vec;
if (vec instanceof THREE.Vector2) return [vec.x, vec.y];
if (typeof vec === "number") return [vec, vec];
return [1, 1]; // Default to 1x1 tiles if not specified
}
export const Pathtracer = React.forwardRef<
InstanceType<typeof WebGLPathTracer>,
React.PropsWithChildren<PathtracerProps>
>(
(
{
children,
enabled = true,
minSamples = 1,
samples = 32,
bounces = 4,
resolutionFactor = 1,
renderPriority = 1,
filteredGlossyFactor = 0,
renderDelay = 0,
fadeDuration = 0,
dynamicLowRes = true,
lowResScale = 0.25,
textureSize = [1024, 1024],
rasterizeScene = false,
tiles = [3, 3],
...props
},
ref
) => {
// state objects
const { gl, camera, scene, controls } = useThree();
const pathtracer = useMemo(() => {
const pt = new WebGLPathTracer(gl);
pt.synchronizeRenderSize = true;
// This might not be needed as we arent using setSceneAsync
//pt.setBVHWorker(new ParallelMeshBVHWorker())
return pt;
}, [gl]);
// Expose the pathtracer instance via ref
React.useImperativeHandle(ref, () => pathtracer, [pathtracer]);
//* Single handler for all props
React.useLayoutEffect(() => {
applyProps(pathtracer, {
bounces,
filteredGlossyFactor,
renderDelay,
fadeDuration,
minSamples,
dynamicLowRes,
lowResScale,
rasterizeScene,
textureSize,
tiles
});
pathtracer.renderScale = resolutionFactor;
pathtracer.reset();
}, [props, resolutionFactor, bounces, minSamples, pathtracer]);
React.useEffect(() => {
if (enabled) pathtracer.reset();
}, [enabled]);
const api = React.useMemo<PathtracerAPI>(
() => ({
/**
* Update the pathtracer scene. Call this after adding or removing objects from the scene
*/
update: () => {
pathtracer.setScene(scene, camera);
},
/**
* Reset the pathtracer. Call this after changing any pathtracing properties
*/
reset: () => {
pathtracer.reset();
},
/**
* @deprecated Use `pathtracer` instead
*/
renderer: pathtracer,
pathtracer: pathtracer // Use this instead. Keeps base three renderer seperate mentally
}),
[pathtracer, scene, camera]
);
//* Initialize the pathtracer
React.useLayoutEffect(() => {
// scene.updateMatrixWorld()
pathtracer.setScene(scene, camera);
}, [scene, camera]);
// Bind control listeners
React.useLayoutEffect(() => {
// setup control listeners
const controlListener = () => {
pathtracer.updateCamera();
};
// @ts-ignore
if (controls) controls.addEventListener("change", controlListener);
return () => {
// @ts-ignore
if (controls) controls.removeEventListener("change", controlListener);
};
}, [controls, pathtracer]);
useFrame(({ camera, gl, scene }) => {
if (enabled && pathtracer.samples < (samples ?? Infinity))
pathtracer.renderSample();
if (!enabled) gl.render(scene, camera);
}, 1);
return <context.Provider value={api}>{children}</context.Provider>;
}
);
export function usePathtracer() {
const ctx = React.useContext(context);
if (!ctx) throw new Error("usePathtracer must be used within a Pathtracer");
return ctx;
}
================================================
FILE: package/src/PhysicalCamera.tsx
================================================
import { extend, ThreeElement, useThree } from "@react-three/fiber";
import { useLayoutEffect, useEffect, useRef } from "react";
import { PhysicalCamera as PhysicalCameraImpl } from "three-gpu-pathtracer";
import { usePathtracer } from "./Pathtracer";
export function PhysicalCamera(
props: ThreeElement<typeof PhysicalCameraImpl> & { manual?: boolean }
) {
extend({ PhysicalCamera: PhysicalCameraImpl });
const { pathtracer } = usePathtracer();
const set = useThree(({ set }) => set);
const camera = useThree(({ camera }) => camera);
const size = useThree(({ size }) => size);
const cameraRef = useRef<PhysicalCameraImpl>(null!);
useLayoutEffect(() => {
if (!props.manual) {
// Calculate correct aspect ratio
cameraRef.current.aspect = size.width / size.height;
}
}, [size, props]);
useLayoutEffect(() => {
// Update the camera's projection matrix
cameraRef.current.updateProjectionMatrix();
});
useLayoutEffect(() => {
// Set as the default camera
const oldCam = camera;
set(() => ({ camera: cameraRef.current }));
return () => set(() => ({ camera: oldCam }));
}, [cameraRef, set]);
// Update the pathtracer
useEffect(() => void pathtracer.updateCamera());
// @ts-expect-error
return <physicalCamera ref={cameraRef} {...props} />;
}
export { PhysicalCameraImpl };
================================================
FILE: package/src/PhysicalSpotLight.tsx
================================================
import { extend, ThreeElement } from "@react-three/fiber";
import { useEffect } from "react";
import { PhysicalSpotLight as PhysicalSpotLightImpl } from "three-gpu-pathtracer";
import { usePathtracer } from "./Pathtracer";
export function PhysicalSpotLight(
props: ThreeElement<typeof PhysicalSpotLightImpl>
) {
extend({ PhysicalSpotLight: PhysicalSpotLightImpl });
const { pathtracer } = usePathtracer();
useEffect(() => void pathtracer.updateLights());
// @ts-expect-error
return <physicalSpotLight {...props} />;
}
export { PhysicalSpotLightImpl };
================================================
FILE: package/src/ShapedAreaLight.tsx
================================================
import { extend, ThreeElement } from "@react-three/fiber";
import { useEffect } from "react";
import { ShapedAreaLight as ShapedAreaLightImpl } from "three-gpu-pathtracer";
import { usePathtracer } from "./Pathtracer";
export function ShapedAreaLight(
props: ThreeElement<typeof ShapedAreaLightImpl>
) {
extend({ ShapedAreaLight: ShapedAreaLightImpl });
const { pathtracer } = usePathtracer();
useEffect(() => void pathtracer.updateLights());
// @ts-expect-error
return <shapedAreaLight {...props} />;
}
export { ShapedAreaLightImpl };
================================================
FILE: package/src/index.ts
================================================
export * from "./FogVolumeMaterial";
export * from "./Pathtracer";
export * from "./PhysicalCamera";
export * from "./PhysicalSpotLight";
export * from "./ShapedAreaLight";
================================================
FILE: package/tsconfig.json
================================================
{
"compilerOptions": {
"noEmit": true,
"allowSyntheticDefaultImports": true,
"jsx": "react-jsx",
"strict": true,
"moduleResolution": "Node",
"skipLibCheck": true,
"allowJs": true,
"lib": ["esnext", "dom"]
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
================================================
FILE: package/vite.config.ts
================================================
import react from "@vitejs/plugin-react";
import fs from "fs/promises";
import path from "path";
import excludeDependenciesFromBundle from "rollup-plugin-exclude-dependencies-from-bundle";
import { defineConfig } from "vite";
import dts from "vite-plugin-dts";
function copyFiles() {
return {
name: "copy-license",
closeBundle: async () => {
await fs.copyFile("../LICENSE.md", "./dist/LICENSE.md");
await fs.copyFile("../README.md", "./dist/README.md");
await fs.copyFile("./package.json", "./dist/package.json");
}
};
}
export default defineConfig({
build: {
outDir: "dist",
lib: {
entry: path.resolve(__dirname, "src/index.ts"),
name: "react-three-gpu-pathtracer",
formats: ["es", "cjs"],
fileName: (format) => `react-three-gpu-pathtracer.${format}.js`
},
rollupOptions: {
external: ["react", "three", "@react-three/fiber"],
plugins: [
// @ts-ignore
excludeDependenciesFromBundle({
dependencies: true,
peerDependencies: true
})
]
},
sourcemap: true,
emptyOutDir: true
},
plugins: [react(), dts(), copyFiles()]
});
================================================
FILE: package.json
================================================
{
"name": "@react-three/gpu-pathtracer-monorepo",
"description": "Monorepo for @react-three/gpu-pathtracer-monorepo and its examples",
"version": "1.0.0",
"private": true,
"repository": "https://github.com/pmndrs/react-three-gpu-pathtracer",
"author": "Faraz Shaikh <farazzshaikh@gmail.com> (https://twitter.com/CantBeFaraz)",
"contributors": [
"Dennis Smolek <Dennis@smolek.dev> (https://twitter.com/DennisSmolek)"
],
"license": "MIT",
"homepage": "https://github.com/pmndrs/react-three-gpu-pathtracer",
"workspaces": [
"package",
"example"
],
"scripts": {
"build:package": "yarn --cwd package build",
"build": "yarn --cwd example build",
"preview": "yarn --cwd example preview",
"dev": "yarn --cwd example dev",
"release": "yarn --cwd package release"
},
"packageManager": "yarn@4.9.2"
}
================================================
FILE: tsconfig.json
================================================
{
"compilerOptions": {
"module": "esnext",
"target": "es2018",
"allowSyntheticDefaultImports": true,
"jsx": "react",
"strict": true,
"preserveSymlinks": true,
"moduleResolution": "Node",
"esModuleInterop": true,
"declaration": true,
"skipLibCheck": true,
"removeComments": false
},
"include": ["package/src/**/*"],
"exclude": ["node_modules", "package/dist/**/*"]
}
gitextract_mschw82f/ ├── .gitignore ├── .prettierrc ├── .yarnrc.yml ├── LICENSE.md ├── README.md ├── example/ │ ├── .codesandbox/ │ │ └── tasks.json │ ├── README.md │ ├── csb.vite.config.js │ ├── index.html │ ├── package.json │ ├── public/ │ │ └── game_boy_classic.glb │ ├── src/ │ │ ├── App.tsx │ │ ├── Controls.js │ │ ├── Model.jsx │ │ ├── Tag.jsx │ │ ├── index.css │ │ ├── index.js │ │ ├── main.tsx │ │ └── vite-env.d.ts │ ├── tsconfig.json │ ├── tsconfig.tsbuildinfo │ └── vite.config.ts ├── package/ │ ├── package.json │ ├── src/ │ │ ├── FogVolumeMaterial.tsx │ │ ├── Pass.ts │ │ ├── Pathtracer.tsx │ │ ├── PhysicalCamera.tsx │ │ ├── PhysicalSpotLight.tsx │ │ ├── ShapedAreaLight.tsx │ │ └── index.ts │ ├── tsconfig.json │ └── vite.config.ts ├── package.json └── tsconfig.json
SYMBOL INDEX (26 symbols across 11 files)
FILE: example/src/App.tsx
function Floor (line 24) | function Floor() {
function UI (line 41) | function UI({ infoRef }) {
function Thing (line 62) | function Thing() {
function App (line 92) | function App() {
FILE: example/src/Controls.js
function Controls (line 4) | function Controls() {
FILE: example/src/Model.jsx
function Model (line 9) | function Model(props) {
function convertOpacityToTransmission (line 19) | function convertOpacityToTransmission(model) {
FILE: example/src/Tag.jsx
function Tag (line 4) | function Tag() {
FILE: package/src/FogVolumeMaterial.tsx
function FogVolumeMaterial (line 6) | function FogVolumeMaterial(
FILE: package/src/Pass.ts
class Pass (line 10) | class Pass {
method setSize (line 23) | public setSize(
method render (line 30) | public render(
class FullScreenQuad (line 44) | class FullScreenQuad<TMaterial extends Material = Material> {
method constructor (line 49) | constructor(material: TMaterial) {
method material (line 53) | public get material(): TMaterial {
method material (line 57) | public set material(value: TMaterial) {
method dispose (line 61) | public dispose(): void {
method render (line 65) | public render(renderer: WebGLRenderer): void {
FILE: package/src/Pathtracer.tsx
type PathtracerProps (line 12) | type PathtracerProps = ElementProps<typeof WebGLPathTracer> & {
type PathtracerAPI (line 23) | interface PathtracerAPI {
function fiberVec2ToArr (line 33) | function fiberVec2ToArr(vec: Vector2): [number, number] {
function usePathtracer (line 157) | function usePathtracer() {
FILE: package/src/PhysicalCamera.tsx
function PhysicalCamera (line 6) | function PhysicalCamera(
FILE: package/src/PhysicalSpotLight.tsx
function PhysicalSpotLight (line 6) | function PhysicalSpotLight(
FILE: package/src/ShapedAreaLight.tsx
function ShapedAreaLight (line 6) | function ShapedAreaLight(
FILE: package/vite.config.ts
function copyFiles (line 8) | function copyFiles() {
Condensed preview — 34 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (40K chars).
[
{
"path": ".gitignore",
"chars": 258,
"preview": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\nlerna-debug.log*\n\nnode_modules\ndist\ndis"
},
{
"path": ".prettierrc",
"chars": 87,
"preview": "{\n \"trailingComma\": \"none\",\n \"tabWidth\": 2,\n \"semi\": true,\n \"singleQuote\": false\n}\n"
},
{
"path": ".yarnrc.yml",
"chars": 25,
"preview": "nodeLinker: node-modules\n"
},
{
"path": "LICENSE.md",
"chars": 1067,
"preview": "MIT License\n\nCopyright (c) 2024 Poimandres\n\nPermission is hereby granted, free of charge, to any person obtaining a copy"
},
{
"path": "README.md",
"chars": 5859,
"preview": "<br />\n\n<h1 align=\"center\">react-three-gpu-pathtracer</h1>\n<h3 align=\"center\">⚡️ A React abstraction for the popular <a "
},
{
"path": "example/.codesandbox/tasks.json",
"chars": 752,
"preview": "{\n // These tasks will run in order when initializing your CodeSandbox project.\n \"setupTasks\": [\n {\n \"name\": \""
},
{
"path": "example/README.md",
"chars": 245,
"preview": "<br />\n\n<h3 align=\"center\">react-three-gpu-pathtracer</h3>\n<h3 align=\"center\">⚡️ A React abstraction for the popular <a "
},
{
"path": "example/csb.vite.config.js",
"chars": 183,
"preview": "import react from '@vitejs/plugin-react'\nimport { defineConfig } from 'vite'\n\n// https://vitejs.dev/config/\nexport defau"
},
{
"path": "example/index.html",
"chars": 541,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <link\n rel=\"icon\"\n type=\"image/svg+"
},
{
"path": "example/package.json",
"chars": 760,
"preview": "{\n \"name\": \"@react-three/gpu-pathtracer-example\",\n \"private\": true,\n \"version\": \"0.0.0\",\n \"type\": \"module\",\n \"scrip"
},
{
"path": "example/src/App.tsx",
"chars": 3543,
"preview": "import {\n Bounds,\n Center,\n Circle,\n Environment,\n OrbitControls,\n PerspectiveCamera,\n Stats,\n useTexture\n} from"
},
{
"path": "example/src/Controls.js",
"chars": 1409,
"preview": "import { presetsObj } from \"@react-three/drei/helpers/environment-assets\";\nimport { folder, useControls } from \"leva\";\n\n"
},
{
"path": "example/src/Model.jsx",
"chars": 1413,
"preview": "/*\nAuto-generated by: https://github.com/pmndrs/gltfjsx\n*/\n\nimport { useGLTF } from \"@react-three/drei\";\nimport { useLay"
},
{
"path": "example/src/Tag.jsx",
"chars": 2613,
"preview": "import { Logo } from \"@pmndrs/branding\";\nimport { FaGithub } from \"react-icons/fa\";\n\nexport default function Tag() {\n r"
},
{
"path": "example/src/index.css",
"chars": 1552,
"preview": "body {\n margin: 0;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", \"Roboto\", \"Oxygen\",\n \"Ubuntu\", \"Can"
},
{
"path": "example/src/index.js",
"chars": 254,
"preview": "import React from \"react\";\nimport ReactDOM from \"react-dom/client\";\nimport \"./index.css\";\nimport App from \"./App\";\n\ncons"
},
{
"path": "example/src/main.tsx",
"chars": 234,
"preview": "import { StrictMode } from \"react\";\nimport { createRoot } from \"react-dom/client\";\nimport App from \"./App.tsx\";\nimport \""
},
{
"path": "example/src/vite-env.d.ts",
"chars": 38,
"preview": "/// <reference types=\"vite/client\" />\n"
},
{
"path": "example/tsconfig.json",
"chars": 583,
"preview": "{\n \"compilerOptions\": {\n \"target\": \"ES2020\",\n \"useDefineForClassFields\": true,\n \"lib\": [\"ES2020\", \"DOM\", \"DOM."
},
{
"path": "example/tsconfig.tsbuildinfo",
"chars": 154,
"preview": "{\"root\":[\"./src/app.tsx\",\"./src/controls.js\",\"./src/model.jsx\",\"./src/tag.jsx\",\"./src/index.js\",\"./src/main.tsx\",\"./src/"
},
{
"path": "example/vite.config.ts",
"chars": 379,
"preview": "import react from \"@vitejs/plugin-react\";\nimport path from \"path\";\nimport { defineConfig } from \"vite\";\n\n// https://vite"
},
{
"path": "package/package.json",
"chars": 1646,
"preview": "{\n \"name\": \"@react-three/gpu-pathtracer\",\n \"version\": \"0.3.2\",\n \"description\": \"⚡️ A React abstraction for the popula"
},
{
"path": "package/src/FogVolumeMaterial.tsx",
"chars": 569,
"preview": "import { extend, ThreeElement } from \"@react-three/fiber\";\nimport { useEffect } from \"react\";\nimport { FogVolumeMaterial"
},
{
"path": "package/src/Pass.ts",
"chars": 1838,
"preview": "import {\n Material,\n Mesh,\n OrthographicCamera,\n PlaneGeometry,\n WebGLRenderer,\n WebGLRenderTarget\n} from \"three\";"
},
{
"path": "package/src/Pathtracer.tsx",
"chars": 4336,
"preview": "import {\n applyProps,\n ElementProps,\n useFrame,\n useThree,\n Vector2\n} from \"@react-three/fiber\";\nimport React, { us"
},
{
"path": "package/src/PhysicalCamera.tsx",
"chars": 1352,
"preview": "import { extend, ThreeElement, useThree } from \"@react-three/fiber\";\nimport { useLayoutEffect, useEffect, useRef } from "
},
{
"path": "package/src/PhysicalSpotLight.tsx",
"chars": 566,
"preview": "import { extend, ThreeElement } from \"@react-three/fiber\";\nimport { useEffect } from \"react\";\nimport { PhysicalSpotLight"
},
{
"path": "package/src/ShapedAreaLight.tsx",
"chars": 550,
"preview": "import { extend, ThreeElement } from \"@react-three/fiber\";\nimport { useEffect } from \"react\";\nimport { ShapedAreaLight a"
},
{
"path": "package/src/index.ts",
"chars": 173,
"preview": "export * from \"./FogVolumeMaterial\";\nexport * from \"./Pathtracer\";\nexport * from \"./PhysicalCamera\";\nexport * from \"./Ph"
},
{
"path": "package/tsconfig.json",
"chars": 311,
"preview": "{\n \"compilerOptions\": {\n \"noEmit\": true,\n \"allowSyntheticDefaultImports\": true,\n \"jsx\": \"react-jsx\",\n \"stri"
},
{
"path": "package/vite.config.ts",
"chars": 1172,
"preview": "import react from \"@vitejs/plugin-react\";\nimport fs from \"fs/promises\";\nimport path from \"path\";\nimport excludeDependenc"
},
{
"path": "package.json",
"chars": 852,
"preview": "{\n \"name\": \"@react-three/gpu-pathtracer-monorepo\",\n \"description\": \"Monorepo for @react-three/gpu-pathtracer-monorepo "
},
{
"path": "tsconfig.json",
"chars": 418,
"preview": "{\n \"compilerOptions\": {\n \"module\": \"esnext\",\n \"target\": \"es2018\",\n \"allowSyntheticDefaultImports\": true,\n \""
}
]
// ... and 1 more files (download for full content)
About this extraction
This page contains the full source code of the pmndrs/react-three-gpu-pathtracer GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 34 files (34.9 KB), approximately 10.2k tokens, and a symbol index with 26 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.