Compare commits
2 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
d28d7896de | ||
78683f6115 |
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
*.webm filter=lfs diff=lfs merge=lfs -text
|
||||
*.png filter=lfs diff=lfs merge=lfs -text
|
@ -1,7 +1,8 @@
|
||||
# oktaeder
|
||||
3D rendering library for WebGPU
|
||||
|
||||
[oktaeder.webm](https://github.com/iszn11/oktaeder/assets/7891270/5dbcb03a-608f-41b8-860e-8e9c8e09e242)
|
||||
<video src="https://gitea.renati.me/renati/oktaeder/media/branch/main/oktaeder.webm" autoplay controls loop>
|
||||
</video>
|
||||
|
||||
This project ships with [bun.lockb](https://bun.sh/docs/install/lockfile)
|
||||
lockfile for the [Bun](https://bun.sh/) JavaScript runtime. You should be able
|
||||
|
@ -1,4 +1,6 @@
|
||||
import { Color, DirectionalLight, Mesh, Node, PerspectiveCamera, PointLight, Quaternion, Scene, Submesh, Vector3 } from "../src/data/index";
|
||||
/// <reference types="../node_modules/@webgpu/types" />
|
||||
|
||||
import { Color, Mesh, Node, PerspectiveCamera, PointLight, Quaternion, Scene, Submesh, Vector3 } from "../src/data/index";
|
||||
import { Renderer, degToRad } from "../src/oktaeder";
|
||||
import "./style.css";
|
||||
|
||||
@ -16,38 +18,74 @@ const camera = new PerspectiveCamera({
|
||||
farPlane: Infinity,
|
||||
});
|
||||
|
||||
const vertexBuffer = renderer.createVertexBuffer({ vertexCount: 6 });
|
||||
const vertexBuffer = renderer.createVertexBuffer({ vertexCount: 12, texCoord: true });
|
||||
vertexBuffer.writeTypedArray(0, {
|
||||
position: new Float32Array([
|
||||
0, 0, 1,
|
||||
1, 0, 0,
|
||||
0, 1, 0,
|
||||
-1, 0, 0,
|
||||
0, 0, -1,
|
||||
0, 0, -1,
|
||||
0, 0, -1,
|
||||
1, 0, 0,
|
||||
0, -1, 0,
|
||||
0, 1, 0,
|
||||
-1, 0, 0,
|
||||
0, 0, -1,
|
||||
0, 0, 1,
|
||||
]),
|
||||
texCoord: new Float32Array([
|
||||
0.5, 0.7113,
|
||||
0.333333, 1,
|
||||
0.166666, 0.7113,
|
||||
0.333333, 0.4226,
|
||||
0, 0.4226,
|
||||
0, 1,
|
||||
1, 1,
|
||||
0.666666, 1,
|
||||
0.833333, 0.7113,
|
||||
0.666666, 0.4226,
|
||||
1, 0.4226,
|
||||
0.5, 0.7113,
|
||||
]),
|
||||
});
|
||||
|
||||
const indexBuffer = renderer.createIndexBuffer({ indexCount: 24, indexFormat: "uint16" });
|
||||
indexBuffer.writeArray(0, [
|
||||
0, 4, 3,
|
||||
4, 1, 3,
|
||||
1, 5, 3,
|
||||
5, 0, 3,
|
||||
4, 0, 2,
|
||||
1, 4, 2,
|
||||
0, 2, 1,
|
||||
3, 4, 2,
|
||||
5, 1, 2,
|
||||
0, 5, 2,
|
||||
2, 0, 3,
|
||||
6, 8, 7,
|
||||
9, 8, 10,
|
||||
7, 8, 11,
|
||||
11, 8, 9,
|
||||
]);
|
||||
|
||||
const submesh: Submesh = { start: 0, length: 24 };
|
||||
|
||||
const mesh = new Mesh({ vertexBuffer, indexBuffer, submeshes: [submesh] });
|
||||
|
||||
const imageBitmap = await loadImageBitmap("/uvmap.png");
|
||||
|
||||
const texture = renderer.createTexture({
|
||||
format: "srgb",
|
||||
width: imageBitmap.width,
|
||||
height: imageBitmap.height,
|
||||
usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.RENDER_ATTACHMENT,
|
||||
});
|
||||
|
||||
renderer._device.queue.copyExternalImageToTexture(
|
||||
{ source: imageBitmap, flipY: false },
|
||||
{ texture: texture._texture },
|
||||
{ width: imageBitmap.width, height: imageBitmap.height },
|
||||
);
|
||||
|
||||
const material = renderer.createMaterial({
|
||||
baseColor: Color.white(),
|
||||
baseColorPartialCoverageTexture: texture,
|
||||
roughness: 0.5,
|
||||
metallic: 1,
|
||||
metallic: 0,
|
||||
});
|
||||
|
||||
const node = new Node({ mesh, materials: [material] });
|
||||
@ -55,25 +93,13 @@ const node = new Node({ mesh, materials: [material] });
|
||||
const scene = new Scene({
|
||||
nodes: [
|
||||
node,
|
||||
new Node({
|
||||
translation: new Vector3(-1, 1, 0),
|
||||
light: new PointLight({ color: new Color(1, 0, 0) }),
|
||||
}),
|
||||
new Node({
|
||||
translation: new Vector3(0, 1, -1),
|
||||
light: new PointLight({ color: new Color(0, 1, 0) }),
|
||||
light: new PointLight({ color: new Color(1, 1, 1) }),
|
||||
}),
|
||||
new Node({
|
||||
translation: new Vector3(1, 1, 0),
|
||||
light: new PointLight({ color: new Color(0, 0, 1) }),
|
||||
}),
|
||||
new Node({
|
||||
translation: new Vector3(0, 1, 1),
|
||||
light: new PointLight({ color: new Color(1, 1, 0) }),
|
||||
}),
|
||||
new Node({
|
||||
rotation: Quaternion.fromRotationYZ(degToRad(-90)),
|
||||
light: new DirectionalLight({ color: new Color(0.5, 0.5, 0.5) }),
|
||||
translation: new Vector3(0, -1, -1),
|
||||
light: new PointLight({ color: new Color(1, 1, 1) }),
|
||||
}),
|
||||
new Node({
|
||||
translation: new Vector3(0, 0.8, -3),
|
||||
@ -91,6 +117,14 @@ function onResize(this: Window) {
|
||||
|
||||
const _quaternion = Quaternion.identity();
|
||||
|
||||
async function loadImageBitmap(url: string) {
|
||||
const res = await fetch(url);
|
||||
const blob = await res.blob();
|
||||
const imageBitmap = await createImageBitmap(blob, { colorSpaceConversion: "none" });
|
||||
|
||||
return imageBitmap;
|
||||
}
|
||||
|
||||
function draw(timeMs: number) {
|
||||
const time = 0.001 * timeMs;
|
||||
node.setRotation(_quaternion.setRotationZX(-0.5 * time));
|
||||
|
BIN
example/uvmap.png
(Stored with Git LFS)
Normal file
BIN
example/uvmap.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
oktaeder.webm
(Stored with Git LFS)
Normal file
BIN
oktaeder.webm
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -22,12 +22,12 @@
|
||||
"build": "tsc --build"
|
||||
},
|
||||
"dependencies": {
|
||||
"tslib": "^2.6.1"
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@webgpu/types": "^0.1.34",
|
||||
"esbuild": "^0.19.2",
|
||||
"typescript": "5.1.6"
|
||||
"@webgpu/types": "^0.1.40",
|
||||
"esbuild": "^0.20.2",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"exports": {
|
||||
".": {
|
||||
|
@ -5,6 +5,7 @@
|
||||
*/
|
||||
|
||||
import * as data from "./data";
|
||||
import * as resources from "./resources";
|
||||
|
||||
/* INITIAL SUPPORT PLAN
|
||||
*
|
||||
@ -94,7 +95,7 @@ import * as data from "./data";
|
||||
|
||||
export interface ParseResult {
|
||||
readonly cameras: readonly data.Camera[];
|
||||
readonly materials: readonly data.Material[];
|
||||
readonly materials: readonly resources.Material[];
|
||||
readonly lights: readonly data.Light[];
|
||||
readonly scenes: readonly data.Scene[];
|
||||
readonly scene: data.Scene | null;
|
||||
@ -181,7 +182,7 @@ export async function parse(gltf: ArrayBufferView, {
|
||||
}: ParseOptions = {}): Promise<ParseResult> {
|
||||
|
||||
const cameras: data.Camera[] = [];
|
||||
const materials: data.Material[] = [];
|
||||
const materials: resources.Material[] = [];
|
||||
const lights: data.Light[] = [];
|
||||
const scenes: data.Scene[] = [];
|
||||
const scene: data.Scene | null = null;
|
||||
@ -267,6 +268,10 @@ export async function parse(gltf: ArrayBufferView, {
|
||||
|
||||
// --- JSON CHUNK ----------------------------------------------------------
|
||||
|
||||
void(stopOnFirstError);
|
||||
void(treatWarningsAsErrors);
|
||||
void(rest);
|
||||
|
||||
throw new Error("TODO");
|
||||
|
||||
// --- BIN CHUNK -----------------------------------------------------------
|
||||
|
@ -49,7 +49,7 @@ export class Renderer {
|
||||
_textureWhite: Texture2D;
|
||||
/** 1×1 rgba8unorm texture of [0, 0, 0, 255] */
|
||||
_textureBlack: Texture2D;
|
||||
/** 1×1 rgba8unorm texture of [128, 128, 128, 255] */
|
||||
/** 1×1 rgba8unorm texture of [128, 128, 255, 255] */
|
||||
_textureNormal: Texture2D;
|
||||
|
||||
_depthBuffer: Texture2D;
|
||||
@ -110,7 +110,7 @@ export class Renderer {
|
||||
height: 1,
|
||||
format: "linear",
|
||||
});
|
||||
this._textureNormal.writeFull(new Uint8Array([128, 128, 128, 255]));
|
||||
this._textureNormal.writeFull(new Uint8Array([128, 128, 255, 255]));
|
||||
|
||||
const framebufferTexture = this._context.getCurrentTexture();
|
||||
this._depthBuffer = new Texture2D(this, {
|
||||
|
@ -46,10 +46,10 @@ export function _createPipeline(renderer: Renderer, {
|
||||
|
||||
const shaderModule = renderer._device.createShaderModule({
|
||||
code: shaderCode,
|
||||
hints: {
|
||||
"vert": { layout: renderer._pipelineLayout },
|
||||
"frag": { layout: renderer._pipelineLayout },
|
||||
},
|
||||
compilationHints: [
|
||||
{ entryPoint: "vert", layout: renderer._pipelineLayout },
|
||||
{ entryPoint: "frag", layout: renderer._pipelineLayout },
|
||||
],
|
||||
});
|
||||
|
||||
let vertexLocation = 0;
|
||||
@ -318,17 +318,17 @@ fn frag(fragment: Varyings) -> @location(0) vec4<f32> {
|
||||
var emissive = _Material.emissive;
|
||||
var ior = _Material.ior;
|
||||
${texCoord ? `
|
||||
let baseColorPartialCoverageTexel = texture(_BaseColorPartialCoverageTexture, _Sampler, fragment.texCoord);
|
||||
let baseColorPartialCoverageTexel = textureSample(_BaseColorPartialCoverageTexture, _Sampler, fragment.texCoord);
|
||||
baseColor *= baseColorPartialCoverageTexel.rgb;
|
||||
partialCoverage *= baseColorPartialCoverageTexel.a;
|
||||
let roughnessMetallicTexel = texture(_RoughnessMetallicTexture, _Sampler, fragment.texCoord);
|
||||
let roughnessMetallicTexel = textureSample(_RoughnessMetallicTexture, _Sampler, fragment.texCoord);
|
||||
roughness *= roughnessMetallicTexel.g;
|
||||
metallic *= roughnessMetallicTexel.b;
|
||||
let emissiveTexel = texture(_EmissiveTexture, _Sampler, fragment.texCoord);
|
||||
let emissiveTexel = textureSample(_EmissiveTexture, _Sampler, fragment.texCoord);
|
||||
emissive *= emissiveTexel.rgb;
|
||||
` : ""}
|
||||
${lightTexCoord ? `
|
||||
let occlusionTexel = texture(_OcclusionTexture, _Sampler, fragment.lightTexCoord);
|
||||
let occlusionTexel = textureSample(_OcclusionTexture, _Sampler, fragment.lightTexCoord);
|
||||
occlusion += _Material.occlusionTextureStrength * (occlusionTexel.r - 1.0);
|
||||
` : ""}
|
||||
|
||||
@ -348,10 +348,10 @@ fn frag(fragment: Varyings) -> @location(0) vec4<f32> {
|
||||
` : `
|
||||
let matrixTStoVS = screenSpaceMatrixTStoVS(positionVS, geometricNormalVS, fragment.texCoord);
|
||||
`}
|
||||
let normalTextureTexel = texture(_NormalTexture, _Sampler, fragment.texCoord);
|
||||
let normalTextureTexel = textureSample(_NormalTexture, _Sampler, fragment.texCoord);
|
||||
var normalTS = normalTextureTexel.xyz * 2.0 - 1.0;
|
||||
normalTS.xy *= _Material.normalScale;
|
||||
let actualNormalVS = normalize(matrixTStoVS * geometricNormalVS);
|
||||
normalTS = vec3(normalTS.xy * _Material.normalScale, normalTS.z);
|
||||
let actualNormalVS = normalize(matrixTStoVS * normalTS);
|
||||
` : `
|
||||
let actualNormalVS = geometricNormalVS;
|
||||
`}
|
||||
|
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user