Glls
A compiler for a Scheme-like language targeting the GLSL, for Chicken Scheme
Install / Use
/learn @AlexCharlton/GllsREADME
glls
glls (GL Lisp Shaders) lets you write GLSL (OpenGL Shader Language) shaders in a convenient pseudo-scheme language in Chicken Scheme. The compilation into GLSL happens at compile-time for zero run-time cost. Run-time compilation and dynamic recompilation is also supported. To those that want to dynamically construct shaders: I solute you.
In addition to the eponymous module, glls also provides the glls-render module. glls-render enhances glls to create automatic rendering functions for each pipeline. When compiled, these rendering functions are created in efficient C, although dynamic functions are also provided. See the section Automatic render functions for details.
The idea for glls was hugely inspired by Varjo. Before learning about Varjo, I had never considered the possibility of writing shaders in anything but the GLSL. Seeing them being written in Lisp was a major, "Of course!" moment.
That said, while this library bears some superficial resemblance to Varjo, the approach is quite different. While Varjo does a lot of work to validate the the lispy-glls expressions (including type checking), glls only performs cursory syntactic checking. The result of this is that one could probably write shaders in Varjo without knowing the GLSL and could be reasonably sure that those shaders would always compile to something that would mostly work. glls makes no such promises, so it is entirely possible to generate GLSL that won’t compile. Being able to understand GLSL code is therefore a prerequisite for successful shader debugging. The GLSL code output by glls is beautifully formatted, thanks to Alex Shinn’s amazing fmt library. fmt is responsible for far more than just the GLSL formatting, since it is basically a compiler of its own. The compilation portion of glsl is more or less a thin layer on top of fmt.
glls should work on Linux, Mac OS X, Windows, and with OpenGL ES. glls will automatically compile with ES support on ARM hardware, or when gles is defined during compilation (e.g. chicken-install -D gles).
Installation
This repository is a Chicken Scheme egg.
It is part of the Chicken egg index and can be installed with chicken-install glls.
Requirements
- make
- fmt
- matchable
- miscmacros
- opengl-glew
- srfi-42
Documentation
glls contains three modules: glls-render, glls, and glls-compiler. glls-render rexports glls and glls-compiler and is used when you want the functionality of glls plus the addition of automatically generated render functions. glls is the primary module, providing the main interface to shaders and pipelines, which also rexports glls-compiler. glls-compiler provides the functions used to compile shaders.
[parameter] glsl-version
The default GLSL version used by shaders. Defaults to 120 on GL ES platforms, 330 otherwise. Can also be a list, see <version> under Shader syntax. When compiling a file with a shader, modifying this parameter will only take effect if you change it before the compilation phase. E.g.:
(use-for-syntax glls)
(begin-for-syntax (glsl-version 300))
Shaders
[record] (shader TYPE SOURCE INPUTS OUTPUTS UNIFORMS PROGRAM)
Used to represent shaders. Returned by define-shader and create-shader. It should not typically be necessary to access the slots of this record.
[macro] (define-shader SHADER-NAME GLLS-SHADER)
Defines a new shader named NAME. The (unquoted) form GLLS-SHADER should conform to language defined in the section The glls shader language. Before shaders are used, they must be compiled by OpenGL with compile-shader.
[procedure] (create-shader GLLS-SHADER )
Creates (at run-time) a new shader. The form GLLS-SHADER should conform to language defined in the section The glls shader language. Before shaders are used, they must be compiled by OpenGL with compile-shader.
[procedure] (compile-glls GLLS-SHADER)
Returns the source string for a shader. The form GLLS-SHADER should conform to language defined in the section The glls shader language.
[procedure] (compile-shader SHADER)
Compile (in OpenGL) SHADER. Nothing is done if the shader has already been compiled. This typically does not need to be called, since compile-pipeline does so. Must be called while there is an active OpenGL context.
Pipelines
Pipelines are the term that glsl uses to describe a collection of shaders that will be linked together. This is equivalent to a GL program, just less ambiguously named.
[record] (pipeline SHADERS ATTRIBUTES UNIFORMS PROGRAM)
Created with define-pipeline or create-pipeline, contains the data needed for a pipeline. SHADERS is the list of shader records. ATTRIBUTES and UNIFORMS are lists of the attributes and uniforms of the shader, specified as (name . type) pairs before compilation (with compile-pipeline or compile-pipelines) and (name location type) lists after compilation. PROGRAM is the GL ID of the program (always 0 before compilation).
[macro] (define-pipeline PIPELINE-NAME . SHADERS)
Defines a new pipeline named NAME. The SHADERS should either be forms conforming to language defined in the section The glls shader language, shaders defined by define-shader, or a mix of the two. Pipelines must have at least one vertex and one fragment shader to be able to compile. Before pipelines are used, they must be compiled by OpenGL with compile-pipeline or compile-pipelines.
define-pipeline behaves differently when it is being evaluated and when a given pipeline is being redefined. In this case, the new pipeline inherits the GL program ID of the old one. Additionally, the pipeline is compiled by OpenGL right away (and as a consequence, so are any pipelines that are pending compilation). This is done so that pipelines can be edited and reevaluated in a REPL session and one’s scene will be updated as expected. See the interactive example for an example of how this can be accomplished.
define-pipeline has additional effects when used with the glls-render module (see Automatic render functions).
[procedure] (create-pipeline . SHADERS)
Creates (at run-time) a new pipeline. The SHADERS should either be forms conforming to language defined in the section The glls shader language, shaders, or a mix of the two. Pipelines must have at least one vertex and one fragment shader to be able to compile. Before pipelines are used, they must be compiled by OpenGL with compile-pipeline or compile-pipelines.
[procedure] (compile-pipeline PIPELINE)
Compile (in OpenGL) the PIPELINE and sets its PROGRAM slot to the OpenGL program ID. If the pipeline’s PROGRAM slot is already set to a non-zero value, this ID will be reused for the new program. Compiles all of the pipeline’s shaders with compile-shader. Must be called while there is an active OpenGL context.
[procedure] (compile-pipelines)
Compile (as per compile-pipeline) all the pipelines defined by define-pipeline and create-pipeline. Must be called while there is an active OpenGL context.
[procedure] (pipeline-uniform UNIFORM PIPELINE)
Return the location of UNIFORM. The PIPELINE must be compiled before this function can be used.
[procedure] (pipeline-attribute ATTRIBUTE PIPELINE)
Return the location of ATTRIBUTE. The PIPELINE must be compiled before this function can be used.
[procedure] (pipeline-mesh-attributes PIPELINE)
Return a list of (ATTRIBUTE-NAME . LOCATION) pairs, suitable for passing to gl-utils’ mesh-make-vao!.
The glls shader language
Shader syntax
The shaders of glls – the forms that define-shader, define-pipeline, etc. expect – have the following syntax:
(<type> [input: <inputs>] [uniform: <uniforms>] [output: <outputs>]
[version: <version>] [use: <imports>] [export: <exports])
<body> ...
type is the keyword type of the shader. It must be one of #:vertex, #:fragment, #:geometry, #:tess-control, #:tess-evaluation, or #:compute.
inputs is a list of the input variables for the shader. These are given in (name type) lists.
uniforms is a list of the uniform variables for the shader. These are given in (name type) lists.
outputs is a list of the output variables from the shader. These are given in (name type) lists.
version is the integer version number of the shader, i.e. the number you would write at the top of the shader source (e.g. #version 410). Defaults to the glsl-version parameter. This can also be a list of the form '(<version> <specifiers> ...) where the specifiers will be appended to the #version line when compiled to glsl. For example, '(300 es) becomes #version 300 es.
imports is the list of shaders that the current shader depends on. See the section Shaders that export for more details. Defaults to ()
exports is the list of symbols that the current shader exports. See the section Shaders that export for more details. Defaults to ()
body is the form representing the code of the shader. See the section Shader Lisp for an explanation of the kind of code that is expected.
Shader Lisp
For the most part, the Lisp used to define glls shaders looks like Scheme with one notable difference: types must be specified whenever a variable or function is defined
