Repository: ektogamat/r3f-webgpu-starter
Branch: main
Commit: c87883c66090
Files: 24
Total size: 34.7 KB
Directory structure:
gitextract_zhgr58d5/
├── .gitignore
├── README.md
├── package.json
├── public/
│ ├── darth-transformed.glb
│ ├── favicon/
│ │ ├── about.txt
│ │ └── site.webmanifest
│ ├── hall-transformed.glb
│ ├── index.html
│ ├── naboo_royal_starship-transformed.glb
│ └── probe-transformed.glb
└── src/
├── App.js
├── components/
│ ├── Darth.js
│ ├── Hall.js
│ ├── JetEngineMaterial.js
│ ├── Light_Environment.js
│ ├── ManciniCanvas.js
│ ├── Overlay.js
│ ├── Probe.js
│ ├── ResizeHandler.js
│ ├── Royal.js
│ ├── VaderScene.js
│ └── WebGPUPostProcessing.js
├── index.js
└── styles.css
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional stylelint cache
.stylelintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# vuepress v2.x temp and cache directory
.temp
.cache
# Docusaurus cache and generated files
.docusaurus
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
.vercel
================================================
FILE: README.md
================================================
# React Three Fiber WebGPU Post Processing
<h4>by Anderson Mancini</h4>
[](https://r3f-webgpu-post-processing.vercel.app/)
A very simple scene to demonstrate how to integrate Threejs WebGPU with React Three Fiber using Post Processing effects.
[See the demo here](https://r3f-webgpu-post-processing.vercel.app/)
### Getting Started using this demo project
Download and install Node.js on your computer (https://nodejs.org/en/download/).
Then, open VSCODE, drag the project folder to it. Open VSCODE terminal and install dependencies (you need to do this only in the first time)
```shell
npm install
```
Run this command in your terminal to open a local server at localhost:3000
```shell
npm run start
```
### Can you leave a star please?
I genuinely appreciate your support! If you're willing to show your appreciation, you can <strong>give me a star on GitHub 🎉 </strong>or consider buying a coffee to support my development at https://www.buymeacoffee.com/andersonmancini. The funds received will be utilized to create more valuable content about Three.js and invest in acquiring new courses. Thank you for your consideration!
================================================
FILE: package.json
================================================
{
"name": "basic-demo",
"version": "1.0.0",
"description": "Shows how to form self-contained components with their own state and user interaction.",
"keywords": [
"interaction",
"pointer-events"
],
"main": "src/index.js",
"dependencies": {
"@react-three/drei": "^9.99.0",
"@react-three/fiber": "^8.15.0",
"@types/three": "^0.177.0",
"maath": "0.10.8",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-scripts": "5.0.1",
"three": "^0.177.0",
"three-mesh-bvh": "^0.8.0"
},
"scripts": {
"start": "set HOST=0.0.0.0 && react-scripts start",
"build": "GENERATE_SOURCEMAP=false react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject",
"deploy": "vercel --prod"
},
"browserslist": [
">1%",
"not dead",
"not ie <= 11",
"not op_mini all"
]
}
================================================
FILE: public/favicon/about.txt
================================================
This favicon was generated using the following graphics from Twitter Twemoji:
- Graphics Title: 1f441-200d-1f5e8.svg
- Graphics Author: Copyright 2020 Twitter, Inc and other contributors (https://github.com/twitter/twemoji)
- Graphics Source: https://github.com/twitter/twemoji/blob/master/assets/svg/1f441-200d-1f5e8.svg
- Graphics License: CC-BY 4.0 (https://creativecommons.org/licenses/by/4.0/)
================================================
FILE: public/favicon/site.webmanifest
================================================
{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}
================================================
FILE: public/index.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no"
/>
<title>WebGPU Post Processing Example - By Anderson Mancini</title>
<meta
name="description"
content="This is a demo of React Three Fiber using post processing with threejs and WebGPU, featuring Screen Space Reflections."
/>
<meta
property="og:url"
content="https://r3f-webgpu-post-processing.vercel.app"
/>
<meta property="og:type" content="website" />
<meta
property="og:title"
content="This is a demo of React Three Fiber using post processing with threejs and WebGPU, featuring Screen Space Reflections."
/>
<meta
property="og:description"
content="This is a demo of React Three Fiber using post processing with threejs and WebGPU, featuring Screen Space Reflections."
/>
<meta
property="og:image"
content="https://r3f-webgpu-post-processing.vercel.app/social.jpg"
/>
<meta name="twitter:card" content="summary_large_image" />
<meta
property="twitter:domain"
content="r3f-webgpu-post-processing.vercel.app"
/>
<meta
property="twitter:url"
content="https://r3f-webgpu-post-processing.vercel.app"
/>
<meta
name="twitter:title"
content="This is a demo of React Three Fiber using post processing with threejs and WebGPU, featuring Screen Space Reflections."
/>
<meta
name="twitter:description"
content="This is a demo of React Three Fiber using post processing with threejs and WebGPU, featuring Screen Space Reflections."
/>
<meta
name="twitter:image"
content="https://r3f-webgpu-post-processing.vercel.app/social.jpg"
/>
<meta
name="keywords"
content="react three fiber, webgpu, post processing, threejs, HTML, CSS, JavaScript"
/>
<meta name="author" content="Anderson Mancini.dev" />
<meta name="msapplication-TileColor" content="#000000" />
<meta name="theme-color" content="#000000" />
<link
rel="apple-touch-icon"
sizes="180x180"
href="favicon/apple-touch-icon.png"
/>
<link
rel="icon"
type="image/png"
sizes="32x32"
href="favicon/favicon-32x32.png"
/>
<link
rel="icon"
type="image/png"
sizes="16x16"
href="favicon/favicon-16x16.png"
/>
<link rel="manifest" href="favicon/site.webmanifest" />
</head>
<body>
<noscript> You need to enable JavaScript to run this app. </noscript>
<div id="root"></div>
</body>
</html>
================================================
FILE: src/App.js
================================================
import { useState } from "react";
import { Loader } from "@react-three/drei";
import { WebGPUPostProcessing } from "./components/WebGPUPostProcessing";
import { Hall } from "./components/Hall";
import { Overlay } from "./components/Overlay";
import RoyalNaboo from "./components/Royal";
import { Light_Environment } from "./components/Light_Environment";
import { VaderScene } from "./components/VaderScene";
import { ManciniCanvas } from "./components/ManciniCanvas";
export default function App() {
const [currentScene, setCurrentScene] = useState("vader");
const [quality, setQuality] = useState("default");
const [isPostProcessingEnabled, setIsPostProcessingEnabled] = useState(true);
// Disable frameloop by default, waiting for WebGPU to be ready
return (
<>
<Overlay
isPostProcessingEnabled={isPostProcessingEnabled}
setIsPostProcessingEnabled={setIsPostProcessingEnabled}
setCurrentScene={setCurrentScene}
currentScene={currentScene}
setQuality={setQuality}
quality={quality}
/>
<Loader />
<ManciniCanvas quality={quality}>
<color attach="background" args={["black"]} />
{isPostProcessingEnabled && (
<WebGPUPostProcessing
strength={0.25}
radius={0.1}
quality={quality}
/>
)}
<Light_Environment />
<group position={[-1, 0, 0]}>
<Hall position={[16.3, 0, -0.15]} scale={[1, 1, 1.3]} />
<group
visible={currentScene === "vader"}
scale={[0.8, 0.8, 0.8]}
position={[0, -0.25, 0]}
>
<VaderScene />
</group>
<RoyalNaboo
visible={currentScene === "royal"}
position={[3, -0.5, -2.2]}
scale={0.35}
rotation={[0, 0, 0]}
/>
</group>
</ManciniCanvas>
</>
);
}
================================================
FILE: src/components/Darth.js
================================================
/*
Auto-generated by: https://github.com/pmndrs/gltfjsx
Author: AFTONBLADET (https://sketchfab.com/wallander)
License: CC-BY-4.0 (http://creativecommons.org/licenses/by/4.0/)
Source: https://sketchfab.com/3d-models/darth-vader-by-makeamo-5b3371f4789c41eeaa691c9a3dfe1a96
Title: Darth Vader by Makeamo
*/
import React, { useRef } from "react";
import { useGLTF } from "@react-three/drei";
export function Darth(props) {
const { nodes, materials } = useGLTF("/darth-transformed.glb");
materials.Sabel_svart.emissiveIntensity = 20;
materials.darthvader_VaderConsolesFlickermat.roughness = 0.2;
materials.darthvader_VaderCapemat.roughness = 0.2;
materials.darthvader_VaderCapemat.metalness = 0.7;
materials.darthvader_VaderHelmetRimmat.roughness = 0.3;
materials.darthvader_VaderHelmetRimmat.metalness = 0.6;
materials.darthvader_VaderBodyArmourmat.roughness = 0.2;
materials.darthvader_VaderBodyArmourmat.metalness = 0.9;
return (
<group {...props} dispose={null}>
<mesh
castShadow
receiveShadow
geometry={nodes.DARTH_Sabel_vit_0.geometry}
material={materials.Sabel_vit}
/>
<mesh
castShadow
receiveShadow
geometry={nodes.DARTH_darthvader_VaderConsolesFlickermat_0.geometry}
material={materials.darthvader_VaderConsolesFlickermat}
/>
<mesh
castShadow
receiveShadow
geometry={nodes.DARTH_darthvader_VaderCapemat_0.geometry}
material={materials.darthvader_VaderCapemat}
/>
<mesh
castShadow
receiveShadow
geometry={nodes.DARTH_darthvader_VaderHelmetRimmat_0.geometry}
material={materials.darthvader_VaderHelmetRimmat}
/>
<mesh
castShadow
receiveShadow
geometry={nodes.DARTH_darthvader_VaderBodyArmourmat_0.geometry}
material={materials.darthvader_VaderBodyArmourmat}
/>
<mesh
castShadow
receiveShadow
geometry={nodes.DARTH_Laser_0.geometry}
material={materials.Laser}
/>
<mesh
castShadow
receiveShadow
geometry={nodes.DARTH_Sabel_svart_0.geometry}
material={materials.Sabel_svart}
/>
</group>
);
}
useGLTF.preload("/darth-transformed.glb");
================================================
FILE: src/components/Hall.js
================================================
/*
Auto-generated by: https://github.com/pmndrs/gltfjsx
*/
import React, { useRef } from "react";
import { useGLTF } from "@react-three/drei";
import { FrontSide } from "three";
export function Hall(props) {
const { nodes, materials } = useGLTF("/hall-transformed.glb");
materials.VenatorV3_SmallDoor_WallLight.emissiveIntensity = 2.6;
materials.Venator_Floor.color.set("black");
materials.Venator_Floor.roughness = 0.95;
materials.Venator_Floor.normalMap = null;
// materials.Venator_Floor.roughnessMap = null;
// materials.Venator_Floor.roughnessMap = null;
// materials.Venator_Floor.metalnessMap = null;
// materials.Venator_Floor.emissiveIntensity = 0;
materials.Venator_Floor.metalness = 1;
materials.Venator_Floor.metalnessMap = null;
materials.VenatorV3_WallPanels.color.set("grey");
materials.VenatorV3_WallPanels.roughness = 0.8;
materials.VenatorV3_WallPanels.metalness = 0.5;
materials.VenatorV3_WallPanels.side = FrontSide;
materials.VenatorV3_LargeDoor.side = FrontSide;
materials.VenatorV3_SmallDoor_WallLight.side = FrontSide;
return (
<group {...props} dispose={null}>
<group rotation={[-Math.PI / 2, 0, 0]} scale={0.012}>
<group rotation={[Math.PI / 2, 0, 0]}>
<group position={[-1448.93, -5.342, -159.366]} scale={6.463}>
<mesh
receiveShadow
geometry={nodes.WallPanel21_VenatorV3_WallPanels_0001.geometry}
material={materials.VenatorV3_WallPanels}
/>
<mesh
receiveShadow
geometry={nodes.WallPanel21_VenatorV3_WallPanels_0001_1.geometry}
material={materials.VenatorV3_LargeDoor}
/>
<mesh
receiveShadow
geometry={nodes.WallPanel21_VenatorV3_WallPanels_0001_2.geometry}
material={materials.Venator_Floor}
>
{/* <meshPhysicalMaterial
color="black"
metalness={0.8}
roughness={0.7}
/> */}
</mesh>
<mesh
receiveShadow
geometry={nodes.WallPanel21_VenatorV3_WallPanels_0001_3.geometry}
material={materials.VenatorV3_SmallDoor_WallLight}
/>
</group>
</group>
</group>
</group>
);
}
useGLTF.preload("/hall-transformed.glb");
================================================
FILE: src/components/JetEngineMaterial.js
================================================
import { useFrame } from "@react-three/fiber";
import {
uniform,
float,
vec2,
sin,
cos,
uv,
mod,
div,
sub,
length,
pow,
abs,
vec3,
add,
mul,
} from "three/tsl";
export function useJetEngineMaterial() {
// Create uniform and shader calculations
const uTime = uniform(float(0.0));
const TAU = float(6.28318530718);
const MAX_ITER = float(8);
const inten = float(0.007);
// Get UV coordinates and time
const currentUV = uv();
const currentTime = mul(uTime, float(2.5));
// Rotate UV coordinates
const angle = float(3.147);
const s = sin(angle);
const c = cos(angle);
// Create rotation matrix manually
const rotatedX = add(mul(currentUV.x, c), mul(currentUV.y, s));
const rotatedY = sub(mul(currentUV.x, s), mul(currentUV.y, c));
const uvRotated = add(vec2(rotatedX, rotatedY), mul(uTime, float(2.4)));
// Calculate base coordinates
const p = sub(mod(mul(uvRotated, TAU), TAU), float(256.0));
// Initialize accumulator
let accumulator = float(0.9);
// Unroll the loop
for (let i = 0; i < 8; i++) {
const t = mul(currentTime, sub(float(1.0), div(float(3.5), float(i + 1))));
const px = add(cos(sub(t, p.x)), sin(add(t, p.y)));
const py = add(sin(sub(t, p.y)), cos(add(t, p.x)));
const iVec = add(p, vec2(px, py));
const lenVec = vec2(
div(p.x, div(sin(add(iVec.x, t)), inten)),
div(p.y, div(cos(add(iVec.y, t)), inten))
);
accumulator = add(accumulator, div(float(1.1), length(lenVec)));
}
// Final color calculations
accumulator = div(accumulator, float(MAX_ITER));
accumulator = sub(float(1.1), pow(accumulator, float(1.5)));
const engineColor = pow(abs(accumulator), float(30.0));
const finalColor = add(
vec3(engineColor).mul(vec3(0, 0.5, 1.0)),
vec3(0.0, 0.0, 0.0)
).mul(float(25.0));
// Update time in animation frame
useFrame((state, delta) => {
uTime.value += delta * 0.5;
});
return {
key: uTime.id,
colorNode: finalColor,
};
}
================================================
FILE: src/components/Light_Environment.js
================================================
import { Environment } from "@react-three/drei";
import { OrbitControls } from "@react-three/drei";
export function Light_Environment() {
return (
<>
<directionalLight
position={[-0.7, 1.8, 0.1]}
intensity={6}
castShadow
shadow-mapSize={[128, 128]}
shadow-camera-near={2}
shadow-camera-far={100}
shadow-camera-top={3}
shadow-camera-right={3}
shadow-camera-bottom={-3}
shadow-camera-left={-3}
shadow-bias={-0.002}
/>
<OrbitControls
target={[2, -0.6, 0]}
// zoomSpeed={0.8}
screenSpacePanning={false}
dampingFactor={0.08}
maxPolarAngle={Math.PI / 1.75}
minPolarAngle={Math.PI / 2.7}
maxDistance={2.4}
minDistance={1}
minZoom={0.5}
maxZoom={1}
/>
<Environment
preset="warehouse"
environmentIntensity={0.2}
environmentRotation={[0.4, 0, 1.4]}
/>
</>
);
}
================================================
FILE: src/components/ManciniCanvas.js
================================================
import { Canvas, extend } from "@react-three/fiber";
import { useRef, useState } from "react";
import * as THREE from "three/webgpu";
import { ResizeHandler } from "./ResizeHandler";
extend(THREE);
export function ManciniCanvas({ quality, children }) {
const rendererRef = useRef();
const [frameloop, setFrameloop] = useState("never");
return (
<Canvas
onCreated={(state) => {
state.setSize(window.innerWidth, window.innerHeight);
}}
frameloop={frameloop}
dpr={quality === "default" ? 1 : [1, 1.5]}
camera={{
position: [18.6, -0.6, 0],
near: 0.1,
far: 50,
fov: 65,
// zoom: 1,
}}
shadows={"variance"}
gl={(canvas) => {
const renderer = new THREE.WebGPURenderer({
canvas,
powerPreference: "high-performance",
antialias: false,
alpha: false,
stencil: false,
});
// Initialize WebGPU and store renderer reference
renderer.init().then(() => setFrameloop("always"));
rendererRef.current = renderer;
return renderer;
}}
>
{children}
<ResizeHandler quality={quality} rendererRef={rendererRef} />
</Canvas>
);
}
================================================
FILE: src/components/Overlay.js
================================================
export function Overlay({
isPostProcessingEnabled,
setIsPostProcessingEnabled,
currentScene,
setCurrentScene,
quality,
setQuality,
}) {
return (
<div className="overlay">
<header>
<h1>
R3F <span>WebGPU</span>
</h1>
<p>
This is a demo of React Three Fiber using post processing with threejs
and WebGPU, featuring Screen Space Reflections.
</p>
</header>
<footer>
<p className="footer-text">
Created by <a href="https://andersonmancini.dev">Anderson Mancini</a>
</p>
<div className="footer-buttons">
<button
onClick={() => setIsPostProcessingEnabled(!isPostProcessingEnabled)}
>
{isPostProcessingEnabled ? "Disable" : "Enable"} Post Processing
</button>
<button
className="toggle"
onClick={() =>
setCurrentScene(currentScene === "vader" ? "royal" : "vader")
}
>
Toggle Scene
</button>
<button
onClick={() =>
setQuality(quality === "default" ? "high" : "default")
}
className="toggle-quality"
>
{quality === "default" ? "Higher Quality" : "Performance Mode"}
</button>
</div>
<a
href="https://github.com/ektogamat/r3f-webgpu-starter"
download
className="download-button"
>
<SvgIcon />
</a>
</footer>
</div>
);
}
const SvgIcon = (props) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="white"
>
<path
d="m20.59 12-3.3-3.3a1 1 0 1 1 1.42-1.4l4 4a1 1 0 0 1 0 1.4l-4 4a1 1 0 0 1-1.42-1.4zM3.4 12l3.3 3.3a1 1 0 0 1-1.42 1.4l-4-4a1 1 0 0 1 0-1.4l4-4A1 1 0 0 1 6.7 8.7zm7.56 8.24a1 1 0 0 1-1.94-.48l4-16a1 1 0 1 1 1.94.48z"
className="heroicon-ui"
></path>
</svg>
);
================================================
FILE: src/components/Probe.js
================================================
import React, { useRef } from "react";
import { useGLTF } from "@react-three/drei";
export function Probe(props) {
const { nodes, materials } = useGLTF("/probe-transformed.glb");
const lightMaterial = materials.light.clone();
lightMaterial.emissiveIntensity = 10;
lightMaterial.emissive.set(props.color);
return (
<group {...props} dispose={null}>
<group position={[-0.423, 0.036, -0.084]} rotation={[-Math.PI / 2, 0, 0]}>
<mesh
castShadow
receiveShadow
geometry={nodes["cam_low_Material_#26_0_1"].geometry}
material={materials.Material_26}
/>
<mesh
castShadow
receiveShadow
geometry={nodes["cam_low_Material_#26_0_2"].geometry}
material={lightMaterial}
/>
</group>
<mesh
castShadow
receiveShadow
geometry={nodes["ball_low_Material_#26_0"].geometry}
material={materials.Material_26}
position={[-0.423, 0.036, -0.084]}
rotation={[-Math.PI / 2, 0, 0]}
/>
<mesh
castShadow
receiveShadow
geometry={nodes["d_low_Material_#26_0"].geometry}
material={materials.Material_26}
position={[-0.423, 0.036, -0.084]}
rotation={[-Math.PI / 2, 0, 0]}
/>
<mesh
castShadow
receiveShadow
geometry={nodes["inner_low_Material_#26_0"].geometry}
material={materials.Material_26}
position={[-0.211, -0.033, 7.813]}
rotation={[-Math.PI / 2, 0, 0]}
/>
<mesh
castShadow
receiveShadow
geometry={nodes["Object018_Material_#26_0"].geometry}
material={lightMaterial}
position={[-0.423, 0.036, -0.084]}
rotation={[-Math.PI / 2, 0, 0]}
/>
<mesh
castShadow
receiveShadow
geometry={nodes["shell_low_Material_#26_0"].geometry}
position={[-0.423, 0.036, -0.084]}
rotation={[-Math.PI / 2, 0, 0]}
>
<meshPhysicalMaterial
color="white"
roughness={0.35}
metalness={1}
clearcoat={1}
/>
</mesh>
</group>
);
}
useGLTF.preload("/probe-transformed.glb");
================================================
FILE: src/components/ResizeHandler.js
================================================
import { useEffect } from "react";
export function ResizeHandler({ quality, rendererRef }) {
useEffect(() => {
const handleResize = () => {
if (rendererRef.current) {
rendererRef.current.setSize(window.innerWidth, window.innerHeight);
}
};
window.addEventListener("resize", handleResize);
// Cleanup
return () => window.removeEventListener("resize", handleResize);
}, [quality]);
return null;
}
================================================
FILE: src/components/Royal.js
================================================
/*
Auto-generated by: https://github.com/pmndrs/gltfjsx
Command: npx gltfjsx@6.1.4 naboo_royal_starship.glb --transform --simplify
Author: hobbit84 (https://sketchfab.com/hobbit84)
License: CC-BY-NC-4.0 (http://creativecommons.org/licenses/by-nc/4.0/)
Source: https://sketchfab.com/3d-models/naboo-royal-starship-j-type-327-nubian-f631077977754b5591298ecfa201380b
Title: Naboo Royal Starship (J-type 327 Nubian)
*/
import React from "react";
import { Float, useGLTF } from "@react-three/drei";
import { AdditiveBlending, DoubleSide } from "three";
import { useJetEngineMaterial } from "./JetEngineMaterial";
export default function RoyalNaboo(props) {
const { nodes, materials } = useGLTF("/naboo_royal_starship-transformed.glb");
const { key, colorNode } = useJetEngineMaterial();
materials["blinn1.002"].roughness = 0.15;
materials["blinn1.002"].metalness = 1;
materials["blinn1.002"].roughnessMap = null;
return (
<Float speed={2} floatIntensity={0.2} rotationIntensity={0.3}>
<group {...props} dispose={null}>
<group position={[-0.01, 0, 6.5]} rotation={[-Math.PI / 2, 0, Math.PI]}>
<mesh
castShadow
receiveShadow
geometry={nodes.Object_2.geometry}
material={materials["Material.002"]}
/>
<mesh
castShadow
receiveShadow
geometry={nodes.Object_3.geometry}
material={materials["aiwindow.002"]}
/>
<mesh
castShadow
receiveShadow
geometry={nodes.Object_4.geometry}
material={materials["blinn1.002"]}
/>
<mesh
castShadow
receiveShadow
geometry={nodes.Object_5.geometry}
material={materials["blinn1.002"]}
/>
<mesh
castShadow
receiveShadow
geometry={nodes.Object_6.geometry}
material={materials["blinn1.002"]}
/>
<mesh position={[-1.48, -4.1, -0.36]}>
<cylinderGeometry args={[0.2, 0.01, 2.9, 16, 8, true]} />
<meshStandardNodeMaterial
key={key}
colorNode={colorNode}
transparent
blending={AdditiveBlending}
side={DoubleSide}
emissiveNode={colorNode}
/>
</mesh>
<mesh position={[1.48, -4.1, -0.36]}>
<cylinderGeometry args={[0.2, 0.01, 2.9, 16, 8, true]} />
<meshStandardNodeMaterial
key={key}
colorNode={colorNode}
transparent
blending={AdditiveBlending}
side={DoubleSide}
emissiveNode={colorNode}
/>
</mesh>
<mesh
rotation={[Math.PI / 2, 0, 0]}
position={[1.51, -2.68, -0.37]}
renderOrder={-1}
>
<torusGeometry args={[0.2, 0.03, 24, 24]} />
<meshStandardNodeMaterial
color="red"
transparent
emissive="cyan"
emissiveIntensity={10}
/>
</mesh>
<mesh
rotation={[Math.PI / 2, 0, 0]}
position={[-1.5, -2.68, -0.36]}
renderOrder={-1}
>
<torusGeometry args={[0.2, 0.03, 24, 24]} />
<meshStandardNodeMaterial
color="cyan"
transparent
emissive="cyan"
emissiveIntensity={10}
/>
</mesh>
</group>
</group>
</Float>
);
}
useGLTF.preload("/naboo_royal_starship-transformed.glb");
================================================
FILE: src/components/VaderScene.js
================================================
import { Float } from "@react-three/drei";
import { Probe } from "./Probe";
import { Darth } from "./Darth";
export function VaderScene() {
return (
<>
<Float speed={3.5} floatIntensity={0.2} rotationIntensity={0.3}>
<Probe
position={[3, 0, 1.2]}
scale={0.05}
rotation={[0, Math.PI / 2, 0]}
color="red"
/>
</Float>
<Float speed={6.5} floatIntensity={0.4} rotationIntensity={0.5}>
<Probe
position={[-1.5, 0, -1.5]}
scale={0.1}
rotation={[0, Math.PI / 2, 0]}
color="cyan"
/>
</Float>
<Float speed={3.5} floatIntensity={0.2} rotationIntensity={0.3}>
<Probe
position={[3.5, -0.8, -1.2]}
scale={0.05}
rotation={[0, Math.PI / 2, 0]}
color="red"
/>
</Float>
<Float speed={3.5} floatIntensity={1.5} rotationIntensity={0.4}>
<Probe
position={[3, 0.2, -2.8]}
scale={0.05}
rotation={[0, Math.PI / 2 - 0.2, 0.1]}
color="cyan"
/>
</Float>
<Float speed={3.5} floatIntensity={1.5} rotationIntensity={0.4}>
<Probe
position={[2.6, 0.2, 2.8]}
scale={0.05}
rotation={[0, Math.PI / 2 + 0.5, 0.1]}
color="cyan"
/>
</Float>
<Darth
scale={0.008}
position={[3.5, -1.325, 0.4]}
rotation={[0, Math.PI / 2, 0]}
/>
</>
);
}
================================================
FILE: src/components/WebGPUPostProcessing.js
================================================
import * as THREE from "three/webgpu";
import {
pass,
mrt,
output,
transformedNormalView,
metalness,
blendColor,
depth,
emissive,
} from "three/tsl";
import { bloom } from "three/addons/tsl/display/BloomNode.js";
import { ssr } from "three/addons/tsl/display/SSRNode.js";
import { smaa } from "three/addons/tsl/display/SMAANode.js";
import { useThree, useFrame } from "@react-three/fiber";
import { useEffect, useRef } from "react";
export function WebGPUPostProcessing({
strength = 2.5,
radius = 0.5,
quality = "default",
}) {
const { gl: renderer, scene, camera, size } = useThree();
const postProcessingRef = useRef(null);
useEffect(() => {
if (!renderer || !scene || !camera) return;
// Create post-processing setup with specific filters
const scenePass = pass(scene, camera, {
minFilter: THREE.LinearFilter,
magFilter: THREE.LinearFilter,
});
// Setup Multiple Render Targets (MRT)
scenePass.setMRT(
mrt({
output: output,
normal: transformedNormalView,
metalness: metalness,
emissive: emissive,
})
);
// Get texture nodes
const scenePassColor = scenePass.getTextureNode("output");
const scenePassNormal = scenePass.getTextureNode("normal");
const scenePassDepth = scenePass.getTextureNode("depth");
const scenePassMetalness = scenePass.getTextureNode("metalness");
const scenePassEmissive = scenePass.getTextureNode("emissive");
// Create SSR pass
const ssrPass = ssr(
scenePassColor,
scenePassDepth,
scenePassNormal,
scenePassMetalness,
camera
);
ssrPass.resolutionScale = 0.65;
ssrPass.maxDistance.value = 0.65;
ssrPass.opacity.value = 0.85;
ssrPass.thickness.value = 0.015;
// Create bloom pass
const bloomPass = bloom(scenePassEmissive, strength, radius, 0.6);
// Blend SSR over beauty with SMAA
const outputNode = smaa(blendColor(scenePassColor.add(bloomPass), ssrPass));
// Setup post-processing
const postProcessing = new THREE.PostProcessing(renderer);
postProcessing.outputNode = outputNode;
postProcessingRef.current = postProcessing;
// Handle window resize
if (postProcessingRef.current.setSize) {
postProcessingRef.current.setSize(size.width, size.height);
postProcessingRef.current.needsUpdate = true;
}
return () => {
postProcessingRef.current = null;
};
}, [renderer, scene, camera, size, strength, radius, quality]);
useFrame(({ gl, scene, camera }) => {
if (postProcessingRef.current) {
gl.clear();
postProcessingRef.current.render();
}
}, 1);
return null;
}
================================================
FILE: src/index.js
================================================
import { createRoot } from 'react-dom/client'
import './styles.css'
import App from './App'
createRoot(window.root).render(<App />)
================================================
FILE: src/styles.css
================================================
@import url("https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap");
:root {
--bg-background: #111827;
--clr-card: #1f2937;
--clr-1: #6420aa;
--clr-2: #ff3ea5;
--clr-3: #ff7ed4;
}
* {
box-sizing: border-box;
}
html,
body,
canvas {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
font-family: "Inter", sans-serif;
overflow: hidden;
touch-action: none;
}
body {
background: #020202;
}
.overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1000;
pointer-events: none;
display: flex;
justify-content: center;
align-items: flex-start;
}
header {
background-image: linear-gradient(
to bottom,
#020202,
rgba(0, 0, 0, 0.775),
#02020200
);
width: 100%;
height: 170px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
header h1 {
margin: 0;
padding: 0;
color: white;
font-size: 3rem;
font-weight: 300;
letter-spacing: -0.04em;
text-transform: uppercase;
text-align: center;
}
header h1 span {
font-weight: 900;
}
header p {
color: white;
font-size: 0.8rem;
font-weight: 200;
letter-spacing: -0.04em;
text-align: center;
margin-top: 0.5rem;
padding: 0;
max-width: 500px;
}
footer {
width: 100%;
height: 110px;
display: flex;
justify-content: center;
align-items: center;
position: absolute;
bottom: 0;
}
.footer-buttons {
display: flex;
gap: 2rem;
}
@property --gradient-angle {
syntax: "<angle>";
initial-value: 90deg;
inherits: false;
}
footer button {
color: white;
padding: 1rem 1.3rem;
border-radius: 0.5rem;
box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
font-size: 0.45rem;
text-transform: uppercase;
letter-spacing: 0.06em;
font-weight: 300;
pointer-events: all;
cursor: pointer;
position: relative;
cursor: pointer;
border: none;
border-radius: 50px;
background-color: var(--clr-card);
transition: all 0.3s ease;
}
footer button:hover {
background: rgb(65, 60, 79);
color: white;
}
footer button:hover:after {
transform: scale(1.5);
filter: blur(2rem);
transition: all 0.3s ease;
}
footer button::after,
footer button::before {
content: " ";
position: absolute;
z-index: -1;
inset: -0.06rem;
background: conic-gradient(
from var(--gradient-angle),
var(--clr-card),
var(--clr-1),
var(--clr-2),
var(--clr-3),
var(--clr-2),
var(--clr-1),
var(--clr-card)
);
border-radius: inherit;
animation: rotate 2.5s ease-in-out infinite;
transition: all 0.3s ease;
}
footer button.toggle::before {
content: " ";
position: absolute;
z-index: -1;
inset: -0.06rem;
background: conic-gradient(
from var(--gradient-angle),
var(--clr-card),
var(--clr-1),
var(--clr-2),
var(--clr-3),
var(--clr-2),
var(--clr-1),
var(--clr-card)
);
border-radius: inherit;
animation: rotate2 3.5s ease-in-out infinite;
transition: all 0.3s ease;
}
footer button::after {
filter: blur(3rem);
}
@keyframes rotate {
0% {
--gradient-angle: 0deg;
}
100% {
--gradient-angle: 360deg;
}
}
@keyframes rotate2 {
0% {
--gradient-angle: 0deg;
}
50% {
--gradient-angle: 120deg;
}
100% {
--gradient-angle: 360deg;
}
}
.footer-text {
color: white;
font-size: 0.8rem;
font-weight: 200;
letter-spacing: -0.04em;
text-align: center;
padding: 0;
position: absolute;
bottom: 20px;
left: 20px;
width: max-content;
opacity: 0.3;
}
.footer-text a {
color: white;
font-weight: 400;
text-decoration: none;
pointer-events: all;
}
.download-button {
position: fixed;
bottom: 20px;
right: 20px;
background-color: transparent;
border: 1px solid white;
border-radius: 50px;
padding: 10px 10px;
text-align: center;
cursor: pointer;
z-index: 1000;
pointer-events: all;
display: flex;
opacity: 0.3;
}
.download-button img {
width: 20px;
height: 20px;
vertical-align: middle;
}
.download-button:hover {
background-color: #ffffff7b;
}
/* Mobile */
@media (max-width: 768px) {
header h1 {
font-size: 2.5rem;
}
header p {
font-size: 0.9rem;
max-width: 300px;
}
.download-button {
display: none;
}
.footer-buttons {
gap: 0.5rem;
}
footer {
height: 150px;
}
footer button {
padding: 0.9rem 1.3rem;
font-size: 0.5rem;
}
footer button.toggle-quality {
display: none;
}
.footer-text {
font-size: 0.6rem;
bottom: 10px;
left: 50%;
transform: translateX(-50%);
}
}
gitextract_zhgr58d5/
├── .gitignore
├── README.md
├── package.json
├── public/
│ ├── darth-transformed.glb
│ ├── favicon/
│ │ ├── about.txt
│ │ └── site.webmanifest
│ ├── hall-transformed.glb
│ ├── index.html
│ ├── naboo_royal_starship-transformed.glb
│ └── probe-transformed.glb
└── src/
├── App.js
├── components/
│ ├── Darth.js
│ ├── Hall.js
│ ├── JetEngineMaterial.js
│ ├── Light_Environment.js
│ ├── ManciniCanvas.js
│ ├── Overlay.js
│ ├── Probe.js
│ ├── ResizeHandler.js
│ ├── Royal.js
│ ├── VaderScene.js
│ └── WebGPUPostProcessing.js
├── index.js
└── styles.css
SYMBOL INDEX (12 symbols across 12 files)
FILE: src/App.js
function App (line 11) | function App() {
FILE: src/components/Darth.js
function Darth (line 12) | function Darth(props) {
FILE: src/components/Hall.js
function Hall (line 8) | function Hall(props) {
FILE: src/components/JetEngineMaterial.js
function useJetEngineMaterial (line 20) | function useJetEngineMaterial() {
FILE: src/components/Light_Environment.js
function Light_Environment (line 4) | function Light_Environment() {
FILE: src/components/ManciniCanvas.js
function ManciniCanvas (line 8) | function ManciniCanvas({ quality, children }) {
FILE: src/components/Overlay.js
function Overlay (line 1) | function Overlay({
FILE: src/components/Probe.js
function Probe (line 4) | function Probe(props) {
FILE: src/components/ResizeHandler.js
function ResizeHandler (line 3) | function ResizeHandler({ quality, rendererRef }) {
FILE: src/components/Royal.js
function RoyalNaboo (line 15) | function RoyalNaboo(props) {
FILE: src/components/VaderScene.js
function VaderScene (line 5) | function VaderScene() {
FILE: src/components/WebGPUPostProcessing.js
function WebGPUPostProcessing (line 18) | function WebGPUPostProcessing({
Condensed preview — 24 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (38K chars).
[
{
"path": ".gitignore",
"chars": 2055,
"preview": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nlerna-debug.log*\n.pnpm-debug.log*\n\n# Diagnostic reports"
},
{
"path": "README.md",
"chars": 1211,
"preview": "# React Three Fiber WebGPU Post Processing\n\n<h4>by Anderson Mancini</h4>\n\n[\nLicense: C"
},
{
"path": "src/components/Hall.js",
"chars": 2393,
"preview": "/*\nAuto-generated by: https://github.com/pmndrs/gltfjsx\n*/\n\nimport React, { useRef } from \"react\";\nimport { useGLTF } fr"
},
{
"path": "src/components/JetEngineMaterial.js",
"chars": 2006,
"preview": "import { useFrame } from \"@react-three/fiber\";\nimport {\n uniform,\n float,\n vec2,\n sin,\n cos,\n uv,\n mod,\n div,\n "
},
{
"path": "src/components/Light_Environment.js",
"chars": 992,
"preview": "import { Environment } from \"@react-three/drei\";\nimport { OrbitControls } from \"@react-three/drei\";\n\nexport function Lig"
},
{
"path": "src/components/ManciniCanvas.js",
"chars": 1239,
"preview": "import { Canvas, extend } from \"@react-three/fiber\";\nimport { useRef, useState } from \"react\";\nimport * as THREE from \"t"
},
{
"path": "src/components/Overlay.js",
"chars": 2003,
"preview": "export function Overlay({\n isPostProcessingEnabled,\n setIsPostProcessingEnabled,\n currentScene,\n setCurrentScene,\n "
},
{
"path": "src/components/Probe.js",
"chars": 2209,
"preview": "import React, { useRef } from \"react\";\nimport { useGLTF } from \"@react-three/drei\";\n\nexport function Probe(props) {\n co"
},
{
"path": "src/components/ResizeHandler.js",
"chars": 445,
"preview": "import { useEffect } from \"react\";\n\nexport function ResizeHandler({ quality, rendererRef }) {\n useEffect(() => {\n co"
},
{
"path": "src/components/Royal.js",
"chars": 3656,
"preview": "/*\nAuto-generated by: https://github.com/pmndrs/gltfjsx\nCommand: npx gltfjsx@6.1.4 naboo_royal_starship.glb --transform "
},
{
"path": "src/components/VaderScene.js",
"chars": 1485,
"preview": "import { Float } from \"@react-three/drei\";\nimport { Probe } from \"./Probe\";\nimport { Darth } from \"./Darth\";\n\nexport fun"
},
{
"path": "src/components/WebGPUPostProcessing.js",
"chars": 2688,
"preview": "import * as THREE from \"three/webgpu\";\nimport {\n pass,\n mrt,\n output,\n transformedNormalView,\n metalness,\n blendCo"
},
{
"path": "src/index.js",
"chars": 133,
"preview": "import { createRoot } from 'react-dom/client'\nimport './styles.css'\nimport App from './App'\n\ncreateRoot(window.root).ren"
},
{
"path": "src/styles.css",
"chars": 4600,
"preview": "@import url(\"https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=s"
}
]
// ... and 4 more files (download for full content)
About this extraction
This page contains the full source code of the ektogamat/r3f-webgpu-starter GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 24 files (34.7 KB), approximately 10.6k tokens, and a symbol index with 12 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.