{ "cells": [ { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "%gui qt" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n# Drawing a rotated 3D cube\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "import math\nimport numpy as np\n\nfrom vispy import app\nfrom vispy.gloo import gl\n\n\ndef checkerboard(grid_num=8, grid_size=32):\n row_even = grid_num // 2 * [0, 1]\n row_odd = grid_num // 2 * [1, 0]\n Z = np.row_stack(grid_num // 2 * (row_even, row_odd)).astype(np.uint8)\n return 255 * Z.repeat(grid_size, axis=0).repeat(grid_size, axis=1)\n\n\ndef rotate(M, angle, x, y, z, point=None):\n angle = math.pi * angle / 180\n c, s = math.cos(angle), math.sin(angle)\n n = math.sqrt(x * x + y * y + z * z)\n x /= n\n y /= n\n z /= n\n cx, cy, cz = (1 - c) * x, (1 - c) * y, (1 - c) * z\n R = np.array([[cx * x + c, cy * x - z * s, cz * x + y * s, 0],\n [cx * y + z * s, cy * y + c, cz * y - x * s, 0],\n [cx * z - y * s, cy * z + x * s, cz * z + c, 0],\n [0, 0, 0, 1]], dtype=M.dtype).T\n M[...] = np.dot(M, R)\n return M\n\n\ndef translate(M, x, y=None, z=None):\n y = x if y is None else y\n z = x if z is None else z\n T = np.array([[1.0, 0.0, 0.0, x],\n [0.0, 1.0, 0.0, y],\n [0.0, 0.0, 1.0, z],\n [0.0, 0.0, 0.0, 1.0]], dtype=M.dtype).T\n M[...] = np.dot(M, T)\n return M\n\n\ndef frustum(left, right, bottom, top, znear, zfar):\n M = np.zeros((4, 4), dtype=np.float32)\n M[0, 0] = +2.0 * znear / (right - left)\n M[2, 0] = (right + left) / (right - left)\n M[1, 1] = +2.0 * znear / (top - bottom)\n M[3, 1] = (top + bottom) / (top - bottom)\n M[2, 2] = -(zfar + znear) / (zfar - znear)\n M[3, 2] = -2.0 * znear * zfar / (zfar - znear)\n M[2, 3] = -1.0\n return M\n\n\ndef perspective(fovy, aspect, znear, zfar):\n h = math.tan(fovy / 360.0 * math.pi) * znear\n w = h * aspect\n return frustum(-w, w, -h, h, znear, zfar)\n\n\ndef makecube():\n \"\"\" Generate vertices & indices for a filled cube \"\"\"\n\n vtype = [('a_position', np.float32, 3),\n ('a_texcoord', np.float32, 2)]\n itype = np.uint32\n\n # Vertices positions\n p = np.array([[1, 1, 1], [-1, 1, 1], [-1, -1, 1], [1, -1, 1],\n [1, -1, -1], [1, 1, -1], [-1, 1, -1], [-1, -1, -1]])\n\n # Texture coords\n t = np.array([[0, 0], [0, 1], [1, 1], [1, 0]])\n\n faces_p = [0, 1, 2, 3, 0, 3, 4, 5, 0, 5, 6,\n 1, 1, 6, 7, 2, 7, 4, 3, 2, 4, 7, 6, 5]\n faces_t = [0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2,\n 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3]\n\n vertices = np.zeros(24, vtype)\n vertices['a_position'] = p[faces_p]\n vertices['a_texcoord'] = t[faces_t]\n\n indices = np.resize(\n np.array([0, 1, 2, 0, 2, 3], dtype=itype), 6 * (2 * 3))\n indices += np.repeat(4 * np.arange(6), 6).astype(np.uint32)\n\n return vertices, indices\n\n\ncube_vertex = \"\"\"\nuniform mat4 u_model;\nuniform mat4 u_view;\nuniform mat4 u_projection;\nattribute vec3 a_position;\nattribute vec2 a_texcoord;\nvarying vec2 v_texcoord;\nvoid main()\n{\n gl_Position = u_projection * u_view * u_model * vec4(a_position,1.0);\n v_texcoord = a_texcoord;\n}\n\"\"\"\n\ncube_fragment = \"\"\"\nuniform sampler2D u_texture;\nvarying vec2 v_texcoord;\nvoid main()\n{\n gl_FragColor = texture2D(u_texture, v_texcoord);\n}\n\"\"\"\n\n\nclass Canvas(app.Canvas):\n def __init__(self):\n app.Canvas.__init__(self, size=(512, 512),\n title='Rotating cube (GL version)',\n keys='interactive')\n\n def on_initialize(self, event):\n # Build & activate cube program\n self.cube = gl.glCreateProgram()\n vertex = gl.glCreateShader(gl.GL_VERTEX_SHADER)\n fragment = gl.glCreateShader(gl.GL_FRAGMENT_SHADER)\n gl.glShaderSource(vertex, cube_vertex)\n gl.glShaderSource(fragment, cube_fragment)\n gl.glCompileShader(vertex)\n gl.glCompileShader(fragment)\n gl.glAttachShader(self.cube, vertex)\n gl.glAttachShader(self.cube, fragment)\n gl.glLinkProgram(self.cube)\n gl.glDetachShader(self.cube, vertex)\n gl.glDetachShader(self.cube, fragment)\n gl.glUseProgram(self.cube)\n\n # Get data & build cube buffers\n vcube_data, self.icube_data = makecube()\n vcube = gl.glCreateBuffer()\n gl.glBindBuffer(gl.GL_ARRAY_BUFFER, vcube)\n gl.glBufferData(gl.GL_ARRAY_BUFFER, vcube_data, gl.GL_STATIC_DRAW)\n icube = gl.glCreateBuffer()\n gl.glBindBuffer(gl.GL_ELEMENT_ARRAY_BUFFER, icube)\n gl.glBufferData(gl.GL_ELEMENT_ARRAY_BUFFER,\n self.icube_data, gl.GL_STATIC_DRAW)\n\n # Bind cube attributes\n stride = vcube_data.strides[0]\n offset = 0\n loc = gl.glGetAttribLocation(self.cube, \"a_position\")\n gl.glEnableVertexAttribArray(loc)\n gl.glVertexAttribPointer(loc, 3, gl.GL_FLOAT, False, stride, offset)\n\n offset = vcube_data.dtype[\"a_position\"].itemsize\n loc = gl.glGetAttribLocation(self.cube, \"a_texcoord\")\n gl.glEnableVertexAttribArray(loc)\n gl.glVertexAttribPointer(loc, 2, gl.GL_FLOAT, False, stride, offset)\n\n # Create & bind cube texture\n crate = checkerboard()\n texture = gl.glCreateTexture()\n gl.glTexParameterf(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MIN_FILTER,\n gl.GL_LINEAR)\n gl.glTexParameterf(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MAG_FILTER,\n gl.GL_LINEAR)\n gl.glTexParameterf(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_WRAP_S,\n gl.GL_CLAMP_TO_EDGE)\n gl.glTexParameterf(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_WRAP_T,\n gl.GL_CLAMP_TO_EDGE)\n gl.glTexImage2D(gl.GL_TEXTURE_2D, 0, gl.GL_LUMINANCE, gl.GL_LUMINANCE,\n gl.GL_UNSIGNED_BYTE, crate.shape[:2])\n gl.glTexSubImage2D(gl.GL_TEXTURE_2D, 0, 0, 0, gl.GL_LUMINANCE,\n gl.GL_UNSIGNED_BYTE, crate)\n loc = gl.glGetUniformLocation(self.cube, \"u_texture\")\n gl.glUniform1i(loc, texture)\n gl.glBindTexture(gl.GL_TEXTURE_2D, 0)\n\n # Create & bind cube matrices\n view = np.eye(4, dtype=np.float32)\n model = np.eye(4, dtype=np.float32)\n projection = np.eye(4, dtype=np.float32)\n translate(view, 0, 0, -7)\n self.phi, self.theta = 60, 20\n rotate(model, self.theta, 0, 0, 1)\n rotate(model, self.phi, 0, 1, 0)\n loc = gl.glGetUniformLocation(self.cube, \"u_model\")\n gl.glUniformMatrix4fv(loc, 1, False, model)\n loc = gl.glGetUniformLocation(self.cube, \"u_view\")\n gl.glUniformMatrix4fv(loc, 1, False, view)\n loc = gl.glGetUniformLocation(self.cube, \"u_projection\")\n gl.glUniformMatrix4fv(loc, 1, False, projection)\n\n # OpenGL initalization\n gl.glClearColor(0.30, 0.30, 0.35, 1.00)\n gl.glEnable(gl.GL_DEPTH_TEST)\n self._resize(*(self.size + self.physical_size))\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.glDrawElements(gl.GL_TRIANGLES, self.icube_data.size,\n gl.GL_UNSIGNED_INT, None)\n\n def on_resize(self, event):\n self._resize(*(event.size + event.physical_size))\n\n def _resize(self, width, height, physical_width, physical_height):\n gl.glViewport(0, 0, physical_width, physical_height)\n projection = perspective(35.0, width / float(height), 2.0, 10.0)\n loc = gl.glGetUniformLocation(self.cube, \"u_projection\")\n gl.glUniformMatrix4fv(loc, 1, False, projection)\n\n def on_timer(self, event):\n self.theta += .5\n self.phi += .5\n model = np.eye(4, dtype=np.float32)\n rotate(model, self.theta, 0, 0, 1)\n rotate(model, self.phi, 0, 1, 0)\n loc = gl.glGetUniformLocation(self.cube, \"u_model\")\n gl.glUniformMatrix4fv(loc, 1, False, model)\n self.update()\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 }