-
Notifications
You must be signed in to change notification settings - Fork 12
/
ExtendedShaderMaterial.ts
86 lines (74 loc) · 3.17 KB
/
ExtendedShaderMaterial.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
import {ShaderMaterial2} from './ShaderMaterial2'
import {getTexelDecoding2} from '../../three'
import {
BufferGeometry,
Camera,
ColorSpace,
IUniform,
LinearSRGBColorSpace,
Object3D,
Scene,
Shader,
ShaderMaterialParameters,
Vector2,
WebGLRenderer,
} from 'three'
import {shaderReplaceString} from '../../utils'
import {IMaterialUserData} from '../IMaterial'
export class ExtendedShaderMaterial extends ShaderMaterial2 {
declare ['constructor']: (typeof ExtendedShaderMaterial) & (typeof ShaderMaterial2)
textures: {colorSpace: ColorSpace, id: string}[] = []
userData: IMaterialUserData
constructor(parameters: ShaderMaterialParameters, textureIds: string[], isRawShaderMaterial = false) {
super(parameters, isRawShaderMaterial)
this.setTextureIds(textureIds)
}
setTextureIds(ids: string[]) {
if (this.textures.map(t=>t.id).join(';') !== ids.join(';')) {
this.textures = ids.map(t=>({id: t, colorSpace: LinearSRGBColorSpace}))
this.setDirty()
}
}
private _setUniformTexSize(uniform?: IUniform, t?: {width: number, height: number}) {
if (!t || !uniform) return
const w = t?.width ?? 512
const h = t?.height ?? 512
const last = uniform.value
if (!last.isVector2) console.warn('uniform is not a Vector2')
if (last && Math.abs(last.x - w) + Math.abs(last.y - h) > 0.1) {
last.x = w; last.y = h
this.uniformsNeedUpdate = true
}
}
onBeforeRender(renderer: WebGLRenderer, scene: Scene, camera: Camera, geometry: BufferGeometry, object: Object3D): void {
this._setUniformTexSize(this.uniforms.screenSize, renderer.getRenderTarget() ?? renderer.getSize(new Vector2()))
for (const item of this.textures) {
const textureID = item.id
const t = this.uniforms[textureID]?.value
if (t) {
this._setUniformTexSize(this.uniforms[textureID + 'Size'], t.image)
if (t.colorSpace !== item.colorSpace) {
item.colorSpace = t.colorSpace
this.needsUpdate = true
}
}
}
super.onBeforeRender(renderer, scene, camera, geometry, object)
}
onBeforeCompile(s: Shader, renderer: WebGLRenderer) {
const pars = '\n' + this.textures
.map(t=>`uniform sampler2D ${t.id}; \n`
+ getTexelDecoding2(t.id ?? 'input', t.colorSpace ?? LinearSRGBColorSpace)).join('\n')
if (s.fragmentShader.includes('#include <encodings_pars_fragment>')) {
s.fragmentShader = shaderReplaceString(s.fragmentShader, '#include <encodings_pars_fragment>', pars, {append: true})
} else if (s.fragmentShader.includes('precision highp float;')) {
s.fragmentShader = shaderReplaceString(s.fragmentShader, 'precision highp float;', pars, {append: true})
} else {
s.fragmentShader = pars + s.fragmentShader
}
super.onBeforeCompile(s, renderer)
}
customProgramCacheKey(): string {
return super.customProgramCacheKey() + this.textures.map(t=>t.id + t.colorSpace).join(';')
}
}