littlejsengine 1.20.0 → 1.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/dist/littlejs.d.ts +1041 -81
- package/dist/littlejs.esm.js +6191 -379
- package/dist/littlejs.esm.min.js +22 -9
- package/dist/littlejs.js +6238 -476
- package/dist/littlejs.min.js +21 -8
- package/dist/littlejs.release.js +3500 -387
- package/package.json +3 -2
- package/plugins/gltf.js +61 -9
- package/plugins/lightSystem.js +44 -1
- package/plugins/math3d.js +132 -30
- package/plugins/particleEffects.js +745 -0
- package/plugins/pluginExport.js +42 -0
- package/plugins/postProcess.js +246 -39
- package/plugins/render3d.js +850 -182
- package/plugins/render3dDebug.js +2 -1
- package/plugins/render3dEditor.js +2576 -0
- package/plugins/render3dExtras.js +421 -15
- package/plugins/render3dLevel.js +352 -0
- package/plugins/render3dVoxels.js +12 -0
- package/plugins/sceneSystem.js +59 -0
- package/plugins/tweenSystem.js +4 -2
- package/src/engine.js +199 -34
- package/src/engineAudio.js +18 -9
- package/src/engineBuild.mjs +4 -0
- package/src/engineDebug.js +55 -1
- package/src/engineDraw.js +141 -7
- package/src/engineEditor.js +52 -22
- package/src/engineExport.js +8 -0
- package/src/engineInput.js +8 -4
- package/src/engineLogo.js +19 -0
- package/src/engineObject.js +3 -2
- package/src/engineParticles.js +80 -11
- package/src/engineRelease.js +13 -1
- package/src/engineSettings.js +38 -0
- package/src/engineWebGL.js +27 -10
package/plugins/pluginExport.js
CHANGED
|
@@ -29,6 +29,15 @@ export
|
|
|
29
29
|
PostProcessPlugin,
|
|
30
30
|
postProcessBloom,
|
|
31
31
|
postProcessBloomShader,
|
|
32
|
+
postProcessEffects,
|
|
33
|
+
postProcessGlow,
|
|
34
|
+
postProcessScanlines,
|
|
35
|
+
postProcessNoise,
|
|
36
|
+
postProcessVignette,
|
|
37
|
+
postProcessCurve,
|
|
38
|
+
postProcessChromatic,
|
|
39
|
+
postProcessOutline,
|
|
40
|
+
postProcessTV,
|
|
32
41
|
|
|
33
42
|
// Light System
|
|
34
43
|
lightSystem,
|
|
@@ -103,6 +112,10 @@ export
|
|
|
103
112
|
tweenUpdate,
|
|
104
113
|
Ease,
|
|
105
114
|
|
|
115
|
+
// Scene System
|
|
116
|
+
setScene,
|
|
117
|
+
getScene,
|
|
118
|
+
|
|
106
119
|
// Path Finding
|
|
107
120
|
PathFinder,
|
|
108
121
|
PathFinderNode,
|
|
@@ -151,6 +164,7 @@ export
|
|
|
151
164
|
buildRibbon,
|
|
152
165
|
buildLoft,
|
|
153
166
|
buildSky,
|
|
167
|
+
normalMapFromHeight,
|
|
154
168
|
buildExtrude,
|
|
155
169
|
buildText3D,
|
|
156
170
|
HeightMap,
|
|
@@ -161,6 +175,7 @@ export
|
|
|
161
175
|
FirstPersonCamera3D,
|
|
162
176
|
ParticleEmitter3D,
|
|
163
177
|
Trail3D,
|
|
178
|
+
LensFlare3D,
|
|
164
179
|
engineObjectsCollect3D,
|
|
165
180
|
engineObjectsCallback3D,
|
|
166
181
|
engineObjectsRaycast3D,
|
|
@@ -177,6 +192,12 @@ export
|
|
|
177
192
|
debugLine3D,
|
|
178
193
|
debugPoint3D,
|
|
179
194
|
|
|
195
|
+
// 3D Level
|
|
196
|
+
level3DAddType,
|
|
197
|
+
level3DAddMesh,
|
|
198
|
+
level3DLoad,
|
|
199
|
+
level3DVoxelSetup,
|
|
200
|
+
|
|
180
201
|
// Three.js
|
|
181
202
|
threeJS,
|
|
182
203
|
ThreeJSPlugin,
|
|
@@ -193,4 +214,25 @@ export
|
|
|
193
214
|
loadAtlas,
|
|
194
215
|
parseAtlas,
|
|
195
216
|
spritesReady,
|
|
217
|
+
|
|
218
|
+
// Particle Effects
|
|
219
|
+
particleEffectGroups,
|
|
220
|
+
particleEffectSettings,
|
|
221
|
+
particleEffectBehaviors,
|
|
222
|
+
particleEffectShapes,
|
|
223
|
+
particleEffectSanitize,
|
|
224
|
+
particleEffectRecolor,
|
|
225
|
+
particleEffectsAdd,
|
|
226
|
+
particleEffectsGet,
|
|
227
|
+
particleEffectsParse,
|
|
228
|
+
particleEffectsText,
|
|
229
|
+
particleEffectsLoad,
|
|
230
|
+
particleEffectShapeTile,
|
|
231
|
+
particleEffect,
|
|
232
|
+
particleEffect3D,
|
|
233
|
+
particleEffectsBuiltIn,
|
|
234
|
+
particleEffectApply,
|
|
235
|
+
particleEffectApply3D,
|
|
236
|
+
particleEffectFromEmitter,
|
|
237
|
+
particleEffectsAddBehavior,
|
|
196
238
|
}
|
package/plugins/postProcess.js
CHANGED
|
@@ -46,8 +46,8 @@ class PostProcessPlugin
|
|
|
46
46
|
ASSERT(!(includeMainCanvas && feedbackTexture), 'Post process cannot both include main canvas and use feedback texture');
|
|
47
47
|
postProcess = this;
|
|
48
48
|
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
/** @property {string} - The shadertoy style mainImage code it shades with, see setShaderCode */
|
|
50
|
+
this.shaderCode = shaderCode || postProcessEffects(); // no code passes the frame through
|
|
51
51
|
|
|
52
52
|
/** @property {WebGLProgram|undefined} - Shader for post processing
|
|
53
53
|
* @type {WebGLProgram|undefined} */
|
|
@@ -93,18 +93,7 @@ class PostProcessPlugin
|
|
|
93
93
|
'gl_Position=vec4(p+p-1.,1,1);'+ // set position
|
|
94
94
|
'}' // end of shader
|
|
95
95
|
,
|
|
96
|
-
|
|
97
|
-
'precision highp float;'+ // use highp for accuracy
|
|
98
|
-
'uniform sampler2D iChannel0;'+ // input texture
|
|
99
|
-
'uniform sampler2D iChannel1;'+ // the previous frame's output, when feedbackTexture is set
|
|
100
|
-
'uniform vec3 iResolution;'+ // size of output texture
|
|
101
|
-
'uniform float iTime;'+ // time
|
|
102
|
-
'out vec4 c;'+ // out color
|
|
103
|
-
'\n' + shaderCode + '\n'+ // insert custom shader code
|
|
104
|
-
'void main(){'+ // shader entry point
|
|
105
|
-
'mainImage(c,gl_FragCoord.xy);'+ // call post process function
|
|
106
|
-
'c.a=1.;'+ // always use full alpha
|
|
107
|
-
'}' // end of shader
|
|
96
|
+
postProcessFragmentSource(postProcess.shaderCode)
|
|
108
97
|
);
|
|
109
98
|
|
|
110
99
|
// setup VAO for post processing
|
|
@@ -117,6 +106,10 @@ class PostProcessPlugin
|
|
|
117
106
|
const pLocation = glContext.getAttribLocation(postProcess.shader, 'p');
|
|
118
107
|
glContext.enableVertexAttribArray(pLocation);
|
|
119
108
|
glContext.vertexAttribPointer(pLocation, 2, glContext.FLOAT, false, vertexByteStride, 0);
|
|
109
|
+
|
|
110
|
+
// the engine's own vertex array and buffer back, its next batch is written into the buffer bound
|
|
111
|
+
glContext.bindVertexArray(glPolyMode ? glPolyVAO : glInstancedVAO);
|
|
112
|
+
glContext.bindBuffer(glContext.ARRAY_BUFFER, glArrayBuffer);
|
|
120
113
|
}
|
|
121
114
|
function postProcessContextLost()
|
|
122
115
|
{
|
|
@@ -134,16 +127,17 @@ class PostProcessPlugin
|
|
|
134
127
|
{
|
|
135
128
|
if (headlessMode || !glEnable) return;
|
|
136
129
|
|
|
137
|
-
//
|
|
130
|
+
// clear out the buffer, before anything here binds its own
|
|
131
|
+
glFlush();
|
|
132
|
+
|
|
133
|
+
// made now if WebGL was off when the plugin was made, when a lost context came back, or when
|
|
134
|
+
// setShaderCode gave it new code
|
|
138
135
|
if (!postProcess.shader)
|
|
139
136
|
{
|
|
140
137
|
if (glContext.isContextLost()) return;
|
|
141
138
|
initPostProcess();
|
|
142
139
|
}
|
|
143
140
|
|
|
144
|
-
// clear out the buffer
|
|
145
|
-
glFlush();
|
|
146
|
-
|
|
147
141
|
// ensure we render to the default framebuffer (in case any earlier
|
|
148
142
|
// caller this frame left a render target bound)
|
|
149
143
|
glContext.bindFramebuffer(glContext.FRAMEBUFFER, null);
|
|
@@ -192,21 +186,38 @@ class PostProcessPlugin
|
|
|
192
186
|
glContext.bindTexture(glContext.TEXTURE_2D, postProcess.feedbackTexture);
|
|
193
187
|
}
|
|
194
188
|
|
|
189
|
+
// the 3D depth, when render3D draws it, on the third texture unit, with what sceneDepth needs to read it
|
|
190
|
+
const depth = typeof render3D != 'undefined' && render3D?.depthTexture ? render3D : undefined;
|
|
191
|
+
glContext.activeTexture(glContext.TEXTURE2);
|
|
192
|
+
glContext.bindTexture(glContext.TEXTURE_2D, depth?.cameraDepthTexture || null);
|
|
193
|
+
glContext.activeTexture(glContext.TEXTURE0);
|
|
194
|
+
const camera = depth?.camera;
|
|
195
|
+
camera && glContext.uniform3f(glUniformLocation(postProcess.shader, 'iDepthRange'), camera.near,
|
|
196
|
+
camera.far == Infinity ? 0 : camera.far, camera.orthographic ? 1 : 0);
|
|
197
|
+
|
|
195
198
|
// set uniforms and draw
|
|
196
199
|
const uniformLocation = (name)=>glUniformLocation(postProcess.shader, name);
|
|
197
200
|
glContext.uniform1i(uniformLocation('iChannel0'), 0);
|
|
198
201
|
glContext.uniform1i(uniformLocation('iChannel1'), 1);
|
|
202
|
+
glContext.uniform1i(uniformLocation('iChannel2'), 2);
|
|
199
203
|
glContext.uniform1f(uniformLocation('iTime'), time);
|
|
200
204
|
glContext.uniform3f(uniformLocation('iResolution'), mainCanvas.width, mainCanvas.height, 1);
|
|
201
205
|
glContext.drawArrays(glContext.TRIANGLE_STRIP, 0, 4);
|
|
202
206
|
|
|
203
207
|
if (feedbackTexture)
|
|
204
208
|
{
|
|
205
|
-
// keep this frame's output for the next one,
|
|
209
|
+
// keep this frame's output for the next one, in the feedback texture still bound to the second
|
|
210
|
+
// unit, then hand the first texture unit back to the engine
|
|
211
|
+
glContext.activeTexture(glContext.TEXTURE1);
|
|
206
212
|
glContext.texImage2D(glContext.TEXTURE_2D, 0, glContext.RGBA, glContext.RGBA, glContext.UNSIGNED_BYTE, glCanvas);
|
|
207
213
|
glContext.activeTexture(glContext.TEXTURE0);
|
|
208
214
|
}
|
|
209
215
|
|
|
216
|
+
// the depth leaves the third unit for whatever draws next
|
|
217
|
+
glContext.activeTexture(glContext.TEXTURE2);
|
|
218
|
+
glContext.bindTexture(glContext.TEXTURE_2D, null);
|
|
219
|
+
glContext.activeTexture(glContext.TEXTURE0);
|
|
220
|
+
|
|
210
221
|
// restore defaults so subsequent dynamic texture uploads aren't flipped or premultiplied
|
|
211
222
|
glContext.pixelStorei(glContext.UNPACK_FLIP_Y_WEBGL, false);
|
|
212
223
|
glContext.pixelStorei(glContext.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
|
|
@@ -219,10 +230,52 @@ class PostProcessPlugin
|
|
|
219
230
|
glSetInstancedMode(true);
|
|
220
231
|
}
|
|
221
232
|
}
|
|
233
|
+
|
|
234
|
+
/** Shade with new code from the next frame on, to switch effects while the game runs; the shader is made again
|
|
235
|
+
* @param {string} [shaderCode] - Shadertoy style mainImage code, postProcessEffects builds it; none passes the
|
|
236
|
+
* frame through */
|
|
237
|
+
setShaderCode(shaderCode)
|
|
238
|
+
{
|
|
239
|
+
ASSERT(!shaderCode || typeof shaderCode === 'string', 'shader code must be a string');
|
|
240
|
+
this.shaderCode = shaderCode || postProcessEffects();
|
|
241
|
+
if (!this.shader || headlessMode || !glContext) return;
|
|
242
|
+
|
|
243
|
+
// what the new shader replaces, made again by the next render
|
|
244
|
+
glContext.deleteProgram(this.shader);
|
|
245
|
+
glContext.deleteTexture(this.texture);
|
|
246
|
+
this.feedbackTexture && glContext.deleteTexture(this.feedbackTexture);
|
|
247
|
+
glContext.deleteVertexArray(this.vao);
|
|
248
|
+
this.shader = this.texture = this.feedbackTexture = this.vao = undefined;
|
|
249
|
+
}
|
|
222
250
|
}
|
|
223
251
|
|
|
224
252
|
///////////////////////////////////////////////////////////////////////////////
|
|
225
253
|
|
|
254
|
+
// the fragment shader of the post process pass around its mainImage snippet: the frame on iChannel0, the last output
|
|
255
|
+
// on iChannel1 with a feedback texture, and the 3D depth on iChannel2 when render3D.depthTexture is on, read with
|
|
256
|
+
// sceneDepth(uv), the distance from the camera along its view in world units, uv 0 to 1 across the screen
|
|
257
|
+
function postProcessFragmentSource(shaderCode)
|
|
258
|
+
{
|
|
259
|
+
return '#version 300 es\n' + // specify GLSL ES version
|
|
260
|
+
'precision highp float;'+ // use highp for accuracy
|
|
261
|
+
'uniform sampler2D iChannel0;'+ // input texture
|
|
262
|
+
'uniform sampler2D iChannel1;'+ // the previous frame's output, when feedbackTexture is set
|
|
263
|
+
'uniform sampler2D iChannel2;'+ // the 3D depth, when render3D.depthTexture is on
|
|
264
|
+
'uniform vec3 iResolution;'+ // size of output texture
|
|
265
|
+
'uniform float iTime;'+ // time
|
|
266
|
+
'uniform vec3 iDepthRange;'+ // the camera's near, its far or 0 for none, and 1 when orthographic
|
|
267
|
+
'out vec4 c;'+ // out color
|
|
268
|
+
// the depth texture's value back to a distance, as the camera's projection put it there
|
|
269
|
+
'float sceneDepth(vec2 uv){'+
|
|
270
|
+
'float d=texture(iChannel2,uv).r*2.-1.,n=iDepthRange.x,f=iDepthRange.y;'+
|
|
271
|
+
'return iDepthRange.z>0.?(d*(f-n)+f+n)/2.:f>0.?2.*n*f/(f+n-d*(f-n)):2.*n/(1.-d);}'+
|
|
272
|
+
'\n' + shaderCode + '\n'+ // insert custom shader code
|
|
273
|
+
'void main(){'+ // shader entry point
|
|
274
|
+
'mainImage(c,gl_FragCoord.xy);'+ // call post process function
|
|
275
|
+
'c.a=1.;'+ // always use full alpha
|
|
276
|
+
'}'; // end of shader
|
|
277
|
+
}
|
|
278
|
+
|
|
226
279
|
/**
|
|
227
280
|
* Shader code for a bloom effect, the bright parts of the image blurred back over it
|
|
228
281
|
* - Pass it to PostProcessPlugin, or edit the string to build an effect on top of it
|
|
@@ -234,9 +287,66 @@ class PostProcessPlugin
|
|
|
234
287
|
*/
|
|
235
288
|
function postProcessBloomShader(threshold=.6, strength=1, size=6)
|
|
236
289
|
{
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
290
|
+
return postProcessEffects(postProcessGlow(threshold, strength, size));
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Set up post processing with a bloom effect, so bright colors and lights glow
|
|
295
|
+
* @param {number} [threshold] - Brightness where the glow starts, 0 is everything and 1 is only pure white
|
|
296
|
+
* @param {number} [strength] - How much glow to add
|
|
297
|
+
* @param {number} [size] - How far the glow spreads in pixels
|
|
298
|
+
* @param {boolean} [includeMainCanvas] - Glow the 2D canvas too, off by default so HUD text stays crisp
|
|
299
|
+
* (a HUD drawn with WebGL in gameRenderPost glows either way, draw it with useWebGL=false)
|
|
300
|
+
* @return {PostProcessPlugin}
|
|
301
|
+
* @memberof PostProcess
|
|
302
|
+
* @example
|
|
303
|
+
* postProcessBloom(); // in gameInit, after any Render3DPlugin
|
|
304
|
+
*/
|
|
305
|
+
function postProcessBloom(threshold=.6, strength=1, size=6, includeMainCanvas=false)
|
|
306
|
+
{ return new PostProcessPlugin(postProcessBloomShader(threshold, strength, size), includeMainCanvas); }
|
|
307
|
+
|
|
308
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
309
|
+
// Effects: pieces of shader code, each with its settings written in as numbers, that postProcessEffects joins in
|
|
310
|
+
// order into one shader; a piece works on c, the pixel's color, and uv, where it is on the screen from 0 to 1, with p
|
|
311
|
+
// its pixel; one that bends uv or samples the frame, the curve, chromatic and glow, goes before the ones that shade
|
|
312
|
+
|
|
313
|
+
// a number as GLSL writes it, a float with a point
|
|
314
|
+
const postProcessNumber = (n)=> (ASSERT(isNumber(n), 'effect settings must be numbers', n), n.toFixed(4));
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Join effects into one post process shader, in the order given, for PostProcessPlugin or setShaderCode
|
|
318
|
+
* - Each effect is a piece of shader code on c, the pixel's color, and uv, where it is on the screen from 0 to 1;
|
|
319
|
+
* your own code is a piece too, a line of GLSL or many, dropped in where you put it
|
|
320
|
+
* - Put the ones that bend the picture or sample it first: postProcessCurve, postProcessChromatic, postProcessGlow
|
|
321
|
+
* @param {...string} effects - The pieces, from the effect functions or your own code
|
|
322
|
+
* @return {string} - Shadertoy style mainImage code
|
|
323
|
+
* @example
|
|
324
|
+
* new PostProcessPlugin(postProcessEffects(
|
|
325
|
+
* postProcessScanlines(.5), postProcessVignette(), 'c.rgb *= vec3(1, .9, .8);'));
|
|
326
|
+
* @memberof PostProcess
|
|
327
|
+
*/
|
|
328
|
+
function postProcessEffects(...effects)
|
|
329
|
+
{
|
|
330
|
+
ASSERT(effects.every((e)=> typeof e === 'string'), 'each effect is a piece of shader code, a string');
|
|
331
|
+
return 'void mainImage(out vec4 c, vec2 p)\n{\n' +
|
|
332
|
+
' vec2 uv = p / iResolution.xy;\n' +
|
|
333
|
+
' c = texture(iChannel0, uv);\n' +
|
|
334
|
+
effects.map((e)=> ' {\n' + e + '\n }\n').join('') + '}\n';
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Bright parts glow, the bloom as an effect to join with others; postProcessBloom sets up bloom on its own
|
|
339
|
+
* @param {number} [threshold] - Brightness where the glow starts, 0 is everything and 1 is only pure white
|
|
340
|
+
* @param {number} [strength] - How much glow to add
|
|
341
|
+
* @param {number} [size] - How far the glow spreads in pixels, which also sets how many samples it takes
|
|
342
|
+
* @return {string}
|
|
343
|
+
* @memberof PostProcess
|
|
344
|
+
*/
|
|
345
|
+
function postProcessGlow(threshold=.6, strength=1, size=6)
|
|
346
|
+
{
|
|
347
|
+
ASSERT(isNumber(threshold) && isNumber(strength) && isNumber(size), 'glow settings must be numbers');
|
|
348
|
+
ASSERT(size > 0, 'glow size must be above zero');
|
|
349
|
+
ASSERT(size <= 32, 'a glow this wide takes a sample every few pixels of every ring, which is hundreds of samples a pixel', size);
|
|
240
350
|
|
|
241
351
|
// Taps on three rings over a disc of the given size, one every three pixels or so of each ring
|
|
242
352
|
// so there is no gap wide enough to show. The count follows the ring all the way out: hold it
|
|
@@ -258,27 +368,124 @@ function postProcessBloomShader(threshold=.6, strength=1, size=6)
|
|
|
258
368
|
glow += max(vec3(0), texture(iChannel0, uv + vec2(cos(a), sin(a)) * ${radius.toFixed(4)} / iResolution.xy).rgb - ${threshold.toFixed(4)});
|
|
259
369
|
}`;
|
|
260
370
|
}
|
|
261
|
-
return `
|
|
262
|
-
void mainImage(out vec4 color, vec2 pixel)
|
|
263
|
-
{
|
|
264
|
-
vec2 uv = pixel / iResolution.xy;
|
|
265
|
-
color = texture(iChannel0, uv);
|
|
371
|
+
return ` // glow
|
|
266
372
|
vec3 glow = vec3(0);${code}
|
|
267
|
-
|
|
268
|
-
}`;
|
|
373
|
+
c.rgb += glow * ${(strength / taps).toFixed(6)};`;
|
|
269
374
|
}
|
|
270
375
|
|
|
271
376
|
/**
|
|
272
|
-
*
|
|
273
|
-
* @param {number} [
|
|
274
|
-
* @param {number} [
|
|
275
|
-
* @
|
|
276
|
-
* @
|
|
277
|
-
|
|
278
|
-
|
|
377
|
+
* Scan lines across the screen, like an old TV
|
|
378
|
+
* @param {number} [strength] - How dark the lines are, and how bright between them
|
|
379
|
+
* @param {number} [spacing] - Pixels from one line to the next
|
|
380
|
+
* @return {string}
|
|
381
|
+
* @memberof PostProcess
|
|
382
|
+
*/
|
|
383
|
+
function postProcessScanlines(strength=.5, spacing=6)
|
|
384
|
+
{
|
|
385
|
+
return ` // scanlines
|
|
386
|
+
c.rgb *= 1. - ${postProcessNumber(strength)} * cos(p.y * 6.2832 / ${postProcessNumber(spacing)});`;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* Static noise over the picture, changing every frame
|
|
391
|
+
* @param {number} [strength] - How bright the static is
|
|
392
|
+
* @param {number} [size] - Size of a speck in pixels
|
|
393
|
+
* @return {string}
|
|
394
|
+
* @memberof PostProcess
|
|
395
|
+
*/
|
|
396
|
+
function postProcessNoise(strength=.1, size=2)
|
|
397
|
+
{
|
|
398
|
+
return ` // noise
|
|
399
|
+
vec2 q = fract((floor(p / ${postProcessNumber(size)}) + mod(iTime * 500., 1e3)) * .3197);
|
|
400
|
+
c.rgb += ${postProcessNumber(strength)} * fract(1. + sin(51. * q.x + 73. * q.y) * 13753.3);`;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Darken toward the edges and corners
|
|
405
|
+
* @param {number} [strength] - How dark the corners get, 1 is black
|
|
406
|
+
* @param {number} [falloff] - How far in it reaches, low darkens most of the screen, high only the corners
|
|
407
|
+
* @return {string}
|
|
408
|
+
* @memberof PostProcess
|
|
409
|
+
*/
|
|
410
|
+
function postProcessVignette(strength=1, falloff=3)
|
|
411
|
+
{
|
|
412
|
+
return ` // vignette
|
|
413
|
+
vec2 d = uv * 2. - 1.;
|
|
414
|
+
c.rgb *= 1. - ${postProcessNumber(strength)} * min(1., pow(dot(d, d) / 2., ${postProcessNumber(falloff)}));`;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* Bend the picture like the bulged glass of an old TV, black past the corners; put it first
|
|
419
|
+
* @param {number} [strength] - How much it bends
|
|
420
|
+
* @return {string}
|
|
421
|
+
* @memberof PostProcess
|
|
422
|
+
*/
|
|
423
|
+
function postProcessCurve(strength=.1)
|
|
424
|
+
{
|
|
425
|
+
return ` // curve
|
|
426
|
+
vec2 d = uv * 2. - 1.;
|
|
427
|
+
d *= 1. + ${postProcessNumber(strength)} * dot(d, d);
|
|
428
|
+
uv = d * .5 + .5;
|
|
429
|
+
c = all(lessThan(abs(d), vec2(1))) ? texture(iChannel0, uv) : vec4(0, 0, 0, 1);`;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* Split red and blue apart toward the edges, like a cheap lens; put it before what shades the picture
|
|
434
|
+
* @param {number} [strength] - How far apart at the edge, as a part of the screen
|
|
435
|
+
* @return {string}
|
|
436
|
+
* @memberof PostProcess
|
|
437
|
+
*/
|
|
438
|
+
function postProcessChromatic(strength=.005)
|
|
439
|
+
{
|
|
440
|
+
return ` // chromatic
|
|
441
|
+
vec2 d = (uv - .5) * ${postProcessNumber(strength)} * 2.;
|
|
442
|
+
c.r = texture(iChannel0, uv + d).r;
|
|
443
|
+
c.b = texture(iChannel0, uv - d).b;`;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* Draw lines where the 3D depth jumps, around objects and along their creases; needs render3D.depthTexture on
|
|
448
|
+
* @param {Color} [color] - The lines' color, its alpha how strong they are
|
|
449
|
+
* @param {number} [thickness] - How wide the lines are in pixels
|
|
450
|
+
* @param {number} [threshold] - How big a jump makes a line, as a part of the distance, lower draws more
|
|
451
|
+
* @return {string}
|
|
279
452
|
* @memberof PostProcess
|
|
453
|
+
*/
|
|
454
|
+
function postProcessOutline(color=BLACK, thickness=1, threshold=.02)
|
|
455
|
+
{
|
|
456
|
+
ASSERT(isColor(color), 'outline color must be a Color');
|
|
457
|
+
const n = postProcessNumber;
|
|
458
|
+
return ` // outline
|
|
459
|
+
vec2 o = ${n(thickness)} / iResolution.xy;
|
|
460
|
+
float d = sceneDepth(uv);
|
|
461
|
+
float dx = abs(sceneDepth(uv + vec2(o.x, 0)) + sceneDepth(uv - vec2(o.x, 0)) - 2. * d);
|
|
462
|
+
float dy = abs(sceneDepth(uv + vec2(0, o.y)) + sceneDepth(uv - vec2(0, o.y)) - 2. * d);
|
|
463
|
+
vec4 line = vec4(${n(color.r)}, ${n(color.g)}, ${n(color.b)}, ${n(color.a)});
|
|
464
|
+
c.rgb = mix(c.rgb, line.rgb, line.a * step(${n(threshold)}, max(dx, dy) / d));`;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
/**
|
|
468
|
+
* The look of an old TV, as one effect to use alone or join with others: static noise, scan lines, a soft glow and
|
|
469
|
+
* a vignette, and a bulged screen when curve is set; any setting at 0 leaves that part out
|
|
470
|
+
* @param {Object} [settings]
|
|
471
|
+
* @param {number} [settings.noise] - Static noise strength
|
|
472
|
+
* @param {number} [settings.scanlines] - Scan line strength
|
|
473
|
+
* @param {number} [settings.scanlineSpacing] - Pixels from one scan line to the next
|
|
474
|
+
* @param {number} [settings.glow] - Soft glow strength
|
|
475
|
+
* @param {number} [settings.vignette] - Vignette strength
|
|
476
|
+
* @param {number} [settings.curve] - How much the screen bulges, 0 by default for flat
|
|
477
|
+
* @return {string}
|
|
280
478
|
* @example
|
|
281
|
-
*
|
|
479
|
+
* new PostProcessPlugin(postProcessEffects(postProcessTV({scanlines: .4, curve: .1})));
|
|
480
|
+
* @memberof PostProcess
|
|
282
481
|
*/
|
|
283
|
-
function
|
|
284
|
-
{
|
|
482
|
+
function postProcessTV({noise=.1, scanlines=.5, scanlineSpacing=6, glow=.4, vignette=1, curve=0}={})
|
|
483
|
+
{
|
|
484
|
+
const parts = [];
|
|
485
|
+
curve && parts.push(postProcessCurve(curve));
|
|
486
|
+
glow && parts.push(postProcessGlow(0, glow, 2));
|
|
487
|
+
scanlines && parts.push(postProcessScanlines(scanlines, scanlineSpacing));
|
|
488
|
+
noise && parts.push(postProcessNoise(noise, 1));
|
|
489
|
+
vignette && parts.push(postProcessVignette(vignette, 6));
|
|
490
|
+
return parts.map((part)=> ' {\n' + part + '\n }').join('\n');
|
|
491
|
+
}
|