Skip to content

Instantly share code, notes, and snippets.

@Aeva
Last active March 31, 2017 16:53
Show Gist options
  • Save Aeva/e2c7673f08651f59ae5c8435a2066345 to your computer and use it in GitHub Desktop.
Save Aeva/e2c7673f08651f59ae5c8435a2066345 to your computer and use it in GitHub Desktop.
scheme shader dsl mockup

This is a mockup for a scheme shader DSL. The main idea here is to conceptualize shaders as being a dependency resolution problem instead of a mess of procedural code. So instead, we define some named data structures. These structures can name other structures as “dependencies”. Data members follow the form of a name, an optional value, and zero or more tags to specify type. Type is optional, but if the struct is under-specified, evaluating it will throw an error.

The variant macro can be used to provide polymorphism (maybe the macro should just be “polymorph”).

Lastly, the vertex and fragment shader macros can take any number of structs as dependencies, but in this case, they only need “model”. The rest will be pulled in automatically.

This lets us do a number of things:

  1. C structs and helper APIs can be easily generated automatically from the struct and variant macros.
  2. By inferring ALL THE THINGS, code is smaller, more specific.
  3. Logic is pulled out of explicit shader stages and on to data structures. So for example, you could define a standard model struct and a skinning varriant, and use them both in various shaders.
  4. Because this is just scheme, you can import structs and shaders from other scheme modules and mix and match.

Below is a sample shader and its intended compiled output:

(struct <scene> ()
        (elapsed-time #:float)) ;; will be a uniform

(struct <camera> ()
        (matrix #:mat4)) ;; will be a uniform

;; 'camera' is listed as a dependency
(struct <model> (camera)
        ;; this will be an attribute
        (local-position #:vec3 #:vertex)

        ;; this will be an attribute and a varrying variable
        (texture-coordinate #:vec2 #:vertex)

        ;; these both will be a uniform, though maybe also several
        ;; attributes if instancing was used
        (world-matrix #:mat4)
        (texture #:image)
  
        ;; inlined value or const
        (some-thing 3 #:float)
        (world-position (* world-matrix local-position))

        ;; inlined value or const
        (screen-position (* camera:matrix world-matrix local-position)))
 
;; This extends model and adds a new dependency.
(variant <spinner> <model> (scene)
         (world-position
          (* (mat4-rotate-z (/ scene:elapsed-time 1000)) local-position))
         (screen-position
          (* camera:matrix (mat4-rotate-z (/ scene:elapsed-time 1000)) 
             local-position)))

;; Define a vertex shader:
(vertex-shader <simple> (model) model:screen-position)

;; Define a fragment shader:
(fragment-shader <simple> (model)
                 (sample model:texture model:texture-coordinate))

This might translate to the following shader pair:

// pseudo vertex shader
attribute vec3 model_local_position;
attribute vec2 model_texture_coordinate;
varying vec2 share_model_texture_coordinate;

uniform float elapsed_time;
uniform mat4 camera_matrix;
uniform int model_variant; 
uniform mat4 model_world_matrix;

void main ()
{
  share_model_texture_coordinate = model_texture_coordinate;
  vec4 model_screen_position;
  if (model_variant == 0)
  {
    model_screen_position = camera_matrix * model_world_matrix * model_local_position;
  }
  else if (model_variant == 1)
  {
    model_screen_position = camera_matrix * mat4_rotate_z(elapsed_time * 0.001) * model_world_matrix * model_local_position;
  }
  gl_Position = model_screen_position;
}


// pseudo fragment shader
varying vec2 share_model_texture_coordinate;
uniform sampler2D model_texture;

void main ()
{
  gl_FragColor = texture2D(model_texture, share_model_texture_coordinates);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment