File: quad_instanced.py

package info (click to toggle)
python-vispy 0.15.2-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 8,868 kB
  • sloc: python: 59,799; javascript: 6,800; makefile: 69; sh: 6
file content (144 lines) | stat: -rw-r--r-- 4,839 bytes parent folder | download | duplicates (2)
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()