ElmでWebGLしてみる

2018年12月13日 engineering

この記事はElm2(完全版) Advent Calendar 2018の13日目の記事です。

はじめに

Elmが楽しいので、WebGLも動かしてみました。
今回はサンプルを動かして簡単にソースを見て行きます。

環境

  • OSX 10.13.6 (High Sierra)
  • Elm 0.19.0

WebGLライブラリについて

現状(2018/12/10)、elmの0.19.0ではelm-explorations/webglを用いると良さそうです。 https://package.elm-lang.org/packages/elm-explorations/webgl/latest

ちなみにこいつにたどり着くまでにちょっと苦労しました。

どうやらElmにもWebGLのライブラリがあるらしい
↓
Googleで「elm webgl」と検索
↓
検索の一番上に引っかかるのが、elm-community/elm-webgl
↓
deprecated orz
↓
elm-community/webglにリンクが貼られている。
↓
elm-community/webglを見ると使えそうな雰囲気。
↓
githubを見るとdeprecated
↓
elm-explorations/webgl !!

ソースコードを見る

elm-explorations/webgl のsampleコードを軽く眺めて見ます。

ソースコードはこちらのを使用します。examples/cube.elm

Main部分は以下のような感じです。

main : Program Value Float Float
main =
    Browser.element
        { init = \_ -> ( 0, Cmd.none )
        , view = view
        , subscriptions = (\_ -> onAnimationFrameDelta Basics.identity)
        , update = (\dt theta -> ( theta + dt / 5000, Cmd.none ))
        }

通常通りBrowser.elementを使って表示しています。 ラムダ式で書かれていて型がわかりにくいので、関数化してみる以下のような感じでしょうか?

type alias Model =
    Float


type Msg
    = Delta Float


main : Program () Model Msg
main =
    Browser.element
        { init = init
        , view = view
        , subscriptions = subscriptions
        , update = update
        }


init : () -> ( Model, Cmd Msg )
init _ =
    ( 0, Cmd.none )


update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        Delta dt ->
            ( model + dt / 5000, Cmd.none )


subscriptions : Model -> Sub Msg
subscriptions theta =
    onAnimationFrameDelta (\dt -> Delta dt)


view : Model -> Html Msg
view theta =
    WebGL.toHtml
        [ width 400
        , height 400
        , style "display" "block"
        ]
        [ WebGL.entity
            vertexShader
            fragmentShader
            cubeMesh
            (uniforms theta)
        ]

MsgもModelもFloatだったので型をつけてみました。
ここらへんは通常のElmアプリとあんまり変わらない感じですね。
違うところといえば、subscriptionsの onAnimationFrameDelta と view の WebGL.toHtml/entity くらいでしょうか。

onAnimationFrameDelta は 毎フレーム呼ばれ前フレームとの差分のdelta ミリ秒を取得できるものです。アニメーションなどをステップ実行する際に便利なやつですね。

WebGL.toHtmlで Html msg を生成しています。

width, heightでWebGLを描画するCanvasの描画サイズを指定しています。
WebGL.entity は以下のような定義になっています。

entity :
    Shader attributes uniforms varyings
    -> Shader {} uniforms varyings
    -> Mesh attributes
    -> uniforms
    -> Entity

WebGL.entityには、VertexShader, FragmentShader、メッシュ情報、Uniformを渡しています。

具体的なGLSLについての説明は省略しますが、以下のサイトは非常に参考になるのでオススメです。
https://wgld.org/

実際に動かして見ると以下のような感じです。

おわりに

WebGLをElmでの扱い方を簡単に紹介しましたが、意外と手軽にできるのと、あとは型があるので、その通りに実装すればある程度動くものが作れるというのはElmでWebGLを使う大きなメリットかなと思います。

これを用いて色々と作って見たら面白そうだなと思いました。