@bigmistqke/view.gl 0.1.8 → 0.1.9
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/.claude/settings.local.json +2 -1
- package/README.md +30 -0
- package/dev/src/examples/bezier.ts +3 -2
- package/dev/src/examples/game-of-life.ts +13 -30
- package/dev/src/examples/glsl-composition.ts +3 -3
- package/dev/src/examples/grid.ts +20 -14
- package/dev/src/examples/pix-sim.ts +29 -22
- package/dev/src/examples/ray-casting.ts +455 -0
- package/dev/src/examples/sdf.ts +98 -0
- package/dev/src/examples/symbol.ts +2 -2
- package/dev/src/index.ts +12 -11
- package/dev/src/utils.ts +18 -4
- package/dist/index.d.mts +2 -2
- package/dist/tag.d.mts +21 -4
- package/dist/tag.mjs +78 -39
- package/dist/tag.mjs.map +1 -1
- package/dist/{types-Bp9VAVe3.d.mts → types-DaV9McmR.d.mts} +18 -14
- package/package.json +1 -1
- package/src/tag.ts +111 -44
- package/src/types.ts +36 -22
- package/test/tag.test.ts +462 -0
package/README.md
CHANGED
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
- 📝 [attribute[kind](name, options?)](#-attributekindname-options)
|
|
33
33
|
- 🔗 [interleave(name, layout, options?)](#-interleavename-layout-options)
|
|
34
34
|
- ⚙️ [compile(gl, vertex, fragment)](#️-compilegl-vertex-fragment)
|
|
35
|
+
- 🔄 [compile.toQuad(gl, fragment)](#-compiletoquadgl-fragment-options)
|
|
35
36
|
- 🔍 [compile.toString(shader)](#-compiletostringshader)
|
|
36
37
|
- 📋 [compile.toSchema(shader)](#-compiletoschemaschader)
|
|
37
38
|
- 🛠️ [Utils](#️-utils)
|
|
@@ -550,6 +551,35 @@ const vertexShader = glsl`
|
|
|
550
551
|
|
|
551
552
|
Compiles shaders to a `WebGLProgram` and extracts typesafe schema and view.
|
|
552
553
|
|
|
554
|
+
### 🔄 compile.toQuad(gl, fragment, options?)
|
|
555
|
+
|
|
556
|
+
Convenient helper for fullscreen quad rendering with fragment shaders. Automatically creates a vertex shader with a quad geometry and handles vertex buffer setup.
|
|
557
|
+
|
|
558
|
+
```typescript
|
|
559
|
+
const { program, schema, view } = compile.toQuad(gl, fragmentShader)
|
|
560
|
+
|
|
561
|
+
// Ready to render - no vertex setup required
|
|
562
|
+
view.uniforms.time.set(performance.now())
|
|
563
|
+
gl.drawArrays(gl.TRIANGLES, 0, 6)
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
**Perfect for:**
|
|
567
|
+
- Fragment shader effects (ray-marching, post-processing, etc.)
|
|
568
|
+
- Fullscreen compute-style shaders
|
|
569
|
+
- Quick prototyping and experimentation
|
|
570
|
+
|
|
571
|
+
**Generated vertex shader:**
|
|
572
|
+
- Creates `a_quad` attribute automatically
|
|
573
|
+
- Outputs `uv` varying (same as vertex position: `[-1,1]` range)
|
|
574
|
+
- Sets up clip-space quad covering the entire screen
|
|
575
|
+
|
|
576
|
+
**Parameters:**
|
|
577
|
+
- `gl`: WebGL rendering context
|
|
578
|
+
- `fragment`: Fragment shader with embedded resources
|
|
579
|
+
- `options`: Optional compilation options (same as `compile()`)
|
|
580
|
+
|
|
581
|
+
**Returns:** Same as `compile()` with pre-configured quad rendering
|
|
582
|
+
|
|
553
583
|
```typescript
|
|
554
584
|
const { program, schema, view, vertex, fragment } = compile(gl, vertexShader, fragmentShader)
|
|
555
585
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { attribute, compile, glsl, interleave, uniform } from 'view.gl/tag'
|
|
2
|
-
import {
|
|
2
|
+
import { cursor, dom } from '../utils'
|
|
3
3
|
|
|
4
4
|
// Create canvas and WebGL context
|
|
5
|
-
const canvas =
|
|
5
|
+
const canvas = dom('canvas', { parentElement: document.body })
|
|
6
6
|
canvas.width = window.innerWidth
|
|
7
7
|
canvas.height = window.innerHeight
|
|
8
8
|
const gl = canvas.getContext('webgl2', { antialias: true })!
|
|
@@ -357,6 +357,7 @@ updateLineData()
|
|
|
357
357
|
/**********************************************************************************/
|
|
358
358
|
|
|
359
359
|
function render() {
|
|
360
|
+
console.log('render!')
|
|
360
361
|
// Update canvas size if window was resized
|
|
361
362
|
if (canvas.width !== window.innerWidth || canvas.height !== window.innerHeight) {
|
|
362
363
|
canvas.width = window.innerWidth
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { FramebufferDefinition } from 'src/types'
|
|
2
2
|
import { createFramebuffer } from 'src/utils'
|
|
3
|
-
import {
|
|
3
|
+
import { compile, glsl, uniform } from 'view.gl/tag'
|
|
4
|
+
import { dom } from '../utils'
|
|
4
5
|
|
|
5
6
|
let playing = false
|
|
6
7
|
|
|
7
|
-
const canvas =
|
|
8
|
+
const canvas = dom('canvas')
|
|
8
9
|
const gl = canvas.getContext('webgl', { antialias: false })!
|
|
9
10
|
if (!gl) {
|
|
10
11
|
throw new Error('WebGL not supported')
|
|
@@ -85,18 +86,6 @@ canvas.height = 256 * 2
|
|
|
85
86
|
const WIDTH = canvas.width
|
|
86
87
|
const HEIGHT = canvas.height
|
|
87
88
|
|
|
88
|
-
// --- Shaders ---
|
|
89
|
-
|
|
90
|
-
// Vertex shader (simple fullscreen quad)
|
|
91
|
-
const vertex = glsl`
|
|
92
|
-
${attribute.vec2('a_vertex')}
|
|
93
|
-
varying vec2 v_uv;
|
|
94
|
-
void main() {
|
|
95
|
-
v_uv = a_vertex * 0.5 + 0.5;
|
|
96
|
-
gl_Position = vec4(a_vertex, 0, 1);
|
|
97
|
-
}
|
|
98
|
-
`
|
|
99
|
-
|
|
100
89
|
// Fragment shader for Game of Life step
|
|
101
90
|
// We read from u_texture the current state
|
|
102
91
|
// Alive cells are red > 0.5, dead < 0.5
|
|
@@ -115,15 +104,17 @@ const stepFragment = glsl`
|
|
|
115
104
|
}
|
|
116
105
|
|
|
117
106
|
void main() {
|
|
107
|
+
vec2 uv = v_uv * 0.5 + 0.5;
|
|
108
|
+
|
|
118
109
|
int sum = 0;
|
|
119
110
|
// Iterate neighbors -1,0,1
|
|
120
111
|
for (int y = -1; y <= 1; y++) {
|
|
121
112
|
for (int x = -1; x <= 1; x++) {
|
|
122
113
|
if (x == 0 && y == 0) continue;
|
|
123
|
-
sum += getCell(
|
|
114
|
+
sum += getCell(uv + vec2(float(x), float(y)) * u_texelSize);
|
|
124
115
|
}
|
|
125
116
|
}
|
|
126
|
-
int current = getCell(
|
|
117
|
+
int current = getCell(uv);
|
|
127
118
|
|
|
128
119
|
int nextState = 0;
|
|
129
120
|
if (current == 1) {
|
|
@@ -144,22 +135,14 @@ const renderFragment = glsl`
|
|
|
144
135
|
${uniform.sampler2D('u_texture')}
|
|
145
136
|
|
|
146
137
|
void main() {
|
|
147
|
-
|
|
138
|
+
vec2 uv = v_uv * 0.5 + 0.5;
|
|
139
|
+
float cell = texture2D(u_texture, uv).r;
|
|
148
140
|
gl_FragColor = vec4(vec3(cell), 1.0);
|
|
149
141
|
}`
|
|
150
142
|
|
|
151
143
|
// --- Create programs ---
|
|
152
|
-
const { program: stepProgram, view: stepView } = compile(gl,
|
|
153
|
-
const { program: renderProgram, view: renderView } = compile(gl,
|
|
154
|
-
attributes: {
|
|
155
|
-
a_vertex: {
|
|
156
|
-
buffer: stepView.attributes.a_vertex.buffer,
|
|
157
|
-
},
|
|
158
|
-
},
|
|
159
|
-
})
|
|
160
|
-
|
|
161
|
-
// Setup quad
|
|
162
|
-
stepView.attributes.a_vertex.set(new Float32Array([-1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1]))
|
|
144
|
+
const { program: stepProgram, view: stepView } = compile.toQuad(gl, stepFragment)
|
|
145
|
+
const { program: renderProgram, view: renderView } = compile.toQuad(gl, renderFragment)
|
|
163
146
|
|
|
164
147
|
// Initialize two framebuffers (with their respective texture) for ping-pong
|
|
165
148
|
const framebufferOptions = {
|
|
@@ -210,7 +193,7 @@ function step() {
|
|
|
210
193
|
stepView.uniforms.u_texture.set(0)
|
|
211
194
|
stepView.uniforms.u_texelSize.set(1 / WIDTH, 1 / HEIGHT)
|
|
212
195
|
|
|
213
|
-
stepView.attributes.
|
|
196
|
+
stepView.attributes.a_quad.bind()
|
|
214
197
|
|
|
215
198
|
gl.drawArrays(gl.TRIANGLES, 0, 6)
|
|
216
199
|
}
|
|
@@ -225,7 +208,7 @@ function render() {
|
|
|
225
208
|
gl.bindTexture(gl.TEXTURE_2D, write.texture)
|
|
226
209
|
renderView.uniforms.u_texture.set(0)
|
|
227
210
|
|
|
228
|
-
renderView.attributes.
|
|
211
|
+
renderView.attributes.a_quad.bind()
|
|
229
212
|
|
|
230
213
|
gl.drawArrays(gl.TRIANGLES, 0, 6)
|
|
231
214
|
}
|
|
@@ -8,13 +8,13 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { attribute, compile, glsl, uniform } from 'view.gl/tag'
|
|
11
|
-
import {
|
|
11
|
+
import { dom } from '../utils'
|
|
12
12
|
|
|
13
|
-
const canvas =
|
|
13
|
+
const canvas = dom('canvas', {
|
|
14
14
|
width: window.innerWidth,
|
|
15
15
|
height: window.innerHeight,
|
|
16
16
|
parentElement: document.body,
|
|
17
|
-
style:
|
|
17
|
+
style: { width: '100%' },
|
|
18
18
|
})
|
|
19
19
|
|
|
20
20
|
const gl = canvas.getContext('webgl2')!
|
package/dev/src/examples/grid.ts
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { attribute, compile, glsl, uniform } from 'view.gl/tag'
|
|
2
|
-
import {
|
|
2
|
+
import { cursor, dom } from '../utils'
|
|
3
3
|
|
|
4
|
-
const canvas =
|
|
4
|
+
const canvas = dom('canvas', {
|
|
5
|
+
width: window.innerWidth,
|
|
6
|
+
height: window.innerHeight,
|
|
7
|
+
parentElement: document.body,
|
|
8
|
+
})
|
|
5
9
|
const gl = canvas.getContext('webgl2', { antialias: false })!
|
|
6
10
|
|
|
7
11
|
if (!gl) {
|
|
@@ -14,7 +18,7 @@ const state = {
|
|
|
14
18
|
elements: [] as Array<ReturnType<typeof createGridElement>>,
|
|
15
19
|
}
|
|
16
20
|
|
|
17
|
-
const container =
|
|
21
|
+
const container = dom('div', { parentElement: document.body })
|
|
18
22
|
state.elements.push(createGridElement({ x: 100, y: 100 }))
|
|
19
23
|
|
|
20
24
|
/**********************************************************************************/
|
|
@@ -29,28 +33,30 @@ function createGridElement(position = { x: 0, y: 0 }) {
|
|
|
29
33
|
y: (position.y - state.position.y) / state.scale,
|
|
30
34
|
}
|
|
31
35
|
|
|
32
|
-
const
|
|
36
|
+
const div = dom('div', {
|
|
33
37
|
innerText: 'HALLO WORLD',
|
|
34
|
-
style:
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
38
|
+
style: {
|
|
39
|
+
position: 'fixed',
|
|
40
|
+
zIndex: '10',
|
|
41
|
+
left: '0px',
|
|
42
|
+
top: '0px',
|
|
43
|
+
transformOrigin: 'top left',
|
|
44
|
+
background: 'white',
|
|
45
|
+
padding: '2px',
|
|
46
|
+
borderRadius: '2px',
|
|
47
|
+
},
|
|
42
48
|
parentElement: container,
|
|
43
49
|
})
|
|
44
50
|
|
|
45
51
|
function update() {
|
|
46
|
-
|
|
52
|
+
div.style.transform = `translate3d(${state.position.x + position.x * state.scale}px,${
|
|
47
53
|
state.position.y + position.y * state.scale
|
|
48
54
|
}px,0) scale(${state.scale})`
|
|
49
55
|
}
|
|
50
56
|
update()
|
|
51
57
|
|
|
52
58
|
return {
|
|
53
|
-
element,
|
|
59
|
+
element: div,
|
|
54
60
|
position,
|
|
55
61
|
update,
|
|
56
62
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { FramebufferDefinition } from 'src/types'
|
|
2
2
|
import { createFramebuffer } from 'src/utils'
|
|
3
3
|
import { attribute, compile, glsl, uniform } from 'view.gl/tag'
|
|
4
|
-
import {
|
|
4
|
+
import { dom } from '../utils'
|
|
5
5
|
|
|
6
6
|
export const MATERIALS = {
|
|
7
7
|
SAND: {
|
|
@@ -24,36 +24,41 @@ let playing = false
|
|
|
24
24
|
let currentMaterial: (typeof MATERIALS)[keyof typeof MATERIALS]['id'] = MATERIALS.SAND.id
|
|
25
25
|
|
|
26
26
|
// Create UI container
|
|
27
|
-
const container =
|
|
28
|
-
style:
|
|
27
|
+
const container = dom('div', {
|
|
28
|
+
style: {
|
|
29
|
+
display: 'flex',
|
|
30
|
+
flexDirection: 'column',
|
|
31
|
+
alignItems: 'center',
|
|
32
|
+
gap: '10px',
|
|
33
|
+
},
|
|
29
34
|
parentElement: document.body,
|
|
30
35
|
})
|
|
31
36
|
|
|
32
37
|
// Create material buttons
|
|
33
|
-
const buttonContainer =
|
|
38
|
+
const buttonContainer = dom('div', {
|
|
34
39
|
style: 'display: flex; gap: 10px; margin-bottom: 10px;',
|
|
35
40
|
parentElement: container,
|
|
36
41
|
})
|
|
37
42
|
|
|
38
43
|
Object.entries(MATERIALS).forEach(([name, material]) => {
|
|
39
|
-
|
|
40
|
-
textContent: name.toLowerCase(),
|
|
44
|
+
dom('button', {
|
|
41
45
|
'data-name': name,
|
|
42
46
|
'data-id': material.id.toString(),
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
47
|
+
textContent: name.toLowerCase(),
|
|
48
|
+
style: {
|
|
49
|
+
padding: '5px 10px',
|
|
50
|
+
fontSize: '14px',
|
|
51
|
+
fontFamily: 'arial',
|
|
52
|
+
cursor: 'pointer',
|
|
53
|
+
color: 'black',
|
|
54
|
+
background:
|
|
55
|
+
currentMaterial === material.id
|
|
56
|
+
? `rgb(${material.color.map(c => Math.floor(c * 255)).join(',')})`
|
|
57
|
+
: `rgba(${material.color.map(c => Math.floor(c * 255)).join(',')}, 0.75)`,
|
|
58
|
+
borderColor: name === 'SAND' ? 'black' : 'white',
|
|
59
|
+
border: `2px solid rgb(${material.color.map(c => Math.floor(c * 255)).join(',')})`,
|
|
60
|
+
borderRadius: '2px',
|
|
61
|
+
},
|
|
57
62
|
onclick() {
|
|
58
63
|
currentMaterial = material.id
|
|
59
64
|
// Update all button borders
|
|
@@ -79,7 +84,7 @@ function spray() {
|
|
|
79
84
|
return Math.floor(Math.random() * 8) * (Math.random() < 0.5 ? 1 : -1)
|
|
80
85
|
}
|
|
81
86
|
|
|
82
|
-
const canvas =
|
|
87
|
+
const canvas = dom('canvas', {
|
|
83
88
|
parentElement: container,
|
|
84
89
|
onmousedown() {
|
|
85
90
|
const rect = canvas.getBoundingClientRect()
|
|
@@ -462,7 +467,9 @@ void main() {
|
|
|
462
467
|
// --- Create programs ---
|
|
463
468
|
const { program: stepProgram, view: stepView } = compile(gl, vertex, stepFragment)
|
|
464
469
|
const { program: renderProgram, view: renderView } = compile(gl, vertex, renderFragment, {
|
|
465
|
-
|
|
470
|
+
schema: {
|
|
471
|
+
attributes: { a_vertex: { buffer: stepView.attributes.a_vertex.buffer } },
|
|
472
|
+
},
|
|
466
473
|
})
|
|
467
474
|
|
|
468
475
|
gl.useProgram(renderProgram)
|