{ "cells": [ { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "%gui qt" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n# Example demonstrating simulation of fireworks using point sprites\n\n(adapted from the \"OpenGL ES 2.0 Programming Guide\")\nThis example demonstrates a series of explosions that last one second. The\nvisualization during the explosion is highly optimized using a Vertex Buffer\nObject (VBO). After each explosion, vertex data for the next explosion are\ncalculated, such that each explostion is unique.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "import numpy as np\n\nfrom vispy import app\nfrom vispy.gloo import gl\n\n\nvertex_code = \"\"\"\n#version 120\n\nuniform float time;\nuniform vec3 center;\nattribute float lifetime;\nattribute vec3 start;\nattribute vec3 end;\nvarying float v_lifetime;\nvoid main () {\n if (time < lifetime) {\n gl_Position.xyz = start + (time * end) + center;\n gl_Position.w = 1.0;\n gl_Position.y -= 1.5 * time * time;\n } else {\n gl_Position = vec4(-1000, -1000, 0, 0);\n }\n v_lifetime = clamp(1.0 - (time / lifetime), 0.0, 1.0);\n gl_PointSize = (v_lifetime * v_lifetime) * 40.0;\n}\n\"\"\"\n\nfragment_code = \"\"\"\n#version 120\n\nuniform vec4 color;\nvarying float v_lifetime;\nvoid main()\n{\n float d = 1 - length(gl_PointCoord - vec2(.5,.5)) / (sqrt(2)/2);\n gl_FragColor = d*color;\n gl_FragColor.a = d;\n gl_FragColor.a *= v_lifetime;\n}\n\"\"\"\n\n\nclass Canvas(app.Canvas):\n def __init__(self):\n app.Canvas.__init__(self, size=(800, 600), title='GL Fireworks',\n keys='interactive')\n\n def on_initialize(self, event):\n # Build & activate program\n self.program = gl.glCreateProgram()\n vertex = gl.glCreateShader(gl.GL_VERTEX_SHADER)\n fragment = gl.glCreateShader(gl.GL_FRAGMENT_SHADER)\n gl.glShaderSource(vertex, vertex_code)\n gl.glShaderSource(fragment, fragment_code)\n gl.glCompileShader(vertex)\n gl.glCompileShader(fragment)\n gl.glAttachShader(self.program, vertex)\n gl.glAttachShader(self.program, fragment)\n gl.glLinkProgram(self.program)\n gl.glDetachShader(self.program, vertex)\n gl.glDetachShader(self.program, fragment)\n gl.glUseProgram(self.program)\n\n # Build vertex buffer\n n = 10000\n self.data = np.zeros(n, dtype=[('lifetime', np.float32),\n ('start', np.float32, 3),\n ('end', np.float32, 3)])\n vbuffer = gl.glCreateBuffer()\n gl.glBindBuffer(gl.GL_ARRAY_BUFFER, vbuffer)\n gl.glBufferData(gl.GL_ARRAY_BUFFER, self.data, gl.GL_DYNAMIC_DRAW)\n\n # Bind buffer attributes\n stride = self.data.strides[0]\n\n offset = 0\n loc = gl.glGetAttribLocation(self.program, \"lifetime\")\n gl.glEnableVertexAttribArray(loc)\n gl.glVertexAttribPointer(loc, 1, gl.GL_FLOAT, False, stride, offset)\n\n offset = self.data.dtype[\"lifetime\"].itemsize\n loc = gl.glGetAttribLocation(self.program, \"start\")\n gl.glEnableVertexAttribArray(loc)\n gl.glVertexAttribPointer(loc, 3, gl.GL_FLOAT, False, stride, offset)\n\n offset = self.data.dtype[\"start\"].itemsize\n loc = gl.glGetAttribLocation(self.program, \"end\")\n gl.glEnableVertexAttribArray(loc)\n gl.glVertexAttribPointer(loc, 3, gl.GL_FLOAT, False, stride, offset)\n\n # OpenGL initalization\n self.elapsed_time = 0\n gl.glClearColor(0, 0, 0, 1)\n gl.glDisable(gl.GL_DEPTH_TEST)\n gl.glEnable(gl.GL_BLEND)\n gl.glBlendFunc(gl.GL_SRC_ALPHA, gl.GL_ONE)\n gl.glEnable(34370) # gl.GL_VERTEX_PROGRAM_POINT_SIZE\n gl.glEnable(34913) # gl.GL_POINT_SPRITE\n gl.glViewport(0, 0, *self.physical_size)\n self.new_explosion()\n self.timer = app.Timer('auto', self.on_timer, start=True)\n\n def on_draw(self, event):\n gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT)\n gl.glDrawArrays(gl.GL_POINTS, 0, len(self.data))\n\n def on_resize(self, event):\n gl.glViewport(0, 0, *event.physical_size)\n\n def on_timer(self, event):\n self.elapsed_time += 1. / 60.\n if self.elapsed_time > 1.5:\n self.new_explosion()\n self.elapsed_time = 0.0\n\n loc = gl.glGetUniformLocation(self.program, \"time\")\n gl.glUniform1f(loc, self.elapsed_time)\n self.update()\n\n def new_explosion(self):\n n = len(self.data)\n color = np.random.uniform(0.1, 0.9, 4).astype(np.float32)\n color[3] = 1.0 / n ** 0.08\n loc = gl.glGetUniformLocation(self.program, \"color\")\n gl.glUniform4f(loc, *color)\n\n center = np.random.uniform(-0.5, 0.5, 3)\n loc = gl.glGetUniformLocation(self.program, \"center\")\n gl.glUniform3f(loc, *center)\n\n self.data['lifetime'] = np.random.normal(2.0, 0.5, (n,))\n self.data['start'] = np.random.normal(0.0, 0.2, (n, 3))\n self.data['end'] = np.random.normal(0.0, 1.2, (n, 3))\n gl.glBufferData(gl.GL_ARRAY_BUFFER, self.data, gl.GL_DYNAMIC_DRAW)\n\nif __name__ == '__main__':\n c = Canvas()\n c.show()\n app.run()" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.19" } }, "nbformat": 4, "nbformat_minor": 0 }