glre 0.20.0 → 0.22.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/README.md +150 -80
- package/dist/index.d.ts +442 -236
- package/dist/index.js +46 -13
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +46 -13
- package/dist/index.mjs.map +1 -1
- package/dist/native.d.ts +7 -530
- package/dist/native.js +46 -13
- package/dist/native.js.map +1 -1
- package/dist/native.mjs +46 -13
- package/dist/native.mjs.map +1 -1
- package/dist/react.d.ts +7 -478
- package/dist/react.js +46 -13
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +46 -13
- package/dist/react.mjs.map +1 -1
- package/dist/solid.d.ts +7 -424
- package/dist/solid.js +46 -13
- package/dist/solid.js.map +1 -1
- package/dist/solid.mjs +46 -13
- package/dist/solid.mjs.map +1 -1
- package/package.json +26 -61
- package/src/code/glsl.ts +186 -0
- package/src/code/wgsl.ts +170 -0
- package/src/index.ts +105 -0
- package/src/native.ts +24 -0
- package/src/node/cache.ts +67 -0
- package/src/node/const.ts +147 -0
- package/src/node/conv.ts +122 -0
- package/src/node/index.ts +96 -0
- package/src/node/node.ts +114 -0
- package/src/node/types.ts +101 -0
- package/src/node/uniform.ts +99 -0
- package/src/react.ts +18 -0
- package/src/solid.ts +15 -0
- package/src/types.ts +90 -0
- package/src/utils.ts +53 -0
- package/src/webgl/buffer.ts +78 -0
- package/src/webgl/index.ts +79 -0
- package/src/webgl/program.ts +61 -0
- package/src/webgl/shader.ts +60 -0
- package/src/webgl/texture.ts +93 -0
- package/src/webgpu/buffer.ts +96 -0
- package/src/webgpu/device.ts +91 -0
- package/src/webgpu/index.ts +40 -0
- package/src/webgpu/pipeline.ts +94 -0
- package/src/webgpu/texture.ts +139 -0
- package/dist/types-2792569d.d.ts +0 -7
package/src/solid.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { createGL, isGL } from './index'
|
|
2
|
+
import type { GL } from './types'
|
|
3
|
+
export * from './index'
|
|
4
|
+
|
|
5
|
+
export const onGL = (props?: Partial<GL>) => {
|
|
6
|
+
const gl = isGL(props) ? props : createGL(props)
|
|
7
|
+
gl.ref = (el: HTMLCanvasElement | null) => {
|
|
8
|
+
if (el) {
|
|
9
|
+
gl.el = el
|
|
10
|
+
gl.gl = el.getContext('webgl2')
|
|
11
|
+
gl.mount()
|
|
12
|
+
} else gl.clean()
|
|
13
|
+
}
|
|
14
|
+
return gl
|
|
15
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { Nested, EventState } from 'reev'
|
|
2
|
+
import { X } from './node'
|
|
3
|
+
import type { Fun, Queue, Frame } from 'refr'
|
|
4
|
+
export type { Fun, Queue, Frame }
|
|
5
|
+
export type Uniform = number | number[]
|
|
6
|
+
export type Attribute = number[]
|
|
7
|
+
export type Attributes = Record<string, Attribute>
|
|
8
|
+
export type Uniforms = Record<string, Uniform>
|
|
9
|
+
export type PrecisionMode = 'highp' | 'mediump' | 'lowp'
|
|
10
|
+
|
|
11
|
+
export type GLClearMode = 'COLOR_BUFFER_BIT' | 'DEPTH_BUFFER_BIT' | 'STENCIL_BUFFER_BIT'
|
|
12
|
+
|
|
13
|
+
export type GLDrawMode =
|
|
14
|
+
| 'POINTS'
|
|
15
|
+
| 'LINE_STRIP'
|
|
16
|
+
| 'LINE_LOOP'
|
|
17
|
+
| 'LINES'
|
|
18
|
+
| 'TRIANGLE_STRIP'
|
|
19
|
+
| 'TRIANGLE_FAN'
|
|
20
|
+
| 'TRIANGLES'
|
|
21
|
+
|
|
22
|
+
export type GLDrawType = 'UNSIGNED_BYTE' | 'UNSIGNED_SHORT' | 'UNSIGNED_INT'
|
|
23
|
+
|
|
24
|
+
export type GL = EventState<{
|
|
25
|
+
/**
|
|
26
|
+
* initial value
|
|
27
|
+
*/
|
|
28
|
+
isNative: boolean
|
|
29
|
+
isWebGL: boolean
|
|
30
|
+
isLoop: boolean
|
|
31
|
+
isGL: true
|
|
32
|
+
width: number
|
|
33
|
+
height: number
|
|
34
|
+
size: [number, number]
|
|
35
|
+
mouse: [number, number]
|
|
36
|
+
count: number
|
|
37
|
+
vs: string | X
|
|
38
|
+
fs: string | X
|
|
39
|
+
vert: string | X
|
|
40
|
+
frag: string | X
|
|
41
|
+
vertex: string | X
|
|
42
|
+
fragment: string | X
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* for webgl
|
|
46
|
+
*/
|
|
47
|
+
int: PrecisionMode
|
|
48
|
+
float: PrecisionMode
|
|
49
|
+
sampler2D: PrecisionMode
|
|
50
|
+
samplerCube: PrecisionMode
|
|
51
|
+
lastActiveUnit: number
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* core state
|
|
55
|
+
*/
|
|
56
|
+
gl: any
|
|
57
|
+
pg: any
|
|
58
|
+
el: any
|
|
59
|
+
queue: Queue
|
|
60
|
+
frame: Frame
|
|
61
|
+
stride: Nested<number>
|
|
62
|
+
location: Nested<any> // @TODO Nested<(key: string, value: number: number[], ibo: number[]) => number>
|
|
63
|
+
activeUnit: Nested<number>
|
|
64
|
+
default: any
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* events
|
|
68
|
+
*/
|
|
69
|
+
ref?: any
|
|
70
|
+
init(): void
|
|
71
|
+
loop(): void
|
|
72
|
+
mount(): void
|
|
73
|
+
clean(): void
|
|
74
|
+
render(): void
|
|
75
|
+
resize(e?: Event): void
|
|
76
|
+
mousemove(e: Event): void
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* setter
|
|
80
|
+
*/
|
|
81
|
+
_uniform?(key: string, value: Uniform, isMatrix?: boolean): GL
|
|
82
|
+
uniform(key: string, value: Uniform, isMatrix?: boolean): GL
|
|
83
|
+
uniform(target: { [key: string]: Uniform }): GL
|
|
84
|
+
_texture?(key: string, value: string): GL
|
|
85
|
+
texture(key: string, value: string): GL
|
|
86
|
+
texture(target: { [key: string]: string }): GL
|
|
87
|
+
_attribute?(key: string, value: Attribute, iboValue?: Attribute): GL
|
|
88
|
+
attribute(key: string, value: Attribute, iboValue?: Attribute): GL
|
|
89
|
+
attribute(target: { [key: string]: Attribute }): GL
|
|
90
|
+
}>
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export const is = {
|
|
2
|
+
arr: Array.isArray,
|
|
3
|
+
bol: (a: unknown): a is boolean => typeof a === 'boolean',
|
|
4
|
+
str: (a: unknown): a is string => typeof a === 'string',
|
|
5
|
+
num: (a: unknown): a is number => typeof a === 'number',
|
|
6
|
+
int: (a: unknown): a is number => Number.isInteger(a),
|
|
7
|
+
fun: (a: unknown): a is Function => typeof a === 'function',
|
|
8
|
+
und: (a: unknown): a is undefined => typeof a === 'undefined',
|
|
9
|
+
nul: (a: unknown): a is null => a === null,
|
|
10
|
+
set: (a: unknown): a is Set<unknown> => a instanceof Set,
|
|
11
|
+
map: (a: unknown): a is Map<unknown, unknown> => a instanceof Map,
|
|
12
|
+
obj: (a: unknown): a is object => !!a && a.constructor.name === 'Object',
|
|
13
|
+
nan: (a: unknown): a is number => typeof a === 'number' && Number.isNaN(a),
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const isServer = () => {
|
|
17
|
+
return typeof window === 'undefined'
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const isWebGPUSupported = () => {
|
|
21
|
+
if (isServer()) return false
|
|
22
|
+
return 'gpu' in navigator
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* each
|
|
27
|
+
*/
|
|
28
|
+
type EachFn<Value, Key, This> = (this: This, value: Value, key: Key) => void
|
|
29
|
+
type Eachable<Value = any, Key = any, This = any> = {
|
|
30
|
+
forEach(cb: EachFn<Value, Key, This>, ctx?: This): void
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const each = <Value, Key, This>(obj: Eachable<Value, Key, This>, fn: EachFn<Value, Key, This>) => obj.forEach(fn)
|
|
34
|
+
|
|
35
|
+
export const flush = <Value extends Function, Key, This>(obj: Eachable<Value, Key, This>, ...args: any[]) => {
|
|
36
|
+
each(obj, (f) => f(...args))
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* other
|
|
41
|
+
*/
|
|
42
|
+
export const replace = (x = '', from = '_', to = '/') => x.split(from).join(to)
|
|
43
|
+
export const ext = (src = '.pdf') => src.split('.').pop()?.toLowerCase() ?? ''
|
|
44
|
+
export const fig = (x = 0) => `${x}`.split('.')[1]?.length ?? 0
|
|
45
|
+
export const dig = (x = 0) => `${x}`.split('.')[0]?.length - (x < 0 ? 1 : 0)
|
|
46
|
+
export const sig = (value = 0, digit = -2) => {
|
|
47
|
+
digit *= -1
|
|
48
|
+
digit = Math.pow(10, digit)
|
|
49
|
+
value *= digit
|
|
50
|
+
value = Math.round(value)
|
|
51
|
+
value /= digit
|
|
52
|
+
return value
|
|
53
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// WebGLバッファー作成と管理
|
|
2
|
+
export const createVbo = (c: WebGLRenderingContext, data: number[]) => {
|
|
3
|
+
const buffer = c.createBuffer()
|
|
4
|
+
if (!buffer) throw new Error('Failed to create VBO')
|
|
5
|
+
c.bindBuffer(c.ARRAY_BUFFER, buffer)
|
|
6
|
+
c.bufferData(c.ARRAY_BUFFER, new Float32Array(data), c.STATIC_DRAW)
|
|
7
|
+
c.bindBuffer(c.ARRAY_BUFFER, null)
|
|
8
|
+
return buffer
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// インデックスバッファーオブジェクト作成
|
|
12
|
+
export const createIbo = (c: WebGLRenderingContext, data: number[]) => {
|
|
13
|
+
const buffer = c.createBuffer()
|
|
14
|
+
if (!buffer) throw new Error('Failed to create IBO')
|
|
15
|
+
c.bindBuffer(c.ELEMENT_ARRAY_BUFFER, buffer)
|
|
16
|
+
c.bufferData(
|
|
17
|
+
c.ELEMENT_ARRAY_BUFFER,
|
|
18
|
+
new Int16Array(data),
|
|
19
|
+
c.STATIC_DRAW
|
|
20
|
+
)
|
|
21
|
+
c.bindBuffer(c.ELEMENT_ARRAY_BUFFER, null)
|
|
22
|
+
return buffer
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// アトリビュート設定
|
|
26
|
+
export const createAttribute = (
|
|
27
|
+
c: WebGLRenderingContext,
|
|
28
|
+
stride: number,
|
|
29
|
+
location: number,
|
|
30
|
+
vbo: WebGLBuffer,
|
|
31
|
+
ibo?: WebGLBuffer
|
|
32
|
+
) => {
|
|
33
|
+
c.bindBuffer(c.ARRAY_BUFFER, vbo)
|
|
34
|
+
c.enableVertexAttribArray(location)
|
|
35
|
+
c.vertexAttribPointer(location, stride, c.FLOAT, false, 0, 0)
|
|
36
|
+
if (ibo) c.bindBuffer(c.ELEMENT_ARRAY_BUFFER, ibo)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// バッファーの削除
|
|
40
|
+
export const deleteBuffer = (c: WebGLRenderingContext, buffer: WebGLBuffer) => {
|
|
41
|
+
c.deleteBuffer(buffer)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// 頂点ストライドを計算
|
|
45
|
+
export const vertexStride = (
|
|
46
|
+
count: number,
|
|
47
|
+
value: number[],
|
|
48
|
+
iboValue?: number[]
|
|
49
|
+
) => {
|
|
50
|
+
if (iboValue) count = Math.max(...iboValue) + 1
|
|
51
|
+
const stride = value.length / count
|
|
52
|
+
if (stride !== Math.floor(stride))
|
|
53
|
+
console.warn(`Vertex Stride Error: count ${count} is mismatch`)
|
|
54
|
+
return Math.floor(stride)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// バッファーデータの更新
|
|
58
|
+
export const updateVbo = (
|
|
59
|
+
c: WebGLRenderingContext,
|
|
60
|
+
buffer: WebGLBuffer,
|
|
61
|
+
data: number[],
|
|
62
|
+
usage = c.STATIC_DRAW
|
|
63
|
+
) => {
|
|
64
|
+
c.bindBuffer(c.ARRAY_BUFFER, buffer)
|
|
65
|
+
c.bufferData(c.ARRAY_BUFFER, new Float32Array(data), usage)
|
|
66
|
+
c.bindBuffer(c.ARRAY_BUFFER, null)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export const updateIbo = (
|
|
70
|
+
c: WebGLRenderingContext,
|
|
71
|
+
buffer: WebGLBuffer,
|
|
72
|
+
data: number[],
|
|
73
|
+
usage = c.STATIC_DRAW
|
|
74
|
+
) => {
|
|
75
|
+
c.bindBuffer(c.ELEMENT_ARRAY_BUFFER, buffer)
|
|
76
|
+
c.bufferData(c.ELEMENT_ARRAY_BUFFER, new Int16Array(data), usage)
|
|
77
|
+
c.bindBuffer(c.ELEMENT_ARRAY_BUFFER, null)
|
|
78
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { nested } from 'reev'
|
|
2
|
+
import { createAttribute, createIbo, createVbo, vertexStride } from './buffer'
|
|
3
|
+
import { createProgram, deleteProgram, getUniformType } from './program'
|
|
4
|
+
import { createFragmentShader, createVertexShader } from './shader'
|
|
5
|
+
import { activeTexture, createTexture } from './texture'
|
|
6
|
+
import { glsl } from '../code/glsl'
|
|
7
|
+
import { is } from '../utils'
|
|
8
|
+
import type { X } from '../node'
|
|
9
|
+
import type { GL } from '../types'
|
|
10
|
+
export * from './buffer'
|
|
11
|
+
export * from './program'
|
|
12
|
+
export * from './shader'
|
|
13
|
+
export * from './texture'
|
|
14
|
+
|
|
15
|
+
const a_position = [-1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1]
|
|
16
|
+
|
|
17
|
+
export const webgl = (gl: GL) => {
|
|
18
|
+
gl('init', () => {
|
|
19
|
+
const c = gl.gl
|
|
20
|
+
let vs = gl.vs || gl.vert || gl.vertex
|
|
21
|
+
let fs = gl.fs || gl.frag || gl.fragment
|
|
22
|
+
if (is.obj(fs)) fs = glsl(fs as X)
|
|
23
|
+
if (is.obj(vs)) vs = glsl(vs as X)
|
|
24
|
+
if (gl.count === 6) gl.attribute({ a_position })
|
|
25
|
+
gl.pg = createProgram(c, createVertexShader(c, vs), createFragmentShader(c, fs))
|
|
26
|
+
gl.location = nested((key, isAttribute = false) => {
|
|
27
|
+
return isAttribute ? c.getAttribLocation(gl.pg, key) : c.getUniformLocation(gl.pg, key)
|
|
28
|
+
})
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
gl('clean', () => {
|
|
32
|
+
const c = gl.gl
|
|
33
|
+
deleteProgram(c, gl.pg)
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
gl('render', () => {
|
|
37
|
+
const c = gl.gl
|
|
38
|
+
c.useProgram(gl.pg)
|
|
39
|
+
gl.queue.flush()
|
|
40
|
+
c.clear(c.COLOR_BUFFER_BIT)
|
|
41
|
+
c.viewport(0, 0, ...gl.size)
|
|
42
|
+
c.drawArrays(c.TRIANGLES, 0, gl.count)
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
gl('_attribute', (key = '', value: number[], iboValue: number[]) => {
|
|
46
|
+
const c = gl.gl
|
|
47
|
+
const n = gl.count
|
|
48
|
+
const loc = gl.location(key, true)
|
|
49
|
+
const vbo = createVbo(c, value)
|
|
50
|
+
const ibo = createIbo(c, iboValue)
|
|
51
|
+
const stride = vertexStride(n, value, iboValue)
|
|
52
|
+
createAttribute(c, stride, loc, vbo, ibo)
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
gl('_uniform', (key: string, value = 0, isMatrix = false) => {
|
|
56
|
+
const type = getUniformType(value, isMatrix)
|
|
57
|
+
const c = gl.gl
|
|
58
|
+
const loc = gl.location(key)
|
|
59
|
+
if (isMatrix) c[type](loc, false, value)
|
|
60
|
+
else c[type](loc, value)
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
const _loadFn = (image: HTMLImageElement) => {
|
|
64
|
+
const loc = gl.location(image.alt)
|
|
65
|
+
const unit = gl.activeUnit(image.alt)
|
|
66
|
+
const tex = createTexture(gl.gl, image)
|
|
67
|
+
activeTexture(gl.gl, loc, unit, tex)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
gl('_texture', (alt: string, src: string) => {
|
|
71
|
+
const image = new Image()
|
|
72
|
+
image.addEventListener('load', _loadFn.bind(null, image), false)
|
|
73
|
+
Object.assign(image, { src, alt, crossOrigin: 'anonymous' })
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
let _activeUnit = 0
|
|
77
|
+
gl.activeUnit = nested(() => _activeUnit++)
|
|
78
|
+
return gl
|
|
79
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// WebGLプログラム作成と管理
|
|
2
|
+
export const createProgram = (
|
|
3
|
+
c: WebGLRenderingContext,
|
|
4
|
+
vs: WebGLShader,
|
|
5
|
+
fs: WebGLShader
|
|
6
|
+
) => {
|
|
7
|
+
const pg = c.createProgram()
|
|
8
|
+
if (!pg) throw new Error('Failed to create pg')
|
|
9
|
+
c.attachShader(pg, vs)
|
|
10
|
+
c.attachShader(pg, fs)
|
|
11
|
+
c.linkProgram(pg)
|
|
12
|
+
if (c.getProgramParameter(pg, c.LINK_STATUS)) return pg
|
|
13
|
+
const error = c.getProgramInfoLog(pg)
|
|
14
|
+
c.deleteProgram(pg)
|
|
15
|
+
throw new Error(`Could not link pg: ${error}`)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// プログラムの削除
|
|
19
|
+
export const deleteProgram = (c: WebGLRenderingContext, pg: WebGLProgram) => {
|
|
20
|
+
c.deleteProgram(pg)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// プログラムの情報を取得
|
|
24
|
+
export const getProgramInfo = (c: WebGLRenderingContext, pg: WebGLProgram) => {
|
|
25
|
+
return {
|
|
26
|
+
linked: c.getProgramParameter(pg, c.LINK_STATUS),
|
|
27
|
+
log: c.getProgramInfoLog(pg),
|
|
28
|
+
activeAttributes: c.getProgramParameter(
|
|
29
|
+
pg,
|
|
30
|
+
c.ACTIVE_ATTRIBUTES
|
|
31
|
+
),
|
|
32
|
+
activeUniforms: c.getProgramParameter(pg, c.ACTIVE_UNIFORMS),
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// ユニフォームタイプの取得
|
|
37
|
+
export const getUniformType = (value: number | number[], isMatrix = false) => {
|
|
38
|
+
let length = typeof value === 'number' ? 0 : value?.length
|
|
39
|
+
if (!length) return `uniform1f`
|
|
40
|
+
if (!isMatrix) return `uniform${length}fv`
|
|
41
|
+
length = Math.sqrt(length) << 0
|
|
42
|
+
return `uniformMatrix${length}fv`
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// ユニフォーム位置を取得
|
|
46
|
+
export const getUniformLocation = (
|
|
47
|
+
c: WebGLRenderingContext,
|
|
48
|
+
pg: WebGLProgram,
|
|
49
|
+
name: string
|
|
50
|
+
) => {
|
|
51
|
+
return c.getUniformLocation(pg, name)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// アトリビュート位置を取得
|
|
55
|
+
export const getAttribLocation = (
|
|
56
|
+
c: WebGLRenderingContext,
|
|
57
|
+
pg: WebGLProgram,
|
|
58
|
+
name: string
|
|
59
|
+
) => {
|
|
60
|
+
return c.getAttribLocation(pg, name)
|
|
61
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
const defaultVertexShader = /* cpp */ `
|
|
2
|
+
attribute vec4 a_position;
|
|
3
|
+
void main() {
|
|
4
|
+
gl_Position = a_position;
|
|
5
|
+
}
|
|
6
|
+
`
|
|
7
|
+
|
|
8
|
+
const defaultFragmentShader = /* cpp */ `
|
|
9
|
+
precision mediump float;
|
|
10
|
+
uniform vec2 iResolution;
|
|
11
|
+
void main() {
|
|
12
|
+
gl_FragColor = vec4(fract(gl_FragCoord.xy / iResolution), 0, 1);
|
|
13
|
+
}
|
|
14
|
+
`
|
|
15
|
+
|
|
16
|
+
export const deleteShader = (c: WebGLRenderingContext, shader: WebGLShader) => {
|
|
17
|
+
c.deleteShader(shader)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// WebGLシェーダー作成と管理
|
|
21
|
+
const createShader = (
|
|
22
|
+
c: WebGLRenderingContext,
|
|
23
|
+
source: string,
|
|
24
|
+
type: number
|
|
25
|
+
) => {
|
|
26
|
+
const shader = c.createShader(type)
|
|
27
|
+
if (!shader) throw new Error('Failed to create shader')
|
|
28
|
+
c.shaderSource(shader, source)
|
|
29
|
+
c.compileShader(shader)
|
|
30
|
+
if (c.getShaderParameter(shader, c.COMPILE_STATUS)) return shader
|
|
31
|
+
const error = c.getShaderInfoLog(shader)
|
|
32
|
+
deleteShader(c, shader)
|
|
33
|
+
throw new Error(`Could not compile shader: ${error}`)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export const createVertexShader = (
|
|
37
|
+
c: WebGLRenderingContext,
|
|
38
|
+
source = defaultVertexShader
|
|
39
|
+
) => {
|
|
40
|
+
return createShader(c, source, c.VERTEX_SHADER)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export const createFragmentShader = (
|
|
44
|
+
c: WebGLRenderingContext,
|
|
45
|
+
source = defaultFragmentShader
|
|
46
|
+
) => {
|
|
47
|
+
return createShader(c, source, c.FRAGMENT_SHADER)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// シェーダーの情報を取得
|
|
51
|
+
export const getShaderInfo = (
|
|
52
|
+
c: WebGLRenderingContext,
|
|
53
|
+
shader: WebGLShader
|
|
54
|
+
) => {
|
|
55
|
+
return {
|
|
56
|
+
compiled: c.getShaderParameter(shader, c.COMPILE_STATUS),
|
|
57
|
+
log: c.getShaderInfoLog(shader),
|
|
58
|
+
source: c.getShaderSource(shader),
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// WebGLテクスチャ作成と管理
|
|
2
|
+
export const createTexture = (
|
|
3
|
+
c: WebGLRenderingContext,
|
|
4
|
+
img: HTMLImageElement
|
|
5
|
+
) => {
|
|
6
|
+
const texture = c.createTexture()
|
|
7
|
+
if (!texture) throw new Error('Failed to create texture')
|
|
8
|
+
c.bindTexture(c.TEXTURE_2D, texture)
|
|
9
|
+
c.texImage2D(c.TEXTURE_2D, 0, c.RGBA, c.RGBA, c.UNSIGNED_BYTE, img)
|
|
10
|
+
c.generateMipmap(c.TEXTURE_2D)
|
|
11
|
+
c.texParameteri(c.TEXTURE_2D, c.TEXTURE_MIN_FILTER, c.LINEAR)
|
|
12
|
+
c.texParameteri(c.TEXTURE_2D, c.TEXTURE_MAG_FILTER, c.LINEAR)
|
|
13
|
+
c.texParameteri(c.TEXTURE_2D, c.TEXTURE_WRAP_S, c.CLAMP_TO_EDGE)
|
|
14
|
+
c.texParameteri(c.TEXTURE_2D, c.TEXTURE_WRAP_T, c.CLAMP_TO_EDGE)
|
|
15
|
+
c.bindTexture(c.TEXTURE_2D, null)
|
|
16
|
+
return texture
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// テクスチャユニットをアクティブ化
|
|
20
|
+
export const activeTexture = (
|
|
21
|
+
c: WebGLRenderingContext,
|
|
22
|
+
location: WebGLUniformLocation | null,
|
|
23
|
+
unit: number,
|
|
24
|
+
texture: WebGLTexture
|
|
25
|
+
) => {
|
|
26
|
+
if (!location) return
|
|
27
|
+
c.uniform1i(location, unit)
|
|
28
|
+
c.activeTexture(c.TEXTURE0 + unit)
|
|
29
|
+
c.bindTexture(c.TEXTURE_2D, texture)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// テクスチャの削除
|
|
33
|
+
export const deleteTexture = (
|
|
34
|
+
c: WebGLRenderingContext,
|
|
35
|
+
texture: WebGLTexture
|
|
36
|
+
) => {
|
|
37
|
+
c.deleteTexture(texture)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// 空のテクスチャを作成
|
|
41
|
+
export const createEmptyTexture = (
|
|
42
|
+
c: WebGLRenderingContext,
|
|
43
|
+
w = 1280,
|
|
44
|
+
h = 800,
|
|
45
|
+
format = c.RGBA,
|
|
46
|
+
type = c.UNSIGNED_BYTE
|
|
47
|
+
) => {
|
|
48
|
+
const texture = c.createTexture()
|
|
49
|
+
if (!texture) throw new Error('Failed to create empty texture')
|
|
50
|
+
c.bindTexture(c.TEXTURE_2D, texture)
|
|
51
|
+
// prettier-ignore
|
|
52
|
+
c.texImage2D(c.TEXTURE_2D, 0, format, w, h, 0, format, type, null)
|
|
53
|
+
c.texParameteri(c.TEXTURE_2D, c.TEXTURE_MIN_FILTER, c.LINEAR)
|
|
54
|
+
c.texParameteri(c.TEXTURE_2D, c.TEXTURE_MAG_FILTER, c.LINEAR)
|
|
55
|
+
c.texParameteri(c.TEXTURE_2D, c.TEXTURE_WRAP_S, c.CLAMP_TO_EDGE)
|
|
56
|
+
c.texParameteri(c.TEXTURE_2D, c.TEXTURE_WRAP_T, c.CLAMP_TO_EDGE)
|
|
57
|
+
c.bindTexture(c.TEXTURE_2D, null)
|
|
58
|
+
return texture
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// キューブマップテクスチャを作成
|
|
62
|
+
export const createCubeTexture = (
|
|
63
|
+
c: WebGLRenderingContext,
|
|
64
|
+
imgs: HTMLImageElement[]
|
|
65
|
+
) => {
|
|
66
|
+
if (imgs.length !== 6)
|
|
67
|
+
throw new Error('Cube texture requires exactly 6 imgs')
|
|
68
|
+
const texture = c.createTexture()
|
|
69
|
+
if (!texture) throw new Error('Failed to create cube texture')
|
|
70
|
+
c.bindTexture(c.TEXTURE_CUBE_MAP, texture)
|
|
71
|
+
const faces = [
|
|
72
|
+
c.TEXTURE_CUBE_MAP_POSITIVE_X,
|
|
73
|
+
c.TEXTURE_CUBE_MAP_NEGATIVE_X,
|
|
74
|
+
c.TEXTURE_CUBE_MAP_POSITIVE_Y,
|
|
75
|
+
c.TEXTURE_CUBE_MAP_NEGATIVE_Y,
|
|
76
|
+
c.TEXTURE_CUBE_MAP_POSITIVE_Z,
|
|
77
|
+
c.TEXTURE_CUBE_MAP_NEGATIVE_Z,
|
|
78
|
+
]
|
|
79
|
+
// prettier-ignore
|
|
80
|
+
faces.forEach((face, index) => {
|
|
81
|
+
c.texImage2D( face, 0, c.RGBA, c.RGBA, c.UNSIGNED_BYTE, imgs[index])
|
|
82
|
+
})
|
|
83
|
+
c.generateMipmap(c.TEXTURE_CUBE_MAP)
|
|
84
|
+
// prettier-ignore
|
|
85
|
+
c.texParameteri(c.TEXTURE_CUBE_MAP, c.TEXTURE_MIN_FILTER, c.LINEAR_MIPMAP_LINEAR)
|
|
86
|
+
c.texParameteri(c.TEXTURE_CUBE_MAP, c.TEXTURE_MAG_FILTER, c.LINEAR)
|
|
87
|
+
c.texParameteri(c.TEXTURE_CUBE_MAP, c.TEXTURE_WRAP_S, c.CLAMP_TO_EDGE)
|
|
88
|
+
c.texParameteri(c.TEXTURE_CUBE_MAP, c.TEXTURE_WRAP_T, c.CLAMP_TO_EDGE)
|
|
89
|
+
|
|
90
|
+
c.bindTexture(c.TEXTURE_CUBE_MAP, null)
|
|
91
|
+
|
|
92
|
+
return texture
|
|
93
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { is } from '../utils'
|
|
2
|
+
|
|
3
|
+
// WebGPUバッファー管理
|
|
4
|
+
export const createBuffer = (
|
|
5
|
+
device: any,
|
|
6
|
+
data: Float32Array | Uint32Array | number[],
|
|
7
|
+
usage: number
|
|
8
|
+
) => {
|
|
9
|
+
const array = is.arr(data) ? new Float32Array(data) : data
|
|
10
|
+
const buffer = device.createBuffer({
|
|
11
|
+
size: array.byteLength,
|
|
12
|
+
usage,
|
|
13
|
+
mappedAtCreation: true,
|
|
14
|
+
})
|
|
15
|
+
if (array instanceof Float32Array)
|
|
16
|
+
new Float32Array(buffer.getMappedRange()).set(array)
|
|
17
|
+
else if (array instanceof Uint32Array)
|
|
18
|
+
new Uint32Array(buffer.getMappedRange()).set(array)
|
|
19
|
+
buffer.unmap()
|
|
20
|
+
return buffer
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// 頂点バッファー作成
|
|
24
|
+
export const createVertexBuffer = (device: any, data: number[]) => {
|
|
25
|
+
return createBuffer(device, data, 0x20) // GPUBufferUsage.VERTEX
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// インデックスバッファー作成
|
|
29
|
+
export const createIndexBuffer = (device: any, data: number[]) => {
|
|
30
|
+
return createBuffer(device, new Uint32Array(data), 0x40) // GPUBufferUsage.INDEX
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// ユニフォームバッファー作成
|
|
34
|
+
export const createUniformBuffer = (device: any, size: number) => {
|
|
35
|
+
return device.createBuffer({
|
|
36
|
+
size,
|
|
37
|
+
usage: 0x40 | 0x4, // GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
|
|
38
|
+
mappedAtCreation: false,
|
|
39
|
+
})
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// ストレージバッファー作成
|
|
43
|
+
export const createStorageBuffer = (device: any, size: number) => {
|
|
44
|
+
return device.createBuffer({
|
|
45
|
+
size,
|
|
46
|
+
usage: 0x80 | 0x4, // GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST
|
|
47
|
+
mappedAtCreation: false,
|
|
48
|
+
})
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// バッファーデータ更新
|
|
52
|
+
export const updateBuffer = (
|
|
53
|
+
device: any,
|
|
54
|
+
buffer: any,
|
|
55
|
+
data: Float32Array | Uint32Array | number[],
|
|
56
|
+
offset = 0
|
|
57
|
+
) => {
|
|
58
|
+
const array = is.arr(data) ? new Float32Array(data) : data
|
|
59
|
+
device.queue.writeBuffer(buffer, offset, array)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// バッファーの削除
|
|
63
|
+
export const destroyBuffer = (buffer: any) => buffer.destroy()
|
|
64
|
+
|
|
65
|
+
// バッファーサイズの計算
|
|
66
|
+
export const calculateBufferSize = (data: number[]) => data.length * 4 // 4 bytes per float
|
|
67
|
+
|
|
68
|
+
// アライメント調整
|
|
69
|
+
export const alignBufferSize = (size: number, alignment = 256) => {
|
|
70
|
+
return Math.ceil(size / alignment) * alignment
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// 複数のバッファーを作成
|
|
74
|
+
export const createBuffers = (
|
|
75
|
+
device: any,
|
|
76
|
+
bufferConfigs: Array<{
|
|
77
|
+
data: number[]
|
|
78
|
+
usage: number
|
|
79
|
+
}>
|
|
80
|
+
): any[] => {
|
|
81
|
+
return bufferConfigs.map((config) => {
|
|
82
|
+
return createBuffer(device, config.data, config.usage)
|
|
83
|
+
})
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// バッファー使用量の定数
|
|
87
|
+
export const BufferUsage = {
|
|
88
|
+
VERTEX: 0x20,
|
|
89
|
+
INDEX: 0x40,
|
|
90
|
+
UNIFORM: 0x40,
|
|
91
|
+
STORAGE: 0x80,
|
|
92
|
+
COPY_SRC: 0x4,
|
|
93
|
+
COPY_DST: 0x8,
|
|
94
|
+
MAP_READ: 0x1,
|
|
95
|
+
MAP_WRITE: 0x2,
|
|
96
|
+
} as const
|