luxscura 0.1.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 +21 -0
- package/README.md +99 -0
- package/dist/aabb.d.ts +9 -0
- package/dist/appearances/glossy/appearance.d.ts +16 -0
- package/dist/appearances/glossy/index.d.ts +2 -0
- package/dist/appearances/glossy/material.d.ts +20 -0
- package/dist/appearances/matte.d.ts +16 -0
- package/dist/appearances/pbr/appearance.d.ts +42 -0
- package/dist/appearances/pbr/index.d.ts +2 -0
- package/dist/appearances/pbr/material.d.ts +27 -0
- package/dist/cube-vertices.d.ts +1 -0
- package/dist/glossy.js +336 -0
- package/dist/glossy.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +1393 -0
- package/dist/index.js.map +1 -0
- package/dist/lighting.d.ts +44 -0
- package/dist/matte.js +182 -0
- package/dist/matte.js.map +1 -0
- package/dist/pbr.js +1179 -0
- package/dist/pbr.js.map +1 -0
- package/dist/program.d.ts +128 -0
- package/dist/renderer.d.ts +64 -0
- package/package.json +68 -0
package/dist/pbr.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pbr.js","names":[],"sources":["../src/appearances/pbr/material.ts","../src/appearances/pbr/appearance.ts"],"sourcesContent":["import { f32, type Infer, struct, vec3f } from 'typegpu/data'\nimport { mix } from 'typegpu/std'\n\n/**\n * GPU material schema for an opaque surface using metallic/roughness shading.\n * Colors are linear RGB. Callers must supply finite values within the documented\n * ranges; constructing this struct does not clamp or validate material values.\n */\nexport const PbrMaterial = struct({\n\t/** Diffuse color for nonmetals, specular reflection color for metals. Each channel is in [0, 1]. */\n\tbaseColor: vec3f,\n\n\t/** Nonmetal at 0, metal at 1; intermediate values blend the responses. Not a shininess control. */\n\tmetallic: f32,\n\n\t/**\n\t * Surface roughness in [0, 1]: 0 produces sharp direct highlights, 1 broad ones.\n\t * Direct shading applies a small internal floor at the smooth end for stability.\n\t * Passed to the environment callback; the consumer owns reflection filtering.\n\t */\n\troughness: f32,\n\n\t/**\n\t * Nonnegative outgoing linear RGB added independently of illumination; may exceed 1.\n\t * Does not illuminate other surfaces or disable this material's reflections.\n\t */\n\temission: vec3f,\n})\nexport type PbrMaterial = Infer<typeof PbrMaterial>\n\n/** Debug material for visualizing a normalized float That varies over the surface. */\nexport function debugPbrMaterial(luminance: number): PbrMaterial {\n\t'use gpu'\n\treturn PbrMaterial({\n\t\tbaseColor: vec3f(0),\n\t\tmetallic: f32(0),\n\t\troughness: f32(1),\n\t\temission: vec3f(luminance),\n\t})\n}\n\nexport function mixPbrMaterials(\n\ta: PbrMaterial,\n\tb: PbrMaterial,\n\tamount: number,\n): PbrMaterial {\n\t'use gpu'\n\treturn PbrMaterial({\n\t\tbaseColor: mix(a.baseColor, b.baseColor, amount),\n\t\tmetallic: mix(a.metallic, b.metallic, amount),\n\t\troughness: mix(a.roughness, b.roughness, amount),\n\t\temission: mix(a.emission, b.emission, amount),\n\t})\n}\n","import { type Infer, struct, type v3f, vec3f } from 'typegpu/data'\nimport {\n\tclamp,\n\tdot,\n\tmax,\n\tmix,\n\tnormalize,\n\tpow,\n\treflect,\n\tsqrt,\n} from 'typegpu/std'\nimport type { RaymarchLighting } from '../../lighting'\nimport type { RaymarchAppearance, RaymarchResult } from '../../program'\nimport { PbrMaterial } from './material'\n\n/** Position, normal, and PBR material consumed by the lighting calculation. */\nexport const PbrSurfaceSample = struct({\n\tposition: vec3f,\n\tnormal: vec3f,\n\tmaterial: PbrMaterial,\n})\nexport type PbrSurfaceSample = Infer<typeof PbrSurfaceSample>\n\n/** Schlick Fresnel approximation; cosTheta is the incident angle's cosine in [0, 1]. */\nfunction fresnelSchlick(cosTheta: number, specularF0: v3f): v3f {\n\t'use gpu'\n\treturn specularF0 + (1 - specularF0) * pow(1 - cosTheta, 5)\n}\n\n/**\n * GGX microfacet distribution; normalDotHalf is in [0, 1].\n * alpha is material roughness squared, with a positive floor applied by the caller.\n */\nfunction distributionGGX(normalDotHalf: number, alpha: number): number {\n\t'use gpu'\n\tconst alphaSquared = alpha * alpha\n\tconst denominator = normalDotHalf * normalDotHalf * (alphaSquared - 1) + 1\n\treturn alphaSquared / (Math.PI * denominator * denominator)\n}\n\n/**\n * Height-correlated Smith GGX visibility, including G / (4 NdotL NdotV).\n * Both cosines must be in (0, 1]; alpha uses the same positive floor as GGX.\n */\nfunction visibilitySmithGGX(\n\tnormalDotView: number,\n\tnormalDotLight: number,\n\talpha: number,\n): number {\n\t'use gpu'\n\tconst alphaSquared = alpha * alpha\n\tconst viewTerm =\n\t\tnormalDotLight *\n\t\tsqrt(normalDotView * normalDotView * (1 - alphaSquared) + alphaSquared)\n\tconst lightTerm =\n\t\tnormalDotView *\n\t\tsqrt(normalDotLight * normalDotLight * (1 - alphaSquared) + alphaSquared)\n\treturn 0.5 / (viewTerm + lightTerm)\n}\n\n/** Combines the microfacet distribution, visibility, and Fresnel response. */\nfunction calculateSpecularResponse(\n\tnormalDotView: number,\n\tnormalDotLight: number,\n\tnormalDotHalf: number,\n\talpha: number,\n\tfresnel: v3f,\n): v3f {\n\t'use gpu'\n\tconst distribution = distributionGGX(normalDotHalf, alpha)\n\tconst visibility = visibilitySmithGGX(normalDotView, normalDotLight, alpha)\n\treturn distribution * visibility * fresnel\n}\n\n/** Material response including the surface-facing factor, before light color/intensity. */\nfunction evaluateDirectLightResponse(\n\tnormal: v3f,\n\tviewDirection: v3f,\n\tlightDirection: v3f,\n\tnormalDotView: number,\n\tdiffuseColor: v3f,\n\tspecularF0: v3f,\n\talpha: number,\n): v3f {\n\t'use gpu'\n\tconst normalDotLight = clamp(dot(normal, lightDirection), 0, 1)\n\tif (normalDotView === 0 || normalDotLight === 0) return vec3f(0)\n\n\tconst halfwayDirection = normalize(viewDirection + lightDirection)\n\tconst normalDotHalf = clamp(dot(normal, halfwayDirection), 0, 1)\n\tconst viewDotHalf = clamp(dot(viewDirection, halfwayDirection), 0, 1)\n\tconst fresnel = fresnelSchlick(viewDotHalf, specularF0)\n\tconst specular = calculateSpecularResponse(\n\t\tnormalDotView,\n\t\tnormalDotLight,\n\t\tnormalDotHalf,\n\t\talpha,\n\t\tfresnel,\n\t)\n\n\t// Only the nonmetal response contributes diffuse reflection.\n\tconst dielectricFresnel = fresnelSchlick(viewDotHalf, vec3f(0.04))\n\tconst diffuse = ((1 - dielectricFresnel) * diffuseColor) / Math.PI\n\treturn normalDotLight * (diffuse + specular)\n}\n\n/**\n * Creates a GPU surface shader combining direct lighting, material emission,\n * and environment reflections.\n */\nexport function createPbrShadeSurface({\n\tlighting,\n\tenvironment,\n}: {\n\tlighting: () => RaymarchLighting\n\tenvironment: (direction: v3f, roughness: number) => v3f\n}) {\n\tfunction calculateDirectLighting(\n\t\tviewDirection: v3f,\n\t\tnormal: v3f,\n\t\tmaterial: PbrMaterial,\n\t\tspecularF0: v3f,\n\t): v3f {\n\t\t'use gpu'\n\n\t\tconst diffuseColor = material.baseColor * (1 - material.metallic)\n\t\tlet accumulatedLight = lighting().ambient * diffuseColor\n\t\tconst directionalLights = lighting().directionalLights\n\t\tconst normalDotView = clamp(dot(normal, viewDirection), 0, 1)\n\t\tif (normalDotView === 0) return accumulatedLight\n\n\t\tconst minimumAlpha = 0.001 // Keep the GGX response finite even for material roughness zero.\n\t\tconst alpha = max(material.roughness * material.roughness, minimumAlpha)\n\n\t\tfor (const light of directionalLights) {\n\t\t\tif (light.intensity === 0) continue\n\n\t\t\tconst lightDirection = -1 * normalize(light.direction)\n\t\t\tconst response = evaluateDirectLightResponse(\n\t\t\t\tnormal,\n\t\t\t\tviewDirection,\n\t\t\t\tlightDirection,\n\t\t\t\tnormalDotView,\n\t\t\t\tdiffuseColor,\n\t\t\t\tspecularF0,\n\t\t\t\talpha,\n\t\t\t)\n\t\t\taccumulatedLight += light.color * light.intensity * response\n\t\t}\n\n\t\treturn accumulatedLight\n\t}\n\n\tfunction calculateReflection(\n\t\tviewDirection: v3f,\n\t\tnormal: v3f,\n\t\tspecularF0: v3f,\n\t\troughness: number,\n\t): v3f {\n\t\t'use gpu'\n\n\t\tconst reflectionDirection = reflect(-1 * viewDirection, normal)\n\t\tconst environmentColor = environment(reflectionDirection, roughness)\n\t\tconst facing = max(dot(normal, viewDirection), 0)\n\t\tconst fresnel = fresnelSchlick(facing, specularF0)\n\n\t\treturn environmentColor * fresnel\n\t}\n\n\treturn function shadeSurface(\n\t\tcameraPosition: v3f,\n\t\tsample: PbrSurfaceSample,\n\t): v3f {\n\t\t'use gpu'\n\n\t\tconst material = sample.material\n\t\tconst viewDirection = normalize(cameraPosition - sample.position)\n\n\t\tconst dielectricF0 = vec3f(0.04) // Normal-incidence reflectance for a nonmetal with IOR 1.5.\n\t\tconst specularF0 = mix(dielectricF0, material.baseColor, material.metallic)\n\n\t\tconst directLighting = calculateDirectLighting(\n\t\t\tviewDirection,\n\t\t\tsample.normal,\n\t\t\tmaterial,\n\t\t\tspecularF0,\n\t\t)\n\n\t\tconst reflection = calculateReflection(\n\t\t\tviewDirection,\n\t\t\tsample.normal,\n\t\t\tspecularF0,\n\t\t\tmaterial.roughness,\n\t\t)\n\n\t\treturn directLighting + material.emission + reflection\n\t}\n}\n\n/** GPU function sampling a PBR material from the raymarch result. */\nexport type PbrMaterialSampler = (result: RaymarchResult) => PbrMaterial\n\n/**\n * Creates a metallic/roughness appearance for a raymarch program.\n *\n * Material sampling runs for hits and misses so derivatives remain available.\n * Lighting runs only for hits. The returned color on a miss is ignored by the\n * renderer.\n */\nexport function createPbrAppearance({\n\tmaterial,\n\tlighting,\n\tenvironment,\n}: {\n\tmaterial: PbrMaterialSampler\n\tlighting: () => RaymarchLighting\n\t/**\n\t * Incoming linear RGB from a normalized reflection direction. Called only on\n\t * hits; texture sampling here should use an explicit level of detail.\n\t */\n\tenvironment: (direction: v3f, roughness: number) => v3f\n}): RaymarchAppearance {\n\tconst shadeSurface = createPbrShadeSurface({ lighting, environment })\n\n\treturn function pbrAppearance(result: RaymarchResult): v3f {\n\t\t'use gpu'\n\t\tconst materialSample = material(result)\n\t\tif (!result.isHit) return vec3f(0)\n\n\t\treturn shadeSurface(\n\t\t\tresult.rayOrigin,\n\t\t\tPbrSurfaceSample({\n\t\t\t\tposition: result.position,\n\t\t\t\tnormal: result.normal,\n\t\t\t\tmaterial: materialSample,\n\t\t\t}),\n\t\t)\n\t}\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { type Infer, type v3f } from 'typegpu/data';
|
|
2
|
+
import { AABB } from './aabb';
|
|
3
|
+
export declare const RaymarchCamera: import("typegpu/data").WgslStruct<{
|
|
4
|
+
viewProjectionMatrix: import("typegpu/data").Mat4x4f;
|
|
5
|
+
position: import("typegpu/data").Vec3f;
|
|
6
|
+
}>;
|
|
7
|
+
export type RaymarchCamera = Infer<typeof RaymarchCamera>;
|
|
8
|
+
/** Result of the primary ray march, with data available for appearance. */
|
|
9
|
+
export declare const RaymarchResult: import("typegpu/data").WgslStruct<{
|
|
10
|
+
/** Whether the primary ray reached the surface within the march limits. */
|
|
11
|
+
isHit: import("typegpu/data").Bool;
|
|
12
|
+
/** Hit position, or the final ray sample position on a miss, in world space. */
|
|
13
|
+
position: import("typegpu/data").Vec3f;
|
|
14
|
+
/** Outward surface normal on a hit; -rayDirection on a miss. */
|
|
15
|
+
normal: import("typegpu/data").Vec3f;
|
|
16
|
+
/** Camera position in world space. */
|
|
17
|
+
rayOrigin: import("typegpu/data").Vec3f;
|
|
18
|
+
/** Unit direction from the camera into the scene. */
|
|
19
|
+
rayDirection: import("typegpu/data").Vec3f;
|
|
20
|
+
/** World-space distance from rayOrigin to position. */
|
|
21
|
+
rayDistance: import("typegpu/data").F32;
|
|
22
|
+
/** Number of primary ray-march iterations, excluding normal/shadow samples. */
|
|
23
|
+
stepCount: import("typegpu/data").U32;
|
|
24
|
+
/** Projected depth written for a hit; 1 on a miss. */
|
|
25
|
+
projectedDepth: import("typegpu/data").F32;
|
|
26
|
+
/** Pixel-space fragment coordinates, with pixel centers at half-integers. */
|
|
27
|
+
fragmentCoord: import("typegpu/data").Vec2f;
|
|
28
|
+
/** Index of the bounds instance being rendered. */
|
|
29
|
+
instanceIndex: import("typegpu/data").U32;
|
|
30
|
+
/** World-space bounds for this instance. */
|
|
31
|
+
bounds: import("typegpu/data").WgslStruct<{
|
|
32
|
+
min: import("typegpu/data").Vec3f;
|
|
33
|
+
max: import("typegpu/data").Vec3f;
|
|
34
|
+
}>;
|
|
35
|
+
}>;
|
|
36
|
+
export type RaymarchResult = Infer<typeof RaymarchResult>;
|
|
37
|
+
/**
|
|
38
|
+
* GPU function calculating linear RGB for every visible fragment after marching.
|
|
39
|
+
* The renderer uses the result only on hits, with opaque coverage and its own
|
|
40
|
+
* depth. Derivative-dependent work must run outside hit-dependent branches.
|
|
41
|
+
*/
|
|
42
|
+
export type RaymarchAppearance = (result: RaymarchResult) => v3f;
|
|
43
|
+
export type RaymarchBoundsProvider = (instanceIdx: number) => AABB;
|
|
44
|
+
export type RaymarchInitializer = (instanceIdx: number) => void;
|
|
45
|
+
export type RaymarchVisibilityTest = (rayPoint: v3f, instanceIdx: number, bounds: AABB) => boolean;
|
|
46
|
+
export type RaymarchDistanceFunction = (point: v3f, instanceIdx: number, aabb: AABB) => number;
|
|
47
|
+
export type RaymarchProgramOptions = {
|
|
48
|
+
/** Diagnostic metadata for consumers. Unused by the renderer. */
|
|
49
|
+
label?: string;
|
|
50
|
+
/** Surface hit tolerance in world units. */
|
|
51
|
+
epsilon: number;
|
|
52
|
+
/** Normal sampling offset; defaults to epsilon. */
|
|
53
|
+
epsilonNormal?: number;
|
|
54
|
+
/** Maximum number of marching steps per ray; defaults to 100. */
|
|
55
|
+
maxSteps?: number;
|
|
56
|
+
/** Write surface hit depth to the depth buffer; defaults to true. */
|
|
57
|
+
depthWriteEnabled?: boolean;
|
|
58
|
+
/** Comparison used to test surface hit depth against the depth buffer; defaults to 'less'. */
|
|
59
|
+
depthCompare?: GPUCompareFunction;
|
|
60
|
+
} & ({
|
|
61
|
+
/** Allow marching past the bounding-box exit. Defaults to false; requires maxDistance when true. */
|
|
62
|
+
marchBeyondBounds?: false;
|
|
63
|
+
/** Maximum distance to march from the bounding-box entry point, in world units. */
|
|
64
|
+
maxDistance?: number;
|
|
65
|
+
} | {
|
|
66
|
+
/** Allow marching past the bounding-box exit. Defaults to false; requires maxDistance when true. */
|
|
67
|
+
marchBeyondBounds: true;
|
|
68
|
+
/** Maximum distance to march from the bounding-box entry point, in world units. */
|
|
69
|
+
maxDistance: number;
|
|
70
|
+
});
|
|
71
|
+
/** Geometry and visibility callbacks compiled into a raymarching pipeline. */
|
|
72
|
+
export interface RaymarchSurface {
|
|
73
|
+
/** GPU function initializing per-fragment state before raymarching. */
|
|
74
|
+
init?: RaymarchInitializer;
|
|
75
|
+
/** GPU function providing each instance's axis-aligned bounding box. */
|
|
76
|
+
bounds: RaymarchBoundsProvider;
|
|
77
|
+
/**
|
|
78
|
+
* GPU function masking out rays that should not render. Returning false
|
|
79
|
+
* skips raymarching for that fragment.
|
|
80
|
+
*/
|
|
81
|
+
isRayVisible?: RaymarchVisibilityTest;
|
|
82
|
+
/** GPU function returning the signed distance from a point to the surface. */
|
|
83
|
+
sd: RaymarchDistanceFunction;
|
|
84
|
+
}
|
|
85
|
+
/** Camera, geometry, and appearance compiled into one raymarching pipeline. */
|
|
86
|
+
export interface RaymarchProgram {
|
|
87
|
+
/** GPU function providing the current camera state. */
|
|
88
|
+
camera: () => RaymarchCamera;
|
|
89
|
+
/** Surface bounds, signed distance, and optional visibility behavior. */
|
|
90
|
+
surface: RaymarchSurface;
|
|
91
|
+
/** GPU function returning linear RGB for the raymarch result. */
|
|
92
|
+
appearance: RaymarchAppearance;
|
|
93
|
+
}
|
|
94
|
+
/** Versioned factory that creates a program from setup-time context. */
|
|
95
|
+
export interface RaymarchProgramDefinition<TContext> {
|
|
96
|
+
version: number;
|
|
97
|
+
options: RaymarchProgramOptions;
|
|
98
|
+
create: (context: TContext) => RaymarchProgram;
|
|
99
|
+
}
|
|
100
|
+
/** Minimal hot-module context used to retain a program across reevaluations. */
|
|
101
|
+
export interface RaymarchHotContext {
|
|
102
|
+
data: Record<string, unknown>;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Creates a versioned program definition without allocating GPU resources. It
|
|
106
|
+
* owns the factory for surface, camera, and appearance callbacks,
|
|
107
|
+
* and the options used to compile them into a raymarching pipeline. Callbacks
|
|
108
|
+
* may read changing buffer data without rebuilding the pipeline.
|
|
109
|
+
*
|
|
110
|
+
* When `hot` is provided, reevaluating the module updates the existing program
|
|
111
|
+
* definition and increments its version. Renderer instances observe that
|
|
112
|
+
* version and rebuild their pipelines while retaining setup-time resources. The
|
|
113
|
+
* defining module must call `import.meta.hot?.accept()` for this behavior, and
|
|
114
|
+
* may currently define only one HMR-backed raymarch program.
|
|
115
|
+
*
|
|
116
|
+
* ```ts
|
|
117
|
+
* const program = createRaymarchProgram(options, create, import.meta.hot)
|
|
118
|
+
* import.meta.hot?.accept()
|
|
119
|
+
* ```
|
|
120
|
+
*
|
|
121
|
+
* @returns A read-only program definition consumed by raymarch renderer factories.
|
|
122
|
+
*/
|
|
123
|
+
export declare function createRaymarchProgram<TContext = undefined>(options: RaymarchProgramOptions, create: (context: TContext) => RaymarchProgram,
|
|
124
|
+
/**
|
|
125
|
+
* Optional hot-module context used to preserve and update the program
|
|
126
|
+
* across module reevaluations.
|
|
127
|
+
*/
|
|
128
|
+
hot?: RaymarchHotContext): Readonly<RaymarchProgramDefinition<TContext>>;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { type ColorAttachment, type DepthStencilAttachment, type TgpuRenderPipeline, type TgpuRoot } from 'typegpu';
|
|
2
|
+
import { type Vec4f } from 'typegpu/data';
|
|
3
|
+
import { type RaymarchProgram, type RaymarchProgramDefinition } from './program';
|
|
4
|
+
export interface RaymarchRenderTargetOptions {
|
|
5
|
+
/**
|
|
6
|
+
* Format of the color attachment.
|
|
7
|
+
* @default navigator.gpu.getPreferredCanvasFormat()
|
|
8
|
+
*/
|
|
9
|
+
colorFormat?: GPUTextureFormat;
|
|
10
|
+
/**
|
|
11
|
+
* Format of the depth/stencil attachment.
|
|
12
|
+
* @default 'depth24plus'
|
|
13
|
+
*/
|
|
14
|
+
depthStencilFormat?: Extract<GPUTextureFormat, `depth${string}`>;
|
|
15
|
+
/**
|
|
16
|
+
* Number of samples per pixel in each attachment.
|
|
17
|
+
* @default 1
|
|
18
|
+
*/
|
|
19
|
+
sampleCount?: 1 | 4;
|
|
20
|
+
}
|
|
21
|
+
/** Attachments and instance count for one raymarch draw submission. */
|
|
22
|
+
export interface RaymarchRenderOptions {
|
|
23
|
+
colorAttachment: ColorAttachment;
|
|
24
|
+
depthStencilAttachment: DepthStencilAttachment;
|
|
25
|
+
/** @default 1 */
|
|
26
|
+
instances?: number;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Creates a renderer for a raymarch program. It adds bounds rendering,
|
|
30
|
+
* raymarching, normal calculation, appearance, depth, and draw submission to the
|
|
31
|
+
* surface supplied by the program.
|
|
32
|
+
*
|
|
33
|
+
* Program creation and preparation are repeated when the program version
|
|
34
|
+
* changes, allowing shader HMR to rebuild the pipeline without replacing the
|
|
35
|
+
* supplied setup-time context.
|
|
36
|
+
*
|
|
37
|
+
* @returns A function that draws a requested number of raymarched instances.
|
|
38
|
+
*/
|
|
39
|
+
export declare function createRaymarchRenderer<TContext = undefined>({ root, program, context, renderTarget, prepare, preparePipeline, }: {
|
|
40
|
+
root: TgpuRoot;
|
|
41
|
+
/** Program providing pipeline options, a version, and the shader factory. */
|
|
42
|
+
program: Readonly<RaymarchProgramDefinition<TContext>>;
|
|
43
|
+
/** Render target configuration used to create a compatible pipeline. */
|
|
44
|
+
renderTarget?: Readonly<RaymarchRenderTargetOptions>;
|
|
45
|
+
/**
|
|
46
|
+
* Optional program transformation applied after program creation and before
|
|
47
|
+
* pipeline compilation.
|
|
48
|
+
*/
|
|
49
|
+
prepare?: (program: RaymarchProgram) => RaymarchProgram;
|
|
50
|
+
/**
|
|
51
|
+
* Optional pipeline transformation applied after pipeline creation and before
|
|
52
|
+
* the pipeline is captured by the render function.
|
|
53
|
+
*/
|
|
54
|
+
preparePipeline?: (pipeline: TgpuRenderPipeline<{
|
|
55
|
+
color: Vec4f;
|
|
56
|
+
}>) => TgpuRenderPipeline<{
|
|
57
|
+
color: Vec4f;
|
|
58
|
+
}>;
|
|
59
|
+
} & (undefined extends TContext ? {
|
|
60
|
+
context?: TContext;
|
|
61
|
+
} : {
|
|
62
|
+
/** @deprecated use closures instead. */
|
|
63
|
+
context: TContext;
|
|
64
|
+
})): (options: RaymarchRenderOptions) => void;
|
package/package.json
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "luxscura",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "A TypeGPU ray marching renderer",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Alex Wayne",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/AlexJWayne/luxscura.git",
|
|
10
|
+
"directory": "packages/luxscura"
|
|
11
|
+
},
|
|
12
|
+
"homepage": "https://alexjwayne.github.io/luxscura/",
|
|
13
|
+
"bugs": "https://github.com/AlexJWayne/luxscura/issues",
|
|
14
|
+
"keywords": [
|
|
15
|
+
"webgpu",
|
|
16
|
+
"typegpu",
|
|
17
|
+
"raymarching",
|
|
18
|
+
"renderer",
|
|
19
|
+
"sdf"
|
|
20
|
+
],
|
|
21
|
+
"type": "module",
|
|
22
|
+
"sideEffects": false,
|
|
23
|
+
"main": "./dist/index.js",
|
|
24
|
+
"types": "./dist/index.d.ts",
|
|
25
|
+
"exports": {
|
|
26
|
+
".": {
|
|
27
|
+
"types": "./dist/index.d.ts",
|
|
28
|
+
"import": "./dist/index.js",
|
|
29
|
+
"default": "./dist/index.js"
|
|
30
|
+
},
|
|
31
|
+
"./glossy": {
|
|
32
|
+
"types": "./dist/appearances/glossy/index.d.ts",
|
|
33
|
+
"import": "./dist/glossy.js",
|
|
34
|
+
"default": "./dist/glossy.js"
|
|
35
|
+
},
|
|
36
|
+
"./matte": {
|
|
37
|
+
"types": "./dist/appearances/matte.d.ts",
|
|
38
|
+
"import": "./dist/matte.js",
|
|
39
|
+
"default": "./dist/matte.js"
|
|
40
|
+
},
|
|
41
|
+
"./pbr": {
|
|
42
|
+
"types": "./dist/appearances/pbr/index.d.ts",
|
|
43
|
+
"import": "./dist/pbr.js",
|
|
44
|
+
"default": "./dist/pbr.js"
|
|
45
|
+
},
|
|
46
|
+
"./package.json": "./package.json"
|
|
47
|
+
},
|
|
48
|
+
"files": [
|
|
49
|
+
"dist"
|
|
50
|
+
],
|
|
51
|
+
"scripts": {
|
|
52
|
+
"build": "vite build && tsc -p tsconfig.build.json",
|
|
53
|
+
"prepack": "bun run build",
|
|
54
|
+
"test": "bun test",
|
|
55
|
+
"typecheck": "tsc --noEmit"
|
|
56
|
+
},
|
|
57
|
+
"peerDependencies": {
|
|
58
|
+
"typegpu": "^0.12.6"
|
|
59
|
+
},
|
|
60
|
+
"devDependencies": {
|
|
61
|
+
"@types/bun": "^1.3.14",
|
|
62
|
+
"@webgpu/types": "^0.1.69",
|
|
63
|
+
"typegpu": "^0.12.6",
|
|
64
|
+
"typescript": "npm:tsover@6.0.2",
|
|
65
|
+
"unplugin-typegpu": "^0.12.4",
|
|
66
|
+
"vite": "^8.0.0"
|
|
67
|
+
}
|
|
68
|
+
}
|