teaching machines

Showing a 3-D model on our Raspberry Pi

October 22, 2012 by . Filed under buster, public.

Eric got his OBJ loader working, we think, so it came time for me to integrate his code with my demo renderers. It kind of worked. I added a normal attribute to the vertex shader and sent it along to the fragment shader, where I calculated some simple directional lighting in eye space. I drew the indexed geometry. And the result is a bit off:

Al’s shadiness is shady.

We’re looking at Al’s backside, but we’re seeing through some of him. It looks like my depth test isn’t working. That’s work for another day.

Our test renderer looks like this:

from pyopengles import *

from render import Renderer
from eric import TriMesh
import render
import time
import sys

WIDTH = 1680
HEIGHT = 1050

class TriMoveRenderer(Renderer):
  def __init__(self):
    self.context = EGL()
    self.startTime = time.time()

    self.vertexShader = """
      attribute vec3 position;
      attribute vec3 normal;

      varying vec3 fnormal;

      void main() {
        gl_Position = vec4(position, 1.0);
        fnormal = normal;
      }
    """

    self.fragmentShader = """
      precision mediump float;

      uniform float time;
      uniform vec2 mouse;
      uniform vec2 resolution;

      const vec3 albedo = vec3(1.0);
      const float ambientStrength = 0.1;
      const float diffuseStrength = 1.0 - ambientStrength;
      const vec3 lightDirection = normalize(vec3(5.0, 5.0, -5.0));
      const vec3 eyeDirection = vec3(0.0, 0.0, -1.0);
      const float shininess = 50.0;

      varying vec3 fnormal;

      void main() {
        vec3 normal = normalize(fnormal);

        vec3 ambient = ambientStrength * albedo;

        float nDotL = dot(normal, lightDirection);
        vec3 diffuse = diffuseStrength * nDotL * albedo;

        vec3 halfVector = normalize(eyeDirection + lightDirection);
        float nDotH = dot(normal, halfVector);
        vec3 specular = pow(nDotH, cos(shininess)) * vec3(1.0);

        vec3 color = ambient + diffuse + specular;

        gl_FragColor = vec4(color, 1.0);
      }
    """

    self.binding = ((0, 'position'),
                    (1, 'normal'),)
    self.program = self.context.get_program(self.vertexShader, self.fragmentShader, self.binding)

    opengles.glUseProgram(self.program)
    self.mouseUID = opengles.glGetUniformLocation(self.program, "mouse")
    self.resolutionUID = opengles.glGetUniformLocation(self.program, "resolution")
    self.timeUID = opengles.glGetUniformLocation(self.program, "time")
    opengles.glUseProgram(0)

    opengles.glUseProgram(self.program)
    opengles.glUniform2f(self.resolutionUID, eglfloat(WIDTH), eglfloat(HEIGHT))
    opengles.glUniform2f(self.mouseUID, eglfloat(0.5), eglfloat(0.5))
    opengles.glUseProgram(0)

    opengles.glClearColor(eglfloat(0.45), eglfloat(0.35), eglfloat(0.15), eglfloat(1.0))

    mesh = TriMesh('../al.obj')
    self.vertices = eglfloats(mesh.verts)
    self.normals = eglfloats(mesh.vNorms)
    self.faces = eglshorts(mesh.faces)
    self.checkError('after init')

    opengles.glEnable(GL_DEPTH_TEST)

  def onMouseEvent(self, event):
    opengles.glUseProgram(self.program)
    opengles.glUniform2f(self.mouseUID, eglfloat(event.x / float(WIDTH)), eglfloat(event.y / float(HEIGHT)))
    opengles.glUseProgram(0)

  def onDraw(self):
    opengles.glViewport(0, 0, self.context.width, self.context.height);
    opengles.glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
    opengles.glPointSize(eglfloat(5.0))

    self.checkError('before use program')

    opengles.glUseProgram(self.program)

    opengles.glUniform2f(self.resolutionUID, eglfloat(WIDTH), eglfloat(HEIGHT))
    opengles.glUniform1f(self.timeUID, eglfloat(time.time() - self.startTime))

    opengles.glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, self.vertices)
    opengles.glEnableVertexAttribArray(0)

    opengles.glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, self.normals)
    opengles.glEnableVertexAttribArray(1)

    opengles.glDrawElements(GL_TRIANGLES, len(self.faces), GL_UNSIGNED_SHORT, self.faces)
    opengles.glUseProgram(0)

    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 TriMoveRenderer() as renderer:
    render.go(WIDTH, HEIGHT, 0.02, renderer)

I added an eglshorts to pyopengles, as glDrawElements wouldn’t accept ints. I also learned how to make a package in Python: make a directory named with your package name and add an __init__.py file inside of it. I made a package named eric and dropped TriMesh.py inside.