openframeworksでアニメーションを作成していると、多角形の処理を大量に実行すると処理に時間がかかってしまっていました。毎回の動きに対してCPUを動かして処理するため、時間がかかってしまいます。そこで、GLSL(OpenGL Shading Language) 用いて計算することで処理の効率化を行います。
1.shader
openFrameworks + Shader (GLSL) 入門より
Shaderとは、もともとは3Dコンピュータグラフィクスで、シェーディング (陰影処理) をするプログラムのことを指していました。従来は、開発者やデザイナーは、グラフィクスカード (GPU) に固定機能として実装された定形の処理しか使えませんでした (固定機能シェーダー)。2000年代に入って、プログラマブル・シェーダーが登場します。これまでブラックボックスだったシェーダー自体が、プログラム可能になりました。OpenGLではGLSLというプログラミング言語が策定されシェーダーをプログラムすることが可能です。画面上の膨大なピクセル情報を、高い並列処理性能を持つGPUで実行することにより、CPUで実行するよりもはるかに高いパフォーマンスを実現できるようになりました。こちらのlinkにある図を参照すると分かりやすいです。
1.1 The Graphics Pipeline
shadingを効率的に処理するには、CPUでの演算処理ではなく、GPUを扱う必要があります。
GPUを処理するには、点や色の情報をGPUに適した変換して処理を行います。処理を行うために、作成するファイルがいくつかありますが、GPUの処理の概要を知っておくと理解の助けになります。
グラフィカルパイプラインは、グラフィックカードが処理しやすいように頂点からピクセルカラーに変更するまでの工程です。頂点の情報を繋ぎ、ピクセルごとに出力します。工程については、こちらのPipeline overviewを参照してください。
1.2 種類
###1.3.1 頂点シェーダー(vertex shader)
頂点の位置や色などを計算するプログラム。モデルの変形や動きを制御することができる。
###1.3.2 フラグメントシェーダー(fragment shader)
ピクセルの色や明るさなどを計算するプログラム。テクスチャや陰影、反射などの表現をすることができる。
###1.3.3 ジオメトリシェーダー(Geometry Shader)
頂点シェーダーの後に実行され、与えられた頂点から面に分割し、新しいプリミティブを生成したりすることができる。
###1.3.4 テッセレーションシェーダー(essellation Control Shader)
頂点シェーダーの後に実行され、モデルの細かさを動的に調整することができる。
###1.3.5 コンピュートシェーダー(Compute shader)
グラフィックスとは関係なく、任意の並列計算を行うことができる。
2. coding
プロジェクトを作成し、bin/data 以下にシェーダーファイルを作成します。頂点シェーダー、フラグメントシェーダーを作成しています。プログラムで実行するには、main.cppでGLの設定、app.cppでシェーダーファイルの呼び出しが必要になります。
2.1 mian.cpp
ofGLWindowSettings
を下記のように設定します。設定した状況とPC、CPUについて、coutで確認できます。PCがどのGLに対応しているかは、settings.setGLVersionをコメントアウトすることで確認できます。
#include "ofMain.h"
#include "ofApp.h"
//========================================================================
int main() {
//Use ofGLFWWindowSettings for more options like multi-monitor fullscreen
ofGLWindowSettings settings;
settings.setSize(1920, 1080);
settings.setGLVersion(4, 6); // set up GL version 4.6
settings.windowMode = OF_WINDOW; //can also be OF_FULLSCREEN
auto window = ofCreateWindow(settings);
cout << "Vendor :" << glGetString(GL_VENDOR) << '\n';
cout << "GPU : " << glGetString(GL_RENDERER) << '\n';
cout << "OpenGL ver. " << glGetString(GL_VERSION) << '\n';
ofRunApp(window, make_shared<ofApp>());
ofRunMainLoop();
}
//<Result>
//Vendor :Intel
//GPU : Intel(R) HD Graphics 620
//OpenGL ver. 4.6.0
2.2 shader.vert
ofshader.vert
は、頂点シェーダーを作成するファイルになります。ファイル内では、uniform変数を用いて記述します。この変数は、シェーダーで処理するために扱う変数になります。ここでは、頂点の情報を入れています。
#version 460
uniform mat4 modelViewProjectionMatrix;
in vec4 position;
void main(){
gl_Position = modelViewProjectionMatrix * position;
}
2.3 shader.frag
ofshader.vert
は、描写するピクセルの色を決めるファイルになります。こちらもofshader.vert
と同様にuniform変数を用いて、RGBを設定したリ、深度を設定しています。
#version 460
const int number_of_targets = 80;
uniform float time;
uniform vec2 resolution;
uniform vec2 targets[number_of_targets];
uniform vec3 colors[number_of_targets];
out vec4 outputColor;
void main() {
vec2 p = (gl_FragCoord.xy * 2.0 - resolution) / min(resolution.x, resolution.y);
vec3 color = vec3(0.0);
for(int i = 0; i < number_of_targets; i++){
vec2 t = vec2(targets[i].x, -targets[i].y) / min(resolution.x, resolution.y) * 2.0;
t.xy += vec2(-resolution.x, resolution.y) / min(resolution.x, resolution.y);
float r = 0.02 / length(p - t) * colors[i].x;
float g = 0.02 / length(p - t) * colors[i].y;
float b = 0.02 / length(p - t) * colors[i].z;
vec3 c = vec3(smoothstep(0.01, 1.0, r), smoothstep(0.01, 1.0, g), smoothstep(0.01, 1.0, b));
color += c;
}
outputColor = vec4(color, 1.0);
}
2.4 codingの注意点
OpenGLに入門する前に知っておきたかったことによると、描画方法がOpenGL3.2以降では異なります。
shaderは、OpenGLが使えるか、またどのバージョンが利用できるかは、OSがOpenGLをサポートしているかどうかと、使っているGPUがOpenGLに対応しているかどうかに依存します。OpenGLには、旧式のFixed Function Pipelineによる描画方法と、新式のProgrammable Pipelineによる描画方法があります。
OpenGL3.2以降は、旧式の描画命令は互換プロファイルでのみサポートされています。 新式の描画命令は、BufferやVariable TypeなどのOpenGLの内部構造を意識したコードを書く必要があるので、理解するまでに時間がかかりますが、プログラミングの自由度・アプリのパフォーマンスは向上します。 今後、旧式はサポートが打ち切られることも考えられるため、新しく勉強する人には新式の書き方を覚えることをおすすめします。
3.近年の動向
OpenGLに入門する前に知っておきたかったことによると、OpenCLのレガシー化が進んでいる状況です。
OpenGLはラスタライズ方式のレンダリングパイプラインを実装する前提で設計されているのですが、CGの研究が進むにつれてサンプリング方式のレンダリング手法や既存のパイプラインの枠に収まらない計算手法が登場し始めました。 加えて、GPUがシミュレーションやDeep LearningなどCG以外の目的で使われるようになったことで、GPUのハードウェア設計が多様化し、OpenGLの仕様ではハードウェア性能を限界まで引き出すことができなくなってきました。 つまりOpenGLの規格がレガシー化しつつあるわけです。 これを受けて近年では、Vulkanという新しいグラフィックAPIの策定・普及が進んでいます。
また2018年6月には、macOSにおいてOpenGLが非推奨化されることが発表されました。 アップルはこれまでもOpenGLのバージョン追従が遅い傾向にあったのですが、今回思い切ってサポートを打ち切った形です。
4.参考リンク
下記リンクを参考にしました。
4.1 shader
GLSL で暖を取るための準備をしよう! GLSL お役立ちマニュアル
シェーダープログラミングの意義とその実装
hapter-1:-the-graphics-pipeline