Created
September 2, 2018 00:37
-
-
Save endarthur/59a44007c5ab42b17f6d258507a99e5b to your computer and use it in GitHub Desktop.
adapted from https://cyrille.rossant.net/shaders-opengl/
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
# PyQt4 imports | |
from PyQt5 import QtGui, QtCore, QtWidgets, QtOpenGL | |
from PyQt5.QtOpenGL import QGLWidget | |
# PyOpenGL imports | |
import OpenGL.GL as gl | |
import OpenGL.arrays.vbo as glvbo | |
# Window creation function. | |
def create_window(window_class): | |
"""Create a Qt window in Python, or interactively in IPython with Qt GUI | |
event loop integration: | |
# in ~/.ipython/ipython_config.py | |
c.TerminalIPythonApp.gui = 'qt' | |
c.TerminalIPythonApp.pylab = 'qt' | |
See also: | |
http://ipython.org/ipython-doc/dev/interactive/qtconsole.html#qt-and-the-qtconsole | |
""" | |
app_created = False | |
app = QtCore.QCoreApplication.instance() | |
if app is None: | |
app = QtWidgets.QApplication(sys.argv) | |
app_created = True | |
app.references = set() | |
window = window_class() | |
app.references.add(window) | |
window.show() | |
if app_created: | |
app.exec_() | |
return window | |
def compile_vertex_shader(source): | |
"""Compile a vertex shader from source.""" | |
vertex_shader = gl.glCreateShader(gl.GL_VERTEX_SHADER) | |
gl.glShaderSource(vertex_shader, source) | |
gl.glCompileShader(vertex_shader) | |
# check compilation error | |
result = gl.glGetShaderiv(vertex_shader, gl.GL_COMPILE_STATUS) | |
if not(result): | |
raise RuntimeError(gl.glGetShaderInfoLog(vertex_shader)) | |
return vertex_shader | |
def compile_fragment_shader(source): | |
"""Compile a fragment shader from source.""" | |
fragment_shader = gl.glCreateShader(gl.GL_FRAGMENT_SHADER) | |
gl.glShaderSource(fragment_shader, source) | |
gl.glCompileShader(fragment_shader) | |
# check compilation error | |
result = gl.glGetShaderiv(fragment_shader, gl.GL_COMPILE_STATUS) | |
if not(result): | |
raise RuntimeError(gl.glGetShaderInfoLog(fragment_shader)) | |
return fragment_shader | |
def link_shader_program(vertex_shader, fragment_shader): | |
"""Create a shader program with from compiled shaders.""" | |
program = gl.glCreateProgram() | |
gl.glAttachShader(program, vertex_shader) | |
gl.glAttachShader(program, fragment_shader) | |
gl.glLinkProgram(program) | |
# check linking error | |
result = gl.glGetProgramiv(program, gl.GL_LINK_STATUS) | |
if not(result): | |
raise RuntimeError(gl.glGetProgramInfoLog(program)) | |
return program | |
# Vertex shader | |
VS = """ | |
#version 330 | |
// Attribute variable that contains coordinates of the vertices. | |
layout(location = 0) in vec2 position; | |
// Main function, which needs to set `gl_Position`. | |
void main() | |
{ | |
// The final position is transformed from a null signal to a sinewave here. | |
// We pass the position to gl_Position, by converting it into | |
// a 4D vector. The last coordinate should be 0 when rendering 2D figures. | |
gl_Position = vec4(position.x, .2 * sin(20 * position.x), 0., 1.); | |
} | |
""" | |
# Fragment shader | |
FS = """ | |
#version 330 | |
// Output variable of the fragment shader, which is a 4D vector containing the | |
// RGBA components of the pixel color. | |
out vec4 out_color; | |
// Main fragment shader function. | |
void main() | |
{ | |
// We simply set the pixel color to yellow. | |
out_color = vec4(1., 1., 0., 1.); | |
} | |
""" | |
class GLPlotWidget(QGLWidget): | |
# default window size | |
width, height = 600, 600 | |
def initializeGL(self): | |
"""Initialize OpenGL, VBOs, upload data on the GPU, etc.""" | |
# background color | |
gl.glClearColor(0, 0, 0, 0) | |
# create a Vertex Buffer Object with the specified data | |
self.vbo = glvbo.VBO(self.data) | |
# compile the vertex shader | |
vs = compile_vertex_shader(VS) | |
# compile the fragment shader | |
fs = compile_fragment_shader(FS) | |
# compile the vertex shader | |
self.shaders_program = link_shader_program(vs, fs) | |
def paintGL(self): | |
"""Paint the scene.""" | |
# clear the buffer | |
gl.glClear(gl.GL_COLOR_BUFFER_BIT) | |
# bind the VBO | |
self.vbo.bind() | |
# tell OpenGL that the VBO contains an array of vertices | |
# prepare the shader | |
gl.glEnableVertexAttribArray(0) | |
# these vertices contain 2 single precision coordinates | |
gl.glVertexAttribPointer(0, 2, gl.GL_FLOAT, gl.GL_FALSE, 0, None) | |
gl.glUseProgram(self.shaders_program) | |
# draw "count" points from the VBO | |
gl.glDrawArrays(gl.GL_LINE_STRIP, 0, len(self.data)) | |
def resizeGL(self, width, height): | |
"""Called upon window resizing: reinitialize the viewport.""" | |
# update the window size | |
self.width, self.height = width, height | |
# paint within the whole window | |
gl.glViewport(0, 0, width, height) | |
if __name__ == '__main__': | |
# import numpy for generating random data points | |
import sys | |
import numpy as np | |
# null signal | |
data = np.zeros((10000, 2), dtype=np.float32) | |
data[:,0] = np.linspace(-1., 1., len(data)) | |
# define a Qt window with an OpenGL widget inside it | |
class TestWindow(QtWidgets.QMainWindow): | |
def __init__(self): | |
super(TestWindow, self).__init__() | |
# initialize the GL widget | |
self.widget = GLPlotWidget() | |
self.widget.data = data | |
# put the window at the screen position (100, 100) | |
self.setGeometry(100, 100, self.widget.width, self.widget.height) | |
self.setCentralWidget(self.widget) | |
self.show() | |
# show the window | |
win = create_window(TestWindow) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment