レガシーコード生産ガイド

私に教えられることなら

S式→JSトランスレータをGLSLにも対応させた

紹介ではなく振り返り用のメモ。

なにした

Scheme(Gauche)でS式→JSトランスレータ書いた - レガシーコード生産ガイド時点でJavaScript専用だったS式からのトランスレータjasjを、マクロを使えるようにして、GLSLにも少しだけ対応させた。

コード(GLSLは下の方)動作サンプル

多言語に対応するため、コア部分を書き直し、Gaucheのモジュール機構も調べて分割した。マクロについては、単純にハッシュテーブルにマクロ変換用関数を登録して、トランスレート時にどのマクロテーブルを使うか指定するようにした。

あとはGLSL用に、JavaScript版からいくつかの変更(暗黙のreturn除去・letやvarを除去)を行った。

コード例

GLSLはまだ学び始めたばかりだけど、早速

  • uniform, attributeなどの宣言が長い。
  • varyingで同じ宣言をコピーするのが面倒、ミスしやすい。
  • 関数適用や計算を入れ子にすることが多いので、普通に入れ子にすると把握できなくなる。

などの不満があったので、以下のマクロを書いた。

  • @uniforms @attributes などで一気に宣言する。
  • @varyingsは更に型と変数名を保存して、@verayings-receiveで同じ宣言を自動的に追加する。
  • その前の式で<>を置き換えていくcutinマクロで、入れ子ではなく同じレベルで適用していく式を書ける。

頂点シェーダ、フラグメントシェーダのコードは次のようなカンジになる。マクロの定義は一番下に置いておく。

;; vertex shader
;; -----------------------------------------------------------------------------
(define-jasj vs [:glsl glmacs]
  (@attributes
   [vec3 position] [vec2 texcoord] [vec4 color] [vec3 normal])
  (@uniforms [mat4 mvp_mat])
  (@varyings [vec2 vtexcoord] [vec3 vnormal] [vec4 vcolor])
  (defn main void []
    (sets! [vtexcoord texcoord] [vnormal normal] [vcolor color])
    (cutin (@vec4 position 1.0) (* mvp_mat <>) (set! gl_Position <>))))

;; fragment shader
;; -----------------------------------------------------------------------------
(define-jasj fs [:glsl glmacs]
  (precision mediump float)
  (@uniforms [mat4 inv_mat] [vec3 light_dir] [vec3 cam_pos]
             [vec3 cam_point] [sampler2D texture])
  (@varyings-receive)
  (defn main void []
    (vec3 eye_dir (- cam_pos cam_point))
    (vec3 inv_eye
          (cutin (@vec4 eye_dir 1.0) (* inv_mat <>)
                 (normalize <>) (@ <> xyz)))
    (vec3 inv_light
          (cutin (@vec4 light_dir 1.0)
                 (* inv_mat <>)
                 (@ <> xyz)))
    (vec3 half_vec (normalize (+ inv_light inv_eye)))
    (float diff
           (cutin (normalize inv_light) (dot <> vnormal) (clamp <> 0.1 1.0)))
    (float spec
           (cutin (dot half_vec vnormal) (clamp <> 0.0 1.0) (pow <> 20.0)))
    (vec4 vcolor2 (texture2D texture vtexcoord))
    (cutin vcolor2.rgb
           (+ (* <> diff) spec (* vcolor.rgb 0.2))
           (@vec4 <> vcolor2.a)
           (set! gl_FragColor <>))))

おわりに

これでS式→ほげ言語へのトランスレータがそこそこ楽に書けるようになった。htmlやcssも書けるようにして、Web関係で楽したい。あとはC言語もかな。マクロとparedit使えるのが本当に楽だ。

途中で、特にハッシュテーブル登録でどうこうしてる時に「これオブジェクト指向で書きなおした方がいいよな……」と思ったんだけど、何か今の実力だと危ういカンジがしたので全部クロージャでどうにかした。まだ把握できるコードではあると思う。

あとはドキュメントかな……そろそろ自分が忘れ始めそうだ。でもあんまり整備してしまうと、万が一誰かが使って、要望とか出始めたらとてもダルい。応えるのがダルいんじゃなくて、自分の性格的に多分がんばって応えようとしてしまうのがダルい。断れる訓練のために敢えて整備してもいいかもしれないけど。。とりあえず自分用のドキュメントだけは毎日少しずつモードに切り替えて書いていきたい。

マクロ

;; glsl macros & code
;; =============================================================================
(define glmacs (make-jamacs))
(define (consx x) (cut cons x <>))

(define-jamac glmacs @va [type name] `(varying   ,type ,name))

(define-jamac glmacs @attributes args
  (let [(decs (map (consx 'attribute) args))]
    `(splice ,@decs)))

(define-jamac glmacs @uniforms args
  (let [(decs (map (consx 'uniform) args))]
    `(splice ,@decs)))

;; varyings
(define varying-pairs '())

(define-jamac glmacs @varyings args
  (let [(decs (map (consx 'varying) args))]
    (set! varying-pairs args)
    `(splice ,@decs)))

(define-jamac glmacs @varyings-receive []
  (let [(decs (map (consx 'varying) varying-pairs))]
    `(splice ,@decs)))

(define-jamac glmacs sets! args
  (let [(sets (map (consx 'set!) args))]
    `(splice ,@sets)))

;; cut-in macro
(define-jamac glmacs cutin [x . fs]
  (define (replace-sym x)
    (lambda (sym) (if (eq? sym '<>) x sym)))
  (define (replace-cutin expr x)
    (cond
     [(symbol? expr) ((replace-sym x) expr)]
     [(null? expr) '()]
     [(pair? expr)
        (cons (replace-cutin (car expr) x)
              (replace-cutin (cdr expr) x))]
     [else expr]))
  (fold replace-cutin x fs))
広告を非表示にする