Skip to content

Commit

Permalink
Merge pull request #22 from tavurth/master
Browse files Browse the repository at this point in the history
Added OpenGL render example, updated doc to include example
  • Loading branch information
AlexCharlton authored Sep 19, 2017
2 parents f2dd673 + 0585118 commit 543d5f1
Show file tree
Hide file tree
Showing 4 changed files with 254 additions and 0 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,11 @@ These can be loaded through Quicklisp with:

```lisp
(ql:quickload :cl-glfw3-examples)
(cl-glfw3-examples:events-example)
(cl-glfw3-examples:basic-window-example)
(cl-glfw3-examples:particles-basic-example)
(cl-glfw3-examples:fragment-shader-example)
```

### Documentation
Expand Down
2 changes: 2 additions & 0 deletions cl-glfw3-examples.asd
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,6 @@
:pathname "examples/"
:components ((:file "package")
(:file "basic-window")
(:file "fragment-shader")
(:file "particles-basic")
(:file "events")))
161 changes: 161 additions & 0 deletions examples/fragment-shader.lisp
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
(in-package #:cl-glfw3-examples)

(export '(fragment-shader-example))

;; Error handling for shader compilation events
(define-condition compile-error (error)
((message
:initform nil
:initarg :message
:reader compile-error-message
:documentation "The reason given for the error")))

(defparameter +vertex-shader-source+ "
// Input our our time variable
uniform float time;
// Pass to frag shader
varying vec2 vUv;
attribute vec4 vert;
void main()
{
vUv = vert.xy;
vec4 offsets = vec4(cos(time), sin(time), 1., 1.);
gl_Position = gl_ModelViewProjectionMatrix * vert * offsets;
}
")

(defparameter +fragment-shader-source+ "
varying vec2 vUv;
void main()
{
gl_FragColor = vec4(vUv.x, vUv.y, 1., 1.);
}
")

(defvar *shader-prog* -1)
(defvar *frag-shader* nil)
(defvar *vert-shader* nil)

(defvar *shader-time* 0)

(defun render ()
(gl:clear :color-buffer)

;; Update our time variable in the shader
(gl:uniformf
(gl:get-uniform-location *shader-prog* "time")
(incf *shader-time* 0.01))

(gl:with-pushed-matrix

;; Draw a box far back from the camera
(gl:translate 0 0 -800)
(gl:rect -25 -25 25 25)))

(defun check-shader-error (shader)
"Get the current error status of a shader, throw error if status"
(let ((error-string (gl:get-shader-info-log shader)))
(unless (equalp error-string "")
(progn

;; Print to console & then throw error
(format t "~A~%" error-string)
(error
'compile-error
:message error-string)))))

(defun is-invalid-shader (shader)
(= shader -1))

(defun setup-shader ()
;; Keep trying to load our shader (Allow user to fix compile errors)
(loop
while (is-invalid-shader *shader-prog*) do

(with-simple-restart
(retry "Retry compiling shaders.")

;; Create the OpenGL shader in memory
(setf *vert-shader* (gl:create-shader :vertex-shader))
(setf *frag-shader* (gl:create-shader :fragment-shader))

;; Copy our shader source to the OpenGL shader
(gl:shader-source *vert-shader* +vertex-shader-source+)
(gl:shader-source *frag-shader* +fragment-shader-source+)

;; Compile our shader sources into GPU bytecode
(gl:compile-shader *vert-shader*)
(gl:compile-shader *frag-shader*)

(check-shader-error *vert-shader*)
(check-shader-error *frag-shader*)

;; Create the program which controls the shader activation
(setf *shader-prog* (gl:create-program))

;; Then add our shaders to that program
;; The same shader can be attached to different programs
(gl:attach-shader *shader-prog* *vert-shader*)
(gl:attach-shader *shader-prog* *frag-shader*)

;; Linking our shader puts everything together
(gl:link-program *shader-prog*)

;; We'll now draw with this shader in the future
(gl:use-program *shader-prog*))))


;; Standard window setup below this line
;; -------------------------------------

(def-key-callback quit-on-escape (window key scancode action mod-keys)
(declare (ignore window scancode mod-keys))
(when (and (eq key :escape) (eq action :press))
(set-window-should-close)))

(defun set-viewport (width height)
;; Black background
(gl:clear-color 0.2 0.2 0.2 0.2)

(gl:viewport 0 0 width height)
(gl:matrix-mode :projection)
(gl:load-identity)

;; We'll be using a perspective projection
(let ((h (/ height width)))

;; z-near: 9
;; z-far: 50000
(gl:frustum -1 1 (- h) h 9 50000))

(gl:matrix-mode :modelview)
(gl:load-identity))

(def-window-size-callback update-viewport (window w h)
(declare (ignore window))
(set-viewport w h))

(defun fragment-shader-example ()
;; Graphics calls on OS X must occur in the main thread
(with-body-in-main-thread ()
(with-init-window (:title "OpenGL test" :width 600 :height 400)
(set-key-callback 'quit-on-escape)

;; Callback for window resize events
(set-window-size-callback 'update-viewport)
(set-viewport 800 400)

;; Compile our shaders and use the program
(setup-shader)

;; Our render-loop
(loop until (window-should-close-p)
do (render)
do (swap-buffers)
do (poll-events)))))
87 changes: 87 additions & 0 deletions examples/particles-basic.lisp
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
;;;; opengl.lisp
;;;; OpenGL example code borrowed from cl-opengl

(in-package #:cl-glfw3-examples)

(export '(particles-basic-example))

;; Try live-editing these variables in Emacs slime
(defvar color 0)
(defvar rotation 0)

(defparameter *x-min* 1200)
(defparameter *y-min* 800)
(defparameter *z-min* 8000)

(defparameter *x-max* 800)
(defparameter *y-max* 800)
(defparameter *z-max* 800)

(defparameter *n-particles* 1000)

(defun render ()
(gl:clear :color-buffer)

(incf color 0.02)
(incf rotation 0.0001)

(loop
for i from 1 to *n-particles*
do (gl:with-pushed-matrix

;; Random color wave
(gl:color (cos color) (sin color) (sin color))

;; Move to the (random) position of this rectangle
(gl:translate
(- (random *x-max*) (/ *x-min* 2))
(- (random *y-max*) (/ *y-min* 2))
(- (random *z-max*) *z-min* 2))

;; Rotate by the global rotation
(gl:rotate rotation 0.2 0.2 0)

(gl:rect -25 -25 25 25))))

(def-key-callback quit-on-escape (window key scancode action mod-keys)
(declare (ignore window scancode mod-keys))
(when (and (eq key :escape) (eq action :press))
(set-window-should-close)))

(defun set-viewport (width height)
;; Black background
(gl:clear-color 0.2 0.2 0.2 0.2)

(gl:viewport 0 0 width height)
(gl:matrix-mode :projection)
(gl:load-identity)

;; We'll be using a perspective projection
(let ((h (/ height width)))

;; z-near: 9
;; z-far: 50000
(gl:frustum -1 1 (- h) h 9 50000))

(gl:matrix-mode :modelview)
(gl:load-identity))

(def-window-size-callback update-viewport (window w h)
(declare (ignore window))
(set-viewport w h))

(defun particles-basic-example ()
;; Graphics calls on OS X must occur in the main thread
(with-body-in-main-thread ()
(with-init-window (:title "OpenGL particle test" :width 600 :height 400)
(set-key-callback 'quit-on-escape)

;; Callback for window resize events
(set-window-size-callback 'update-viewport)
(set-viewport 800 400)

;; Our render-loop
(loop until (window-should-close-p)
do (render)
do (swap-buffers)
do (poll-events)))))

0 comments on commit 543d5f1

Please sign in to comment.