├── .codesandbox
└── ci.json
├── .gitignore
├── LICENSE
├── README.md
├── babel.config.js
├── example
├── index.html
├── index.tsx
├── package.json
├── pages
│ ├── distortion-material.tsx
│ ├── main.tsx
│ ├── more-instances.tsx
│ └── voronoi.tsx
├── simplex3d.js
├── style.css
├── tsconfig.json
├── voronoi.js
└── yarn.lock
├── logo.jpg
├── make-types.js
├── package.json
├── readme
├── autocomplete.jpg
├── distortion.jpg
├── glslify.jpg
├── multiple-instances.jpg
└── voronoi.jpg
├── src
├── component-material.tsx
├── constants.ts
├── create-material.ts
├── generated.ts
├── helpers
│ └── objects.ts
├── index.tsx
├── proxies.tsx
└── types
│ ├── index.ts
│ └── internal.ts
├── tsconfig.json
└── yarn.lock
/.codesandbox/ci.json:
--------------------------------------------------------------------------------
1 | {
2 | "sandboxes": ["w1uo1", "ttnet"]
3 | }
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.log
2 | .DS_Store
3 | node_modules
4 | .cache
5 | dist
6 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Marco Ludovico Perego
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | [](https://www.npmjs.com/package/component-material)
4 | [](https://www.npmjs.com/package/component-material)
5 | [](https://discord.gg/ZZjjNvJ)
6 |
7 | # Component Material
8 |
9 | Material is a React utility that helps you compose and modify materials in [react-three-fiber](https://github.com/pmndrs/react-three-fiber) and threejs.
10 |
11 | ### Examples
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | ## Quick start
20 |
21 | ```bash
22 | yarn add component-material
23 | ```
24 |
25 | ```jsx
26 | import Material from 'component-material'
27 |
28 | function CustomMaterial(props) {
29 | return (
30 |
38 |
42 |
43 | )
44 | }
45 |
46 | function Sphere() {
47 | return (
48 |
49 |
50 |
51 |
52 | ```
53 |
54 | ## Features
55 |
56 | - Create custom shaders on top of existing threejs Shaders and shader-chunks
57 | - Compose your shader code with modular injects
58 | - Auto-completion, intellisense and typechecking
59 | - Syntax-highlighting with either [tagged glsl-literals](https://marketplace.visualstudio.com/items?itemName=boyswan.glsl-literal) or [comment-tagged templates](https://marketplace.visualstudio.com/items?itemName=bierner.comment-tagged-templates)
60 | - Glslify and imports via [babel-plugin-glsl](https://github.com/onnovisser/babel-plugin-glsl)
61 |
62 | ## ``
63 |
64 | #### `from`
65 |
66 | By default Material extends three's MeshPhysicalMaterial. If you want to extend a different material just use the `from` prop passing the desired material constructor.
67 |
68 | ```jsx
69 |
70 | ```
71 |
72 | #### `uniforms`
73 |
74 | Uniforms used inside shaders can be defined via the `uniforms` prop as follows
75 |
76 | ```jsx
77 |
83 | ```
84 |
85 | This will also create setters and getters for the uniforms automatically, allowing you to mutate them using props and effectively making the material reactive:
86 |
87 | ```jsx
88 | function CustomMaterial({ color }) {
89 | return (
90 |
94 | ```
95 |
96 | - The correspondences between glsl and javascript types can be seen [here](https://threejs.org/docs/#api/en/core/Uniform)
97 | - Uniforms cannot be defined twice in the same shader. So be careful not to define the same uniforms inside the `head` tag.
98 |
99 | #### `varyings`
100 |
101 | Varying variables can be defined directly inside the shader `head` tag or they can be declared as prop:
102 |
103 | ```jsx
104 |
110 | ```
111 |
112 | This is equivalent to adding this code to both your vertex and fragment shaders heads:
113 |
114 | ```glsl
115 | float myVarying1;
116 | vec2 myVarying2;
117 | ```
118 |
119 | - Varyings don't have an initial value, only a type definition
120 | - As uniforms, varyings cannot be defined twice in the same shader, this will give a glsl error. So be careful not to define the same varyings inside the `head` tag.
121 |
122 | ## Fragment- and vertex-shader composition
123 |
124 | The `Frag` and `Vert` tags have the function of injecting the shader text, passed as children, into the preconfigured shader of the threejs material. Let's see what it means with an example:
125 |
126 | ```jsx
127 |
128 |
135 |
139 | ```
140 |
141 | In the code above the `Frag.Head` component adds an easing function `quadraticInOut` to the fragment shader of the material, prepending it before the `main` function of the shader.
142 |
143 | The `Frag.Body` component instead adds a line of code that modify the `gl_FragColor` alpha value, appending it after the last operation of the main function.
144 |
145 | In particular, if we take as an example the fragment shader of the `MeshPhysicalMaterial`, `Frag.Head` prepends the code before [this shader line](https://github.com/mrdoob/three.js/blob/dev/src/renderers/shaders/ShaderLib/meshphysical_frag.glsl.js#L2), `Frag.Body` instead posts the code after [this shader line](https://github.com/mrdoob/three.js/blob/dev/src/renderers/shaders/ShaderLib/meshphysical_frag.glsl.js#L124) (the `dithering_fragment` chunk).
146 |
147 | The same goes for the `Vert` component, which however acts on the vertex shader. In particular, `Vert.Head` prepends the code to [this shader line](https://github.com/mrdoob/three.js/blob/dev/src/renderers/shaders/ShaderLib/meshphysical_vert.glsl.js#L2), while `Vert.Body` appends the code to [this shader line](https://github.com/mrdoob/three.js/blob/dev/src/renderers/shaders/ShaderLib/meshphysical_vert.glsl.js#L60) (the `project_vertex` chunk).
148 |
149 | It is possible to inject the code after a particular chunk just by doing
150 |
151 | ```jsx
152 |
153 | ```
154 |
155 | where `my_chunk` must be replaced with the name of the chunk concerned.
156 |
157 | If we wanted to insert some code just after the `emissivemap_fragment` chunk ([here the reference for the MeshPhysicalMaterial](https://github.com/mrdoob/three.js/blob/dev/src/renderers/shaders/ShaderLib/meshphysical_frag.glsl.js#L99)) then just use the following code
158 |
159 | ```jsx
160 |
161 | ```
162 |
163 | #### `replaceChunk`
164 |
165 | The `replaceChunk` prop is a boolean that allows you to completely replace the chosen chunk, so instead of append the custom shader code after the chunk it will be replaced directly.
166 |
167 | ```jsx
168 |
169 | ```
170 |
171 | ## Common chunks
172 |
173 | The `Common` tag is useful in case vertex shader and fragment shader share some functions.
174 |
175 | ❌ If both the fragment shader and the vertex shader share the easing function `quadraticInOut`, instead of writing
176 |
177 | ```jsx
178 |
185 |
192 | ```
193 |
194 | ✅ we will write
195 |
196 | ```jsx
197 |
204 | ```
205 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | [
4 | '@babel/preset-env',
5 | { targets: { esmodules: true } }
6 | ],
7 | '@babel/preset-react',
8 | '@babel/preset-typescript'
9 | ],
10 | }
11 |
--------------------------------------------------------------------------------
/example/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Playground
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/example/index.tsx:
--------------------------------------------------------------------------------
1 | import 'react-app-polyfill/ie11'
2 | import * as React from 'react'
3 | import * as ReactDOM from 'react-dom'
4 |
5 | const Main = React.lazy(() => import('./pages/main'))
6 | const DistortionMaterial = React.lazy(() => import('./pages/distortion-material'))
7 | const Voronoi = React.lazy(() => import('./pages/voronoi'))
8 | const MoreInstances = React.lazy(() => import('./pages/more-instances'))
9 |
10 | import { Switch, Route } from 'wouter'
11 |
12 | import './style.css'
13 |
14 | function App() {
15 | return (
16 | loading.>}>
17 |
18 | {() => }
19 | {() => }
20 | {() => }
21 | {() => }
22 |
23 |
24 | )
25 | }
26 |
27 | ReactDOM.render(, document.getElementById('root'))
28 |
--------------------------------------------------------------------------------
/example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example",
3 | "version": "1.0.0",
4 | "main": "index.js",
5 | "license": "MIT",
6 | "scripts": {
7 | "start": "parcel index.html",
8 | "build": "parcel build index.html"
9 | },
10 | "dependencies": {
11 | "@react-three/drei": "^4.0.0",
12 | "react-app-polyfill": "^1.0.0",
13 | "react-use-gesture": "^8.0.1",
14 | "tweakpane": "^1.5.7",
15 | "use-tweaks": "^0.3.1",
16 | "wouter": "^2.6.0"
17 | },
18 | "alias": {
19 | "react": "../node_modules/react",
20 | "react-dom": "../node_modules/react-dom/profiling",
21 | "scheduler/tracing": "../node_modules/scheduler/tracing-profiling"
22 | },
23 | "devDependencies": {
24 | "@types/react": "^17.0.0",
25 | "@types/react-dom": "^17.0.0",
26 | "parcel": "1.12.3",
27 | "typescript": "^4.1.2"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/example/pages/distortion-material.tsx:
--------------------------------------------------------------------------------
1 | import 'react-app-polyfill/ie11'
2 | import * as React from 'react'
3 | import { Suspense, useRef } from 'react'
4 | import { Canvas, useFrame } from 'react-three-fiber'
5 | import { Environment, Sphere } from '@react-three/drei'
6 | import { useTweaks } from 'use-tweaks'
7 | import distortion from '../simplex3d'
8 | import M, { GenericMaterial } from '../../dist'
9 |
10 | function Scene() {
11 | const material = useRef()
12 |
13 | const { metalness, clearcoat, roughness, radiusVariationAmplitude, radiusNoiseFrequency } = useTweaks({
14 | metalness: { value: 1, min: 0, max: 1 },
15 | clearcoat: { value: 0.6, min: 0, max: 1 },
16 | roughness: { value: 0.5, min: 0, max: 1 },
17 | radiusVariationAmplitude: { value: 1.25, min: 0, max: 5 },
18 | radiusNoiseFrequency: { value: 0.2, min: 0, max: 2 },
19 | })
20 |
21 | useFrame(({ clock }) => {
22 | if (material.current) {
23 | material.current.time = clock.getElapsedTime()
24 | }
25 | })
26 |
27 | const RADIUS = 4
28 |
29 | return (
30 |
31 |
43 | {/*glsl*/ `
44 | ${distortion}
45 |
46 | float fsnoise(float val1, float val2, float val3){
47 | return snoise(vec3(val1,val2,val3));
48 | }
49 |
50 | vec3 distortFunct(vec3 transformed, float factor) {
51 | float radiusVariation = -fsnoise(
52 | transformed.x * radiusNoiseFrequency + time,
53 | transformed.y * radiusNoiseFrequency + time,
54 | transformed.z * radiusNoiseFrequency + time
55 | ) * radiusVariationAmplitude * factor;
56 | return normalize(transformed) * (radiusVariation + radius);
57 | }
58 |
59 | vec3 orthogonal(vec3 v) {
60 | return normalize(abs(v.x) > abs(v.z) ? vec3(-v.y, v.x, 0.0)
61 | : vec3(0.0, -v.z, v.y));
62 | }
63 |
64 | vec3 distortNormal(vec3 position, vec3 distortedPosition, vec3 normal){
65 | vec3 tangent1 = orthogonal(normal);
66 | vec3 tangent2 = normalize(cross(normal, tangent1));
67 | vec3 nearby1 = position + tangent1 * 0.1;
68 | vec3 nearby2 = position + tangent2 * 0.1;
69 | vec3 distorted1 = distortFunct(nearby1, 1.0);
70 | vec3 distorted2 = distortFunct(nearby2, 1.0);
71 | return normalize(cross(distorted1 - distortedPosition, distorted2 - distortedPosition));
72 | }
73 | `}
74 | {/*glsl*/ `
75 | float updateTime = time / 10.0;
76 | transformed = distortFunct(transformed, 1.0);
77 | vec3 distortedNormal = distortNormal(position, transformed, normal);
78 | vNormal = normal + distortedNormal;
79 | gl_Position = projectionMatrix * modelViewMatrix * vec4(transformed,1.);
80 | `}
81 |
82 |
83 | )
84 | }
85 |
86 | function App() {
87 | return (
88 |
98 | )
99 | }
100 |
101 | export default App
102 |
--------------------------------------------------------------------------------
/example/pages/main.tsx:
--------------------------------------------------------------------------------
1 | import 'react-app-polyfill/ie11'
2 | import * as React from 'react'
3 | import { Suspense, useRef } from 'react'
4 | import { Canvas, useFrame } from 'react-three-fiber'
5 | import { Environment, Sphere } from '@react-three/drei'
6 | import { useTweaks } from 'use-tweaks'
7 |
8 | import M, { GenericMaterial } from '../../dist'
9 |
10 | function Scene() {
11 | const material = useRef(null!)
12 |
13 | const { metalness, roughness } = useTweaks({
14 | metalness: { value: 0.5, min: 0, max: 1 },
15 | roughness: { value: 0.5, min: 0, max: 1 },
16 | })
17 |
18 | useFrame(({ clock }) => {
19 | if (material.current) {
20 | material.current.time = clock.getElapsedTime() * 2
21 | }
22 | })
23 |
24 | return (
25 |
26 |
34 | {/*glsl*/ `
35 | float quadraticInOut(float t) {
36 | float p = 2.0 * t * t;
37 | return t < 0.5 ? p : -p + (4.0 * t) - 1.0;
38 | }
39 | `}
40 | {/*glsl*/ `
41 | gl_FragColor = vec4(gl_FragColor.rgb, quadraticInOut((sin(time)+1.0)/2.0));
42 | `}
43 |
44 |
45 | )
46 | }
47 |
48 | function App() {
49 | return (
50 | <>
51 |
61 | >
62 | )
63 | }
64 |
65 | export default App
66 |
--------------------------------------------------------------------------------
/example/pages/more-instances.tsx:
--------------------------------------------------------------------------------
1 | import 'react-app-polyfill/ie11'
2 | import * as React from 'react'
3 | import { Suspense, useRef } from 'react'
4 | import { Canvas, useFrame } from 'react-three-fiber'
5 | import { Environment, Sphere } from '@react-three/drei'
6 | import { useTweaks } from 'use-tweaks'
7 | import distortion from '../simplex3d'
8 | import M, { GenericMaterial } from '../../dist'
9 |
10 | const RADIUS = 4
11 |
12 | function CustomMaterial(props) {
13 | const material = useRef()
14 | const { metalness, clearcoat, roughness, radiusVariationAmplitude, radiusNoiseFrequency } = useTweaks({
15 | metalness: { value: 1, min: 0, max: 1 },
16 | clearcoat: { value: 0.6, min: 0, max: 1 },
17 | roughness: { value: 0.5, min: 0, max: 1 },
18 | radiusVariationAmplitude: { value: 1.25, min: 0, max: 5 },
19 | radiusNoiseFrequency: { value: 0.2, min: 0, max: 2 },
20 | })
21 | useFrame(({ clock }) => {
22 | if (material.current) {
23 | material.current.time = clock.getElapsedTime()
24 | }
25 | })
26 | return (
27 |
39 | {/*glsl*/ `
40 | ${distortion}
41 |
42 | float fsnoise(float val1, float val2, float val3){
43 | return snoise(vec3(val1,val2,val3));
44 | }
45 |
46 | vec3 distortFunct(vec3 transformed, float factor) {
47 | float radiusVariation = -fsnoise(
48 | transformed.x * radiusNoiseFrequency + time,
49 | transformed.y * radiusNoiseFrequency + time,
50 | transformed.z * radiusNoiseFrequency + time
51 | ) * radiusVariationAmplitude * factor;
52 | return normalize(transformed) * (radiusVariation + radius);
53 | }
54 |
55 | vec3 orthogonal(vec3 v) {
56 | return normalize(abs(v.x) > abs(v.z) ? vec3(-v.y, v.x, 0.0)
57 | : vec3(0.0, -v.z, v.y));
58 | }
59 |
60 | vec3 distortNormal(vec3 position, vec3 distortedPosition, vec3 normal){
61 | vec3 tangent1 = orthogonal(normal);
62 | vec3 tangent2 = normalize(cross(normal, tangent1));
63 | vec3 nearby1 = position + tangent1 * 0.1;
64 | vec3 nearby2 = position + tangent2 * 0.1;
65 | vec3 distorted1 = distortFunct(nearby1, 1.0);
66 | vec3 distorted2 = distortFunct(nearby2, 1.0);
67 | return normalize(cross(distorted1 - distortedPosition, distorted2 - distortedPosition));
68 | }
69 | `}
70 | {/*glsl*/ `
71 | float updateTime = time / 10.0;
72 | transformed = distortFunct(transformed, 1.0);
73 | vec3 distortedNormal = distortNormal(position, transformed, normal);
74 | vNormal = normal + distortedNormal;
75 | gl_Position = projectionMatrix * modelViewMatrix * vec4(transformed,1.);
76 | `}
77 |
78 | )
79 | }
80 |
81 | function Scene({ color, ...props }) {
82 | return (
83 |
84 |
85 |
86 | )
87 | }
88 |
89 | function App() {
90 | return (
91 | <>
92 |
105 | >
106 | )
107 | }
108 |
109 | export default App
110 |
--------------------------------------------------------------------------------
/example/pages/voronoi.tsx:
--------------------------------------------------------------------------------
1 | import 'react-app-polyfill/ie11'
2 | import * as React from 'react'
3 | import { Suspense, useRef } from 'react'
4 | import { Canvas, useFrame } from 'react-three-fiber'
5 | import { Environment, Sphere } from '@react-three/drei'
6 | import { useTweaks } from 'use-tweaks'
7 | import M, { GenericMaterial } from '../../dist'
8 | import voronoi from '../voronoi'
9 |
10 | function Scene(): JSX.Element {
11 | const material = useRef()
12 | const sphere = useRef()
13 |
14 | const { amplitude, frequency, jitter, metalness, roughness } = useTweaks({
15 | metalness: { value: 1, min: 0, max: 1 },
16 | roughness: { value: 1, min: 0, max: 1 },
17 | amplitude: { value: 1, min: -5, max: 5 },
18 | frequency: { value: 0.85, min: 0, max: 10 },
19 | jitter: { value: 0.9, min: 0, max: 2 },
20 | })
21 |
22 | useFrame(({ clock }) => {
23 | if (material.current) {
24 | material.current.time = clock.getElapsedTime()
25 | }
26 | })
27 |
28 | const RADIUS = 4
29 |
30 | return (
31 |
32 |
45 |
46 | {/*glsl*/ `
47 | ${voronoi}
48 |
49 | vec3 distortFunct(vec3 transformed, float factor) {
50 | vec3 deformed = transformed.xyz * vec3(1., 7., 3.);
51 | vec2 f = worley(deformed * frequency * 0.1 + time * 0.4, jitter, false) * amplitude * factor;
52 | return normalize(transformed) * (f.x + radius);
53 | }
54 |
55 | vec3 orthogonal(vec3 v) {
56 | return normalize(abs(v.x) > abs(v.z) ? vec3(-v.y, v.x, 0.0)
57 | : vec3(0.0, -v.z, v.y));
58 | }
59 |
60 | vec3 distortNormal(vec3 position, vec3 distortedPosition, vec3 normal){
61 | vec3 tangent1 = orthogonal(normal);
62 | vec3 tangent2 = normalize(cross(normal, tangent1));
63 | vec3 nearby1 = position + tangent1 * 0.1;
64 | vec3 nearby2 = position + tangent2 * 0.1;
65 | vec3 distorted1 = distortFunct(nearby1, 1.0);
66 | vec3 distorted2 = distortFunct(nearby2, 1.0);
67 | return normalize(cross(distorted1 - distortedPosition, distorted2 - distortedPosition));
68 | }
69 | `}
70 |
71 |
72 | {/*glsl*/ `
73 | float updateTime = time / 10.0;
74 |
75 | transformed = distortFunct(transformed, 1.0);
76 |
77 | vec3 distortedNormal = distortNormal(position, transformed, normal);
78 |
79 | vTransformed = transformed;
80 |
81 | vNormal = normal + distortedNormal;
82 | gl_Position = projectionMatrix * modelViewMatrix * vec4(transformed,1.);
83 | `}
84 |
85 |
86 |
87 | )
88 | }
89 |
90 | function App() {
91 | return (
92 |
103 | )
104 | }
105 |
106 | export default App
107 |
--------------------------------------------------------------------------------
/example/simplex3d.js:
--------------------------------------------------------------------------------
1 | export default `
2 | vec3 mod289(vec3 x) {
3 | return x - floor(x * (1.0 / 289.0)) * 289.0;
4 | }
5 |
6 | vec4 mod289(vec4 x) {
7 | return x - floor(x * (1.0 / 289.0)) * 289.0;
8 | }
9 |
10 | vec4 permute(vec4 x) {
11 | return mod289(((x*34.0)+1.0)*x);
12 | }
13 |
14 | vec4 taylorInvSqrt(vec4 r)
15 | {
16 | return 1.79284291400159 - 0.85373472095314 * r;
17 | }
18 |
19 | float snoise(vec3 v)
20 | {
21 | const vec2 C = vec2(1.0/6.0, 1.0/3.0) ;
22 | const vec4 D = vec4(0.0, 0.5, 1.0, 2.0);
23 |
24 | // First corner
25 | vec3 i = floor(v + dot(v, C.yyy) );
26 | vec3 x0 = v - i + dot(i, C.xxx) ;
27 |
28 | // Other corners
29 | vec3 g = step(x0.yzx, x0.xyz);
30 | vec3 l = 1.0 - g;
31 | vec3 i1 = min( g.xyz, l.zxy );
32 | vec3 i2 = max( g.xyz, l.zxy );
33 |
34 | // x0 = x0 - 0.0 + 0.0 * C.xxx;
35 | // x1 = x0 - i1 + 1.0 * C.xxx;
36 | // x2 = x0 - i2 + 2.0 * C.xxx;
37 | // x3 = x0 - 1.0 + 3.0 * C.xxx;
38 | vec3 x1 = x0 - i1 + C.xxx;
39 | vec3 x2 = x0 - i2 + C.yyy; // 2.0*C.x = 1/3 = C.y
40 | vec3 x3 = x0 - D.yyy; // -1.0+3.0*C.x = -0.5 = -D.y
41 |
42 | // Permutations
43 | i = mod289(i);
44 | vec4 p = permute( permute( permute(
45 | i.z + vec4(0.0, i1.z, i2.z, 1.0 ))
46 | + i.y + vec4(0.0, i1.y, i2.y, 1.0 ))
47 | + i.x + vec4(0.0, i1.x, i2.x, 1.0 ));
48 |
49 | // Gradients: 7x7 points over a square, mapped onto an octahedron.
50 | // The ring size 17*17 = 289 is close to a multiple of 49 (49*6 = 294)
51 | float n_ = 0.142857142857; // 1.0/7.0
52 | vec3 ns = n_ * D.wyz - D.xzx;
53 |
54 | vec4 j = p - 49.0 * floor(p * ns.z * ns.z); // mod(p,7*7)
55 |
56 | vec4 x_ = floor(j * ns.z);
57 | vec4 y_ = floor(j - 7.0 * x_ ); // mod(j,N)
58 |
59 | vec4 x = x_ *ns.x + ns.yyyy;
60 | vec4 y = y_ *ns.x + ns.yyyy;
61 | vec4 h = 1.0 - abs(x) - abs(y);
62 |
63 | vec4 b0 = vec4( x.xy, y.xy );
64 | vec4 b1 = vec4( x.zw, y.zw );
65 |
66 | //vec4 s0 = vec4(lessThan(b0,0.0))*2.0 - 1.0;
67 | //vec4 s1 = vec4(lessThan(b1,0.0))*2.0 - 1.0;
68 | vec4 s0 = floor(b0)*2.0 + 1.0;
69 | vec4 s1 = floor(b1)*2.0 + 1.0;
70 | vec4 sh = -step(h, vec4(0.0));
71 |
72 | vec4 a0 = b0.xzyw + s0.xzyw*sh.xxyy ;
73 | vec4 a1 = b1.xzyw + s1.xzyw*sh.zzww ;
74 |
75 | vec3 p0 = vec3(a0.xy,h.x);
76 | vec3 p1 = vec3(a0.zw,h.y);
77 | vec3 p2 = vec3(a1.xy,h.z);
78 | vec3 p3 = vec3(a1.zw,h.w);
79 |
80 | //Normalise gradients
81 | vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3)));
82 | p0 *= norm.x;
83 | p1 *= norm.y;
84 | p2 *= norm.z;
85 | p3 *= norm.w;
86 |
87 | // Mix final noise value
88 | vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0);
89 | m = m * m;
90 | return 42.0 * dot( m*m, vec4( dot(p0,x0), dot(p1,x1),
91 | dot(p2,x2), dot(p3,x3) ) );
92 | }
93 | `
94 |
--------------------------------------------------------------------------------
/example/style.css:
--------------------------------------------------------------------------------
1 | html,
2 | body {
3 | height: 100vh;
4 | overflow: hidden;
5 | border: none;
6 | margin: 0;
7 | padding: 0;
8 | }
9 |
10 | #root {
11 | height: 100vh;
12 | }
13 |
--------------------------------------------------------------------------------
/example/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "allowSyntheticDefaultImports": false,
4 | "target": "es5",
5 | "module": "commonjs",
6 | "jsx": "react",
7 | "moduleResolution": "node",
8 | "noImplicitAny": false,
9 | "noUnusedLocals": false,
10 | "noUnusedParameters": false,
11 | "removeComments": true,
12 | "strictNullChecks": true,
13 | "preserveConstEnums": true,
14 | "sourceMap": true,
15 | "lib": ["es2015", "es2016", "dom"],
16 | "types": ["node"]
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/example/voronoi.js:
--------------------------------------------------------------------------------
1 | export default /*glsl*/ `
2 | // Permutation polynomial: (34x^2 + x) mod 289
3 | vec3 permute(vec3 x) {
4 | return mod((34.0 * x + 1.0) * x, 289.0);
5 | }
6 |
7 | vec3 dist(vec3 x, vec3 y, vec3 z, bool manhattanDistance) {
8 | return manhattanDistance ? abs(x) + abs(y) + abs(z) : (x * x + y * y + z * z);
9 | }
10 |
11 | vec2 worley(vec3 P, float jitter, bool manhattanDistance) {
12 | float K = 0.142857142857; // 1/7
13 | float Ko = 0.428571428571; // 1/2-K/2
14 | float K2 = 0.020408163265306; // 1/(7*7)
15 | float Kz = 0.166666666667; // 1/6
16 | float Kzo = 0.416666666667; // 1/2-1/6*2
17 |
18 | vec3 Pi = mod(floor(P), 289.0);
19 | vec3 Pf = fract(P) - 0.5;
20 |
21 | vec3 Pfx = Pf.x + vec3(1.0, 0.0, -1.0);
22 | vec3 Pfy = Pf.y + vec3(1.0, 0.0, -1.0);
23 | vec3 Pfz = Pf.z + vec3(1.0, 0.0, -1.0);
24 |
25 | vec3 p = permute(Pi.x + vec3(-1.0, 0.0, 1.0));
26 | vec3 p1 = permute(p + Pi.y - 1.0);
27 | vec3 p2 = permute(p + Pi.y);
28 | vec3 p3 = permute(p + Pi.y + 1.0);
29 |
30 | vec3 p11 = permute(p1 + Pi.z - 1.0);
31 | vec3 p12 = permute(p1 + Pi.z);
32 | vec3 p13 = permute(p1 + Pi.z + 1.0);
33 |
34 | vec3 p21 = permute(p2 + Pi.z - 1.0);
35 | vec3 p22 = permute(p2 + Pi.z);
36 | vec3 p23 = permute(p2 + Pi.z + 1.0);
37 |
38 | vec3 p31 = permute(p3 + Pi.z - 1.0);
39 | vec3 p32 = permute(p3 + Pi.z);
40 | vec3 p33 = permute(p3 + Pi.z + 1.0);
41 |
42 | vec3 ox11 = fract(p11*K) - Ko;
43 | vec3 oy11 = mod(floor(p11*K), 7.0)*K - Ko;
44 | vec3 oz11 = floor(p11*K2)*Kz - Kzo; // p11 < 289 guaranteed
45 |
46 | vec3 ox12 = fract(p12*K) - Ko;
47 | vec3 oy12 = mod(floor(p12*K), 7.0)*K - Ko;
48 | vec3 oz12 = floor(p12*K2)*Kz - Kzo;
49 |
50 | vec3 ox13 = fract(p13*K) - Ko;
51 | vec3 oy13 = mod(floor(p13*K), 7.0)*K - Ko;
52 | vec3 oz13 = floor(p13*K2)*Kz - Kzo;
53 |
54 | vec3 ox21 = fract(p21*K) - Ko;
55 | vec3 oy21 = mod(floor(p21*K), 7.0)*K - Ko;
56 | vec3 oz21 = floor(p21*K2)*Kz - Kzo;
57 |
58 | vec3 ox22 = fract(p22*K) - Ko;
59 | vec3 oy22 = mod(floor(p22*K), 7.0)*K - Ko;
60 | vec3 oz22 = floor(p22*K2)*Kz - Kzo;
61 |
62 | vec3 ox23 = fract(p23*K) - Ko;
63 | vec3 oy23 = mod(floor(p23*K), 7.0)*K - Ko;
64 | vec3 oz23 = floor(p23*K2)*Kz - Kzo;
65 |
66 | vec3 ox31 = fract(p31*K) - Ko;
67 | vec3 oy31 = mod(floor(p31*K), 7.0)*K - Ko;
68 | vec3 oz31 = floor(p31*K2)*Kz - Kzo;
69 |
70 | vec3 ox32 = fract(p32*K) - Ko;
71 | vec3 oy32 = mod(floor(p32*K), 7.0)*K - Ko;
72 | vec3 oz32 = floor(p32*K2)*Kz - Kzo;
73 |
74 | vec3 ox33 = fract(p33*K) - Ko;
75 | vec3 oy33 = mod(floor(p33*K), 7.0)*K - Ko;
76 | vec3 oz33 = floor(p33*K2)*Kz - Kzo;
77 |
78 | vec3 dx11 = Pfx + jitter*ox11;
79 | vec3 dy11 = Pfy.x + jitter*oy11;
80 | vec3 dz11 = Pfz.x + jitter*oz11;
81 |
82 | vec3 dx12 = Pfx + jitter*ox12;
83 | vec3 dy12 = Pfy.x + jitter*oy12;
84 | vec3 dz12 = Pfz.y + jitter*oz12;
85 |
86 | vec3 dx13 = Pfx + jitter*ox13;
87 | vec3 dy13 = Pfy.x + jitter*oy13;
88 | vec3 dz13 = Pfz.z + jitter*oz13;
89 |
90 | vec3 dx21 = Pfx + jitter*ox21;
91 | vec3 dy21 = Pfy.y + jitter*oy21;
92 | vec3 dz21 = Pfz.x + jitter*oz21;
93 |
94 | vec3 dx22 = Pfx + jitter*ox22;
95 | vec3 dy22 = Pfy.y + jitter*oy22;
96 | vec3 dz22 = Pfz.y + jitter*oz22;
97 |
98 | vec3 dx23 = Pfx + jitter*ox23;
99 | vec3 dy23 = Pfy.y + jitter*oy23;
100 | vec3 dz23 = Pfz.z + jitter*oz23;
101 |
102 | vec3 dx31 = Pfx + jitter*ox31;
103 | vec3 dy31 = Pfy.z + jitter*oy31;
104 | vec3 dz31 = Pfz.x + jitter*oz31;
105 |
106 | vec3 dx32 = Pfx + jitter*ox32;
107 | vec3 dy32 = Pfy.z + jitter*oy32;
108 | vec3 dz32 = Pfz.y + jitter*oz32;
109 |
110 | vec3 dx33 = Pfx + jitter*ox33;
111 | vec3 dy33 = Pfy.z + jitter*oy33;
112 | vec3 dz33 = Pfz.z + jitter*oz33;
113 |
114 | vec3 d11 = dist(dx11, dy11, dz11, manhattanDistance);
115 | vec3 d12 =dist(dx12, dy12, dz12, manhattanDistance);
116 | vec3 d13 = dist(dx13, dy13, dz13, manhattanDistance);
117 | vec3 d21 = dist(dx21, dy21, dz21, manhattanDistance);
118 | vec3 d22 = dist(dx22, dy22, dz22, manhattanDistance);
119 | vec3 d23 = dist(dx23, dy23, dz23, manhattanDistance);
120 | vec3 d31 = dist(dx31, dy31, dz31, manhattanDistance);
121 | vec3 d32 = dist(dx32, dy32, dz32, manhattanDistance);
122 | vec3 d33 = dist(dx33, dy33, dz33, manhattanDistance);
123 |
124 | vec3 d1a = min(d11, d12);
125 | d12 = max(d11, d12);
126 | d11 = min(d1a, d13); // Smallest now not in d12 or d13
127 | d13 = max(d1a, d13);
128 | d12 = min(d12, d13); // 2nd smallest now not in d13
129 | vec3 d2a = min(d21, d22);
130 | d22 = max(d21, d22);
131 | d21 = min(d2a, d23); // Smallest now not in d22 or d23
132 | d23 = max(d2a, d23);
133 | d22 = min(d22, d23); // 2nd smallest now not in d23
134 | vec3 d3a = min(d31, d32);
135 | d32 = max(d31, d32);
136 | d31 = min(d3a, d33); // Smallest now not in d32 or d33
137 | d33 = max(d3a, d33);
138 | d32 = min(d32, d33); // 2nd smallest now not in d33
139 | vec3 da = min(d11, d21);
140 | d21 = max(d11, d21);
141 | d11 = min(da, d31); // Smallest now in d11
142 | d31 = max(da, d31); // 2nd smallest now not in d31
143 | d11.xy = (d11.x < d11.y) ? d11.xy : d11.yx;
144 | d11.xz = (d11.x < d11.z) ? d11.xz : d11.zx; // d11.x now smallest
145 | d12 = min(d12, d21); // 2nd smallest now not in d21
146 | d12 = min(d12, d22); // nor in d22
147 | d12 = min(d12, d31); // nor in d31
148 | d12 = min(d12, d32); // nor in d32
149 | d11.yz = min(d11.yz,d12.xy); // nor in d12.yz
150 | d11.y = min(d11.y,d12.z); // Only two more to go
151 | d11.y = min(d11.y,d11.z); // Done! (Phew!)
152 | return sqrt(d11.xy); // F1, F2
153 |
154 | }`
155 |
--------------------------------------------------------------------------------
/logo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pmndrs/component-material/1e9efeb05620162781bcd78ad91fa95259b6b133/logo.jpg
--------------------------------------------------------------------------------
/make-types.js:
--------------------------------------------------------------------------------
1 | import * as THREE from 'three'
2 |
3 | const fragTypes = Object.keys(THREE.ShaderChunk)
4 | .map(x => `"${x}"`)
5 | .filter(x => x.indexOf('frag') > -1)
6 | .join(' | ')
7 | const vertTypes = Object.keys(THREE.ShaderChunk)
8 | .map(x => `"${x}"`)
9 | .filter(x => x.indexOf('vert') > -1)
10 | .join(' | ')
11 | const common = Object.keys(THREE.ShaderChunk)
12 | .map(x => `"${x}"`)
13 | .filter(x => x.indexOf('vert') === -1 && x.indexOf('frag') === -1)
14 | .join(' | ')
15 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "component-material",
3 | "version": "1.0.7",
4 | "description": "",
5 | "repository": {
6 | "type": "git",
7 | "url": "git+https://github.com/emmelleppi/component-material.git"
8 | },
9 | "keywords": [],
10 | "main": "dist/index.js",
11 | "typings": "dist/index.d.ts",
12 | "files": [
13 | "dist",
14 | "src"
15 | ],
16 | "engines": {
17 | "node": ">=10"
18 | },
19 | "scripts": {
20 | "start": "tsdx watch",
21 | "build": "tsdx build",
22 | "test": "tsdx test --passWithNoTests",
23 | "lint": "tsdx lint",
24 | "prepare": "tsdx build",
25 | "size": "size-limit",
26 | "analyze": "size-limit --why"
27 | },
28 | "peerDependencies": {
29 | "react": ">=16"
30 | },
31 | "husky": {
32 | "hooks": {
33 | "pre-commit": "tsdx lint --fix"
34 | }
35 | },
36 | "prettier": {
37 | "semi": false,
38 | "trailingComma": "es5",
39 | "singleQuote": true,
40 | "jsxBracketSameLine": true,
41 | "tabWidth": 2,
42 | "printWidth": 120
43 | },
44 | "author": "Marco Ludovico Perego",
45 | "module": "dist/component-material.esm.js",
46 | "size-limit": [
47 | {
48 | "path": "dist/component-material.cjs.production.min.js",
49 | "limit": "10 KB"
50 | },
51 | {
52 | "path": "dist/component-material.esm.js",
53 | "limit": "10 KB"
54 | }
55 | ],
56 | "devDependencies": {
57 | "@babel/core": "7.13.13",
58 | "@babel/preset-env": "^7.14.2",
59 | "@babel/preset-react": "7.13.13",
60 | "@babel/preset-typescript": "^7.13.0",
61 | "@size-limit/preset-small-lib": "^4.9.0",
62 | "@types/react": "^17.0.0",
63 | "@types/react-dom": "^17.0.0",
64 | "@types/three": "^0.126.2",
65 | "babel-loader": "^8.2.2",
66 | "husky": "^4.3.0",
67 | "react": "^17.0.2",
68 | "react-dom": "^17.0.2",
69 | "react-is": "^17.0.2",
70 | "react-three-fiber": "^6.0.13",
71 | "size-limit": "^4.9.0",
72 | "three": "^0.128.0",
73 | "tsdx": "^0.14.1",
74 | "tslib": "^2.0.3",
75 | "typescript": "^4.1.2"
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/readme/autocomplete.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pmndrs/component-material/1e9efeb05620162781bcd78ad91fa95259b6b133/readme/autocomplete.jpg
--------------------------------------------------------------------------------
/readme/distortion.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pmndrs/component-material/1e9efeb05620162781bcd78ad91fa95259b6b133/readme/distortion.jpg
--------------------------------------------------------------------------------
/readme/glslify.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pmndrs/component-material/1e9efeb05620162781bcd78ad91fa95259b6b133/readme/glslify.jpg
--------------------------------------------------------------------------------
/readme/multiple-instances.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pmndrs/component-material/1e9efeb05620162781bcd78ad91fa95259b6b133/readme/multiple-instances.jpg
--------------------------------------------------------------------------------
/readme/voronoi.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pmndrs/component-material/1e9efeb05620162781bcd78ad91fa95259b6b133/readme/voronoi.jpg
--------------------------------------------------------------------------------
/src/component-material.tsx:
--------------------------------------------------------------------------------
1 | import React, { useMemo, useRef } from 'react'
2 | import { FRAG, VERT } from './constants'
3 | import createMaterial from './create-material'
4 | import { ChildProps, ExtensionShaderObject, ExtensionShadersObject, Uniforms } from './types/internal'
5 | import { ComponentMaterialProps, GenericMaterial } from './types/index'
6 |
7 | function editShader(shader: string, extensions: ExtensionShaderObject) {
8 | Object.entries(extensions).forEach(([key, { value, replaceChunk }]) => {
9 | if (value && shader.includes(key)) {
10 | shader = shader.replace(
11 | `#include <${key}>`,
12 | `
13 | ${replaceChunk ? '' : `#include <${key}>`}
14 | ${value}
15 | `
16 | )
17 | }
18 | })
19 | return shader
20 | }
21 |
22 | function editShaderHead(shader: string, head: string) {
23 | if (head && typeof head === 'string') {
24 | shader = `
25 | ${head}
26 | ${shader}
27 | `
28 | }
29 | return shader
30 | }
31 |
32 | function addUniforms(shader: string, uniforms: Uniforms) {
33 | return `${Object.entries(uniforms)
34 | .map(([key, { type }]) => `uniform ${type} ${key};`)
35 | .join(' ')}
36 | ${shader}
37 | `
38 | }
39 |
40 | function addVarying(shader: string, varying: Uniforms) {
41 | return `${Object.entries(varying)
42 | .map(([key, { type }]) => `varying ${type} ${key};`)
43 | .join(' ')}
44 | ${shader}
45 | `
46 | }
47 |
48 | export const ComponentMaterial = React.forwardRef(
49 | function ComponentMaterial({ children, varyings = {}, uniforms = {}, from, ...props }, ref) {
50 | const uniformsRef = useRef(uniforms)
51 | const varyingsRef = useRef(varyings)
52 |
53 | const _uniforms = useMemo(
54 | () =>
55 | Object.entries(uniforms).reduce((acc: any, [key, { value }]) => {
56 | acc[key] = value
57 | return acc
58 | }, {}),
59 | [uniforms]
60 | )
61 |
62 | const shaders = useMemo(
63 | () =>
64 | React.Children.toArray(children).reduce(
65 | (acc: any, child: any) => {
66 | const shader = child?.props?.children
67 |
68 | if (typeof shader === 'string') {
69 | const replaceChunk = child?.props?.replaceChunk || false
70 | const { chunkName, shaderType }: ChildProps = child.type
71 |
72 | if ([VERT, FRAG].includes(shaderType)) {
73 | if (chunkName === 'Head') {
74 | acc[shaderType].head = acc[shaderType].head.concat(`
75 | ${shader}
76 | `)
77 | } else {
78 | if (!acc[shaderType][chunkName]) {
79 | acc[shaderType][chunkName] = {
80 | value: '',
81 | replaceChunk: false,
82 | }
83 | }
84 |
85 | acc[shaderType][chunkName].replaceChunk = replaceChunk
86 | acc[shaderType][chunkName].value = acc[shaderType][chunkName].value.concat(`
87 | ${shader}
88 | `)
89 | }
90 | } else {
91 | acc.common = acc.common.concat(`
92 | ${shader}
93 | `)
94 | }
95 | }
96 |
97 | return acc
98 | },
99 | {
100 | vert: {
101 | head: '',
102 | },
103 | frag: {
104 | head: '',
105 | },
106 | common: '',
107 | }
108 | ),
109 | [children]
110 | )
111 |
112 | const material = useMemo(() => {
113 | const { vert, frag, common } = shaders
114 | const { head: vertHead, ...vertBody } = vert
115 | const { head: fragHead, ...fragBody } = frag
116 |
117 | const _material = createMaterial(from, uniformsRef.current, shader => {
118 | shader.fragmentShader = editShaderHead(shader.fragmentShader, fragHead)
119 | shader.vertexShader = editShaderHead(shader.vertexShader, vertHead)
120 | shader.fragmentShader = editShaderHead(shader.fragmentShader, common)
121 | shader.vertexShader = editShaderHead(shader.vertexShader, common)
122 | shader.fragmentShader = addUniforms(shader.fragmentShader, uniformsRef.current)
123 | shader.vertexShader = addUniforms(shader.vertexShader, uniformsRef.current)
124 | shader.fragmentShader = addVarying(shader.fragmentShader, varyingsRef.current)
125 | shader.vertexShader = addVarying(shader.vertexShader, varyingsRef.current)
126 | shader.fragmentShader = editShader(shader.fragmentShader, fragBody)
127 | shader.vertexShader = editShader(shader.vertexShader, vertBody)
128 | })
129 | return new _material()
130 | }, [shaders, from])
131 |
132 | return
133 | }
134 | )
135 |
--------------------------------------------------------------------------------
/src/constants.ts:
--------------------------------------------------------------------------------
1 | export const VERT = 'vert'
2 | export const FRAG = 'frag'
3 | export const COMMON = 'common'
4 | export const DEFAULT_VERT_CHUNK = 'project_vertex'
5 | export const DEFAULT_FRAG_CHUNK = 'dithering_fragment'
6 |
--------------------------------------------------------------------------------
/src/create-material.ts:
--------------------------------------------------------------------------------
1 | import { MeshPhysicalMaterial, Shader } from 'three'
2 |
3 | import { getKeyValue, setKeyValue } from './helpers/objects'
4 | import { MaterialConstructor } from './types/index'
5 | import { Uniforms } from './types/internal'
6 |
7 | function createMaterial(
8 | baseMaterial: MaterialConstructor = MeshPhysicalMaterial,
9 | uniforms: Uniforms = {},
10 | onBeforeCompile?: (shader: Shader) => void
11 | ) {
12 | return class ComponentMaterial extends baseMaterial {
13 | constructor(parameters = {}) {
14 | const entries = Object.keys(uniforms)
15 | super(parameters)
16 | this.setValues(parameters)
17 |
18 | entries.forEach(key => {
19 | setKeyValue(this, `_${key}`, { value: uniforms[key] })
20 | Object.defineProperty(this, key, {
21 | get: () => this[`_${key}`].value,
22 | set: v => (this[`_${key}`].value = v),
23 | })
24 | })
25 | }
26 |
27 | onBeforeCompile(shader: Shader) {
28 | const handler = {
29 | get: function(target: Shader, key: keyof Shader) {
30 | return getKeyValue(target, key)
31 | },
32 | set: function(target: Shader, key: keyof Shader, value: any) {
33 | setKeyValue(target, key, value)
34 | // Accoring to ProxyHandler, the set function should return a boolean.
35 | return true
36 | },
37 | }
38 |
39 | const entries = Object.keys(uniforms)
40 | entries.forEach(key => {
41 | shader.uniforms[key] = this[`_${key}`]
42 | })
43 |
44 | const proxiedShader = new Proxy(shader, handler)
45 |
46 | if (onBeforeCompile) {
47 | onBeforeCompile(proxiedShader)
48 | }
49 | }
50 | }
51 | }
52 |
53 | export default createMaterial
54 |
--------------------------------------------------------------------------------
/src/generated.ts:
--------------------------------------------------------------------------------
1 | export type fragmentChunks =
2 | | 'alphamap_fragment'
3 | | 'alphamap_pars_fragment'
4 | | 'alphatest_fragment'
5 | | 'aomap_fragment'
6 | | 'aomap_pars_fragment'
7 | | 'bumpmap_pars_fragment'
8 | | 'clipping_planes_fragment'
9 | | 'clipping_planes_pars_fragment'
10 | | 'color_fragment'
11 | | 'color_pars_fragment'
12 | | 'cube_uv_reflection_fragment'
13 | | 'emissivemap_fragment'
14 | | 'emissivemap_pars_fragment'
15 | | 'encodings_fragment'
16 | | 'encodings_pars_fragment'
17 | | 'envmap_fragment'
18 | | 'envmap_common_pars_fragment'
19 | | 'envmap_pars_fragment'
20 | | 'envmap_physical_pars_fragment'
21 | | 'fog_fragment'
22 | | 'fog_pars_fragment'
23 | | 'gradientmap_pars_fragment'
24 | | 'lightmap_fragment'
25 | | 'lightmap_pars_fragment'
26 | | 'lights_toon_fragment'
27 | | 'lights_toon_pars_fragment'
28 | | 'lights_phong_fragment'
29 | | 'lights_phong_pars_fragment'
30 | | 'lights_physical_fragment'
31 | | 'lights_physical_pars_fragment'
32 | | 'lights_fragment_begin'
33 | | 'lights_fragment_maps'
34 | | 'lights_fragment_end'
35 | | 'logdepthbuf_fragment'
36 | | 'logdepthbuf_pars_fragment'
37 | | 'map_fragment'
38 | | 'map_pars_fragment'
39 | | 'map_particle_fragment'
40 | | 'map_particle_pars_fragment'
41 | | 'metalnessmap_fragment'
42 | | 'metalnessmap_pars_fragment'
43 | | 'normal_fragment_begin'
44 | | 'normal_fragment_maps'
45 | | 'normalmap_pars_fragment'
46 | | 'clearcoat_normal_fragment_begin'
47 | | 'clearcoat_normal_fragment_maps'
48 | | 'clearcoat_pars_fragment'
49 | | 'premultiplied_alpha_fragment'
50 | | 'dithering_fragment'
51 | | 'dithering_pars_fragment'
52 | | 'roughnessmap_fragment'
53 | | 'roughnessmap_pars_fragment'
54 | | 'shadowmap_pars_fragment'
55 | | 'shadowmask_pars_fragment'
56 | | 'specularmap_fragment'
57 | | 'specularmap_pars_fragment'
58 | | 'tonemapping_fragment'
59 | | 'tonemapping_pars_fragment'
60 | | 'transmissionmap_fragment'
61 | | 'transmissionmap_pars_fragment'
62 | | 'uv_pars_fragment'
63 | | 'uv2_pars_fragment'
64 | | 'background_frag'
65 | | 'cube_frag'
66 | | 'depth_frag'
67 | | 'distanceRGBA_frag'
68 | | 'equirect_frag'
69 | | 'linedashed_frag'
70 | | 'meshbasic_frag'
71 | | 'meshlambert_frag'
72 | | 'meshmatcap_frag'
73 | | 'meshtoon_frag'
74 | | 'meshphong_frag'
75 | | 'meshphysical_frag'
76 | | 'normal_frag'
77 | | 'points_frag'
78 | | 'shadow_frag'
79 | | 'sprite_frag'
80 | export type vertexChunks =
81 | | 'begin_vertex'
82 | | 'beginnormal_vertex'
83 | | 'clipping_planes_pars_vertex'
84 | | 'clipping_planes_vertex'
85 | | 'color_pars_vertex'
86 | | 'color_vertex'
87 | | 'defaultnormal_vertex'
88 | | 'displacementmap_pars_vertex'
89 | | 'displacementmap_vertex'
90 | | 'envmap_pars_vertex'
91 | | 'envmap_vertex'
92 | | 'fog_vertex'
93 | | 'fog_pars_vertex'
94 | | 'lights_lambert_vertex'
95 | | 'logdepthbuf_pars_vertex'
96 | | 'logdepthbuf_vertex'
97 | | 'morphnormal_vertex'
98 | | 'morphtarget_pars_vertex'
99 | | 'morphtarget_vertex'
100 | | 'project_vertex'
101 | | 'shadowmap_pars_vertex'
102 | | 'shadowmap_vertex'
103 | | 'skinbase_vertex'
104 | | 'skinning_pars_vertex'
105 | | 'skinning_vertex'
106 | | 'skinnormal_vertex'
107 | | 'uv_pars_vertex'
108 | | 'uv_vertex'
109 | | 'uv2_pars_vertex'
110 | | 'uv2_vertex'
111 | | 'worldpos_vertex'
112 | | 'background_vert'
113 | | 'cube_vert'
114 | | 'depth_vert'
115 | | 'distanceRGBA_vert'
116 | | 'equirect_vert'
117 | | 'linedashed_vert'
118 | | 'meshbasic_vert'
119 | | 'meshlambert_vert'
120 | | 'meshmatcap_vert'
121 | | 'meshtoon_vert'
122 | | 'meshphong_vert'
123 | | 'meshphysical_vert'
124 | | 'normal_vert'
125 | | 'points_vert'
126 | | 'shadow_vert'
127 | | 'sprite_vert'
128 | export type commonChunks = 'head' | 'body' | 'bsdfs' | 'common' | 'lights_pars_begin' | 'packing'
129 |
--------------------------------------------------------------------------------
/src/helpers/objects.ts:
--------------------------------------------------------------------------------
1 | export const getKeyValue = (obj: T, key: K): T[K] => obj[key]
2 | export const setKeyValue = (obj: T, key: K, value: any): T[K] => (obj[key] = value)
3 |
--------------------------------------------------------------------------------
/src/index.tsx:
--------------------------------------------------------------------------------
1 | import { ComponentMaterial } from './component-material'
2 | import { frag, vert, common } from './proxies'
3 |
4 | export * from './types/index'
5 |
6 | export type MT = typeof ComponentMaterial & {
7 | Vert: typeof vert
8 | Frag: typeof frag
9 | Common: typeof common
10 | }
11 |
12 | const M = ComponentMaterial
13 |
14 | Object.defineProperties(ComponentMaterial, {
15 | Vert: {
16 | get: (): typeof vert => vert,
17 | },
18 | Frag: {
19 | get: (): typeof frag => frag,
20 | },
21 | Common: {
22 | get: (): typeof common => common,
23 | },
24 | })
25 |
26 | export default M as MT
27 |
--------------------------------------------------------------------------------
/src/proxies.tsx:
--------------------------------------------------------------------------------
1 | import { DEFAULT_FRAG_CHUNK, DEFAULT_VERT_CHUNK, FRAG, VERT } from './constants'
2 | import { ProxyProps, ProxyComponent } from './types/internal'
3 |
4 | import { fragmentChunks, vertexChunks, commonChunks } from './generated'
5 |
6 | function NullFunction() {
7 | return null
8 | }
9 |
10 | type ShaderProxyHelper = {
11 | [key in T]: any
12 | } & {
13 | Body: ProxyComponent
14 | Head: ProxyComponent
15 | }
16 |
17 | // -- VERTEX PROXY --
18 | const vertHandler = {
19 | get: function(_: any, name: string) {
20 | const Component = function({ children }: ProxyProps) {
21 | return children
22 | }
23 | Object.defineProperty(Component, 'chunkName', { writable: true })
24 | Object.defineProperty(Component, 'shaderType', { writable: true })
25 | Component.chunkName = name === 'Body' ? DEFAULT_VERT_CHUNK : name
26 | Component.shaderType = VERT
27 | return Component
28 | },
29 | }
30 | export const vert: ShaderProxyHelper = new Proxy(NullFunction, vertHandler)
31 |
32 | // -- FRAGMENT PROXY --
33 | const fragHandler = {
34 | get: function(_: any, name: string) {
35 | const Component = function({ children }: ProxyProps) {
36 | return children
37 | }
38 | Object.defineProperty(Component, 'chunkName', { writable: true })
39 | Object.defineProperty(Component, 'shaderType', { writable: true })
40 | Component.chunkName = name === 'Body' ? DEFAULT_FRAG_CHUNK : name
41 | Component.shaderType = FRAG
42 | return Component
43 | },
44 | }
45 |
46 | export const frag: ShaderProxyHelper = new Proxy(NullFunction, fragHandler)
47 |
48 | export function common({ children }: ProxyProps) {
49 | return (children as unknown) as JSX.Element
50 | }
51 |
52 | // TODO
53 | // // -- NOISE PROXY --
54 | // const noise = {
55 | // snoise2: "glsl-noise/simplex/2d",
56 | // snoise3: "glsl-noise/simplex/3d",
57 | // snoise4: "glsl-noise/simplex/4d",
58 | // cnoise2: "glsl-noise/classic/2d",
59 | // cnoise3: "glsl-noise/classic/3d",
60 | // cnoise4: "glsl-noise/classic/4d",
61 | // pnoise2: "glsl-noise/periodic/2d",
62 | // pnoise3: "glsl-noise/periodic/3d",
63 | // pnoise4: "glsl-noise/periodic/4d",
64 | // };
65 | // const noiseHandler = {
66 | // get: function (_, name) {
67 | // const path = noise[name];
68 | // if (path) {
69 | // const pragma = `#pragma glslify: ${name} = require(${path})`;
70 | // const Component = () => null;
71 | // Object.defineProperty(Component, "shaderType", { writable: true });
72 | // Object.defineProperty(Component, "toolShader", { writable: true });
73 | // Component.shaderType = TOOL;
74 | // Component.toolShader = pragma;
75 | // return Component;
76 | // }
77 | // return null;
78 | // },
79 | // };
80 | // export const Noise = new Proxy(() => null, noiseHandler);
81 |
82 | // // -- EASING PROXY --
83 | // const easing = {
84 | // backInOut: "glsl-easings/back-in-out",
85 | // backIn: "glsl-easings/back-in",
86 | // backOut: "glsl-easings/back-out",
87 | // bounceInOut: "glsl-easings/bounce-in-out",
88 | // bounceIn: "glsl-easings/bounce-in",
89 | // bounceOut: "glsl-easings/bounce-out",
90 | // circularInOut: "glsl-easings/circular-in-out",
91 | // circularIn: "glsl-easings/circular-in",
92 | // circularOut: "glsl-easings/circular-out",
93 | // cubicInOut: "glsl-easings/cubic-in-out",
94 | // cubicIn: "glsl-easings/cubic-in",
95 | // cubicOut: "glsl-easings/cubic-out",
96 | // elasticInOut: "glsl-easings/elastic-in-out",
97 | // elasticIn: "glsl-easings/elastic-in",
98 | // elasticOut: "glsl-easings/elastic-out",
99 | // exponentialInOut: "glsl-easings/exponential-in-out",
100 | // exponentialIn: "glsl-easings/exponential-in",
101 | // exponentialOut: "glsl-easings/exponential-out",
102 | // linear: "glsl-easings/linear",
103 | // quadraticInOut: "glsl-easings/quadratic-in-out",
104 | // quadraticIn: "glsl-easings/quadratic-in",
105 | // quadraticOut: "glsl-easings/quadratic-out",
106 | // quarticInOut: "glsl-easings/quartic-in-out",
107 | // quarticIn: "glsl-easings/quartic-in",
108 | // quarticOut: "glsl-easings/quartic-out",
109 | // quinticInOut: "glsl-easings/quintic-in-out",
110 | // quinticIn: "glsl-easings/quintic-in",
111 | // quinticOut: "glsl-easings/quintic-out",
112 | // sineInOut: "glsl-easings/sine-in-out",
113 | // sineIn: "glsl-easings/sine-in",
114 | // sineOut: "glsl-easings/sine-out",
115 | // };
116 | // const easingHandler = {
117 | // get: function (_, name) {
118 | // const path = easing[name];
119 | // if (path) {
120 | // const pragma = `#pragma glslify: ${name} = require(${path})`;
121 | // const Component = () => null;
122 | // Object.defineProperty(Component, "shaderType", { writable: true });
123 | // Object.defineProperty(Component, "toolShader", { writable: true });
124 | // Component.shaderType = TOOL;
125 | // Component.toolShader = pragma;
126 | // return Component;
127 | // }
128 | // return null;
129 | // },
130 | // };
131 | // export const Ease = new Proxy(() => null, easingHandler);
132 |
--------------------------------------------------------------------------------
/src/types/index.ts:
--------------------------------------------------------------------------------
1 | import { Material } from 'three'
2 |
3 | import { Uniforms, Varyings, AllMaterialProps } from './internal'
4 |
5 | export interface MaterialConstructor {
6 | new (...args: any[]): GenericMaterial
7 | }
8 | export type ComponentMaterialProps = AllMaterialProps & {
9 | varyings?: Varyings
10 | uniforms?: Uniforms
11 | from?: MaterialConstructor
12 | }
13 | export interface GenericMaterial extends Material {
14 | [key: string]: any
15 | }
16 | export type ComponentMaterial = (props: ComponentMaterialProps) => GenericMaterial
17 |
--------------------------------------------------------------------------------
/src/types/internal.ts:
--------------------------------------------------------------------------------
1 | import {
2 | MaterialProps,
3 | ShadowMaterialProps,
4 | SpriteMaterialProps,
5 | RawShaderMaterialProps,
6 | ShaderMaterialProps,
7 | PointsMaterialProps,
8 | MeshPhysicalMaterialProps,
9 | MeshStandardMaterialProps,
10 | MeshPhongMaterialProps,
11 | MeshToonMaterialProps,
12 | MeshNormalMaterialProps,
13 | MeshLambertMaterialProps,
14 | MeshDepthMaterialProps,
15 | MeshDistanceMaterialProps,
16 | MeshBasicMaterialProps,
17 | MeshMatcapMaterialProps,
18 | LineDashedMaterialProps,
19 | LineBasicMaterialProps,
20 | } from 'react-three-fiber'
21 |
22 | export type ProxyProps = {
23 | children: Child
24 | }
25 | export type ProxyComponent = (props: ProxyProps) => JSX.Element
26 | export type ExtensionsType = {
27 | value?: string
28 | replaceChunk: boolean
29 | }
30 | export type GLProp = {
31 | value?:
32 | | number
33 | | string
34 | | boolean
35 | | THREE.Texture
36 | | THREE.Vector2
37 | | THREE.Vector3
38 | | THREE.Vector4
39 | | Array
40 | | Float32Array
41 | | THREE.Color
42 | | THREE.Quaternion
43 | | THREE.Matrix3
44 | | THREE.Matrix4
45 | | Int32Array
46 | | THREE.CubeTexture
47 | type: string
48 | }
49 | export type Uniforms = { [key: string]: GLProp }
50 | export type Varyings = {
51 | [key: string]: Omit
52 | }
53 | export type ChildProps = {
54 | chunkName: string
55 | shaderType: string
56 | }
57 | export type ExtensionShaderObject = {
58 | [key: string]: ExtensionsType
59 | }
60 | export type ExtensionShadersObject = {
61 | vert: ExtensionShaderObject & { head: string }
62 | frag: ExtensionShaderObject & { head: string }
63 | common: string
64 | }
65 | export type AllMaterialProps = MaterialProps &
66 | ShadowMaterialProps &
67 | SpriteMaterialProps &
68 | RawShaderMaterialProps &
69 | ShaderMaterialProps &
70 | PointsMaterialProps &
71 | MeshPhysicalMaterialProps &
72 | MeshStandardMaterialProps &
73 | MeshPhongMaterialProps &
74 | MeshToonMaterialProps &
75 | MeshNormalMaterialProps &
76 | MeshLambertMaterialProps &
77 | MeshDepthMaterialProps &
78 | MeshDistanceMaterialProps &
79 | MeshBasicMaterialProps &
80 | MeshMatcapMaterialProps &
81 | LineDashedMaterialProps &
82 | LineBasicMaterialProps
83 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | // see https://www.typescriptlang.org/tsconfig to better understand tsconfigs
3 | "include": ["src", "types"],
4 | "compilerOptions": {
5 | "module": "es2015",
6 | "lib": ["dom", "esnext"],
7 | "importHelpers": true,
8 | // output .d.ts declaration files for consumers
9 | "declaration": true,
10 | // output .js.map sourcemap files for consumers
11 | "sourceMap": true,
12 | // match output dir to input dir. e.g. dist/index instead of dist/src/index
13 | "rootDir": "./src",
14 | // stricter type-checking for stronger correctness. Recommended by TS
15 | "strict": true,
16 | // linter checks for common issues
17 | "noImplicitReturns": true,
18 | "noFallthroughCasesInSwitch": true,
19 | // noUnused* overlap with @typescript-eslint/no-unused-vars, can disable if duplicative
20 | "noUnusedLocals": true,
21 | "noUnusedParameters": true,
22 | // use Node's module resolution algorithm, instead of the legacy TS one
23 | "moduleResolution": "node",
24 | // transpile JSX to React.createElement
25 | "jsx": "react",
26 | // interop between ESM and CJS modules. Recommended by TS
27 | "esModuleInterop": true,
28 | // significant perf increase by skipping checking .d.ts files, particularly those in node_modules. Recommended by TS
29 | "skipLibCheck": true,
30 | // error out if import and file system have a casing mismatch. Recommended by TS
31 | "forceConsistentCasingInFileNames": true,
32 | // `tsdx build` ignores this option, but it is commonly used when type-checking separately with `tsc`
33 | "noEmit": true
34 | }
35 | }
36 |
--------------------------------------------------------------------------------