Created
May 19, 2017 09:16
-
-
Save kmuehlbauer/c521e2b05f5383651d157d5c44344949 to your computer and use it in GitHub Desktop.
Two GL-Widgets with PyQt5
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# !/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