Skip to content
This repository has been archived by the owner on Nov 9, 2017. It is now read-only.

Shaders and GPGPU

ztellman edited this page Sep 13, 2010 · 4 revisions

this is a work in progress

This article assumes a certain amount of familiarity with the OpenGL programmable pipeline and GLSL. Many details are abstracted away in Penumbra’s implementation, but it is still useful to know, for instance, that linear interpolation can be represented by mix(a, b, t) in GLSL. Everything I know about GLSL came from this book, so you might look there. A freely available alternative is the GLSL specification, which can be downloaded here.

The basics

If you’re following a tutorial for standard OpenGL (such as the excellent one here), you may wish to use straight GLSL to define your pipeline. This can be accomplished like so:


(penumbra.opengl.shader/compile-shader
  :vertex
  "void main()
   {
     gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
   }"
  :fragment
  "void main()
   {
     gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
   }")

This will create a pipeline which transforms the geometry normally, and turns everything red, regardless of the vertex color or lighting. However, this is not the suggested approach. An idiomatic usage of Penumbra would instead look like:


(penumbra.opengl/create-program
  :vertex
  '(<- :position (* :model-view-projection-matrix :vertex))
  :fragment
  '(<- :frag-color (color4 1 0 0 1)))

Notice that this mimics Clojure, but uses an unfamiliar operator <- for assignment. Penumbra turns this s-expression representation into GLSL. As GLSL is a statically typed language, but there is a rudimentary type inference capability in place, and constructs such as (let ...), (if ...), (when ...), and (cond ...) are supported. In either of these cases, the returned value can be used by the (with-program ...) macro, in this manner:


(with-program program
  (draw-triangles
    (vertex 0 0)
    (vertex 1 0)
    (vertex 1 1)))

This will render a red triangle. with-program safely nests, and (with-program nil ...) can be used to enable the fixed-function pipeline, which is also enabled outside the scope of with-program. In a more complex example, imagine that instead of only turning the geometry red, we want to be able to change the color. We can accomplish this using a uniform variable, which is accessible at all stages in the pipeline.


(defn init [state]
  (def program (create-program
                :declarations '[(uniform float4 tint)]
                :vertex '(<- :position (* :model-view-projection-matrix :vertex))
                :fragment '(<- :frag-color tint)))
  state)

(defn reshape [[x y w h] state]
  (frustum-view 45 (/ w h) 0.1 10)
  state)

(defn display [_ state]
  (translate -0.5 -0.5 -5)
  (with-program program
    (uniform :tint 1. 1. 0. 1.)
    (draw-triangles
     (vertex 0 0 0)
     (vertex 0 1 0)
     (vertex 1 1 0))))

(app/start {:init init, :reshape reshape, :display display} {})

In this, we define a tint variable, which we set every frame. (uniform ...) must be called within the scope of (with-program ...).

Clone this wiki locally