reze-engine 0.2.12 → 0.2.13
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/dist/engine.d.ts +0 -5
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +0 -55
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -1
- package/package.json +1 -1
- package/src/engine.ts +0 -60
- package/src/index.ts +0 -1
- package/src/pool.ts +0 -483
package/dist/engine.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Quat, Vec3 } from "./math";
|
|
2
|
-
import { PoolOptions } from "./pool";
|
|
3
2
|
export type EngineOptions = {
|
|
4
3
|
ambient?: number;
|
|
5
4
|
bloomIntensity?: number;
|
|
@@ -76,7 +75,6 @@ export declare class Engine {
|
|
|
76
75
|
private currentModel;
|
|
77
76
|
private modelDir;
|
|
78
77
|
private physics;
|
|
79
|
-
private pool;
|
|
80
78
|
private materialSampler;
|
|
81
79
|
private textureCache;
|
|
82
80
|
private opaqueDraws;
|
|
@@ -112,8 +110,6 @@ export declare class Engine {
|
|
|
112
110
|
private setupResize;
|
|
113
111
|
private handleResize;
|
|
114
112
|
private setupCamera;
|
|
115
|
-
private createCameraBindGroupLayout;
|
|
116
|
-
private createCameraBindGroup;
|
|
117
113
|
private setupLighting;
|
|
118
114
|
private addLight;
|
|
119
115
|
private setAmbient;
|
|
@@ -125,7 +121,6 @@ export declare class Engine {
|
|
|
125
121
|
stopRenderLoop(): void;
|
|
126
122
|
dispose(): void;
|
|
127
123
|
loadModel(path: string): Promise<void>;
|
|
128
|
-
addPool(options?: PoolOptions): Promise<void>;
|
|
129
124
|
rotateBones(bones: string[], rotations: Quat[], durationMs?: number): void;
|
|
130
125
|
private setupModelBuffers;
|
|
131
126
|
private setupMaterials;
|
package/dist/engine.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAA;
|
|
1
|
+
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"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"}
|
package/dist/engine.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { Camera } from "./camera";
|
|
2
2
|
import { Quat, Vec3 } from "./math";
|
|
3
|
-
import { Pool } from "./pool";
|
|
4
3
|
import { PmxLoader } from "./pmx-loader";
|
|
5
4
|
import { Physics } from "./physics";
|
|
6
5
|
import { VMDLoader } from "./vmd-loader";
|
|
@@ -27,7 +26,6 @@ export class Engine {
|
|
|
27
26
|
this.currentModel = null;
|
|
28
27
|
this.modelDir = "";
|
|
29
28
|
this.physics = null;
|
|
30
|
-
this.pool = null;
|
|
31
29
|
this.textureCache = new Map();
|
|
32
30
|
// Draw lists
|
|
33
31
|
this.opaqueDraws = [];
|
|
@@ -1231,36 +1229,6 @@ export class Engine {
|
|
|
1231
1229
|
this.camera.aspect = this.canvas.width / this.canvas.height;
|
|
1232
1230
|
this.camera.attachControl(this.canvas);
|
|
1233
1231
|
}
|
|
1234
|
-
// Create camera bind group layout for pool (camera-only)
|
|
1235
|
-
createCameraBindGroupLayout() {
|
|
1236
|
-
return this.device.createBindGroupLayout({
|
|
1237
|
-
label: "camera bind group layout",
|
|
1238
|
-
entries: [
|
|
1239
|
-
{
|
|
1240
|
-
binding: 0,
|
|
1241
|
-
visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT,
|
|
1242
|
-
buffer: {
|
|
1243
|
-
type: "uniform",
|
|
1244
|
-
},
|
|
1245
|
-
},
|
|
1246
|
-
],
|
|
1247
|
-
});
|
|
1248
|
-
}
|
|
1249
|
-
// Create camera bind group for pool
|
|
1250
|
-
createCameraBindGroup(layout) {
|
|
1251
|
-
return this.device.createBindGroup({
|
|
1252
|
-
label: "camera bind group for pool",
|
|
1253
|
-
layout: layout,
|
|
1254
|
-
entries: [
|
|
1255
|
-
{
|
|
1256
|
-
binding: 0,
|
|
1257
|
-
resource: {
|
|
1258
|
-
buffer: this.cameraUniformBuffer,
|
|
1259
|
-
},
|
|
1260
|
-
},
|
|
1261
|
-
],
|
|
1262
|
-
});
|
|
1263
|
-
}
|
|
1264
1232
|
// Step 5: Create lighting buffers
|
|
1265
1233
|
setupLighting() {
|
|
1266
1234
|
this.lightUniformBuffer = this.device.createBuffer({
|
|
@@ -1449,14 +1417,6 @@ export class Engine {
|
|
|
1449
1417
|
this.physics = new Physics(model.getRigidbodies(), model.getJoints());
|
|
1450
1418
|
await this.setupModelBuffers(model);
|
|
1451
1419
|
}
|
|
1452
|
-
async addPool(options) {
|
|
1453
|
-
if (!this.device) {
|
|
1454
|
-
throw new Error("Engine must be initialized before adding pool");
|
|
1455
|
-
}
|
|
1456
|
-
const cameraLayout = this.createCameraBindGroupLayout();
|
|
1457
|
-
this.pool = new Pool(this.device, cameraLayout, this.cameraUniformBuffer, options);
|
|
1458
|
-
await this.pool.init();
|
|
1459
|
-
}
|
|
1460
1420
|
rotateBones(bones, rotations, durationMs) {
|
|
1461
1421
|
this.currentModel?.rotateBones(bones, rotations, durationMs);
|
|
1462
1422
|
}
|
|
@@ -1820,11 +1780,6 @@ export class Engine {
|
|
|
1820
1780
|
}
|
|
1821
1781
|
const pass = encoder.beginRenderPass(this.renderPassDescriptor);
|
|
1822
1782
|
this.drawCallCount = 0;
|
|
1823
|
-
// Render pool first if no model
|
|
1824
|
-
if (this.pool && !this.currentModel) {
|
|
1825
|
-
this.pool.render(pass);
|
|
1826
|
-
this.drawCallCount++;
|
|
1827
|
-
}
|
|
1828
1783
|
if (this.currentModel) {
|
|
1829
1784
|
pass.setVertexBuffer(0, this.vertexBuffer);
|
|
1830
1785
|
pass.setVertexBuffer(1, this.jointsBuffer);
|
|
@@ -1839,16 +1794,6 @@ export class Engine {
|
|
|
1839
1794
|
this.drawCallCount++;
|
|
1840
1795
|
}
|
|
1841
1796
|
}
|
|
1842
|
-
// Pass 1.5: Pool (water plane) - render after opaque, before eyes
|
|
1843
|
-
if (this.pool) {
|
|
1844
|
-
this.pool.render(pass, {
|
|
1845
|
-
vertexBuffer: this.vertexBuffer,
|
|
1846
|
-
jointsBuffer: this.jointsBuffer,
|
|
1847
|
-
weightsBuffer: this.weightsBuffer,
|
|
1848
|
-
indexBuffer: this.indexBuffer,
|
|
1849
|
-
});
|
|
1850
|
-
this.drawCallCount++;
|
|
1851
|
-
}
|
|
1852
1797
|
// Pass 2: Eyes (writes stencil value for hair to test against)
|
|
1853
1798
|
pass.setPipeline(this.eyePipeline);
|
|
1854
1799
|
pass.setStencilReference(this.STENCIL_EYE_VALUE);
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,WAAW,EAAE,MAAM,UAAU,CAAA;AACnD,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,WAAW,EAAE,MAAM,UAAU,CAAA;AACnD,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAA"}
|
package/dist/index.js
CHANGED
package/package.json
CHANGED
package/src/engine.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { Camera } from "./camera"
|
|
2
2
|
import { Quat, Vec3 } from "./math"
|
|
3
3
|
import { Model } from "./model"
|
|
4
|
-
import { Pool, PoolOptions } from "./pool"
|
|
5
4
|
import { PmxLoader } from "./pmx-loader"
|
|
6
5
|
import { Physics } from "./physics"
|
|
7
6
|
import { VMDKeyFrame, VMDLoader } from "./vmd-loader"
|
|
@@ -108,7 +107,6 @@ export class Engine {
|
|
|
108
107
|
private currentModel: Model | null = null
|
|
109
108
|
private modelDir: string = ""
|
|
110
109
|
private physics: Physics | null = null
|
|
111
|
-
private pool: Pool | null = null
|
|
112
110
|
private materialSampler!: GPUSampler
|
|
113
111
|
private textureCache = new Map<string, GPUTexture>()
|
|
114
112
|
// Draw lists
|
|
@@ -1376,38 +1374,6 @@ export class Engine {
|
|
|
1376
1374
|
this.camera.attachControl(this.canvas)
|
|
1377
1375
|
}
|
|
1378
1376
|
|
|
1379
|
-
// Create camera bind group layout for pool (camera-only)
|
|
1380
|
-
private createCameraBindGroupLayout(): GPUBindGroupLayout {
|
|
1381
|
-
return this.device.createBindGroupLayout({
|
|
1382
|
-
label: "camera bind group layout",
|
|
1383
|
-
entries: [
|
|
1384
|
-
{
|
|
1385
|
-
binding: 0,
|
|
1386
|
-
visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT,
|
|
1387
|
-
buffer: {
|
|
1388
|
-
type: "uniform",
|
|
1389
|
-
},
|
|
1390
|
-
},
|
|
1391
|
-
],
|
|
1392
|
-
})
|
|
1393
|
-
}
|
|
1394
|
-
|
|
1395
|
-
// Create camera bind group for pool
|
|
1396
|
-
private createCameraBindGroup(layout: GPUBindGroupLayout): GPUBindGroup {
|
|
1397
|
-
return this.device.createBindGroup({
|
|
1398
|
-
label: "camera bind group for pool",
|
|
1399
|
-
layout: layout,
|
|
1400
|
-
entries: [
|
|
1401
|
-
{
|
|
1402
|
-
binding: 0,
|
|
1403
|
-
resource: {
|
|
1404
|
-
buffer: this.cameraUniformBuffer,
|
|
1405
|
-
},
|
|
1406
|
-
},
|
|
1407
|
-
],
|
|
1408
|
-
})
|
|
1409
|
-
}
|
|
1410
|
-
|
|
1411
1377
|
// Step 5: Create lighting buffers
|
|
1412
1378
|
private setupLighting() {
|
|
1413
1379
|
this.lightUniformBuffer = this.device.createBuffer({
|
|
@@ -1631,15 +1597,6 @@ export class Engine {
|
|
|
1631
1597
|
await this.setupModelBuffers(model)
|
|
1632
1598
|
}
|
|
1633
1599
|
|
|
1634
|
-
public async addPool(options?: PoolOptions) {
|
|
1635
|
-
if (!this.device) {
|
|
1636
|
-
throw new Error("Engine must be initialized before adding pool")
|
|
1637
|
-
}
|
|
1638
|
-
const cameraLayout = this.createCameraBindGroupLayout()
|
|
1639
|
-
this.pool = new Pool(this.device, cameraLayout, this.cameraUniformBuffer, options)
|
|
1640
|
-
await this.pool.init()
|
|
1641
|
-
}
|
|
1642
|
-
|
|
1643
1600
|
public rotateBones(bones: string[], rotations: Quat[], durationMs?: number) {
|
|
1644
1601
|
this.currentModel?.rotateBones(bones, rotations, durationMs)
|
|
1645
1602
|
}
|
|
@@ -2067,12 +2024,6 @@ export class Engine {
|
|
|
2067
2024
|
|
|
2068
2025
|
this.drawCallCount = 0
|
|
2069
2026
|
|
|
2070
|
-
// Render pool first if no model
|
|
2071
|
-
if (this.pool && !this.currentModel) {
|
|
2072
|
-
this.pool.render(pass)
|
|
2073
|
-
this.drawCallCount++
|
|
2074
|
-
}
|
|
2075
|
-
|
|
2076
2027
|
if (this.currentModel) {
|
|
2077
2028
|
pass.setVertexBuffer(0, this.vertexBuffer)
|
|
2078
2029
|
pass.setVertexBuffer(1, this.jointsBuffer)
|
|
@@ -2089,17 +2040,6 @@ export class Engine {
|
|
|
2089
2040
|
}
|
|
2090
2041
|
}
|
|
2091
2042
|
|
|
2092
|
-
// Pass 1.5: Pool (water plane) - render after opaque, before eyes
|
|
2093
|
-
if (this.pool) {
|
|
2094
|
-
this.pool.render(pass, {
|
|
2095
|
-
vertexBuffer: this.vertexBuffer,
|
|
2096
|
-
jointsBuffer: this.jointsBuffer,
|
|
2097
|
-
weightsBuffer: this.weightsBuffer,
|
|
2098
|
-
indexBuffer: this.indexBuffer!,
|
|
2099
|
-
})
|
|
2100
|
-
this.drawCallCount++
|
|
2101
|
-
}
|
|
2102
|
-
|
|
2103
2043
|
// Pass 2: Eyes (writes stencil value for hair to test against)
|
|
2104
2044
|
pass.setPipeline(this.eyePipeline)
|
|
2105
2045
|
pass.setStencilReference(this.STENCIL_EYE_VALUE)
|
package/src/index.ts
CHANGED
package/src/pool.ts
DELETED
|
@@ -1,483 +0,0 @@
|
|
|
1
|
-
import { Vec3 } from "./math"
|
|
2
|
-
|
|
3
|
-
export interface PoolOptions {
|
|
4
|
-
y?: number // Y position (default: 12)
|
|
5
|
-
size?: number // Plane size (default: 100)
|
|
6
|
-
segments?: number // Subdivision count (default: 50)
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export class Pool {
|
|
10
|
-
private device!: GPUDevice
|
|
11
|
-
private vertexBuffer!: GPUBuffer
|
|
12
|
-
private indexBuffer!: GPUBuffer
|
|
13
|
-
private pipeline!: GPURenderPipeline
|
|
14
|
-
private bindGroup!: GPUBindGroup
|
|
15
|
-
private bindGroupLayout!: GPUBindGroupLayout
|
|
16
|
-
private uniformBuffer!: GPUBuffer
|
|
17
|
-
private cameraBindGroupLayout!: GPUBindGroupLayout
|
|
18
|
-
private cameraBindGroup!: GPUBindGroup
|
|
19
|
-
private cameraUniformBuffer!: GPUBuffer
|
|
20
|
-
private indexCount: number = 0
|
|
21
|
-
private y: number
|
|
22
|
-
private size: number
|
|
23
|
-
private segments: number
|
|
24
|
-
private seaColor: Vec3
|
|
25
|
-
private seaLight: Vec3
|
|
26
|
-
private startTime: number = performance.now()
|
|
27
|
-
|
|
28
|
-
constructor(
|
|
29
|
-
device: GPUDevice,
|
|
30
|
-
cameraBindGroupLayout: GPUBindGroupLayout,
|
|
31
|
-
cameraUniformBuffer: GPUBuffer,
|
|
32
|
-
options?: PoolOptions
|
|
33
|
-
) {
|
|
34
|
-
this.device = device
|
|
35
|
-
this.cameraBindGroupLayout = cameraBindGroupLayout
|
|
36
|
-
this.cameraUniformBuffer = cameraUniformBuffer
|
|
37
|
-
this.y = options?.y ?? 15
|
|
38
|
-
this.size = options?.size ?? 100
|
|
39
|
-
this.segments = options?.segments ?? 50
|
|
40
|
-
// Hardcoded dark night pool colors (not used in shader, but kept for uniform buffer)
|
|
41
|
-
this.seaColor = new Vec3(0.02, 0.05, 0.12) // Dark night pool base
|
|
42
|
-
this.seaLight = new Vec3(0.1, 0.3, 0.5) // Light cyan for lit areas
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
public async init() {
|
|
46
|
-
this.createGeometry()
|
|
47
|
-
this.createShader()
|
|
48
|
-
this.createUniforms()
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
private createGeometry() {
|
|
52
|
-
const segments = this.segments
|
|
53
|
-
const size = this.size
|
|
54
|
-
const halfSize = size / 2
|
|
55
|
-
const step = size / segments
|
|
56
|
-
|
|
57
|
-
// Generate vertices
|
|
58
|
-
const vertices: number[] = []
|
|
59
|
-
for (let i = 0; i <= segments; i++) {
|
|
60
|
-
for (let j = 0; j <= segments; j++) {
|
|
61
|
-
const x = -halfSize + j * step
|
|
62
|
-
const z = -halfSize + i * step
|
|
63
|
-
const y = this.y
|
|
64
|
-
const u = j / segments
|
|
65
|
-
const v = i / segments
|
|
66
|
-
|
|
67
|
-
// Position (x, y, z) + UV (u, v)
|
|
68
|
-
vertices.push(x, y, z, u, v)
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// Generate indices
|
|
73
|
-
const indices: number[] = []
|
|
74
|
-
for (let i = 0; i < segments; i++) {
|
|
75
|
-
for (let j = 0; j < segments; j++) {
|
|
76
|
-
const topLeft = i * (segments + 1) + j
|
|
77
|
-
const topRight = topLeft + 1
|
|
78
|
-
const bottomLeft = (i + 1) * (segments + 1) + j
|
|
79
|
-
const bottomRight = bottomLeft + 1
|
|
80
|
-
|
|
81
|
-
// Two triangles per quad
|
|
82
|
-
indices.push(topLeft, bottomLeft, topRight)
|
|
83
|
-
indices.push(topRight, bottomLeft, bottomRight)
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
this.indexCount = indices.length
|
|
88
|
-
|
|
89
|
-
// Create buffers
|
|
90
|
-
this.vertexBuffer = this.device.createBuffer({
|
|
91
|
-
label: "pool vertices",
|
|
92
|
-
size: vertices.length * 4,
|
|
93
|
-
usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
|
|
94
|
-
})
|
|
95
|
-
this.device.queue.writeBuffer(this.vertexBuffer, 0, new Float32Array(vertices))
|
|
96
|
-
|
|
97
|
-
const indexBufferSize = indices.length * 4
|
|
98
|
-
this.indexBuffer = this.device.createBuffer({
|
|
99
|
-
label: "pool indices",
|
|
100
|
-
size: indexBufferSize,
|
|
101
|
-
usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,
|
|
102
|
-
})
|
|
103
|
-
this.device.queue.writeBuffer(this.indexBuffer, 0, new Uint32Array(indices))
|
|
104
|
-
|
|
105
|
-
// Verify: segments=50 should give 50*50*6 = 15000 indices = 60000 bytes
|
|
106
|
-
if (this.indexCount !== indices.length) {
|
|
107
|
-
console.warn(`Pool index count mismatch: expected ${indices.length}, got ${this.indexCount}`)
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
private createShader() {
|
|
112
|
-
const shaderModule = this.device.createShaderModule({
|
|
113
|
-
label: "pool shader",
|
|
114
|
-
code: /* wgsl */ `
|
|
115
|
-
struct CameraUniforms {
|
|
116
|
-
view: mat4x4f,
|
|
117
|
-
projection: mat4x4f,
|
|
118
|
-
viewPos: vec3f,
|
|
119
|
-
_padding: f32,
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
struct PoolUniforms {
|
|
123
|
-
time: f32,
|
|
124
|
-
poolY: f32,
|
|
125
|
-
seaColor: vec3f,
|
|
126
|
-
seaLight: vec3f,
|
|
127
|
-
};
|
|
128
|
-
|
|
129
|
-
struct VertexOutput {
|
|
130
|
-
@builtin(position) position: vec4f,
|
|
131
|
-
@location(0) worldPos: vec3f,
|
|
132
|
-
@location(1) uv: vec2f,
|
|
133
|
-
};
|
|
134
|
-
|
|
135
|
-
@group(0) @binding(0) var<uniform> camera: CameraUniforms;
|
|
136
|
-
@group(1) @binding(0) var<uniform> pool: PoolUniforms;
|
|
137
|
-
|
|
138
|
-
// Procedural noise function (simplified)
|
|
139
|
-
fn hash(p: vec2f) -> f32 {
|
|
140
|
-
var p3 = fract(vec3f(p.xyx) * vec3f(443.8975, 397.2973, 491.1871));
|
|
141
|
-
p3 += dot(p3, p3.yzx + 19.19);
|
|
142
|
-
return fract((p3.x + p3.y) * p3.z);
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
fn noise(p: vec2f) -> f32 {
|
|
146
|
-
let i = floor(p);
|
|
147
|
-
var f = fract(p);
|
|
148
|
-
f = f * f * (3.0 - 2.0 * f);
|
|
149
|
-
|
|
150
|
-
let a = hash(i);
|
|
151
|
-
let b = hash(i + vec2f(1.0, 0.0));
|
|
152
|
-
let c = hash(i + vec2f(0.0, 1.0));
|
|
153
|
-
let d = hash(i + vec2f(1.0, 1.0));
|
|
154
|
-
|
|
155
|
-
return mix(mix(a, b, f.x), mix(c, d, f.x), f.y);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// Layered noise for water height (adapted from Shadertoy - matches reference exactly)
|
|
159
|
-
fn waterHeight(uv: vec2f, time: f32) -> f32 {
|
|
160
|
-
var e = 0.0;
|
|
161
|
-
// Match Shadertoy: time*mod(j,.789)*.1 - time*.05
|
|
162
|
-
for (var j = 1.0; j < 6.0; j += 1.0) {
|
|
163
|
-
let timeOffset = time * (j % 0.789) * 0.1 - time * 0.05;
|
|
164
|
-
let scaledUV = uv * (j * 1.789) + j * 159.45 + timeOffset;
|
|
165
|
-
e += noise(scaledUV) / j;
|
|
166
|
-
}
|
|
167
|
-
return e / 6.0;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
// Calculate water normals from height gradients (matches Shadertoy reference)
|
|
171
|
-
fn waterNormals(uv: vec2f, time: f32) -> vec3f {
|
|
172
|
-
// Match Shadertoy: uv.x *= .25 (scale X differently for more wave detail)
|
|
173
|
-
let scaledUV = vec2f(uv.x * 0.25, uv.y);
|
|
174
|
-
let eps = 0.008; // Match Shadertoy epsilon
|
|
175
|
-
let h = waterHeight(scaledUV, time);
|
|
176
|
-
let hx = waterHeight(scaledUV + vec2f(eps, 0.0), time);
|
|
177
|
-
let hz = waterHeight(scaledUV + vec2f(0.0, eps), time);
|
|
178
|
-
|
|
179
|
-
// Match Shadertoy normal calculation exactly
|
|
180
|
-
let n = vec3f(h - hx, 1.0, h - hz);
|
|
181
|
-
return normalize(n);
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
@vertex fn vs(
|
|
185
|
-
@location(0) position: vec3f,
|
|
186
|
-
@location(1) uv: vec2f
|
|
187
|
-
) -> VertexOutput {
|
|
188
|
-
var output: VertexOutput;
|
|
189
|
-
|
|
190
|
-
// Displace Y based on water height - much higher waves
|
|
191
|
-
let time = pool.time;
|
|
192
|
-
// More wave detail - smaller scale for more waves
|
|
193
|
-
// Wave direction: back-left to front-right (both U and V increase)
|
|
194
|
-
let waveUV = uv * 12.0 + vec2f(time * 0.3, time * 0.2); // Front-right direction (both positive)
|
|
195
|
-
let height = waterHeight(waveUV, time) * 2; // Much higher wave amplitude
|
|
196
|
-
let displacedY = position.y + height;
|
|
197
|
-
|
|
198
|
-
let worldPos = vec3f(position.x, displacedY, position.z);
|
|
199
|
-
output.worldPos = worldPos;
|
|
200
|
-
output.uv = uv;
|
|
201
|
-
output.position = camera.projection * camera.view * vec4f(worldPos, 1.0);
|
|
202
|
-
|
|
203
|
-
return output;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
@fragment fn fs(input: VertexOutput) -> @location(0) vec4f {
|
|
207
|
-
let time = pool.time;
|
|
208
|
-
// More wave detail - smaller scale for more waves (matches Shadertoy approach)
|
|
209
|
-
// Wave direction: back-left to front-right (both U and V increase)
|
|
210
|
-
let uv = input.uv * 12.0 + vec2f(time * 0.3, time * 0.2); // Front-right direction (both positive)
|
|
211
|
-
|
|
212
|
-
// Calculate water normals from height gradients (this creates the wave effect)
|
|
213
|
-
let n = waterNormals(uv, time);
|
|
214
|
-
|
|
215
|
-
// View direction
|
|
216
|
-
let viewDir = normalize(camera.viewPos - input.worldPos);
|
|
217
|
-
|
|
218
|
-
// Fresnel effect for reflection (stronger at glancing angles)
|
|
219
|
-
var fresnel = 1.0 - max(dot(n, viewDir), 0.0);
|
|
220
|
-
fresnel = fresnel * fresnel;
|
|
221
|
-
|
|
222
|
-
// Dark night pool - very dark base
|
|
223
|
-
let darkPoolColor = vec3f(0.01, 0.02, 0.05); // Very dark blue-black
|
|
224
|
-
|
|
225
|
-
// Center spotlight effect - reflection-like bright center
|
|
226
|
-
let centerUV = input.uv - vec2f(0.5, 0.5); // Center at (0.5, 0.5)
|
|
227
|
-
let distFromCenter = length(centerUV);
|
|
228
|
-
// Smaller spotlight area with very smooth, subtle gradient
|
|
229
|
-
let spotlightFalloff = 1.0 - smoothstep(0.0, 0.12, distFromCenter); // Smaller radius (0.12)
|
|
230
|
-
|
|
231
|
-
// Reflection-like bright center - brighter, balanced blue
|
|
232
|
-
let spotlightColor = vec3f(0.2, 0.4, 0.6); // Balanced blue
|
|
233
|
-
let spotlightCenter = vec3f(0.5, 0.65, 0.85); // More white center
|
|
234
|
-
|
|
235
|
-
// Very smooth, subtle gradient mix - multiple smoothstep layers for smoother transition
|
|
236
|
-
let falloff1 = smoothstep(0.0, 0.12, distFromCenter); // Outer edge
|
|
237
|
-
let falloff2 = smoothstep(0.0, 0.08, distFromCenter); // Inner edge
|
|
238
|
-
var color = mix(darkPoolColor, spotlightColor, (1.0 - falloff1) * 0.9); // Brighter outer gradient
|
|
239
|
-
color = mix(color, spotlightCenter, (1.0 - falloff2) * (1.0 - falloff2) * 1.0); // Very bright inner reflection
|
|
240
|
-
|
|
241
|
-
// Add reflection-like effect based on view angle and normals
|
|
242
|
-
let reflectionFactor = max(dot(n, vec3f(0.0, 1.0, 0.0)), 0.0); // More reflection when looking down
|
|
243
|
-
let reflectionBrightness = spotlightFalloff * reflectionFactor * 0.5;
|
|
244
|
-
color += spotlightCenter * reflectionBrightness; // Add reflection-like brightness
|
|
245
|
-
|
|
246
|
-
// Wave-based color variation (matches Shadertoy transparency approach)
|
|
247
|
-
// Match Shadertoy: transparency = dot(n, vec3(0.,.2,1.5)) * 12. + 1.5
|
|
248
|
-
var transparency = dot(n, vec3f(0.0, 0.2, 1.5));
|
|
249
|
-
transparency = (transparency * 12.0 + 1.5);
|
|
250
|
-
|
|
251
|
-
// Match Shadertoy color mixing: mix with seaColor and seaLight (brighter, balanced blue)
|
|
252
|
-
let seaColor = vec3f(0.08, 0.18, 0.3); // Balanced blue
|
|
253
|
-
let seaLight = vec3f(0.12, 0.25, 0.45); // Balanced blue
|
|
254
|
-
// Only apply this mixing subtly to avoid green tint
|
|
255
|
-
color = mix(color, seaColor, clamp(transparency, 0.0, 1.0) * 0.3);
|
|
256
|
-
color = mix(color, seaLight, max(0.0, transparency - 1.5) * 0.2);
|
|
257
|
-
|
|
258
|
-
// Enhanced wave-based color variation for more visible waves
|
|
259
|
-
let waveHeight = waterHeight(uv, time);
|
|
260
|
-
let waveContrast = (waveHeight - 0.5) * 0.3; // Amplify wave contrast
|
|
261
|
-
color += vec3f(0.05, 0.08, 0.15) * waveContrast * spotlightFalloff; // Balanced blue wave highlights
|
|
262
|
-
|
|
263
|
-
// Enhanced underwater glow - white/neutral glow with subtle blue tint, much brighter
|
|
264
|
-
let glowIntensity = spotlightFalloff * 0.6 + fresnel * 0.3 + waveHeight * 0.2; // Stronger glow
|
|
265
|
-
let glowColor = vec3f(0.35, 0.35, 0.45); // Brighter glow with subtle blue tint
|
|
266
|
-
color += glowColor * glowIntensity;
|
|
267
|
-
|
|
268
|
-
// Additional subtle white glow around the center, brighter and more white
|
|
269
|
-
let centerGlow = spotlightFalloff * spotlightFalloff * 0.4; // Soft glow falloff
|
|
270
|
-
let whiteGlow = vec3f(0.55, 0.55, 0.6); // Brighter white glow
|
|
271
|
-
color += whiteGlow * centerGlow * 0.7; // Brighter white center glow
|
|
272
|
-
|
|
273
|
-
// Reflection of dark night sky
|
|
274
|
-
let nightSkyColor = vec3f(0.02, 0.04, 0.08); // Very dark night sky
|
|
275
|
-
let reflection = mix(darkPoolColor, nightSkyColor, fresnel * 0.2);
|
|
276
|
-
color = mix(color, reflection, fresnel * 0.3 * (1.0 - spotlightFalloff)); // Less reflection in spotlight
|
|
277
|
-
|
|
278
|
-
// Specular highlights from underwater lights (bokeh-like bright spots)
|
|
279
|
-
let lightDir1 = normalize(vec3f(0.3, -0.4, 0.6));
|
|
280
|
-
let lightDir2 = normalize(vec3f(-0.3, -0.3, 0.7));
|
|
281
|
-
let reflDir1 = reflect(-lightDir1, n);
|
|
282
|
-
let reflDir2 = reflect(-lightDir2, n);
|
|
283
|
-
var specular1 = max(dot(viewDir, reflDir1), 0.0);
|
|
284
|
-
var specular2 = max(dot(viewDir, reflDir2), 0.0);
|
|
285
|
-
specular1 = pow(specular1, 150.0); // Tight, bright highlights
|
|
286
|
-
specular2 = pow(specular2, 180.0);
|
|
287
|
-
// Subtle white/blue highlights (bokeh effect) - darker, less blue
|
|
288
|
-
color += vec3f(0.8, 0.8, 0.9) * specular1 * 1.2 * spotlightFalloff; // Darker white
|
|
289
|
-
color += vec3f(0.4, 0.5, 0.7) * specular2 * 0.9 * spotlightFalloff; // Darker, less blue
|
|
290
|
-
|
|
291
|
-
return vec4f(color, 0.8); // Half transparent water
|
|
292
|
-
}
|
|
293
|
-
`,
|
|
294
|
-
})
|
|
295
|
-
|
|
296
|
-
// Create bind group layout for pool uniforms
|
|
297
|
-
this.bindGroupLayout = this.device.createBindGroupLayout({
|
|
298
|
-
label: "pool bind group layout",
|
|
299
|
-
entries: [
|
|
300
|
-
{
|
|
301
|
-
binding: 0,
|
|
302
|
-
visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT,
|
|
303
|
-
buffer: {
|
|
304
|
-
type: "uniform",
|
|
305
|
-
},
|
|
306
|
-
},
|
|
307
|
-
],
|
|
308
|
-
})
|
|
309
|
-
|
|
310
|
-
// Create render pipeline
|
|
311
|
-
this.pipeline = this.device.createRenderPipeline({
|
|
312
|
-
label: "pool pipeline",
|
|
313
|
-
layout: this.device.createPipelineLayout({
|
|
314
|
-
bindGroupLayouts: [this.cameraBindGroupLayout, this.bindGroupLayout],
|
|
315
|
-
}),
|
|
316
|
-
vertex: {
|
|
317
|
-
module: shaderModule,
|
|
318
|
-
entryPoint: "vs",
|
|
319
|
-
buffers: [
|
|
320
|
-
{
|
|
321
|
-
arrayStride: 5 * 4, // 3 floats (position) + 2 floats (uv)
|
|
322
|
-
attributes: [
|
|
323
|
-
{
|
|
324
|
-
shaderLocation: 0,
|
|
325
|
-
offset: 0,
|
|
326
|
-
format: "float32x3",
|
|
327
|
-
},
|
|
328
|
-
{
|
|
329
|
-
shaderLocation: 1,
|
|
330
|
-
offset: 3 * 4,
|
|
331
|
-
format: "float32x2",
|
|
332
|
-
},
|
|
333
|
-
],
|
|
334
|
-
},
|
|
335
|
-
],
|
|
336
|
-
},
|
|
337
|
-
fragment: {
|
|
338
|
-
module: shaderModule,
|
|
339
|
-
entryPoint: "fs",
|
|
340
|
-
targets: [
|
|
341
|
-
{
|
|
342
|
-
format: "bgra8unorm",
|
|
343
|
-
blend: {
|
|
344
|
-
color: {
|
|
345
|
-
srcFactor: "src-alpha",
|
|
346
|
-
dstFactor: "one-minus-src-alpha",
|
|
347
|
-
},
|
|
348
|
-
alpha: {
|
|
349
|
-
srcFactor: "one",
|
|
350
|
-
dstFactor: "one-minus-src-alpha",
|
|
351
|
-
},
|
|
352
|
-
},
|
|
353
|
-
},
|
|
354
|
-
],
|
|
355
|
-
},
|
|
356
|
-
primitive: {
|
|
357
|
-
topology: "triangle-list",
|
|
358
|
-
cullMode: "none",
|
|
359
|
-
},
|
|
360
|
-
depthStencil: {
|
|
361
|
-
depthWriteEnabled: true,
|
|
362
|
-
depthCompare: "less-equal",
|
|
363
|
-
format: "depth24plus-stencil8",
|
|
364
|
-
},
|
|
365
|
-
multisample: {
|
|
366
|
-
count: 4,
|
|
367
|
-
},
|
|
368
|
-
})
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
private createUniforms() {
|
|
372
|
-
// Create uniform buffer
|
|
373
|
-
// WGSL uniform buffers require 16-byte alignment:
|
|
374
|
-
// time: f32 (4) + poolY (4) + padding (8) = 16 bytes
|
|
375
|
-
// seaColor: vec3f (12) + padding (4) = 16 bytes
|
|
376
|
-
// seaLight: vec3f (12) + padding (4) = 16 bytes
|
|
377
|
-
// Total: 48 bytes
|
|
378
|
-
this.uniformBuffer = this.device.createBuffer({
|
|
379
|
-
label: "pool uniforms",
|
|
380
|
-
size: 48,
|
|
381
|
-
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
|
|
382
|
-
})
|
|
383
|
-
|
|
384
|
-
// Create bind group
|
|
385
|
-
this.bindGroup = this.device.createBindGroup({
|
|
386
|
-
label: "pool bind group",
|
|
387
|
-
layout: this.bindGroupLayout,
|
|
388
|
-
entries: [
|
|
389
|
-
{
|
|
390
|
-
binding: 0,
|
|
391
|
-
resource: {
|
|
392
|
-
buffer: this.uniformBuffer,
|
|
393
|
-
},
|
|
394
|
-
},
|
|
395
|
-
],
|
|
396
|
-
})
|
|
397
|
-
|
|
398
|
-
// Create camera bind group
|
|
399
|
-
this.cameraBindGroup = this.device.createBindGroup({
|
|
400
|
-
label: "pool camera bind group",
|
|
401
|
-
layout: this.cameraBindGroupLayout,
|
|
402
|
-
entries: [
|
|
403
|
-
{
|
|
404
|
-
binding: 0,
|
|
405
|
-
resource: {
|
|
406
|
-
buffer: this.cameraUniformBuffer,
|
|
407
|
-
},
|
|
408
|
-
},
|
|
409
|
-
],
|
|
410
|
-
})
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
public updateUniforms() {
|
|
414
|
-
const time = (performance.now() - this.startTime) / 1000.0
|
|
415
|
-
// WGSL uniform buffer layout (16-byte aligned):
|
|
416
|
-
// offset 0: time (f32), poolY (f32), padding (8 bytes)
|
|
417
|
-
// offset 16: seaColor (vec3f), padding (4 bytes)
|
|
418
|
-
// offset 32: seaLight (vec3f), padding (4 bytes)
|
|
419
|
-
const data = new Float32Array(12)
|
|
420
|
-
data[0] = time
|
|
421
|
-
data[1] = this.y
|
|
422
|
-
// data[2-3] = padding (unused)
|
|
423
|
-
data[4] = this.seaColor.x
|
|
424
|
-
data[5] = this.seaColor.y
|
|
425
|
-
data[6] = this.seaColor.z
|
|
426
|
-
// data[7] = padding (unused)
|
|
427
|
-
data[8] = this.seaLight.x
|
|
428
|
-
data[9] = this.seaLight.y
|
|
429
|
-
data[10] = this.seaLight.z
|
|
430
|
-
// data[11] = padding (unused)
|
|
431
|
-
|
|
432
|
-
this.device.queue.writeBuffer(this.uniformBuffer, 0, data)
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
public render(
|
|
436
|
-
pass: GPURenderPassEncoder,
|
|
437
|
-
restoreBuffers?: {
|
|
438
|
-
vertexBuffer: GPUBuffer
|
|
439
|
-
jointsBuffer: GPUBuffer
|
|
440
|
-
weightsBuffer: GPUBuffer
|
|
441
|
-
indexBuffer: GPUBuffer
|
|
442
|
-
}
|
|
443
|
-
) {
|
|
444
|
-
this.updateUniforms()
|
|
445
|
-
|
|
446
|
-
// Set pool's pipeline and bind groups FIRST
|
|
447
|
-
pass.setPipeline(this.pipeline)
|
|
448
|
-
pass.setBindGroup(0, this.cameraBindGroup)
|
|
449
|
-
pass.setBindGroup(1, this.bindGroup)
|
|
450
|
-
|
|
451
|
-
// IMPORTANT: Set pool's own buffers AFTER setting pipeline
|
|
452
|
-
// Pool only needs vertex buffer 0 (position + UV), but we must keep buffers 1 and 2 set
|
|
453
|
-
// for subsequent model rendering (eyes, hair, etc.)
|
|
454
|
-
pass.setVertexBuffer(0, this.vertexBuffer)
|
|
455
|
-
// Explicitly keep model's buffers 1 and 2 set - pool pipeline doesn't use them but they must stay
|
|
456
|
-
if (restoreBuffers) {
|
|
457
|
-
pass.setVertexBuffer(1, restoreBuffers.jointsBuffer)
|
|
458
|
-
pass.setVertexBuffer(2, restoreBuffers.weightsBuffer)
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
// Set pool's index buffer - this MUST be set to override model's index buffer
|
|
462
|
-
pass.setIndexBuffer(this.indexBuffer, "uint32")
|
|
463
|
-
|
|
464
|
-
// Draw all pool indices starting from 0
|
|
465
|
-
// Parameters: indexCount, instanceCount, firstIndex, baseVertex, firstInstance
|
|
466
|
-
// We always draw from index 0 with all indices
|
|
467
|
-
pass.drawIndexed(this.indexCount, 1, 0, 0, 0)
|
|
468
|
-
|
|
469
|
-
// Restore model's buffers for subsequent rendering (eyes, hair, etc.)
|
|
470
|
-
// This ensures vertex buffer 0 and index buffer are restored to model's buffers
|
|
471
|
-
if (restoreBuffers) {
|
|
472
|
-
pass.setVertexBuffer(0, restoreBuffers.vertexBuffer)
|
|
473
|
-
pass.setVertexBuffer(1, restoreBuffers.jointsBuffer)
|
|
474
|
-
pass.setVertexBuffer(2, restoreBuffers.weightsBuffer)
|
|
475
|
-
pass.setIndexBuffer(restoreBuffers.indexBuffer, "uint32")
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
public dispose() {
|
|
480
|
-
// Buffers will be cleaned up automatically when device is lost
|
|
481
|
-
// But we could explicitly destroy them if needed
|
|
482
|
-
}
|
|
483
|
-
}
|