Skip to content

Instantly share code, notes, and snippets.

@kmuehlbauer
Created May 19, 2017 09:16
Show Gist options
  • Save kmuehlbauer/c521e2b05f5383651d157d5c44344949 to your computer and use it in GitHub Desktop.
Save kmuehlbauer/c521e2b05f5383651d157d5c44344949 to your computer and use it in GitHub Desktop.
Two GL-Widgets with PyQt5
# !/usr/bin/env python
# used with examples from https://github.com/vispy
from PyQt5 import QtWidgets, QtCore
import sys
import time
import numpy as np
from vispy import app, gloo
from vispy.util.transforms import perspective, translate, rotate
# Create a texture
radius = 32
im1 = np.random.normal(
0.8, 0.3, (radius * 2 + 1, radius * 2 + 1)).astype(np.float32)
# Mask it with a disk
L = np.linspace(-radius, radius, 2 * radius + 1)
(X, Y) = np.meshgrid(L, L)
im1 *= np.array((X ** 2 + Y ** 2) <= radius * radius, dtype='float32')
# Set number of particles, you should be able to scale this to 100000
N = 10000
# Create vertex data container
fire_data = np.zeros(N, [('a_lifetime', np.float32, 1),
('a_startPosition', np.float32, 3),
('a_endPosition', np.float32, 3)])
fire_vert = """
uniform float u_time;
uniform vec3 u_centerPosition;
attribute float a_lifetime;
attribute vec3 a_startPosition;
attribute vec3 a_endPosition;
varying float v_lifetime;
void main () {
if (u_time <= a_lifetime)
{
gl_Position.xyz = a_startPosition + (u_time * a_endPosition);
gl_Position.xyz += u_centerPosition;
gl_Position.y -= 1.0 * u_time * u_time;
gl_Position.w = 1.0;
}
else
gl_Position = vec4(-1000, -1000, 0, 0);
v_lifetime = 1.0 - (u_time / a_lifetime);
v_lifetime = clamp(v_lifetime, 0.0, 1.0);
gl_PointSize = (v_lifetime * v_lifetime) * 40.0;
}
"""
# Deliberately add precision qualifiers to test automatic GLSL code conversion
fire_frag = """
precision highp float;
uniform sampler2D texture1;
uniform vec4 u_color;
varying float v_lifetime;
uniform highp sampler2D s_texture;
void main()
{
highp vec4 texColor;
texColor = texture2D(s_texture, gl_PointCoord);
gl_FragColor = vec4(u_color) * texColor;
gl_FragColor.a *= v_lifetime;
}
"""
class FireCanvas(app.Canvas):
def __init__(self):
app.Canvas.__init__(self, keys='interactive', size=(800, 600))
# Create program
self._program = gloo.Program(fire_vert, fire_frag)
self._program.bind(gloo.VertexBuffer(fire_data))
self._program['s_texture'] = gloo.Texture2D(im1)
# Create first explosion
self._new_explosion()
# Enable blending
gloo.set_state(blend=True, clear_color='black',
blend_func=('src_alpha', 'one'))
gloo.set_viewport(0, 0, self.physical_size[0], self.physical_size[1])
def on_resize(self, event):
width, height = event.physical_size
gloo.set_viewport(0, 0, width, height)
def on_draw(self, event):
# Clear
gloo.clear()
# Draw
self._program['u_time'] = time.time() - self._starttime
self._program.draw('points')
# New explosion?
if time.time() - self._starttime > 1.5:
self._new_explosion()
def _new_explosion(self):
# New centerpos
centerpos = np.random.uniform(-0.5, 0.5, (3,))
self._program['u_centerPosition'] = centerpos
# New color, scale alpha with N
alpha = 1.0 / N ** 0.08
color = np.random.uniform(0.1, 0.9, (3,))
self._program['u_color'] = tuple(color) + (alpha,)
# Create new vertex data
fire_data['a_lifetime'] = np.random.normal(2.0, 0.5, (N,))
fire_data['a_startPosition'] = np.random.normal(0.0, 0.2, (N, 3))
fire_data['a_endPosition'] = np.random.normal(0.0, 1.2, (N, 3))
# Set time to zero
self._starttime = time.time()
cube_vert = """
// Uniforms
// ------------------------------------
uniform mat4 u_model;
uniform mat4 u_view;
uniform mat4 u_projection;
uniform vec4 u_color;
// Attributes
// ------------------------------------
attribute vec3 a_position;
attribute vec4 a_color;
attribute vec3 a_normal;
// Varying
// ------------------------------------
varying vec4 v_color;
void main()
{
v_color = a_color * u_color;
gl_Position = u_projection * u_view * u_model * vec4(a_position,1.0);
}
"""
cube_frag = """
// Varying
// ------------------------------------
varying vec4 v_color;
void main()
{
gl_FragColor = v_color;
}
"""
# -----------------------------------------------------------------------------
def cube():
"""
Build vertices for a colored cube.
V is the vertices
I1 is the indices for a filled cube (use with GL_TRIANGLES)
I2 is the indices for an outline cube (use with GL_LINES)
"""
vtype = [('a_position', np.float32, 3),
('a_normal', np.float32, 3),
('a_color', np.float32, 4)]
# Vertices positions
v = [[1, 1, 1], [-1, 1, 1], [-1, -1, 1], [1, -1, 1],
[1, -1, -1], [1, 1, -1], [-1, 1, -1], [-1, -1, -1]]
# Face Normals
n = [[0, 0, 1], [1, 0, 0], [0, 1, 0],
[-1, 0, 1], [0, -1, 0], [0, 0, -1]]
# Vertice colors
c = [[0, 1, 1, 1], [0, 0, 1, 1], [0, 0, 0, 1], [0, 1, 0, 1],
[1, 1, 0, 1], [1, 1, 1, 1], [1, 0, 1, 1], [1, 0, 0, 1]]
V = np.array([(v[0], n[0], c[0]), (v[1], n[0], c[1]),
(v[2], n[0], c[2]), (v[3], n[0], c[3]),
(v[0], n[1], c[0]), (v[3], n[1], c[3]),
(v[4], n[1], c[4]), (v[5], n[1], c[5]),
(v[0], n[2], c[0]), (v[5], n[2], c[5]),
(v[6], n[2], c[6]), (v[1], n[2], c[1]),
(v[1], n[3], c[1]), (v[6], n[3], c[6]),
(v[7], n[3], c[7]), (v[2], n[3], c[2]),
(v[7], n[4], c[7]), (v[4], n[4], c[4]),
(v[3], n[4], c[3]), (v[2], n[4], c[2]),
(v[4], n[5], c[4]), (v[7], n[5], c[7]),
(v[6], n[5], c[6]), (v[5], n[5], c[5])],
dtype=vtype)
I1 = np.resize(np.array([0, 1, 2, 0, 2, 3], dtype=np.uint32), 6 * (2 * 3))
I1 += np.repeat(4 * np.arange(2 * 3, dtype=np.uint32), 6)
I2 = np.resize(
np.array([0, 1, 1, 2, 2, 3, 3, 0], dtype=np.uint32), 6 * (2 * 4))
I2 += np.repeat(4 * np.arange(6, dtype=np.uint32), 8)
return V, I1, I2
# -----------------------------------------------------------------------------
class CubeCanvas(app.Canvas):
def __init__(self):
app.Canvas.__init__(self, keys='interactive', size=(800, 600))
self.vertices, self.filled, self.outline = cube()
self.filled_buf = gloo.IndexBuffer(self.filled)
self.outline_buf = gloo.IndexBuffer(self.outline)
self.program = gloo.Program(cube_vert, cube_frag)
self.program.bind(gloo.VertexBuffer(self.vertices))
self.view = translate((0, 0, -5))
self.model = np.eye(4, dtype=np.float32)
gloo.set_viewport(0, 0, self.physical_size[0], self.physical_size[1])
self.projection = perspective(45.0, self.size[0] /
float(self.size[1]), 2.0, 10.0)
self.program['u_projection'] = self.projection
self.program['u_model'] = self.model
self.program['u_view'] = self.view
self.theta = 0
self.phi = 0
gloo.set_clear_color('white')
gloo.set_state('opaque')
gloo.set_polygon_offset(1, 1)
# ---------------------------------
def refresh(self):
self.theta += .5
self.phi += .5
self.model = np.dot(rotate(self.theta, (0, 1, 0)),
rotate(self.phi, (0, 0, 1)))
self.program['u_model'] = self.model
self.update()
# ---------------------------------
def on_resize(self, event):
gloo.set_viewport(0, 0, event.physical_size[0], event.physical_size[1])
self.projection = perspective(45.0, event.size[0] /
float(event.size[1]), 2.0, 10.0)
self.program['u_projection'] = self.projection
# ---------------------------------
def on_draw(self, event):
gloo.clear()
# Filled cube
gloo.set_state(blend=False, depth_test=True, polygon_offset_fill=True)
self.program['u_color'] = 1, 1, 1, 1
self.program.draw('triangles', self.filled_buf)
# Outline
gloo.set_state(blend=True, depth_test=True, polygon_offset_fill=False)
gloo.set_depth_mask(False)
self.program['u_color'] = 0, 0, 0, 1
self.program.draw('lines', self.outline_buf)
gloo.set_depth_mask(True)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
QtWidgets.QMainWindow.__init__(self)
self.resize(1000, 500)
self.setWindowTitle('vispy example ...')
self.splitter_h = QtWidgets.QSplitter(QtCore.Qt.Horizontal)
# Central Widget
splitter1 = QtWidgets.QSplitter(QtCore.Qt.Horizontal)
self.cube_canvas = CubeCanvas()
self.cube_canvas.create_native()
self.cube_canvas.native.setParent(self)
self.fireworks_canvas = FireCanvas()
self.fireworks_canvas.create_native()
self.fireworks_canvas.native.setParent(self)
splitter1.addWidget(self.fireworks_canvas.native)
splitter1.addWidget(self.cube_canvas.native)
self.setCentralWidget(splitter1)
self._timer = QtCore.QTimer()
self._timer.setInterval(1.0 / 60)
self._timer.timeout.connect(self.on_timer)
self._timer.start()
@QtCore.pyqtSlot()
def on_timer(self):
self.cube_canvas.refresh()
self.fireworks_canvas.update()
if __name__ == '__main__':
appQt = QtWidgets.QApplication(sys.argv)
win = MainWindow()
win.show()
appQt.exec_()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment