shaderflow 0.4.0.dev1__py2.py3-none-any.whl

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.

Potentially problematic release.


This version of shaderflow might be problematic. Click here for more details.

Files changed (51) hide show
  1. ShaderFlow/Common/Imgui.py +0 -0
  2. ShaderFlow/Common/Notes.py +134 -0
  3. ShaderFlow/Common/__init__.py +0 -0
  4. ShaderFlow/Message.py +147 -0
  5. ShaderFlow/Module.py +162 -0
  6. ShaderFlow/Modules/Audio.py +431 -0
  7. ShaderFlow/Modules/Bouncing.py +94 -0
  8. ShaderFlow/Modules/Camera.py +451 -0
  9. ShaderFlow/Modules/Depth.py +0 -0
  10. ShaderFlow/Modules/Dynamics.py +288 -0
  11. ShaderFlow/Modules/Frametimer.py +79 -0
  12. ShaderFlow/Modules/Keyboard.py +43 -0
  13. ShaderFlow/Modules/Noise.py +80 -0
  14. ShaderFlow/Modules/OpticalFlow.py +1 -0
  15. ShaderFlow/Modules/Piano.py +362 -0
  16. ShaderFlow/Modules/Spectrogram.py +315 -0
  17. ShaderFlow/Modules/Video.py +234 -0
  18. ShaderFlow/Modules/Waveform.py +100 -0
  19. ShaderFlow/Modules/__init__.py +0 -0
  20. ShaderFlow/Resources/Images/ShaderFlow.png +0 -0
  21. ShaderFlow/Resources/Images/ShaderFlow.svg +1 -0
  22. ShaderFlow/Resources/Scenes/Examples/Demo.py +284 -0
  23. ShaderFlow/Resources/Scenes/Examples/GLSL/Bars.frag +20 -0
  24. ShaderFlow/Resources/Scenes/Examples/GLSL/Bouncing.frag +50 -0
  25. ShaderFlow/Resources/Scenes/Examples/GLSL/Life/Simulation.glsl +49 -0
  26. ShaderFlow/Resources/Scenes/Examples/GLSL/Life/Visuals.glsl +31 -0
  27. ShaderFlow/Resources/Scenes/Examples/GLSL/MotionBlur.frag +20 -0
  28. ShaderFlow/Resources/Scenes/Examples/GLSL/Multipass.frag +43 -0
  29. ShaderFlow/Resources/Scenes/Examples/GLSL/RayMarch.frag +59 -0
  30. ShaderFlow/Resources/Scenes/Examples/GLSL/ShaderToy.frag +65 -0
  31. ShaderFlow/Resources/Scenes/Examples/GLSL/Video.frag +5 -0
  32. ShaderFlow/Resources/Scenes/Examples/GLSL/Visualizer.frag +75 -0
  33. ShaderFlow/Resources/Scenes/Examples/GLSL/Waveform.frag +18 -0
  34. ShaderFlow/Resources/Shaders/Fragment/Default.glsl +65 -0
  35. ShaderFlow/Resources/Shaders/Fragment/Final.glsl +7 -0
  36. ShaderFlow/Resources/Shaders/Fragment/Loading.frag +40 -0
  37. ShaderFlow/Resources/Shaders/Fragment/Missing.glsl +23 -0
  38. ShaderFlow/Resources/Shaders/Include/Camera.glsl +166 -0
  39. ShaderFlow/Resources/Shaders/Include/Complex.glsl +0 -0
  40. ShaderFlow/Resources/Shaders/Include/ShaderFlow.glsl +444 -0
  41. ShaderFlow/Resources/Shaders/Vertex/Default.glsl +21 -0
  42. ShaderFlow/Scene.py +1124 -0
  43. ShaderFlow/Shader.py +391 -0
  44. ShaderFlow/Texture.py +528 -0
  45. ShaderFlow/Variable.py +128 -0
  46. ShaderFlow/__init__.py +11 -0
  47. ShaderFlow/__main__.py +22 -0
  48. shaderflow-0.4.0.dev1.dist-info/METADATA +79 -0
  49. shaderflow-0.4.0.dev1.dist-info/RECORD +51 -0
  50. shaderflow-0.4.0.dev1.dist-info/WHEEL +5 -0
  51. shaderflow-0.4.0.dev1.dist-info/entry_points.txt +2 -0
@@ -0,0 +1,234 @@
1
+ import contextlib
2
+ import io
3
+ import sys
4
+ import time
5
+ from collections import deque
6
+ from pathlib import Path
7
+ from typing import Any, Callable, Dict, Tuple
8
+
9
+ import cv2
10
+ import numpy
11
+ import PIL
12
+ from attr import Factory, define
13
+
14
+ from Broken import BrokenAttrs, BrokenRelay, BrokenThread, SameTracker, log
15
+ from Broken.Externals.FFmpeg import BrokenFFmpeg
16
+ from Broken.Types import Hertz, Seconds
17
+ from ShaderFlow.Module import ShaderModule
18
+ from ShaderFlow.Texture import ShaderTexture
19
+
20
+
21
+ @define(slots=False)
22
+ class BrokenSmartVideoFrames(BrokenAttrs):
23
+ path: Path = None
24
+ buffer: Seconds = 60
25
+ threads: int = 6
26
+ quality: int = 95
27
+ time: Seconds = 0
28
+ lossless: bool = True
29
+ _width: int = None
30
+ _height: int = None
31
+ _fps: Hertz = None
32
+ _turbo: Any = None
33
+ _raw: deque = Factory(deque)
34
+ _frames: Dict = Factory(dict)
35
+
36
+ # Dynamically set
37
+ encode: Callable = None
38
+ decode: Callable = None
39
+
40
+ @property
41
+ def max_raw(self) -> int:
42
+ return self.threads*4
43
+
44
+ @property
45
+ def width(self) -> int:
46
+ return self._width
47
+
48
+ @property
49
+ def height(self) -> int:
50
+ return self._height
51
+
52
+ # # Initialization
53
+
54
+ LOSSLESS_MAX_BUFFER_LENGTH = 4
55
+
56
+ def __post__(self):
57
+ self._fps = BrokenFFmpeg.get_video_framerate(self.path)
58
+ self._width, self._height = BrokenFFmpeg.get_video_resolution(self.path)
59
+
60
+ if not all((self._fps, self._width, self._height)):
61
+ raise ValueError("Could not get video metadata")
62
+
63
+ # TurboJPEG will raise if shared lib is not found
64
+ with contextlib.suppress(RuntimeError, ModuleNotFoundError):
65
+ import turbojpeg
66
+ self._turbo = turbojpeg.TurboJPEG()
67
+
68
+ if self.lossless:
69
+ log.warning("Using lossless frames. Limiting buffer length for Out of Memory safety")
70
+ self.buffer = min(self.buffer, BrokenSmartVideoFrames.LOSSLESS_MAX_BUFFER_LENGTH)
71
+ self.encode = lambda frame: frame
72
+ self.decode = lambda frame: frame
73
+
74
+ elif (self._turbo is not None):
75
+ log.success("Using TurboJPEG for compression. Best speeds available")
76
+ self.encode = lambda frame: self._turbo.encode(frame, quality=self.quality)
77
+ self.decode = lambda frame: self._turbo.decode(frame)
78
+
79
+ elif ("cv2" in sys.modules):
80
+ log.success("Using OpenCV for compression. Slower than TurboJPEG but enough")
81
+ self.encode = lambda frame: cv2.imencode(".jpeg", frame)[1]
82
+ self.decode = lambda frame: cv2.imdecode(frame, cv2.IMREAD_COLOR)
83
+
84
+ else:
85
+ log.warning("Using PIL for compression. Performance killer GIL fallback")
86
+ self.decode = lambda frame: PIL.Image.open(io.BytesIO(frame))
87
+ self.encode = lambda frame: PIL.Image.fromarray(frame).save(
88
+ io.BytesIO(), format="jpeg", quality=self.quality
89
+ )
90
+
91
+ # Create worker threads. The good, the bad and the ugly
92
+ BrokenThread(target=self.extractor, daemon=True)
93
+ BrokenThread(target=self.deleter, daemon=True)
94
+ for _ in range(self.threads):
95
+ BrokenThread(target=self.worker, daemon=True)
96
+
97
+ # # Utilities
98
+
99
+ def time2index(self, time: Seconds) -> int:
100
+ return int(time*self._fps)
101
+
102
+ def index2time(self, index: int) -> Seconds:
103
+ return (index/self._fps)
104
+
105
+ # # Check if we can decode and encode with the libraries
106
+
107
+ def get_frame(self, time: Seconds) -> Tuple[int, numpy.ndarray]:
108
+ want = self.time2index(time)
109
+ self.time = time
110
+ import time
111
+
112
+ # Wait until the frame exists
113
+ while (jpeg := self._frames.get(want)) is None:
114
+ time.sleep(0.01)
115
+
116
+ return (want, lambda: self.decode(jpeg))
117
+
118
+ @property
119
+ def buffer_frames(self) -> int:
120
+ return int(self.buffer*self._fps)
121
+
122
+ @property
123
+ def time_index(self) -> int:
124
+ return self.time2index(self.time)
125
+
126
+ @property
127
+ def _future_index(self) -> int:
128
+ return self.time_index + self.buffer_frames
129
+
130
+ def _future_window(self, index: int) -> bool:
131
+ return index < self._future_index
132
+
133
+ @property
134
+ def _past_index(self) -> int:
135
+ return self.time_index - self.buffer_frames
136
+
137
+ def _past_window(self, index: int) -> bool:
138
+ return self._past_index < index
139
+
140
+ def _time_window(self, index: int) -> bool:
141
+ return self._past_window(index) and self._future_window(index)
142
+
143
+ def _should_rewind(self, index: int) -> bool:
144
+ """Point must be older than the past cutoff to trigger a rewind"""
145
+ return (self.time_index + self.buffer_frames) < index
146
+
147
+ # # Workers
148
+
149
+ _oldest: int = 0
150
+ _newest: int = 0
151
+
152
+ def extractor(self):
153
+ def forward():
154
+ for index, frame in enumerate(BrokenFFmpeg.iter_video_frames(self.path)):
155
+
156
+ # Skip already processed frames
157
+ if self._frames.get(index) is not None:
158
+ continue
159
+
160
+ # Skip frames outside of the past time window
161
+ if not self._past_window(index):
162
+ continue
163
+
164
+ while not self._future_window(index):
165
+ if self._should_rewind(index):
166
+ return
167
+ time.sleep(0.01)
168
+
169
+ # Limit how much raw frames there can be
170
+ while len(self._raw) > self.max_raw:
171
+ time.sleep(0.01)
172
+
173
+ self._raw.append((index, frame))
174
+ self._newest = max(self._newest, index)
175
+
176
+ while True:
177
+ forward()
178
+
179
+ def worker(self):
180
+ """Blindly get new frames from the deque, compress and store them"""
181
+ while True:
182
+ try:
183
+ index, frame = self._raw.popleft()
184
+ frame = numpy.array(numpy.flip(frame, axis=0))
185
+ self._frames[index] = self.encode(frame)
186
+ except IndexError:
187
+ time.sleep(0.01)
188
+
189
+ def deleter(self):
190
+ """Delete old frames that are not in the time window"""
191
+ while True:
192
+ for index in range(self._oldest, self._past_index):
193
+ self._frames[index] = None
194
+ self._oldest = index
195
+ for index in range(self._newest, self._future_index, -1):
196
+ self._frames[index] = None
197
+ self._newest = index
198
+ time.sleep(0.5)
199
+
200
+ # -------------------------------------------------------------------------------------------------|
201
+
202
+ @define
203
+ class ShaderVideo(BrokenSmartVideoFrames, ShaderModule):
204
+ name: str = "iVideo"
205
+ texture: ShaderTexture = None
206
+
207
+ temporal: int = 10
208
+ """How many """
209
+
210
+ on_frame: BrokenRelay = Factory(BrokenRelay)
211
+ """Whenever a new video frame is decoded, this attribute is called. Preferably subscribe to
212
+ it with `video.on_frame.subscribe(callable)` or `video.on_frame @ (A, B, C)`, see BokenRelay"""
213
+
214
+ def __post__(self):
215
+ self.texture = ShaderTexture(
216
+ scene=self.scene,
217
+ name=self.name,
218
+ width=self.width,
219
+ height=self.height,
220
+ temporal=self.temporal,
221
+ components=3,
222
+ dtype="f1"
223
+ )
224
+
225
+ __same__: SameTracker = Factory(SameTracker)
226
+
227
+ def update(self):
228
+ index, decode = self.get_frame(self.scene.time)
229
+
230
+ if not self.__same__(index):
231
+ image = decode()
232
+ self.texture.roll()
233
+ self.texture.write(image)
234
+ self.on_frame(image)
@@ -0,0 +1,100 @@
1
+ import math
2
+ from typing import Iterable
3
+
4
+ import numpy
5
+ from attr import Factory, define
6
+
7
+ from Broken import BrokenEnum, SameTracker, nearest
8
+ from Broken.Types import Hertz, Samples, Seconds
9
+ from ShaderFlow.Module import ShaderModule
10
+ from ShaderFlow.Modules.Audio import BrokenAudio
11
+ from ShaderFlow.Texture import ShaderTexture, TextureType
12
+ from ShaderFlow.Variable import ShaderVariable
13
+
14
+
15
+ class WaveformReducer(BrokenEnum):
16
+ def Average(x: numpy.ndarray) -> numpy.ndarray:
17
+ return numpy.sqrt(numpy.mean(numpy.abs(x), axis=2))
18
+
19
+ def RMS(x: numpy.ndarray) -> numpy.ndarray:
20
+ return numpy.sqrt(numpy.sqrt(numpy.mean(x**2, axis=2))*(2**0.5))
21
+
22
+ def STD(x: numpy.ndarray) -> numpy.ndarray:
23
+ return numpy.sqrt(numpy.std(x, axis=2))
24
+
25
+ @define
26
+ class ShaderWaveform(ShaderModule):
27
+
28
+ name: str = "iWaveform"
29
+ """Prefix name and Texture name of the Shader Variables"""
30
+
31
+ audio: BrokenAudio = None
32
+ """Audio class to read the data from"""
33
+
34
+ length: Seconds = 3
35
+ """Horizontal length of the Waveform content"""
36
+
37
+ samplerate: Hertz = 180
38
+ """Number of bars per second"""
39
+
40
+ reducer: WaveformReducer = WaveformReducer.Average
41
+ """How to convert a (channels, length, samples) chunks into (channels, length)"""
42
+
43
+ smooth: bool = True
44
+ """Enables Linear interpolation on the Texture, not much useful for Bars mode"""
45
+
46
+ texture: ShaderTexture = None
47
+ """Internal managed Texture"""
48
+
49
+ @property
50
+ def length_samples(self) -> Samples:
51
+ return int(max(1, self.length*self.scene.fps))
52
+
53
+ def __post__(self):
54
+ self.texture = ShaderTexture(
55
+ scene=self.scene,
56
+ name=self.name,
57
+ height=1,
58
+ mipmaps=False,
59
+ dtype=TextureType.f4,
60
+ )
61
+
62
+ @property
63
+ def chunk_size(self) -> Samples:
64
+ return max(1, int(self.length*self.audio.samplerate/self._points))
65
+
66
+ @property
67
+ def _points(self) -> int:
68
+ return self.length*self.samplerate
69
+
70
+ @property
71
+ def _offset(self) -> int:
72
+ return self.audio.tell % self.chunk_size
73
+
74
+ @property
75
+ def _cutoff(self) -> Samples:
76
+ return nearest(
77
+ number=self.audio.buffer_size,
78
+ multiple=self.chunk_size,
79
+ operator=math.floor,
80
+ type=int,
81
+ )
82
+
83
+ _same: SameTracker = Factory(SameTracker)
84
+
85
+ def update(self):
86
+ if self._same(self.audio.tell):
87
+ return
88
+ self.texture.filter = ("linear" if self.smooth else "nearest")
89
+ self.texture.components = self.audio.channels
90
+ self.texture.width = self._points
91
+ start = -int(self.chunk_size*self._points + self._offset + 1)
92
+ end = -int(self._offset + 1)
93
+ chunks = self.audio.data[:, start:end]
94
+ chunks = chunks.reshape(self.audio.channels, -1, self.chunk_size)
95
+ chunks = self.reducer(chunks)
96
+ chunks = numpy.ascontiguousarray(chunks.T)
97
+ self.texture.write(chunks)
98
+
99
+ def pipeline(self) -> Iterable[ShaderVariable]:
100
+ yield ShaderVariable("uniform", "int", f"{self.name}Length", self.length_samples)
File without changes
@@ -0,0 +1 @@
1
+ <svg width="32" height="32" version="1" xmlns="http://www.w3.org/2000/svg"><g transform="matrix(.91242 0 0 .91242 1.4013026 1.6537216)"><rect style="opacity:.2" width="28" height="29" x="2" y="2" rx="4.1999998" ry="4.1999998"/><rect style="fill:#0286e3;fill-opacity:1" width="28" height="29" x="2" y="1" rx="4.1999998" ry="4.1999998"/><path style="opacity:.1;fill:#fff" d="M6.1992188 1C3.8724189 1 2 2.8724189 2 5.1992188v1C2 3.8724189 3.8724189 2 6.1992188 2H25.800781C28.127581 2 30 3.8724189 30 6.1992188v-1C30 2.8724189 28.127581 1 25.800781 1Z"/><text xml:space="preserve" style="font-size:26.9514px;font-family:'Roboto Slab';-inkscape-font-specification:'Roboto Slab';opacity:.2;fill:#000;fill-opacity:1;stroke:none;stroke-width:42.808;stroke-linejoin:round;stroke-dasharray:none" x="7.6854787" y="26.417761" transform="translate(.21518099 -.33798856)"><tspan x="7.6854787" y="26.417761" style="font-weight:400;fill:#000;fill-opacity:1;stroke-width:42.808;stroke-dasharray:none">S</tspan></text><text xml:space="preserve" style="font-size:26.9514px;font-family:'Roboto Slab';-inkscape-font-specification:'Roboto Slab';fill:#fff;fill-opacity:1;stroke:none;stroke-width:42.8078;stroke-linejoin:round;stroke-dasharray:none" x="7.6843672" y="25.418978" transform="translate(.21518099 -.33798856)"><tspan x="7.6843672" y="25.418978" style="font-style:normal;font-weight:400;fill:#fff;fill-opacity:1;stroke-width:42.8078">S</tspan></text></g></svg>
@@ -0,0 +1,284 @@
1
+ import math
2
+ from pathlib import Path
3
+
4
+ import numpy
5
+ from ShaderFlow import SHADERFLOW
6
+ from ShaderFlow.Common.Notes import BrokenPianoNote
7
+ from ShaderFlow.Modules.Audio import ShaderAudio
8
+ from ShaderFlow.Modules.Bouncing import ShaderBouncing
9
+ from ShaderFlow.Modules.Dynamics import ShaderDynamics
10
+ from ShaderFlow.Modules.Noise import ShaderNoise
11
+ from ShaderFlow.Modules.Spectrogram import ShaderSpectrogram
12
+ from ShaderFlow.Modules.Video import ShaderVideo
13
+ from ShaderFlow.Modules.Waveform import ShaderWaveform
14
+ from ShaderFlow.Scene import ShaderScene
15
+ from ShaderFlow.Shader import ShaderObject
16
+ from ShaderFlow.Texture import ShaderTexture, TextureFilter
17
+ from ShaderFlow.Variable import ShaderVariable
18
+
19
+ from Broken import BrokenPath
20
+
21
+ BACKGROUND = "https://w.wallhaven.cc/full/e7/wallhaven-e778vr.jpg"
22
+
23
+ # -------------------------------------------------------------------------------------------------|
24
+
25
+ class Default(ShaderScene):
26
+ """The most basic ShaderScene, the default shader"""
27
+ ...
28
+
29
+ # -------------------------------------------------------------------------------------------------|
30
+
31
+ class ShaderToy(ShaderScene):
32
+ """ShaderToy Default Shader"""
33
+
34
+ def build(self):
35
+ ShaderScene.build(self)
36
+ self.shader.fragment = (self.directory/"GLSL"/"ShaderToy.frag")
37
+
38
+ # -------------------------------------------------------------------------------------------------|
39
+
40
+ class Nested(ShaderScene):
41
+ """Basic scene with two shaders acting together, main shader referencing the child"""
42
+ __name__ = "Nested Shaders"
43
+
44
+ def build(self):
45
+ ShaderScene.build(self)
46
+ self.child = ShaderObject(scene=self, name="child")
47
+
48
+ # - Left screen is black, right screen is red
49
+ # - Adds content of child shader to final image
50
+ self.shader.fragment = ("""
51
+ void main() {
52
+ fragColor.rgb = vec3(stuv.x, 0, 0);
53
+ fragColor.rgb += texture(child, astuv).rgb;
54
+ fragColor.a = 1;
55
+ }
56
+ """)
57
+
58
+ # Left screen is green, right screen is black
59
+ self.child.fragment = ("""
60
+ void main() {
61
+ fragColor.rgb = vec3(0, 1 - stuv.x, 0);
62
+ fragColor.a = 1;
63
+ }
64
+ """)
65
+
66
+ # -------------------------------------------------------------------------------------------------|
67
+
68
+ class Multipass(ShaderScene):
69
+ """Many Layers ('Buffers') done on a single shader"""
70
+ __name__ = "Multipass"
71
+
72
+ def build(self):
73
+ ShaderScene.build(self)
74
+ ShaderTexture(scene=self, name="background").from_image(BACKGROUND)
75
+ self.shader.texture.layers = 2
76
+ self.shader.fragment = (self.directory/"GLSL"/"Multipass.frag")
77
+
78
+ # -------------------------------------------------------------------------------------------------|
79
+
80
+ class MotionBlur(ShaderScene):
81
+ """Poor's man Motion Blur. If you dislike the effect, definitely don't run this"""
82
+ __name__ = "MotionBlur"
83
+
84
+ def build(self):
85
+ ShaderScene.build(self)
86
+ ShaderTexture(scene=self, name="background").from_image(BACKGROUND)
87
+ self.shader.texture.temporal = 10
88
+ self.shader.texture.layers = 2
89
+ self.shader.fragment = (self.directory/"GLSL"/"MotionBlur.frag")
90
+
91
+ # -------------------------------------------------------------------------------------------------|
92
+
93
+ class Dynamics(ShaderScene):
94
+ """Second order system"""
95
+ __name__ = "Dynamics"
96
+
97
+ def build(self):
98
+ ShaderScene.build(self)
99
+ ShaderTexture(scene=self, name="background").from_image(BACKGROUND)
100
+ self.dynamics = ShaderDynamics(scene=self, name="iShaderDynamics", frequency=4)
101
+ self.shader.fragment = ("""
102
+ void main() {
103
+ vec2 uv = zoom(stuv, 0.85 + 0.1*iShaderDynamics, vec2(0.5));
104
+ fragColor = stexture(background, uv);
105
+ }
106
+ """)
107
+
108
+ def update(self):
109
+ self.dynamics.target = 0.5 * (1 + numpy.sign(numpy.sin(2*math.pi*self.time * 0.5)))
110
+
111
+ # -------------------------------------------------------------------------------------------------|
112
+
113
+ class Noise(ShaderScene):
114
+ """Basics of Simplex noise"""
115
+ __name__ = "Procedural Noise"
116
+
117
+ def build(self):
118
+ ShaderScene.build(self)
119
+ ShaderTexture(scene=self, name="background").from_image(BACKGROUND)
120
+ self.shake_noise = ShaderNoise(scene=self, name="Shake", dimensions=2)
121
+ self.zoom_noise = ShaderNoise(scene=self, name="Zoom")
122
+ self.shader.fragment = ("""
123
+ void main() {
124
+ vec2 uv = zoom(stuv, 0.95 + 0.02*iZoom, vec2(0.5));
125
+ uv += 0.02 * iShake;
126
+ fragColor = stexture(background, uv);
127
+ }
128
+ """)
129
+
130
+ # -------------------------------------------------------------------------------------------------|
131
+
132
+ class Bouncing(ShaderScene):
133
+ """Bouncing Logo animation"""
134
+ __name__ = "Bouncing Logo"
135
+
136
+ def build(self):
137
+ ShaderScene.build(self)
138
+ LOGO = SHADERFLOW.RESOURCES.ICON
139
+ self.dvd = ShaderTexture(scene=self, name="logo").from_image(LOGO)
140
+ self.shader.fragment = (self.directory/"GLSL"/"Bouncing.frag")
141
+ self.bounce = ShaderBouncing(scene=self)
142
+ self.bounce.advanced_ratios(LOGO)
143
+
144
+ def update(self):
145
+ self.bounce.aspect_ratio = self.aspect_ratio
146
+
147
+ def pipeline(self):
148
+ yield from ShaderScene.pipeline(self)
149
+ yield ShaderVariable("uniform", "float", "iLogoSize", 0.3)
150
+
151
+ # -------------------------------------------------------------------------------------------------|
152
+
153
+ class Video(ShaderScene):
154
+ """Video as a Texture demo"""
155
+ __name__ = "Video"
156
+
157
+ def build(self):
158
+ ShaderScene.build(self)
159
+ BUNNY = "https://download.blender.org/demo/movies/BBB/bbb_sunflower_1080p_60fps_normal.mp4.zip"
160
+ self.video = next(BrokenPath.get_external(BUNNY).rglob("*.mp4"))
161
+ ShaderVideo(scene=self, path=self.video)
162
+ self.shader.fragment = (self.directory/"GLSL"/"Video.frag")
163
+
164
+ # -------------------------------------------------------------------------------------------------|
165
+
166
+ class Audio(ShaderScene):
167
+ """Basic audio processing"""
168
+ __name__ = "Audio"
169
+
170
+ def build(self):
171
+ ShaderScene.build(self)
172
+ self.audio = ShaderAudio(scene=self, name="iAudio")
173
+ self.audio.open_recorder()
174
+ self.shader.fragment = ("""
175
+ void main() {
176
+ fragColor = vec4(vec3(iAudioVolume), 1.0);
177
+ }
178
+ """)
179
+
180
+ # -------------------------------------------------------------------------------------------------|
181
+
182
+ class Waveform(ShaderScene):
183
+ """Audio Waveform Oscilloscope demo"""
184
+ __name__ = "Waveform"
185
+
186
+ def build(self):
187
+ ShaderScene.build(self)
188
+ self.audio = ShaderAudio(scene=self, name="iAudio", file="/path/to/audio.ogg")
189
+ self.waveform = ShaderWaveform(scene=self, audio=self.audio)
190
+ self.shader.fragment = (self.directory/"GLSL"/"Waveform.frag")
191
+
192
+ # -------------------------------------------------------------------------------------------------|
193
+
194
+ class Bars(ShaderScene):
195
+ """Basic music bars"""
196
+ __name__ = "Music Bars"
197
+
198
+ def build(self):
199
+ ShaderScene.build(self)
200
+ self.audio = ShaderAudio(scene=self, name="iAudio", file="/path/to/audio.ogg")
201
+ self.spectrogram = ShaderSpectrogram(scene=self, audio=self.audio, length=0)
202
+ self.spectrogram.from_notes(
203
+ start=BrokenPianoNote.from_frequency(20),
204
+ end=BrokenPianoNote.from_frequency(18000),
205
+ piano=True
206
+ )
207
+ self.shader.fragment = (self.directory/"GLSL"/"Bars.frag")
208
+
209
+ # -------------------------------------------------------------------------------------------------|
210
+
211
+ class Visualizer(ShaderScene):
212
+ """Radial Bars Music Visualizer Scene"""
213
+ __name__ = "Visualizer"
214
+
215
+ def build(self):
216
+ ShaderScene.build(self)
217
+ self.audio = ShaderAudio(scene=self, name="iAudio", file="/path/to/audio.ogg")
218
+ self.waveform = ShaderWaveform(scene=self, audio=self.audio)
219
+ self.spectrogram = ShaderSpectrogram(scene=self, length=0, audio=self.audio, smooth=False)
220
+ self.spectrogram.from_notes(
221
+ start=BrokenPianoNote.from_frequency(20),
222
+ end=BrokenPianoNote.from_frequency(14000),
223
+ piano=True
224
+ )
225
+ ShaderTexture(scene=self, name="background").from_image("https://w.wallhaven.cc/full/ex/wallhaven-ex6kmr.jpg")
226
+ ShaderTexture(scene=self, name="logo").from_image(SHADERFLOW.RESOURCES.ICON)
227
+ self.shader.fragment = (self.directory/"GLSL"/"Visualizer.frag")
228
+
229
+ # -------------------------------------------------------------------------------------------------|
230
+
231
+ class RayMarch(ShaderScene):
232
+ """Ray Marching demo"""
233
+ __name__ = "Ray Marching"
234
+
235
+ def build(self):
236
+ ShaderScene.build(self)
237
+ self.shader.fragment = (self.directory/"GLSL"/"RayMarch.frag")
238
+
239
+ # -------------------------------------------------------------------------------------------------|
240
+
241
+ class Batch(ShaderScene):
242
+ """Batch exporting demo. Run with `shaderflow batch -b 1-3 --base /path/to/folder"""
243
+ __name__ = "Batch"
244
+
245
+ def export_name(self, path: Path) -> Path:
246
+ return path.with_stem({
247
+ 1: "SubScene A",
248
+ 2: "SubScene B",
249
+ 3: "SubScene C",
250
+ }[self.export_index])
251
+
252
+ def build(self):
253
+ ShaderScene.build(self)
254
+
255
+ # -------------------------------------------------------------------------------------------------|
256
+
257
+ class Life(ShaderScene):
258
+ """Conway's Game of Life in GLSL"""
259
+ __name__ = "Game of Life"
260
+
261
+ life_period: int = 6
262
+ """Number of frames between each life update"""
263
+
264
+ def setup(self):
265
+ width, height = 192, 108
266
+ random = numpy.random.randint(0, 2, (width, height), dtype=bool)
267
+ self.simulation.texture.size = (width, height)
268
+ self.simulation.texture.write(random.astype(numpy.float32), temporal=1)
269
+
270
+ def build(self):
271
+ ShaderScene.build(self)
272
+ self.simulation = ShaderObject(scene=self, name="iLife")
273
+ self.simulation.texture.temporal = 10
274
+ self.simulation.texture.filter = TextureFilter.Nearest
275
+ self.simulation.texture.components = 1
276
+ self.simulation.texture.track = False
277
+ self.simulation.fragment = (self.directory/"GLSL"/"Life/Simulation.glsl")
278
+ self.shader.fragment = (self.directory/"GLSL"/"Life/Visuals.glsl")
279
+
280
+ def pipeline(self):
281
+ yield from ShaderScene.pipeline(self)
282
+ yield ShaderVariable("uniform", "int", "iLifePeriod", self.life_period)
283
+
284
+ # -------------------------------------------------------------------------------------------------|
@@ -0,0 +1,20 @@
1
+ /*
2
+ // (c) MIT, Tremeschin
3
+ */
4
+
5
+ void main() {
6
+ vec2 intensity = sqrt(texture(iSpectrogram, astuv.yx).xy)/70;
7
+
8
+ if (astuv.y < intensity.x) {
9
+ fragColor.rgb += vec3(1.0, 0.0, 0.0);
10
+ }
11
+ if (astuv.y < intensity.y) {
12
+ fragColor.rgb += vec3(0.0, 1.0, 0.0);
13
+ }
14
+ if (astuv.y < (intensity.y + intensity.x)/2.0) {
15
+ fragColor.rgb += vec3(0.0, 0.0, 1.0);
16
+ }
17
+
18
+ fragColor.rgb += vec3(0.0, 0.0, 0.4*(intensity.x + intensity.y)*(1.0 - astuv.y));
19
+ fragColor.a = 1;
20
+ }