Mac + pyOpenGLでシェーダーアニメーション描く


はじめに

ご覧になった方も多いかと思います。macOS 10.14のOpenGLへのDeprecation
What’s New in macOS(10.14で変わったこと)
Macでの今後のoFの対応、諸々のアプリ開発者の不安が囁かれていますが...
私も今朝、まさにopenGLをpythonで動かしていたこともあり、
ニーズアピールの意味も込めてサンプルをアップします。

できること

LiveCoding用のソフトを使わず、
python のみで GLSL sandboxにあるような時間変化するシェーダープログラムを実行できる。
意外とLiveCodingのように動くpythonコードがなかったです。

環境

  • MacBook Pro (2.7 GHz Intel Core i5,16 GB 1867 MHz DDR3)
  • macOS Sierra
  • python 2.7

手順

環境構築

インストールはnumpyなどの基本的なものを除けば、以下の2つです。

brew install glfw
pip install PyOpenGL

PyOpenGLがpipで入らない場合は、ここを頼りに手で入れましょう。

実装コードはこちらに置いてあります。

git clone https://github.com/mizumasa/pyShaderSample.git
cd pyShaderSample
python shader_loop.py shader_loop.vert shader_loop.frag

import でエラーが出た時

Big Sur 以降、確実にインストールそのままではエラーが出ます

こちらの対策で使えるようになる可能性が高いです。
(2021/11更新 M1 macOS Monterey でもこの修正をすれば使えました!)

コード説明

pyOpenGLでシェーダープログラミングこちらを参考に書きました。
不要な部分などあるかもしれませんが、とりあえずこれで動きます。

重要な部分としては DrawGLScene()の中で毎回呼ばれている、time 変数への値の送信です。

shader_loop.py
glUniform1f( glGetUniformLocation( program, "time" ), time.time() - startTime)

このように time 変数を、毎回描画時にtime.time()関数などの値を利用して更新してあげると、時間変化のあるシェーダープログラムが実行できます。
簡潔に書くために、program変数はglobal化してしまいました。

続いて、シェーダー本体を見ていきます。

shader_loop.frag
uniform float texture_width;
uniform float texture_height;
uniform float time;

void main() {
    vec2 resolution = vec2( texture_height, texture_width );
    vec2 uv = -1. + 2. * gl_FragCoord.xy / resolution.xy;
    gl_FragColor = vec4(
        abs( sin( cos( time + 3. * uv.y ) * 2. * uv.x + time)),
        abs( cos( sin( time + 2. * uv.x ) * 3. * uv.y + time)),
        2.0,
        1.0);
}

texture_width,texture_heightは画面サイズで、InitGL()関数の中でシェーダーに設定しています。KodeLifeやGLSL sandboxなどのLiveCodingソフトでは一般的にtime と resolution変数が使用されているので、resolution変数を作成しています。また、gl_FragCoord.xyが入力の二次元マップ、gl_FragColorが出力のRGBAとなります。(KodeLifeなどからの移植で、出力変数名がfragColorとなっている場合はgl_FragColorと書き換えてください)

shader_loop.frag の中を自由に書き換えれば様々な映像が生成可能です!

実行

python shader_loop.py shader_loop.vert shader_loop.frag

サンプルコードでは、ESCキーを押すと、その時の画像をキャプチャして保存します(output.png)。

以上です!