A texture renderer on the Raspberry Pi
A collaborator is working on using a cluster of Raspberry Pis to build a multi-monitor display wall. We’re using textures in OpenGL to blit each frame across the display. For his benefit, I pieced together a simple texture renderer, where the texture is procedurally generated by XORing the texel indices. XOR textures are interesting:
The renderer, which has lots of dependencies not included here, follows:
#!/usr/bin/env python
import time
import sys
from pyopengles import *
from geometry import *
from glop import *
WIDTH = 1680
HEIGHT = 1050
class XortexRenderer(Renderer):
def __init__(self):
Renderer.__init__(self)
self.context = EGL(depth_size=0)
self.startTime = time.time()
# Position attributes are in [-1, 1] x [-1, 1]. We can turn these into
# texture coordinates ourselves, so we only take in one vertex attribute.
self.vertexShader = """
attribute vec2 position;
varying vec2 texcoord;
void main() {
gl_Position = vec4(position, 0.0, 1.0);
texcoord = position * 0.5 + 0.5;
}
"""
# Color is determined entirely by the texture.
self.fragmentShader = """
precision mediump float;
uniform sampler2D frame;
varying vec2 texcoord;
void main() {
gl_FragColor = texture2D(frame, texcoord);
}
"""
# The position attribute is in slot 0.
self.binding = ((0, 'position'),)
self.program = self.context.get_program(self.vertexShader, self.fragmentShader, self.binding)
# Fill the viewport.
self.vertices = eglfloats((-1.0, -1.0,
1.0, -1.0,
-1.0, 1.0,
1.0, 1.0))
# The will be uploaded to texture unit 0.
opengles.glUseProgram(self.program)
self.frameTextureUniformID = opengles.glGetUniformLocation(self.program, "frame")
opengles.glUniform1i(self.frameTextureUniformID, 0)
opengles.glUseProgram(0)
# Let's make that texture!
self.frameTextureID = eglint()
opengles.glActiveTexture(GL_TEXTURE0)
opengles.glGenTextures(1, ctypes.byref(self.frameTextureID))
opengles.glBindTexture(GL_TEXTURE_2D, ctypes.byref(self.frameTextureID))
opengles.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
opengles.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
# An RGB image. We'll create a colorful XOR texture, 'cuz it's pretty.
width = 128
height = 128
maxIntensity = max(width, height) - 1.0
vals = [0] * width * height * 3
for r in range(height):
for c in range(width):
intensity = int((r ^ c) / maxIntensity * 255)
vals[(r * width + c) * 3 + 0] = intensity * (r < height / 2)
vals[(r * width + c) * 3 + 1] = intensity * (r > height / 2)
vals[(r * width + c) * 3 + 2] = intensity * (c > height / 2)
# Now we ship the texture off. Bye-bye.
opengles.glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, eglubytes(vals))
self.checkError('after init')
def onKeyEvent(self, keycode):
pass
def onMouseEvent(self, event):
pass
def onDraw(self):
opengles.glViewport(0, 0, self.context.width, self.context.height);
opengles.glClear(GL_COLOR_BUFFER_BIT)
opengles.glUseProgram(self.program)
opengles.glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, self.vertices)
opengles.glEnableVertexAttribArray(0)
opengles.glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)
self.checkError('after draw')
openegl.eglSwapBuffers(self.context.display, self.context.surface)
def checkError(self, msg):
e = opengles.glGetError()
if e:
print msg, 'Error: ', hex(e)
sys.exit(1)
if __name__ == "__main__":
with XortexRenderer() as renderer:
render.goMouseless(WIDTH, HEIGHT, 0.02, renderer)