Note
Go to the end to download the full example code.
Animate an Image#
Use a timer to trigger updating an image.
This example demonstrates a 3D Texture. The volume contains noise that is smoothed in the z-direction. Shown is one slice through that volume to give the effect of “morphing” noise.
import numpy as np
from vispy.util.transforms import ortho
from vispy import gloo
from vispy import app
from vispy.visuals.shaders import ModularProgram
# Shape of image to be displayed
D, H, W = 30, 60, 90
# Modulated image
img_array = np.random.uniform(0, 0.1, (D, H, W, 3)).astype(np.float32)
# Depth slices are dark->light
img_array[...] += np.linspace(0, 0.9, D)[:, np.newaxis, np.newaxis, np.newaxis]
# Make vertical direction more green moving upward
img_array[..., 1] *= np.linspace(0, 1, H)[np.newaxis, :, np.newaxis]
# Make horizontal direction more red moving rightward
img_array[..., 0] *= np.linspace(0, 1, W)[np.newaxis, np.newaxis, :]
# A simple texture quad
data = np.zeros(4, dtype=[('a_position', np.float32, 2),
('a_texcoord', np.float32, 2)])
data['a_position'] = np.array([[0, 0], [W, 0], [0, H], [W, H]])
data['a_texcoord'] = np.array([[0, 0], [0, 1], [1, 0], [1, 1]])
VERT_SHADER = """
// Uniforms
uniform mat4 u_model;
uniform mat4 u_view;
uniform mat4 u_projection;
// Attributes
attribute vec2 a_position;
attribute vec2 a_texcoord;
// Varyings
varying vec2 v_texcoord;
// Main
void main (void)
{
v_texcoord = a_texcoord;
gl_Position = u_projection * u_view * u_model * vec4(a_position,0.0,1.0);
}
"""
FRAG_SHADER = """
uniform $sampler_type u_texture;
uniform float i;
varying vec2 v_texcoord;
void main()
{
// step through gradient with i, note that slice (depth) comes last here!
gl_FragColor = $sample(u_texture, vec3(v_texcoord, i));
gl_FragColor.a = 1.0;
}
"""
class Canvas(app.Canvas):
def __init__(self, emulate3d=True):
app.Canvas.__init__(self, keys='interactive', size=((W*5), (H*5)))
if emulate3d:
tex_cls = gloo.TextureEmulated3D
else:
tex_cls = gloo.Texture3D
self.texture = tex_cls(img_array, interpolation='nearest',
wrapping='clamp_to_edge')
self.program = ModularProgram(VERT_SHADER, FRAG_SHADER)
self.program.frag['sampler_type'] = self.texture.glsl_sampler_type
self.program.frag['sample'] = self.texture.glsl_sample
self.program['u_texture'] = self.texture
self.program['i'] = 0.0
self.program.bind(gloo.VertexBuffer(data))
self.view = np.eye(4, dtype=np.float32)
self.model = np.eye(4, dtype=np.float32)
self.projection = np.eye(4, dtype=np.float32)
self.program['u_model'] = self.model
self.program['u_view'] = self.view
self.projection = ortho(0, W, 0, H, -1, 1)
self.program['u_projection'] = self.projection
self.i = 0
gloo.set_clear_color('white')
self._timer = app.Timer('auto', connect=self.on_timer, start=True)
self.show()
def on_resize(self, event):
width, height = event.physical_size
gloo.set_viewport(0, 0, width, height)
self.projection = ortho(0, width, 0, height, -100, 100)
self.program['u_projection'] = self.projection
# Compute the new size of the quad
r = width / float(height)
R = W / float(H)
if r < R:
w, h = width, width / R
x, y = 0, int((height - h) / 2)
else:
w, h = height * R, height
x, y = int((width - w) / 2), 0
data['a_position'] = np.array(
[[x, y], [x + w, y], [x, y + h], [x + w, y + h]])
self.program.bind(gloo.VertexBuffer(data))
def on_timer(self, event):
# cycle every 2 sec
self.i = (self.i + 1./120.) % 1.0
self.update()
def on_draw(self, event):
gloo.clear(color=True, depth=True)
self.program['i'] = 1.9 * np.abs(0.5 - self.i)
self.program.draw('triangle_strip')
if __name__ == '__main__':
# Use emulated3d to switch from an emulated 3D texture to an actual one
canvas = Canvas(emulate3d=True)
app.run()
Total running time of the script: (0 minutes 0.571 seconds)