react-native-shine 0.0.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +20 -0
- package/README.md +35 -0
- package/lib/module/index.js +182 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/roots.js +16 -0
- package/lib/module/roots.js.map +1 -0
- package/lib/module/shaders/bindGroupLayouts.js +45 -0
- package/lib/module/shaders/bindGroupLayouts.js.map +1 -0
- package/lib/module/shaders/bindGroupUtils.js +45 -0
- package/lib/module/shaders/bindGroupUtils.js.map +1 -0
- package/lib/module/shaders/fragmentShaders/bloomFragment.js +66 -0
- package/lib/module/shaders/fragmentShaders/bloomFragment.js.map +1 -0
- package/lib/module/shaders/fragmentShaders/colorMaskFragment.js +28 -0
- package/lib/module/shaders/fragmentShaders/colorMaskFragment.js.map +1 -0
- package/lib/module/shaders/pipelineSetups.js +26 -0
- package/lib/module/shaders/pipelineSetups.js.map +1 -0
- package/lib/module/shaders/resourceManagement.js +19 -0
- package/lib/module/shaders/resourceManagement.js.map +1 -0
- package/lib/module/shaders/tgpuUtils.js +38 -0
- package/lib/module/shaders/tgpuUtils.js.map +1 -0
- package/lib/module/shaders/utils.js +65 -0
- package/lib/module/shaders/utils.js.map +1 -0
- package/lib/module/shaders/vertexShaders/mainVertex.js +35 -0
- package/lib/module/shaders/vertexShaders/mainVertex.js.map +1 -0
- package/lib/module/types/typeUtils.js +92 -0
- package/lib/module/types/typeUtils.js.map +1 -0
- package/lib/module/types/types.js +4 -0
- package/lib/module/types/types.js.map +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/index.d.ts +12 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/roots.d.ts +4 -0
- package/lib/typescript/src/roots.d.ts.map +1 -0
- package/lib/typescript/src/shaders/bindGroupLayouts.d.ts +57 -0
- package/lib/typescript/src/shaders/bindGroupLayouts.d.ts.map +1 -0
- package/lib/typescript/src/shaders/bindGroupUtils.d.ts +53 -0
- package/lib/typescript/src/shaders/bindGroupUtils.d.ts.map +1 -0
- package/lib/typescript/src/shaders/fragmentShaders/bloomFragment.d.ts +6 -0
- package/lib/typescript/src/shaders/fragmentShaders/bloomFragment.d.ts.map +1 -0
- package/lib/typescript/src/shaders/fragmentShaders/colorMaskFragment.d.ts +6 -0
- package/lib/typescript/src/shaders/fragmentShaders/colorMaskFragment.d.ts.map +1 -0
- package/lib/typescript/src/shaders/pipelineSetups.d.ts +7 -0
- package/lib/typescript/src/shaders/pipelineSetups.d.ts.map +1 -0
- package/lib/typescript/src/shaders/resourceManagement.d.ts +3 -0
- package/lib/typescript/src/shaders/resourceManagement.d.ts.map +1 -0
- package/lib/typescript/src/shaders/tgpuUtils.d.ts +6 -0
- package/lib/typescript/src/shaders/tgpuUtils.d.ts.map +1 -0
- package/lib/typescript/src/shaders/utils.d.ts +13 -0
- package/lib/typescript/src/shaders/utils.d.ts.map +1 -0
- package/lib/typescript/src/shaders/vertexShaders/mainVertex.d.ts +6 -0
- package/lib/typescript/src/shaders/vertexShaders/mainVertex.d.ts.map +1 -0
- package/lib/typescript/src/types/typeUtils.d.ts +16 -0
- package/lib/typescript/src/types/typeUtils.d.ts.map +1 -0
- package/lib/typescript/src/types/types.d.ts +33 -0
- package/lib/typescript/src/types/types.d.ts.map +1 -0
- package/package.json +158 -13
- package/src/index.tsx +289 -0
- package/src/roots.ts +16 -0
- package/src/shaders/bindGroupLayouts.ts +40 -0
- package/src/shaders/bindGroupUtils.ts +101 -0
- package/src/shaders/fragmentShaders/bloomFragment.ts +83 -0
- package/src/shaders/fragmentShaders/colorMaskFragment.ts +35 -0
- package/src/shaders/pipelineSetups.ts +33 -0
- package/src/shaders/resourceManagement.ts +22 -0
- package/src/shaders/tgpuUtils.ts +75 -0
- package/src/shaders/utils.ts +103 -0
- package/src/shaders/vertexShaders/mainVertex.ts +35 -0
- package/src/types/typeUtils.ts +122 -0
- package/src/types/types.ts +51 -0
package/package.json
CHANGED
|
@@ -1,24 +1,169 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-shine",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
5
|
-
"main": "index.js",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Fast and efficient way of adding interactive effects that are run entirely on shaders using typeGPU.",
|
|
5
|
+
"main": "./lib/module/index.js",
|
|
6
|
+
"types": "./lib/typescript/src/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"source": "./src/index.tsx",
|
|
10
|
+
"types": "./lib/typescript/src/index.d.ts",
|
|
11
|
+
"default": "./lib/module/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./package.json": "./package.json"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"src",
|
|
17
|
+
"lib",
|
|
18
|
+
"android",
|
|
19
|
+
"ios",
|
|
20
|
+
"cpp",
|
|
21
|
+
"*.podspec",
|
|
22
|
+
"react-native.config.js",
|
|
23
|
+
"!ios/build",
|
|
24
|
+
"!android/build",
|
|
25
|
+
"!android/gradle",
|
|
26
|
+
"!android/gradlew",
|
|
27
|
+
"!android/gradlew.bat",
|
|
28
|
+
"!android/local.properties",
|
|
29
|
+
"!**/__tests__",
|
|
30
|
+
"!**/__fixtures__",
|
|
31
|
+
"!**/__mocks__",
|
|
32
|
+
"!**/.*"
|
|
33
|
+
],
|
|
6
34
|
"scripts": {
|
|
7
|
-
"
|
|
35
|
+
"example": "yarn workspace react-native-shine-example",
|
|
36
|
+
"test": "jest",
|
|
37
|
+
"typecheck": "tsc",
|
|
38
|
+
"lint": "eslint \"**/*.{js,ts,tsx}\"",
|
|
39
|
+
"clean": "del-cli lib",
|
|
40
|
+
"prepare": "bob build",
|
|
41
|
+
"release": "release-it --only-version"
|
|
8
42
|
},
|
|
43
|
+
"keywords": [
|
|
44
|
+
"react-native",
|
|
45
|
+
"ios",
|
|
46
|
+
"android"
|
|
47
|
+
],
|
|
9
48
|
"repository": {
|
|
10
49
|
"type": "git",
|
|
11
|
-
"url": "git+https://github.com/wojtus7/react-native-shine.git"
|
|
50
|
+
"url": "git+https://github.com/wojtus7/react-native-shine.git.git"
|
|
12
51
|
},
|
|
13
|
-
"
|
|
14
|
-
"glare",
|
|
15
|
-
"share",
|
|
16
|
-
"react-native"
|
|
17
|
-
],
|
|
18
|
-
"author": "Wojciech Stanisz",
|
|
52
|
+
"author": "VoidFrog <shadownn0000@gmail.com> (https://github.com/VoidFrog)",
|
|
19
53
|
"license": "MIT",
|
|
20
54
|
"bugs": {
|
|
21
|
-
"url": "https://github.com/wojtus7/react-native-shine/issues"
|
|
55
|
+
"url": "https://github.com/wojtus7/react-native-shine.git/issues"
|
|
56
|
+
},
|
|
57
|
+
"homepage": "https://github.com/wojtus7/react-native-shine.git#readme",
|
|
58
|
+
"publishConfig": {
|
|
59
|
+
"registry": "https://registry.npmjs.org/"
|
|
60
|
+
},
|
|
61
|
+
"devDependencies": {
|
|
62
|
+
"@commitlint/config-conventional": "^19.6.0",
|
|
63
|
+
"@eslint/compat": "^1.2.7",
|
|
64
|
+
"@eslint/eslintrc": "^3.3.0",
|
|
65
|
+
"@eslint/js": "^9.22.0",
|
|
66
|
+
"@evilmartians/lefthook": "^1.5.0",
|
|
67
|
+
"@react-native/babel-preset": "0.78.2",
|
|
68
|
+
"@react-native/eslint-config": "^0.78.0",
|
|
69
|
+
"@release-it/conventional-changelog": "^9.0.2",
|
|
70
|
+
"@types/jest": "^29.5.5",
|
|
71
|
+
"@types/react": "^19.0.12",
|
|
72
|
+
"@webgpu/types": "^0.1.64",
|
|
73
|
+
"commitlint": "^19.6.1",
|
|
74
|
+
"del-cli": "^5.1.0",
|
|
75
|
+
"eslint": "^9.22.0",
|
|
76
|
+
"eslint-config-prettier": "^10.1.1",
|
|
77
|
+
"eslint-plugin-prettier": "^5.2.3",
|
|
78
|
+
"jest": "^29.7.0",
|
|
79
|
+
"prettier": "^3.0.3",
|
|
80
|
+
"react": "19.0.0",
|
|
81
|
+
"react-native": "0.79.5",
|
|
82
|
+
"react-native-builder-bob": "^0.40.13",
|
|
83
|
+
"react-native-reanimated": "^4.0.1",
|
|
84
|
+
"react-native-wgpu": "^0.2.1",
|
|
85
|
+
"react-native-worklets": "^0.4.1",
|
|
86
|
+
"release-it": "^17.10.0",
|
|
87
|
+
"typegpu": "^0.7.0",
|
|
88
|
+
"typescript": "^5.8.3",
|
|
89
|
+
"unplugin-typegpu": "^0.2.2"
|
|
90
|
+
},
|
|
91
|
+
"peerDependencies": {
|
|
92
|
+
"react": "*",
|
|
93
|
+
"react-native": "*",
|
|
94
|
+
"react-native-web": "^0.21.0"
|
|
95
|
+
},
|
|
96
|
+
"workspaces": [
|
|
97
|
+
"example"
|
|
98
|
+
],
|
|
99
|
+
"packageManager": "yarn@3.6.1",
|
|
100
|
+
"jest": {
|
|
101
|
+
"preset": "react-native",
|
|
102
|
+
"modulePathIgnorePatterns": [
|
|
103
|
+
"<rootDir>/example/node_modules",
|
|
104
|
+
"<rootDir>/lib/"
|
|
105
|
+
]
|
|
106
|
+
},
|
|
107
|
+
"commitlint": {
|
|
108
|
+
"extends": [
|
|
109
|
+
"@commitlint/config-conventional"
|
|
110
|
+
]
|
|
111
|
+
},
|
|
112
|
+
"release-it": {
|
|
113
|
+
"git": {
|
|
114
|
+
"commitMessage": "chore: release ${version}",
|
|
115
|
+
"tagName": "v${version}"
|
|
116
|
+
},
|
|
117
|
+
"npm": {
|
|
118
|
+
"publish": true
|
|
119
|
+
},
|
|
120
|
+
"github": {
|
|
121
|
+
"release": true
|
|
122
|
+
},
|
|
123
|
+
"plugins": {
|
|
124
|
+
"@release-it/conventional-changelog": {
|
|
125
|
+
"preset": {
|
|
126
|
+
"name": "angular"
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
"prettier": {
|
|
132
|
+
"quoteProps": "consistent",
|
|
133
|
+
"singleQuote": true,
|
|
134
|
+
"tabWidth": 2,
|
|
135
|
+
"trailingComma": "es5",
|
|
136
|
+
"useTabs": false
|
|
137
|
+
},
|
|
138
|
+
"react-native-builder-bob": {
|
|
139
|
+
"source": "src",
|
|
140
|
+
"output": "lib",
|
|
141
|
+
"targets": [
|
|
142
|
+
[
|
|
143
|
+
"module",
|
|
144
|
+
{
|
|
145
|
+
"esm": true
|
|
146
|
+
}
|
|
147
|
+
],
|
|
148
|
+
[
|
|
149
|
+
"typescript",
|
|
150
|
+
{
|
|
151
|
+
"project": "tsconfig.build.json"
|
|
152
|
+
}
|
|
153
|
+
]
|
|
154
|
+
]
|
|
155
|
+
},
|
|
156
|
+
"create-react-native-library": {
|
|
157
|
+
"languages": "js",
|
|
158
|
+
"type": "library",
|
|
159
|
+
"version": "0.52.0"
|
|
22
160
|
},
|
|
23
|
-
"
|
|
161
|
+
"dependencies": {
|
|
162
|
+
"expo-asset": "^11.1.7",
|
|
163
|
+
"react-native-orientation-locker": "^1.7.0",
|
|
164
|
+
"react-native-reanimated": "^4.0.1",
|
|
165
|
+
"react-native-wgpu": "^0.2.0",
|
|
166
|
+
"react-native-worklets": "^0.4.1",
|
|
167
|
+
"typegpu": "^0.7.0"
|
|
168
|
+
}
|
|
24
169
|
}
|
package/src/index.tsx
ADDED
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
import { useEffect, useRef, useState } from 'react';
|
|
2
|
+
import { Canvas, useDevice, useGPUContext } from 'react-native-wgpu';
|
|
3
|
+
import { getOrInitRoot } from './roots';
|
|
4
|
+
import mainVertex from './shaders/vertexShaders/mainVertex';
|
|
5
|
+
import getBitmapFromURI from './shaders/resourceManagement';
|
|
6
|
+
import {
|
|
7
|
+
createTexture,
|
|
8
|
+
loadTexture,
|
|
9
|
+
clamp,
|
|
10
|
+
rotate2D,
|
|
11
|
+
subscribeToOrientationChange,
|
|
12
|
+
getAngleFromDimensions,
|
|
13
|
+
} from './shaders/utils';
|
|
14
|
+
import type { TgpuTexture } from 'typegpu';
|
|
15
|
+
import {
|
|
16
|
+
bloomOptionsBindGroupLayout,
|
|
17
|
+
colorMaskBindGroupLayout,
|
|
18
|
+
rotationValuesBindGroupLayout,
|
|
19
|
+
textureBindGroupLayout,
|
|
20
|
+
} from './shaders/bindGroupLayouts';
|
|
21
|
+
import {
|
|
22
|
+
SensorType,
|
|
23
|
+
useAnimatedSensor,
|
|
24
|
+
useDerivedValue,
|
|
25
|
+
useSharedValue,
|
|
26
|
+
} from 'react-native-reanimated';
|
|
27
|
+
import * as d from 'typegpu/data';
|
|
28
|
+
import { Platform } from 'react-native';
|
|
29
|
+
import bloomFragment from './shaders/fragmentShaders/bloomFragment';
|
|
30
|
+
import {
|
|
31
|
+
createBloomOptionsBindGroup,
|
|
32
|
+
createBloomOptionsBuffer,
|
|
33
|
+
createColorMaskBindGroup,
|
|
34
|
+
createColorMaskBuffer,
|
|
35
|
+
createRotationBuffer,
|
|
36
|
+
createRotationValuesBindGroup,
|
|
37
|
+
} from './shaders/bindGroupUtils';
|
|
38
|
+
import {
|
|
39
|
+
createBindGroupPairs,
|
|
40
|
+
createBloomOptions,
|
|
41
|
+
createColorMask,
|
|
42
|
+
} from './types/typeUtils';
|
|
43
|
+
import type {
|
|
44
|
+
BindGroupPair,
|
|
45
|
+
BloomOptions,
|
|
46
|
+
ColorMask,
|
|
47
|
+
DeepPartiallyOptional,
|
|
48
|
+
} from './types/types';
|
|
49
|
+
import { attachBindGroups, getDefaultTarget } from './shaders/pipelineSetups';
|
|
50
|
+
import colorMaskFragment from './shaders/fragmentShaders/colorMaskFragment';
|
|
51
|
+
interface ShineProps {
|
|
52
|
+
width: number;
|
|
53
|
+
height: number;
|
|
54
|
+
imageURI: string;
|
|
55
|
+
bloomOptions?: Partial<BloomOptions>;
|
|
56
|
+
colorMaskOptions?: DeepPartiallyOptional<ColorMask, 'baseColor'>;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function Shine({
|
|
60
|
+
width,
|
|
61
|
+
height,
|
|
62
|
+
imageURI,
|
|
63
|
+
bloomOptions,
|
|
64
|
+
colorMaskOptions,
|
|
65
|
+
}: ShineProps) {
|
|
66
|
+
const { device = null } = useDevice();
|
|
67
|
+
const root = device ? getOrInitRoot(device) : null;
|
|
68
|
+
const { ref, context } = useGPUContext();
|
|
69
|
+
const presentationFormat = navigator.gpu.getPreferredCanvasFormat();
|
|
70
|
+
const frameRef = useRef<number | null>(null);
|
|
71
|
+
|
|
72
|
+
const [imageTexture, setImageTexture] = useState<TgpuTexture | null>(null);
|
|
73
|
+
|
|
74
|
+
const orientationAngle = useSharedValue<number>(0); // degrees
|
|
75
|
+
const rotationShared = useSharedValue<[number, number, number]>([0, 0, 0]); // final GPU offsets
|
|
76
|
+
|
|
77
|
+
// Calibration shared values (UI thread)
|
|
78
|
+
const initialGravity = useSharedValue<[number, number, number]>([0, 0, 0]);
|
|
79
|
+
const calibSum = useSharedValue<[number, number, number]>([0, 0, 0]);
|
|
80
|
+
const calibCount = useSharedValue<number>(0);
|
|
81
|
+
const calibrated = useSharedValue<boolean>(false);
|
|
82
|
+
|
|
83
|
+
const gravitySensor = useAnimatedSensor(SensorType.GRAVITY, { interval: 20 });
|
|
84
|
+
|
|
85
|
+
// Subscribe to orientation changes and reset calibration on change
|
|
86
|
+
useEffect(() => {
|
|
87
|
+
orientationAngle.value = getAngleFromDimensions();
|
|
88
|
+
const unsubscribe = subscribeToOrientationChange((angleDeg) => {
|
|
89
|
+
orientationAngle.value = angleDeg;
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
return () => unsubscribe();
|
|
93
|
+
}, [orientationAngle]);
|
|
94
|
+
|
|
95
|
+
// Calibration & mapping logic
|
|
96
|
+
useDerivedValue(() => {
|
|
97
|
+
'worklet';
|
|
98
|
+
|
|
99
|
+
// console.log(orientationAngle.value);
|
|
100
|
+
const v: any = gravitySensor.sensor?.value ??
|
|
101
|
+
gravitySensor.sensor.value ?? { x: 0, y: 0, z: 0 };
|
|
102
|
+
const gx = v.x ?? 0;
|
|
103
|
+
const gy = v.y ?? 0;
|
|
104
|
+
const gz = v.z ?? 0;
|
|
105
|
+
|
|
106
|
+
const CALIBRATION_SAMPLES = 40;
|
|
107
|
+
const alpha = 0.15; // smoothing
|
|
108
|
+
const scale = 0.6;
|
|
109
|
+
|
|
110
|
+
if (!calibrated.value) {
|
|
111
|
+
// accumulate baseline in device coordinates
|
|
112
|
+
const s = calibSum.value;
|
|
113
|
+
const c = calibCount.value + 1;
|
|
114
|
+
calibSum.value = [s[0] + gx, s[1] + gy, s[2] + gz];
|
|
115
|
+
calibCount.value = c;
|
|
116
|
+
|
|
117
|
+
if (c >= CALIBRATION_SAMPLES) {
|
|
118
|
+
const avg = calibSum.value;
|
|
119
|
+
initialGravity.value = [avg[0] / c, avg[1] / c, avg[2] / c];
|
|
120
|
+
calibrated.value = true;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
rotationShared.value = [0, 0, 0];
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const init = initialGravity.value;
|
|
128
|
+
const dx = gx - init[0];
|
|
129
|
+
const dy = gy - init[1];
|
|
130
|
+
const dz = gz - init[2];
|
|
131
|
+
|
|
132
|
+
// Rotate into screen coordinates so offsets auto-swap with orientation
|
|
133
|
+
const [mx, my] = rotate2D([dx, dy], -orientationAngle.value);
|
|
134
|
+
const screenX = mx;
|
|
135
|
+
const screenY = -my;
|
|
136
|
+
|
|
137
|
+
const prev = rotationShared.value;
|
|
138
|
+
const smoothX = prev[0] * (1 - alpha) + screenX * alpha;
|
|
139
|
+
const smoothY = prev[1] * (1 - alpha) + screenY * alpha;
|
|
140
|
+
const smoothZ = prev[2] * (1 - alpha) + dz * alpha;
|
|
141
|
+
|
|
142
|
+
if (orientationAngle.value === 90) {
|
|
143
|
+
rotationShared.value = [
|
|
144
|
+
clamp(smoothY * scale, -1, 1),
|
|
145
|
+
clamp(-smoothX * scale, -1, 1),
|
|
146
|
+
clamp(smoothZ * scale, -1, 1),
|
|
147
|
+
];
|
|
148
|
+
} else {
|
|
149
|
+
rotationShared.value = [
|
|
150
|
+
clamp(smoothX * scale, -1, 1),
|
|
151
|
+
clamp(smoothY * scale, -1, 1),
|
|
152
|
+
clamp(smoothZ * scale, -1, 1),
|
|
153
|
+
];
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
// Resource setup
|
|
158
|
+
useEffect(() => {
|
|
159
|
+
if (!root || !device || !context) return;
|
|
160
|
+
(async () => {
|
|
161
|
+
const bitmap = await getBitmapFromURI(imageURI);
|
|
162
|
+
const texture = await createTexture(root, bitmap);
|
|
163
|
+
setImageTexture(texture);
|
|
164
|
+
await loadTexture(root, bitmap, texture);
|
|
165
|
+
})();
|
|
166
|
+
}, [root, device, context, imageURI]);
|
|
167
|
+
|
|
168
|
+
// Render loop
|
|
169
|
+
useEffect(() => {
|
|
170
|
+
if (!root || !device || !context || !imageTexture) return;
|
|
171
|
+
|
|
172
|
+
context.configure({
|
|
173
|
+
device,
|
|
174
|
+
format: presentationFormat,
|
|
175
|
+
alphaMode: 'premultiplied',
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
const sampler = device.createSampler({
|
|
179
|
+
magFilter: 'linear',
|
|
180
|
+
minFilter: 'linear',
|
|
181
|
+
});
|
|
182
|
+
const textureBindGroup = root.createBindGroup(textureBindGroupLayout, {
|
|
183
|
+
texture: root.unwrap(imageTexture).createView(),
|
|
184
|
+
sampler,
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
const rotationBuffer = createRotationBuffer(root);
|
|
188
|
+
const rotationBindGroup = createRotationValuesBindGroup(
|
|
189
|
+
root,
|
|
190
|
+
rotationBuffer
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
const bloomOptionsBuffer = createBloomOptionsBuffer(
|
|
194
|
+
root,
|
|
195
|
+
createBloomOptions(bloomOptions ?? {})
|
|
196
|
+
);
|
|
197
|
+
const bloomOptionsBindGroup = createBloomOptionsBindGroup(
|
|
198
|
+
root,
|
|
199
|
+
bloomOptionsBuffer
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
const colorMaskBuffer = createColorMaskBuffer(
|
|
203
|
+
root,
|
|
204
|
+
createColorMask(colorMaskOptions ?? { baseColor: [-20, -20, -20] })
|
|
205
|
+
);
|
|
206
|
+
const colorMaskBindGroup = createColorMaskBindGroup(root, colorMaskBuffer);
|
|
207
|
+
|
|
208
|
+
const bloomBGP: BindGroupPair[] = createBindGroupPairs(
|
|
209
|
+
[
|
|
210
|
+
textureBindGroupLayout,
|
|
211
|
+
rotationValuesBindGroupLayout,
|
|
212
|
+
bloomOptionsBindGroupLayout,
|
|
213
|
+
colorMaskBindGroupLayout,
|
|
214
|
+
],
|
|
215
|
+
[
|
|
216
|
+
textureBindGroup,
|
|
217
|
+
rotationBindGroup,
|
|
218
|
+
bloomOptionsBindGroup,
|
|
219
|
+
colorMaskBindGroup,
|
|
220
|
+
]
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
const maskBGP: BindGroupPair[] = createBindGroupPairs(
|
|
224
|
+
[textureBindGroupLayout, colorMaskBindGroupLayout],
|
|
225
|
+
[textureBindGroup, colorMaskBindGroup]
|
|
226
|
+
);
|
|
227
|
+
|
|
228
|
+
let bloomPipeline = root['~unstable']
|
|
229
|
+
.withVertex(mainVertex, {})
|
|
230
|
+
.withFragment(bloomFragment, getDefaultTarget(presentationFormat))
|
|
231
|
+
.createPipeline();
|
|
232
|
+
bloomPipeline = attachBindGroups(bloomPipeline, bloomBGP);
|
|
233
|
+
|
|
234
|
+
let maskPipeline = root['~unstable']
|
|
235
|
+
.withVertex(mainVertex, {})
|
|
236
|
+
.withFragment(colorMaskFragment, getDefaultTarget(presentationFormat))
|
|
237
|
+
.createPipeline();
|
|
238
|
+
maskPipeline = attachBindGroups(maskPipeline, maskBGP);
|
|
239
|
+
|
|
240
|
+
const render = () => {
|
|
241
|
+
const rot = rotationShared.value;
|
|
242
|
+
rotationBuffer.write(d.vec3f(rot[0]!, rot[1]!, rot[2]!));
|
|
243
|
+
|
|
244
|
+
bloomPipeline
|
|
245
|
+
.withColorAttachment({
|
|
246
|
+
view: context.getCurrentTexture().createView(),
|
|
247
|
+
clearValue: [0, 0, 0, 0],
|
|
248
|
+
loadOp: 'clear',
|
|
249
|
+
storeOp: 'store',
|
|
250
|
+
})
|
|
251
|
+
.draw(6);
|
|
252
|
+
|
|
253
|
+
maskPipeline
|
|
254
|
+
.withColorAttachment({
|
|
255
|
+
view: context.getCurrentTexture().createView(),
|
|
256
|
+
loadOp: 'load',
|
|
257
|
+
storeOp: 'store',
|
|
258
|
+
})
|
|
259
|
+
.draw(6);
|
|
260
|
+
|
|
261
|
+
context.present();
|
|
262
|
+
frameRef.current = requestAnimationFrame(render);
|
|
263
|
+
};
|
|
264
|
+
frameRef.current = requestAnimationFrame(render);
|
|
265
|
+
|
|
266
|
+
return () => {
|
|
267
|
+
if (frameRef.current) cancelAnimationFrame(frameRef.current);
|
|
268
|
+
};
|
|
269
|
+
}, [
|
|
270
|
+
device,
|
|
271
|
+
context,
|
|
272
|
+
root,
|
|
273
|
+
presentationFormat,
|
|
274
|
+
imageTexture,
|
|
275
|
+
rotationShared,
|
|
276
|
+
bloomOptions,
|
|
277
|
+
colorMaskOptions,
|
|
278
|
+
]);
|
|
279
|
+
|
|
280
|
+
return (
|
|
281
|
+
<Canvas
|
|
282
|
+
ref={ref}
|
|
283
|
+
style={{ width, height, aspectRatio: width / height }}
|
|
284
|
+
transparent={Platform.OS === 'ios'}
|
|
285
|
+
/>
|
|
286
|
+
);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
export { subscribeToOrientationChange, getAngleFromDimensions };
|
package/src/roots.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { tgpu, type TgpuRoot } from 'typegpu';
|
|
2
|
+
|
|
3
|
+
const deviceToRootMap = new WeakMap<GPUDevice, TgpuRoot>();
|
|
4
|
+
|
|
5
|
+
function getOrInitRoot(device: GPUDevice): TgpuRoot {
|
|
6
|
+
let root = deviceToRootMap.get(device);
|
|
7
|
+
|
|
8
|
+
if (!root) {
|
|
9
|
+
root = tgpu.initFromDevice({ device });
|
|
10
|
+
deviceToRootMap.set(device, root);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
return root;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export { getOrInitRoot };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import tgpu from 'typegpu';
|
|
2
|
+
import * as d from 'typegpu/data';
|
|
3
|
+
|
|
4
|
+
export const textureBindGroupLayout = tgpu.bindGroupLayout({
|
|
5
|
+
texture: { texture: 'float', dimension: '2d', sampleType: 'float' },
|
|
6
|
+
sampler: { sampler: 'filtering' },
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
export const rotationValuesBindGroupLayout = tgpu.bindGroupLayout({
|
|
10
|
+
vec: { uniform: d.vec3f },
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
export const bloomOptionsSchema = d.struct({
|
|
14
|
+
glowPower: d.f32,
|
|
15
|
+
hueShiftAngleMax: d.f32,
|
|
16
|
+
hueShiftAngleMin: d.f32,
|
|
17
|
+
hueBlendPower: d.f32,
|
|
18
|
+
lightIntensity: d.f32,
|
|
19
|
+
bloomIntensity: d.f32,
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
export type bloomOptionsSchema = typeof bloomOptionsSchema;
|
|
23
|
+
|
|
24
|
+
export const bloomOptionsBindGroupLayout = tgpu.bindGroupLayout({
|
|
25
|
+
bloomOptions: { uniform: bloomOptionsSchema },
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
export const colorMaskSchema = d.struct({
|
|
29
|
+
baseColor: d.vec3f,
|
|
30
|
+
rgbToleranceRange: d.struct({
|
|
31
|
+
upper: d.vec3f,
|
|
32
|
+
lower: d.vec3f,
|
|
33
|
+
}),
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
export type colorMaskSchema = typeof colorMaskSchema;
|
|
37
|
+
|
|
38
|
+
export const colorMaskBindGroupLayout = tgpu.bindGroupLayout({
|
|
39
|
+
mask: { uniform: colorMaskSchema },
|
|
40
|
+
});
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { type TgpuBuffer, type TgpuRoot, type UniformFlag } from 'typegpu';
|
|
2
|
+
import * as d from 'typegpu/data';
|
|
3
|
+
import {
|
|
4
|
+
bloomOptionsBindGroupLayout,
|
|
5
|
+
bloomOptionsSchema,
|
|
6
|
+
colorMaskBindGroupLayout,
|
|
7
|
+
colorMaskSchema,
|
|
8
|
+
rotationValuesBindGroupLayout,
|
|
9
|
+
} from './bindGroupLayouts';
|
|
10
|
+
import type {
|
|
11
|
+
BloomOptions,
|
|
12
|
+
ColorMask,
|
|
13
|
+
PartiallyOptional,
|
|
14
|
+
} from '../types/types';
|
|
15
|
+
import {
|
|
16
|
+
colorMaskToTyped,
|
|
17
|
+
createBloomOptions,
|
|
18
|
+
createColorMask,
|
|
19
|
+
mapToF32,
|
|
20
|
+
} from '../types/typeUtils';
|
|
21
|
+
|
|
22
|
+
export const createRotationBuffer = (
|
|
23
|
+
root: TgpuRoot,
|
|
24
|
+
initValues?: { x: number; y: number; z: number }
|
|
25
|
+
) => {
|
|
26
|
+
const init = initValues
|
|
27
|
+
? d.vec3f(initValues.x, initValues.y, initValues.z)
|
|
28
|
+
: d.vec3f(0.0);
|
|
29
|
+
const rotationValuesBuffer = root
|
|
30
|
+
.createBuffer(d.vec3f, init)
|
|
31
|
+
.$usage('uniform');
|
|
32
|
+
|
|
33
|
+
return rotationValuesBuffer;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export const createRotationValuesBindGroup = (
|
|
37
|
+
root: TgpuRoot,
|
|
38
|
+
buffer: TgpuBuffer<d.Vec3f> & UniformFlag
|
|
39
|
+
) => {
|
|
40
|
+
const rotationValuesBindGroup = root.createBindGroup(
|
|
41
|
+
rotationValuesBindGroupLayout,
|
|
42
|
+
{
|
|
43
|
+
vec: buffer,
|
|
44
|
+
}
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
return rotationValuesBindGroup;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export const createBloomOptionsBuffer = (
|
|
51
|
+
root: TgpuRoot,
|
|
52
|
+
initValues?: Partial<BloomOptions>
|
|
53
|
+
) => {
|
|
54
|
+
const bloomOptions: BloomOptions = createBloomOptions({ ...initValues });
|
|
55
|
+
const bloomOptionsTyped = mapToF32(bloomOptions);
|
|
56
|
+
|
|
57
|
+
const bloomOptionsBuffer = root
|
|
58
|
+
.createBuffer(bloomOptionsSchema, bloomOptionsTyped)
|
|
59
|
+
.$usage('uniform');
|
|
60
|
+
|
|
61
|
+
return bloomOptionsBuffer;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export const createBloomOptionsBindGroup = (
|
|
65
|
+
root: TgpuRoot,
|
|
66
|
+
buffer: TgpuBuffer<bloomOptionsSchema> & UniformFlag
|
|
67
|
+
) => {
|
|
68
|
+
const bloomOptionsBindGroup = root.createBindGroup(
|
|
69
|
+
bloomOptionsBindGroupLayout,
|
|
70
|
+
{
|
|
71
|
+
bloomOptions: buffer,
|
|
72
|
+
}
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
return bloomOptionsBindGroup;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
export const createColorMaskBuffer = (
|
|
79
|
+
root: TgpuRoot,
|
|
80
|
+
initValues: PartiallyOptional<ColorMask, 'baseColor'>
|
|
81
|
+
) => {
|
|
82
|
+
const colorMask: ColorMask = createColorMask({ ...initValues });
|
|
83
|
+
const colorMaskTyped = colorMaskToTyped(colorMask);
|
|
84
|
+
|
|
85
|
+
const colorMaskBuffer = root
|
|
86
|
+
.createBuffer(colorMaskSchema, colorMaskTyped)
|
|
87
|
+
.$usage('uniform');
|
|
88
|
+
|
|
89
|
+
return colorMaskBuffer;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
export const createColorMaskBindGroup = (
|
|
93
|
+
root: TgpuRoot,
|
|
94
|
+
buffer: TgpuBuffer<colorMaskSchema> & UniformFlag
|
|
95
|
+
) => {
|
|
96
|
+
const colorMaskBindGroup = root.createBindGroup(colorMaskBindGroupLayout, {
|
|
97
|
+
mask: buffer,
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
return colorMaskBindGroup;
|
|
101
|
+
};
|