Skip to content

Commit 7cfee10

Browse files
committed
chore: updates
1 parent 6be662a commit 7cfee10

File tree

13 files changed

+356
-6
lines changed

13 files changed

+356
-6
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Cubemap image available under a Creative Commons Attribution 3.0 Unported License at https://www.humus.name/index.php?page=Textures&ID=58
Loading
Loading
Loading
Loading
Loading
Loading
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
Author
2+
======
3+
4+
This is the work of Emil Persson, aka Humus.
5+
http://www.humus.name
6+
7+
8+
9+
10+
Legal stuff
11+
===========
12+
13+
This work is free and may be used by anyone for any purpose
14+
and may be distributed freely to anyone using any distribution
15+
media or distribution method as long as this file is included.
16+
Distribution without this file is allowed if it's distributed
17+
with free non-commercial software; however, fair credit of the
18+
original author is expected.
19+
Any commercial distribution of this software requires the written
20+
approval of Emil Persson.
+245
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
import '@nativescript/macos-node-api';
2+
import { createRequire } from 'node:module';
3+
import utils from '../../utils';
4+
5+
//@ts-ignore
6+
const require = createRequire(import.meta.url);
7+
8+
const { requestAnimationFrame } = utils;
9+
10+
import helpers from '../helpers';
11+
12+
const { readTextSync } = helpers;
13+
14+
const { Canvas, GPUBufferUsage, GPUCanvasContext, GPUDevice, GPUTexture, GPUTextureUsage, ImageAsset } = require('../../canvas-napi.darwin-arm64.node');
15+
16+
import { mat4, vec3 } from 'wgpu-matrix';
17+
18+
import { cubeVertexArray, cubeVertexSize, cubeUVOffset, cubePositionOffset, cubeVertexCount } from '../meshes/cube';
19+
20+
export async function run(canvas) {
21+
const adapter = await navigator.gpu?.requestAdapter();
22+
const device = (await adapter?.requestDevice()) as never;
23+
24+
const devicePixelRatio = window.devicePixelRatio;
25+
26+
canvas.width = canvas.clientWidth * devicePixelRatio;
27+
canvas.height = canvas.clientHeight * devicePixelRatio;
28+
29+
const context = canvas.getContext('webgpu');
30+
31+
const presentationFormat = navigator.gpu.getPreferredCanvasFormat();
32+
33+
const basicVertWGSL = readTextSync('./shaders/basic.vert.wgsl');
34+
const sampleCubemapWGSL = readTextSync('./shaders/sampleCubemap.frag.wgsl');
35+
36+
context.configure({
37+
device,
38+
format: presentationFormat,
39+
presentMode: 'fifo',
40+
});
41+
42+
// Create a vertex buffer from the cube data.
43+
const verticesBuffer = device.createBuffer({
44+
size: cubeVertexArray.byteLength,
45+
usage: GPUBufferUsage.VERTEX,
46+
mappedAtCreation: true,
47+
});
48+
49+
new Float32Array(verticesBuffer.getMappedRange()).set(cubeVertexArray);
50+
verticesBuffer.unmap();
51+
52+
const pipeline = device.createRenderPipeline({
53+
layout: 'auto',
54+
vertex: {
55+
module: device.createShaderModule({
56+
code: basicVertWGSL,
57+
}),
58+
buffers: [
59+
{
60+
arrayStride: cubeVertexSize,
61+
attributes: [
62+
{
63+
// position
64+
shaderLocation: 0,
65+
offset: cubePositionOffset,
66+
format: 'float32x4',
67+
},
68+
{
69+
// uv
70+
shaderLocation: 1,
71+
offset: cubeUVOffset,
72+
format: 'float32x2',
73+
},
74+
],
75+
},
76+
],
77+
entryPoint: 'main',
78+
},
79+
fragment: {
80+
module: device.createShaderModule({
81+
code: sampleCubemapWGSL,
82+
}),
83+
targets: [
84+
{
85+
format: presentationFormat,
86+
},
87+
],
88+
entryPoint: 'main',
89+
},
90+
primitive: {
91+
topology: 'triangle-list',
92+
93+
// Since we are seeing from inside of the cube
94+
// and we are using the regular cube geomtry data with outward-facing normals,
95+
// the cullMode should be 'front' or 'none'.
96+
cullMode: 'none',
97+
},
98+
99+
// Enable depth testing so that the fragment closest to the camera
100+
// is rendered in front.
101+
depthStencil: {
102+
depthWriteEnabled: true,
103+
depthCompare: 'less',
104+
format: 'depth24plus',
105+
},
106+
});
107+
108+
const depthTexture = device.createTexture({
109+
size: [canvas.width as number, canvas.height as number],
110+
format: 'depth24plus',
111+
usage: GPUTextureUsage.RENDER_ATTACHMENT,
112+
});
113+
114+
// Fetch the 6 separate images for negative/positive x, y, z axis of a cubemap
115+
// and upload it into a GPUTexture.
116+
let cubemapTexture: GPUTexture;
117+
let imageBitmaps;
118+
{
119+
// The order of the array layers is [+X, -X, +Y, -Y, +Z, -Z]
120+
const imgSrcs = ['posx.jpg', 'negx.jpg', 'posy.jpg', 'negy.jpg', 'posz.jpg', 'negz.jpg'];
121+
const promises = imgSrcs.map(async (src) => {
122+
// const response = await fetch(src);
123+
// return createImageBitmap(await response.blob());
124+
const asset = new ImageAsset();
125+
await asset.fromFile(import.meta.resolve('../assets/cubemap/' + src).replace('file://', ''));
126+
return asset;
127+
});
128+
imageBitmaps = await Promise.all(promises);
129+
130+
cubemapTexture = device.createTexture({
131+
dimension: '2d',
132+
// Create a 2d array texture.
133+
// Assume each image has the same size.
134+
size: [imageBitmaps[0].width, imageBitmaps[0].height, 6],
135+
format: 'rgba8unorm',
136+
usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.RENDER_ATTACHMENT,
137+
});
138+
139+
for (let i = 0; i < imageBitmaps.length; i++) {
140+
const imageBitmap = imageBitmaps[i];
141+
device.queue.copyExternalImageToTexture({ source: imageBitmap }, { texture: cubemapTexture, origin: [0, 0, i] }, [imageBitmap.width, imageBitmap.height]);
142+
}
143+
}
144+
145+
const uniformBufferSize = 4 * 16; // 4x4 matrix
146+
const uniformBuffer = device.createBuffer({
147+
size: uniformBufferSize,
148+
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
149+
});
150+
151+
const sampler = device.createSampler({
152+
magFilter: 'linear',
153+
minFilter: 'linear',
154+
});
155+
156+
const uniformBindGroup = device.createBindGroup({
157+
layout: pipeline.getBindGroupLayout(0),
158+
entries: [
159+
{
160+
binding: 0,
161+
resource: {
162+
buffer: uniformBuffer,
163+
offset: 0,
164+
size: uniformBufferSize,
165+
},
166+
},
167+
{
168+
binding: 1,
169+
resource: sampler,
170+
},
171+
{
172+
binding: 2,
173+
resource: cubemapTexture.createView({
174+
dimension: 'cube',
175+
}),
176+
},
177+
],
178+
});
179+
180+
const renderPassDescriptor = {
181+
colorAttachments: [
182+
{
183+
view: undefined, // Assigned later
184+
loadOp: 'clear',
185+
storeOp: 'store',
186+
},
187+
],
188+
depthStencilAttachment: {
189+
view: depthTexture.createView(),
190+
191+
depthClearValue: 1.0,
192+
depthLoadOp: 'clear',
193+
depthStoreOp: 'store',
194+
},
195+
};
196+
197+
const aspect = (canvas.width as number) / (canvas.height as number);
198+
const projectionMatrix = mat4.perspective((2 * Math.PI) / 5, aspect, 1, 3000);
199+
200+
const modelMatrix = mat4.scaling(vec3.fromValues(1000, 1000, 1000));
201+
const modelViewProjectionMatrix = mat4.create();
202+
const viewMatrix = mat4.identity();
203+
204+
const tmpMat4 = mat4.create();
205+
206+
// Comppute camera movement:
207+
// It rotates around Y axis with a slight pitch movement.
208+
function updateTransformationMatrix() {
209+
const now = Date.now() / 800;
210+
211+
mat4.rotate(viewMatrix, vec3.fromValues(1, 0, 0), (Math.PI / 10) * Math.sin(now), tmpMat4);
212+
mat4.rotate(tmpMat4, vec3.fromValues(0, 1, 0), now * 0.2, tmpMat4);
213+
214+
mat4.multiply(tmpMat4, modelMatrix, modelViewProjectionMatrix);
215+
mat4.multiply(projectionMatrix, modelViewProjectionMatrix, modelViewProjectionMatrix);
216+
}
217+
218+
function frame() {
219+
const texture = context.getCurrentTexture();
220+
221+
if (!texture) {
222+
requestAnimationFrame(frame);
223+
return;
224+
}
225+
226+
updateTransformationMatrix();
227+
device.queue.writeBuffer(uniformBuffer, 0, modelViewProjectionMatrix.buffer, modelViewProjectionMatrix.byteOffset, modelViewProjectionMatrix.byteLength);
228+
229+
renderPassDescriptor.colorAttachments[0].view = texture.createView();
230+
231+
const commandEncoder = device.createCommandEncoder();
232+
const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor as never);
233+
passEncoder.setPipeline(pipeline);
234+
passEncoder.setVertexBuffer(0, verticesBuffer);
235+
passEncoder.setBindGroup(0, uniformBindGroup);
236+
passEncoder.draw(cubeVertexCount);
237+
passEncoder.end();
238+
device.queue.submit([commandEncoder.finish()]);
239+
240+
context.presentSurface();
241+
242+
requestAnimationFrame(frame);
243+
}
244+
requestAnimationFrame(frame);
245+
}

napi/canvas-napi/examples/node/main.mts

+13-5
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,15 @@ import '@nativescript/foundation/dom/index.js';
55
import '../app.ts';
66
import '../../canvas';
77
import installPolyfills from '../../polyfill';
8+
import three from './three';
9+
const { webgpuCube } = three;
810

911
// import { run as texturedCube } from './texturedCube';
1012
// import { run as twoCubes } from './twoCubes.ts';
11-
//import { run as computeBoids } from './gpgpu/computeBoids';
13+
// import { run as computeBoids } from './gpgpu/computeBoids';
1214
// import { run as wireframe } from './graphicsTechniques/wireframe';
1315
import { run as renderBundles } from './renderBundles';
16+
import { run as cubeMap } from './cubeMap';
1417
// @ts-ignore
1518
const require = createRequire(import.meta.url);
1619

@@ -1527,11 +1530,13 @@ canvas.addEventListener('ready', (event) => {
15271530
// webgpuTriangle(canvas);
15281531
// doGL()
15291532
// swarm(canvas);
1530-
// texturedCube(canvas);
1531-
//twoCubes(canvas);
1532-
// computeBoids(canvas);
1533-
//wireframe(canvas);
1533+
//texturedCube(canvas);
1534+
// twoCubes(canvas);
1535+
// computeBoids(canvas);
1536+
// wireframe(canvas);
15341537
renderBundles(canvas);
1538+
//webgpuCube(canvas);
1539+
//cubeMap(canvas);
15351540
});
15361541

15371542
canvas.width = NSScreen.mainScreen.frame.size.width;
@@ -1541,6 +1546,9 @@ window.setAttribute('styleMask', (
15411546
NSWindowStyleMask.Titled | NSWindowStyleMask.Closable | NSWindowStyleMask.Miniaturizable | NSWindowStyleMask.Resizable | NSWindowStyleMask.FullSizeContentView
15421547
) as never);
15431548

1549+
const color = NSColor.colorWithCalibratedHueSaturationBrightnessAlpha(0,0,0.2, 0.5);
1550+
const background = `rgba(${color.redComponent * 255}, ${color.greenComponent * 255}, ${color.blueComponent * 255}, ${color.alphaComponent})`;
1551+
window.style.backgroundColor = background;
15441552
window.appendChild(canvas);
15451553

15461554
document.body.appendChild(window);

napi/canvas-napi/examples/node/package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
"private": true,
1111
"dependencies": {
1212
"teapot": "~1.0.0",
13-
"wgpu-matrix": "~3.3.0"
13+
"wgpu-matrix": "~3.3.0",
14+
"three": "~0.171.0"
1415
},
1516
"devDependencies": {
1617
"tsx": "^4.19.2"
+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
let isAvailable = typeof navigator !== 'undefined' && navigator.gpu !== undefined;
2+
console.log('isAvailable', isAvailable);
3+
4+
import * as THREE from 'three/webgpu';
5+
async function webgpuCube(canvas) {
6+
// const adapter = await navigator.gpu?.requestAdapter();
7+
// const device: GPUDevice = (await adapter?.requestDevice()) as never;
8+
9+
var camera, scene, renderer;
10+
var geometry, material, mesh;
11+
var context;
12+
13+
function animate() {
14+
mesh.rotation.x += 0.01;
15+
mesh.rotation.y += 0.02;
16+
17+
renderer.render(scene, camera);
18+
19+
console.log('animate');
20+
21+
context.presentSurface();
22+
}
23+
24+
async function init() {
25+
canvas.width = canvas.clientWidth * window.devicePixelRatio;
26+
canvas.height = canvas.clientHeight * window.devicePixelRatio;
27+
28+
const innerWidth = canvas.clientWidth;
29+
const innerHeight = canvas.clientHeight;
30+
31+
camera = new THREE.PerspectiveCamera(50, innerWidth / innerHeight, 0.1, 10);
32+
camera.position.z = 1;
33+
34+
scene = new THREE.Scene();
35+
36+
geometry = new THREE.BoxGeometry(0.2, 0.2, 0.2);
37+
material = new THREE.MeshNormalMaterial();
38+
39+
mesh = new THREE.Mesh(geometry, material);
40+
scene.add(mesh);
41+
42+
renderer = new THREE.WebGPURenderer({ canvas });
43+
44+
await renderer.init();
45+
renderer.setPixelRatio(window.devicePixelRatio);
46+
renderer.setSize(canvas.clientWidth, canvas.clientHeight, false);
47+
48+
console.log(canvas.getContext('webgpu'), canvas.getContext('webgl'), canvas.getContext('webgl2'));
49+
50+
context = canvas.getContext('webgpu');
51+
renderer.setAnimationLoop(animate);
52+
}
53+
54+
init();
55+
}
56+
57+
export default {
58+
webgpuCube,
59+
};

0 commit comments

Comments
 (0)