1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
|
# -*- coding: utf-8 -*-
# -----------------------------------------------------------------------------
# Copyright (c) Vispy Development Team. All Rights Reserved.
# Distributed under the (new) BSD License. See LICENSE.txt for more info.
# -----------------------------------------------------------------------------
"""
Displaying quads using Instanced rendering
==========================================
This example is a modification of examples/tutorial/gl/quad.py which
uses instanced rendering to generate many copies of the same quad.
"""
import numpy as np
from vispy import app, use
from vispy.gloo import gl
# we need full gl context for instanced rendering
use(gl='gl+')
vertex_code = """
uniform float scale;
attribute vec4 color;
attribute vec2 position;
attribute vec2 instance_offset;
varying vec4 v_color;
void main()
{
gl_Position = vec4(scale*position + instance_offset, 0.0, 1.0);
v_color = color;
} """
fragment_code = """
varying vec4 v_color;
void main()
{
gl_FragColor = v_color;
} """
class Canvas(app.Canvas):
def __init__(self):
app.Canvas.__init__(self, size=(512, 512), title='Quad (GL)',
keys='interactive')
def on_initialize(self, event):
# Build data
self.data = np.zeros(4, [("position", np.float32, 2),
("color", np.float32, 4)])
self.data['color'] = [(1, 0, 0, 1), (0, 1, 0, 1),
(0, 0, 1, 1), (1, 1, 0, 1)]
self.data['position'] = [(-1, -1), (-1, +1),
(+1, -1), (+1, +1)]
self.n_instances = 1000
self.instances = np.empty(
self.n_instances, [("instance_offset", np.float32, 2)]
)
self.instances['instance_offset'] = (np.random.rand(self.n_instances, 2) - 0.5) * 2
# Build & activate program
# Request a program and shader slots from GPU
program = gl.glCreateProgram()
vertex = gl.glCreateShader(gl.GL_VERTEX_SHADER)
fragment = gl.glCreateShader(gl.GL_FRAGMENT_SHADER)
# Set shaders source
gl.glShaderSource(vertex, vertex_code)
gl.glShaderSource(fragment, fragment_code)
# Compile shaders
gl.glCompileShader(vertex)
gl.glCompileShader(fragment)
# Attach shader objects to the program
gl.glAttachShader(program, vertex)
gl.glAttachShader(program, fragment)
# Build program
gl.glLinkProgram(program)
# Get rid of shaders (no more needed)
gl.glDetachShader(program, vertex)
gl.glDetachShader(program, fragment)
# Make program the default program
gl.glUseProgram(program)
# Build buffer
# Request a buffer slot from GPU
buf = gl.glCreateBuffer()
# Make this buffer the default one
gl.glBindBuffer(gl.GL_ARRAY_BUFFER, buf)
# Upload data
gl.glBufferData(gl.GL_ARRAY_BUFFER, self.data, gl.GL_DYNAMIC_DRAW)
# Bind attributes
stride = self.data.strides[0]
instance_offset = 0
loc = gl.glGetAttribLocation(program, "position")
gl.glEnableVertexAttribArray(loc)
gl.glBindBuffer(gl.GL_ARRAY_BUFFER, buf)
gl.glVertexAttribPointer(loc, 3, gl.GL_FLOAT, False, stride, instance_offset)
instance_offset = self.data.dtype["position"].itemsize
loc = gl.glGetAttribLocation(program, "color")
gl.glEnableVertexAttribArray(loc)
gl.glBindBuffer(gl.GL_ARRAY_BUFFER, buf)
gl.glVertexAttribPointer(loc, 4, gl.GL_FLOAT, False, stride, instance_offset)
# instance buffer
buf = gl.glCreateBuffer()
gl.glBindBuffer(gl.GL_ARRAY_BUFFER, buf)
gl.glBufferData(gl.GL_ARRAY_BUFFER, self.instances, gl.GL_STATIC_DRAW)
stride = self.instances.strides[0]
instance_offset = 0
loc = gl.glGetAttribLocation(program, "instance_offset")
gl.glEnableVertexAttribArray(loc)
gl.glBindBuffer(gl.GL_ARRAY_BUFFER, buf)
gl.glVertexAttribPointer(loc, 2, gl.GL_FLOAT, False, stride, instance_offset)
# this is the magic that says "step by 1 every instance"
gl.glVertexAttribDivisor(loc, 1)
# Bind uniforms
# --------------------------------------
loc = gl.glGetUniformLocation(program, "scale")
gl.glUniform1f(loc, 0.01)
def on_draw(self, event):
gl.glClear(gl.GL_COLOR_BUFFER_BIT)
# you need to call the instanced version of the draw call
gl.glDrawArraysInstanced(gl.GL_TRIANGLE_STRIP, 0, 4, self.n_instances)
def on_resize(self, event):
gl.glViewport(0, 0, *event.physical_size)
if __name__ == '__main__':
c = Canvas()
c.show()
app.run()
|