This project provides a function-oriented approach to composing complex shaders. The library contains:
- Dozens of pure GLSL functions, generally useful and often used in GLSL shaders for both OpenGL & WebGL projects (both Clojure & Clojurescript).
- Automatic Clojure metadata generation for GLSL functions (incl. arg lists) to help with documentation in the REPL or for custom tool development.
- A generic transitive dependency graph resolution mechanism for any shader functions defined (not just in this library).
- An optional basic shader minifier (without name mangling)
- GLSL source code can be specified as strings or read from files/resources
Several shader functions have been gathered from literature and other projects and been partially refactored as pure functions.
[thi.ng/shadergraph "0.3.1"]
STABLE
The brief example below defines a shader pair with standard Lambert
lighting. The defglsl
macro is used to define a GLSL sourcecode
snippet with (optional) dependencies. It also minifies the original
sources at compile time (all snippets provided by this library are
minified this way).
(ns example
(:require
[thi.ng.glsl.core :as glsl :include-macros true]
[thi.ng.glsl.vertex]
[thi.ng.glsl.lighting]))
(glsl/defglsl my-vertex-shader
[thi.ng.glsl.vertex/mvp
thi.ng.glsl.vertex/surface-normal]
"void main() {
vNormal = surfaceNormal(normal, normalMat);
gl_Position = mvp(position, model, view, proj);
}")
(glsl/defglsl my-fragment-shader
[thi.ng.glsl.lighting/lambert]
"void main() {
float lam = lambert(normalize(vNormal), normalize(lightDir));
gl_FragColor = vec4(ambientCol + diffuseCol * lightCol * lam, 1.0);
}")
Apart from the defglsl
macro, the assemble
function is the other
main tool in this library. It takes a map of GLSL dependencies and
sourcecode and returns a transformed/expanded source with all
transitive deps injected in the correct order. The my-vertex-shader
spec above in assembled form will expand to:
(glsl/assemble my-vertex-shader)
;; "vec3 surfaceNormal(vec3 normal,mat4 normalMat){return normalize((normalMat*vec4(normal,.0)).xyz);}vec4 mvp(vec3 pos,mat4 model,mat4 view,mat4 proj){return proj*view*model*vec4(pos,1.);}void main(){vNormal=surfaceNormal(normal,normalMat);gl_Position=mvp(position,model,view,proj);}"
Using another library like, e.g. thi.ng/geom ‘s GL module, these
shader sources can then be combined into a fully defined shader like
this (the make-shader-from-spec
function used below generates all
attribute, varying & uniform definitions and setters automatically and
returns a compiled shader program):
(require '[thi.ng.geom.gl.shaders :as shaders])
(def shader
(shaders/make-shader-from-spec
my-gl-context
{:vs (glsl/assemble my-vertex-shader)
:fs (glsl/assemble my-fragment-shader)
:uniforms {:model :mat4
:view :mat4
:proj :mat4
:normalMat :mat4
:ambientCol :vec3
:diffuseCol :vec3
:lightCol :vec3
:lightDir :vec3}
:attribs {:position :vec3
:normal :vec3}
:varying {:vNormal :vec3}}))
- thi.ng.glsl.core
- thi.ng.glsl.buffers
- thi.ng.glsl.color
- thi.ng.glsl.distancefields
- thi.ng.glsl.fog
- thi.ng.glsl.grid
- thi.ng.glsl.lighting
- thi.ng.glsl.matrix
- thi.ng.glsl.noise
- thi.ng.glsl.vertex
This project is written in a literate programming format and requires
Emacs & Org-mode to generate usable source code. Assuming both tools
are installed, the easiest way to generate a working project is via
command line (make sure emacs
is on your path or else edit its path
in tangle.sh
):
git clone https://github.com/thi-ng/shadergraph.git
cd shadergraph
# tangle selected files
./tangle.sh README.org src/*.org test/*.org
# ...or individual file
./tangle.sh src/core.org
Tangling is the process of extracting & combining source blocks from
.org
files into an actual working project/source tree.
Once tangling is complete, you can cd
into the generated project
directory (babel
in this case) and then use lein
as usual.
The project.clj
file define an alias to trigger a
complete build & tests for both CLJ & CLJS versions.
cd babel
lein cleantest # some tests currently fail due to still missing protocol impls
To only build the Clojurescript version simply run lein cljsbuild
test
from the same directory. A small HTML harness for the resulting
JS file is also located in that folder (babel/index.html
), allowing
for further experimentation in the browser.
Editing code blocks / files in Org-mode, then re-loading & testing
changes is quite trivial. Simply launch a REPL (via lein or Emacs) as
usual. Everytime you’ve made changes to an .org
file, re-tangle it
from Emacs or tangle.sh
, then reload the namespace in the REPL via
(require 'thi.ng.glsl... :reload)
or similar.
[org.clojure/clojure "1.11.1"]
[org.clojure/clojurescript "1.11.4"]
[com.postspectacular/dependency "0.1.2"]
[com.cemerick/clojurescript.test "0.3.3"]
[lein-cljsbuild "1.1.8"]
(defproject <<project-name>> "<<version>>"
:description "WebGL/GLSL shader library & dependency graph for ClojureScript"
:url "<<project-url>>"
:license {:name "Apache Software License"
:url "http://www.apache.org/licenses/LICENSE-2.0"
:distribution :repo}
:scm {:name "git"
:url "<<project-url>>"}
:min-lein-version "2.4.0"
:dependencies [<<dep-clj>>
<<dep-cljs>>
<<dep-dep>>]
:profiles {:dev {:plugins [<<dep-cljsbuild>>
<<dep-cljs-test>>]
:aliases {"cleantest" ["do" "clean," "cljsbuild" "test"]}}}
:cljsbuild {:builds [{:source-paths ["src" "test"]
:id "simple"
:compiler {:output-to "<<cljs-artefact-path>>"
:optimizations :whitespace
:pretty-print true}}
{:source-paths ["src" "test"]
:id "prod"
:compiler {:output-to "<<cljs-artefact-path>>"
:optimizations :advanced
:pretty-print false}}]
:test-commands {"unit-tests" ["phantomjs" :runner "<<cljs-artefact-path>>"]}}
:pom-addition [:developers [:developer
[:name "Karsten Schmidt"]
[:url "https://thi.ng"]
[:timezone "1"]]])
<!DOCTYPE html>
<html lang="en">
<head>
<title><<project-name>> <<version>> test</title>
</head>
<body>
<canvas id="main" width="640" height="480"></canvas>
<script type="text/javascript" src="<<cljs-artefact-path>>"></script>
</body>
</html>
The autogenerated namespace thi.ng.glsl.version
contains a single
symbol version
holding the version string defined above:
(use 'thi.ng.glsl.version)
(prn version)
; "<<version>>"
(ns thi.ng.glsl.version)
(def version "<<version>>")
Version | Released | Description | Lein coordinates | Tagged Github URL |
---|---|---|---|---|
0.3.1 | 2022-04-23 | update deps | [thi.ng/shadergraph "0.3.1"] | 0.3.1 |
0.3.0 | ??? | CLJC refactor, metadata, ns additions | [thi.ng/shadergraph "0.3.0"] | |
0.2.0 | 2016-03-20 | bugfix minify, update fn naming | [thi.ng/shadergraph "0.2.0"] | 0.2.0 |
0.1.1 | 2015-04-27 | bugfix assemble | [thi.ng/shadergraph "0.1.1"] | 0.1.1 |
0.1.0 | 2015-02-25 | 1st release | [thi.ng/shadergraph "0.1.0"] | 0.1.0 |
Name | Role | Website |
Karsten Schmidt | initiator & principal developer | https://thi.ng |
---|---|---|
Tristan Strange | bugfixes | @triss |