Skip to content

Commit

Permalink
Add pixel read feature.
Browse files Browse the repository at this point in the history
Fix #18 - enhancement request from Circu Vitu.
  • Loading branch information
rogerallen committed Aug 9, 2014
1 parent 6b1116d commit 50dbc70
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 11 deletions.
51 changes: 44 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,16 +43,33 @@ I've created a few screencast demos that you can watch:
* [A quick teaser](https://www.youtube.com/watch?v=kyL3xc7MzR0) from the [disco example](https://github.com/overtone/shadertone/blob/master/examples/02demo_disco.clj)
* [Video preview](https://www.youtube.com/watch?v=UMg8Td5Gqhk) showing the very early stages of shadertone.

[Meta-eX](http://meta-ex.com/) has used Shadertone in performance. Here are a vew videos from their May, 2013 Kiev #hotcode conference performance that show how Shadertone can be used:
[after party 1](http://vimeo.com/67487486), [main conference](http://vimeo.com/67487485), [after party 1](http://www.youtube.com/watch?v=CjFCSTQbJx0), [after party 2](http://www.youtube.com/watch?v=CjFCSTQbJx0).

If you're interested, here's [the original announcement](https://groups.google.com/forum/?fromgroups=#!topic/overtone/7bQSJUUviBw) on the Overtone Google Group.
[Meta-eX](http://meta-ex.com/) has used Shadertone in live
performance. Here are a vew videos from their May, 2013 Kiev #hotcode
conference performance that show how Shadertone can be used: [after
party 1](http://vimeo.com/67487486), [main
conference](http://vimeo.com/67487485), [after party
1](http://www.youtube.com/watch?v=CjFCSTQbJx0), [after party
2](http://www.youtube.com/watch?v=CjFCSTQbJx0). Latest news & videos
can always be found at [their website](http://meta-ex.com)

[Repl Electric](http://www.repl-electric.com/) has also used
Shadertone in performance. Here is a wonderful recreation of ["The
Stars" livecoded on Vimeo](http://vimeo.com/95988263).

If you're interested, here's [the original
announcement](https://groups.google.com/forum/?fromgroups=#!topic/overtone/7bQSJUUviBw)
on the Overtone Google Group.

I hope this library allows you to create some happiness. Enjoy!

## Usage

The library is just coming together, so expect change. There are two main ways to use the code. If you want the latest code or are just exploring what shadertone can do, you should clone the repo from github. But, if you have a project idea and want to use shadertone as part of that project, you can specify the current version in your Leiningen project.clj to download the library from clojars.
The library is just coming together, so expect change. There are two
main ways to use the code. If you want the latest code or are just
exploring what shadertone can do, you should clone the repo from
github. But, if you have a project idea and want to use shadertone as
part of that project, you can specify the current version in your
Leiningen project.clj to download the library from clojars.

### Option 1: clone this repository

Expand Down Expand Up @@ -166,7 +183,7 @@ Via a call like this:
##### Synth Inputs
__New in 0.2.0__, you can `tap` a synth value and easily communicate that
You can `tap` a synth value and easily communicate that
value to your GLSL fragment shader. See the "vvv" synth example in
the 00_demo_intro_tour.clj demo and also the core.clj usage.
Expand Down Expand Up @@ -215,7 +232,7 @@ could be useful for other interactive ideas.
### Lisp-like GLSL
__New in 0.2.0__, you can program your GLSL in a lisp-like language. It is a simple, straight translation
You can program your GLSL in a lisp-like language. It is a simple, straight translation
layer from lisp to a GLSL string. For those hoping for Clojure instead of lisp,
I welcome your suggestions and help, but a Clojure-to-C compiler was way too much work for me at this time.
Expand Down Expand Up @@ -254,8 +271,28 @@ lisp into a string for passing to the `start` function.
* return value;
`(return <statement>)`
### Reading Pixels from the Framebuffer
__NEW in 0.2.4__, you can read back a pixel value from your frame
either via `(shadertone.shader/pixel)` function or by accessing the
`@shadertone.shader/pixel-value` atom directly. With this feature,
your fragment-shader could control your synths! Thanks to Circu Vitu
on the Google Group for this idea.
Enable this feature via `(pixel-read-enable! x y)` where x and y are
valid locations within your window. Be careful as there is no error
checking here.
Disable this access via `(pixel-read-disable!)`
## Changes
* __0.2.4 - In progress__
* Enhancement: [Issue #18](https://github.com/overtone/shadertone/issues/18) - Add pixel read feature. Thanks to Circu Vitu.
* Enhancement: [Issue #14](https://github.com/overtone/shadertone/issues/14) - No external change since LWJGL is limited to one window, but code has been improved by reducing the use of global state a bit.
* Enhancement: [Issue #23](https://github.com/overtone/shadertone/issues/23) - Generally improve error handling & robustness. Thanks to [josephwilk](https://github.com/josephwilk)
* __0.2.3 - Released Mar 8, 2014__
* Enhancement: Update to LWJGL 2.9.1
Expand Down
63 changes: 59 additions & 4 deletions src/shadertone/shader.clj
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,13 @@
:tex-types [] ; :cubemap, :previous-frame
;; a user draw function
:user-fn nil
;; pixel read
:pixel-read-enable false
:pixel-read-pos-x 0
:pixel-read-pos-y 0
:pixel-read-data (-> (BufferUtils/createByteBuffer 3)
(.put (byte-array (map byte [0 0 0])))
(.flip))
})

;; GLOBAL STATE ATOMS
Expand All @@ -76,6 +83,8 @@
;; first time.
(defonce watcher-just-started (atom true))
(defonce throw-on-gl-error (atom true))
;;
(defonce pixel-value (atom [0.0 0.0 0.0]))

;; ======================================================================
;; code modified from
Expand Down Expand Up @@ -495,6 +504,13 @@
:i-date-loc i-date-loc
:shader-str fs-shader)))))))

(defn- get-pixel-value
[rgb-bytes]
(let [rf (/ (float (int (bit-and 0xFF (.get rgb-bytes 0)))) 255.0)
gf (/ (float (int (bit-and 0xFF (.get rgb-bytes 1)))) 255.0)
bf (/ (float (int (bit-and 0xFF (.get rgb-bytes 2)))) 255.0)]
[rf gf bf]))

(defn- draw
[locals]
(let [{:keys [width height i-resolution-loc
Expand All @@ -509,7 +525,10 @@
channel-time-buffer
old-pgm-id old-fs-id
tex-ids tex-types
user-fn]} @locals
user-fn
pixel-read-enable
pixel-read-pos-x pixel-read-pos-y
pixel-read-data]} @locals
cur-time (/ (- last-time start-time) 1000.0)
cur-date (Calendar/getInstance)
cur-year (.get cur-date Calendar/YEAR) ;; four digit year
Expand Down Expand Up @@ -601,8 +620,16 @@
(GL11/glBindTexture GL11/GL_TEXTURE_2D (nth tex-ids i))
(GL11/glCopyTexImage2D GL11/GL_TEXTURE_2D 0 GL11/GL_RGBA8 0 0 width height 0)
(GL11/glBindTexture GL11/GL_TEXTURE_2D 0)))
(except-gl-errors "@ draw after copy")

(except-gl-errors "@ draw after copy")))
;; read a pixel value
(when pixel-read-enable
(GL11/glReadPixels pixel-read-pos-x pixel-read-pos-y
1 1
GL11/GL_RGB GL11/GL_UNSIGNED_BYTE
pixel-read-data)
(except-gl-errors "@ draw after pixel read")
(reset! pixel-value (get-pixel-value pixel-read-data)))))

(defn- update
[locals]
Expand Down Expand Up @@ -634,7 +661,7 @@
:mouse-ori-y cur-mouse-ori-y)
(if (:shader-good @locals)
(draw locals)
;; just clear to prevent strobing awfulness
;; else clear to prevent strobing awfulness
(do
(GL11/glClear GL11/GL_COLOR_BUFFER_BIT)
(except-gl-errors "@ bad-draw glClear ")
Expand Down Expand Up @@ -913,10 +940,38 @@
(undecorate-display!)
(start-shader-display mode shader-filename-or-str-atom textures "" false user-data user-fn display-sync-hz)))

(defn throw-exceptions-on-gl-errors
(defn throw-exceptions-on-gl-errors!
"When v is true, throw exceptions when glGetError() returns
non-zero. This is the default setting. When v is false, do not
throw the exception. Perhaps setting to false during a performance
will allow you to avoid over-agressive exceptions. Leave this true
otherwise." [v]
(reset! throw-on-gl-error v))

(defn pixel-read-enable!
"Enable reading a pixel each frame from location x,y. Be sure x,y
are valid or things may crash! Results are available via the
function (pixel) and via the atom @pixel-value"
[x y]
(swap! the-window-state assoc
:pixel-read-enable true
:pixel-read-pos-x x
:pixel-read-pos-y y)
nil)

(defn pixel-read-disable!
"Disable reading pixel values each frame."
[]
(swap! the-window-state assoc
:pixel-read-enable false)
nil)

(defn pixel
"Return the data that was read from the currently drawn frame at the
x,y location specified in the (pixel-read-enable! x y) call. When
enabled, a [red green blue] vector of floating point [0.0,1.0]
values is returned. Otherwise, [0.0 0.0 0.0] is returned."
[]
(if (:pixel-read-enable @the-window-state)
(get-pixel-value (:pixel-read-data @the-window-state))
[0.0 0.0 0.0]))
21 changes: 21 additions & 0 deletions test/shadertone/shader_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,27 @@ void main(void) {
_ (s/stop)]
(is good-start))))

(deftest pixel-read-test
(testing "Simple pixel-read test"
(let [color (atom [1.0 (/ 128.0 255.0) 0.0 1.0])
_ (s/start (atom "
uniform vec4 iColor;
void main(void) {
gl_FragColor = iColor;
}")
:user-data { "iColor" color})
_ (s/pixel-read-enable! 10 10)
_ (Thread/sleep 1000)
rgb1 (s/pixel)
rgb2 (deref s/pixel-value)
_ (s/pixel-read-disable!)
_ (Thread/sleep 100)
rgb3 (s/pixel)
_ (s/stop)]
(is (= (vec (take 3 @color)) rgb1))
(is (= (vec (take 3 @color)) rgb2))
(is (= [0.0 0.0 0.0] rgb3)))))

(deftest simple-fullscreen-file-test
(testing "Simple fullscreen program file acceptance test"
(let [_ (s/start-fullscreen "test/shadertone/simple.glsl" :textures ["textures/wall.png"])
Expand Down

0 comments on commit 50dbc70

Please sign in to comment.