{ "cells": [ { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "%gui qt" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n# Instanced rendering of arbitrarily transformed meshes\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "from vispy import app, gloo, visuals, scene, use\nimport numpy as np\nfrom scipy.spatial.transform import Rotation\nfrom vispy.io import read_mesh, load_data_file\n\n# full gl+ context is required for instanced rendering\nuse(gl='gl+')\n\n\nvertex_shader = \"\"\"\n// these attributes will be defined on an instance basis\nattribute vec3 shift;\nattribute vec4 color;\nattribute vec3 transform_x;\nattribute vec3 transform_y;\nattribute vec3 transform_z;\n\nvarying vec4 v_color;\n\nvoid main() {\n v_color = color;\n // transform is generated from column vectors (new basis vectors)\n // https://en.wikibooks.org/wiki/GLSL_Programming/Vector_and_Matrix_Operations#Constructors\n mat3 instance_transform = mat3(transform_x, transform_y, transform_z);\n vec3 pos_rotated = instance_transform * $position;\n vec4 pos_shifted = vec4(pos_rotated + shift, 1);\n gl_Position = $transform(pos_shifted);\n}\n\"\"\"\n\nfragment_shader = \"\"\"\nvarying vec4 v_color;\n\nvoid main() {\n gl_FragColor = v_color;\n}\n\"\"\"\n\n\nclass InstancedMeshVisual(visuals.Visual):\n def __init__(self, vertices, faces, positions, colors, transforms, subdivisions=5):\n visuals.Visual.__init__(self, vertex_shader, fragment_shader)\n\n self.set_gl_state('translucent', depth_test=True, cull_face=True)\n self._draw_mode = 'triangles'\n\n # set up vertex and index buffer\n self.vbo = gloo.VertexBuffer(vertices.astype(np.float32))\n self.shared_program.vert['position'] = self.vbo\n self._index_buffer = gloo.IndexBuffer(data=faces.astype(np.uint32))\n\n # create a vertex buffer with a divisor argument of 1. This means that the\n # attribute value is set to the next element of the array every 1 instance.\n # The length of the array multiplied by the divisor determines the number\n # of instances\n self.shifts = gloo.VertexBuffer(positions.astype(np.float32), divisor=1)\n self.shared_program['shift'] = self.shifts\n\n # vispy does not handle matrix attributes (likely requires some big changes in GLIR)\n # so we decompose it into three vec3; (column vectors of the matrix)\n transforms = transforms.astype(np.float32)\n self.transforms_x = gloo.VertexBuffer(transforms[..., 0].copy(), divisor=1)\n self.transforms_y = gloo.VertexBuffer(transforms[..., 1].copy(), divisor=1)\n self.transforms_z = gloo.VertexBuffer(transforms[..., 2].copy(), divisor=1)\n self.shared_program['transform_x'] = self.transforms_x\n self.shared_program['transform_y'] = self.transforms_y\n self.shared_program['transform_z'] = self.transforms_z\n\n # we can provide additional buffers with different divisors, as long as the\n # amount of instances (length * divisor) is the same. In this case, we will change\n # color every 5 instances\n self.color = gloo.VertexBuffer(colors.astype(np.float32), divisor=1)\n self.shared_program['color'] = self.color\n\n def _prepare_transforms(self, view):\n view.view_program.vert['transform'] = view.get_transform()\n\n\n# create a visual node class to add it to the canvas\nInstancedMesh = scene.visuals.create_visual_node(InstancedMeshVisual)\n\n# set up vanvas\ncanvas = scene.SceneCanvas(keys='interactive', show=True)\nview = canvas.central_widget.add_view()\nview.camera = 'arcball'\nview.camera.scale_factor = 1000\n\nN = 1000\n\nmesh_file = load_data_file('orig/triceratops.obj.gz')\nvertices, faces, _, _ = read_mesh(mesh_file)\n\nnp.random.seed(0)\npos = (np.random.rand(N, 3) - 0.5) * 1000\ncolors = np.random.rand(N, 4)\ntransforms = Rotation.random(N).as_matrix()\n\nmultimesh = InstancedMesh(vertices * 10, faces, pos, colors, transforms, parent=view.scene)\n# global transforms are applied correctly after the individual instance transforms!\nmultimesh.transform = visuals.transforms.STTransform(scale=(3, 2, 1))\n\n\nif __name__ == '__main__':\n import sys\n if sys.flags.interactive != 1:\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 }