コンピューターグラフィックスの短期コース、アドオン:GLSL


公式の翻訳(少し磨き上げたもの)はこちらから入手できます。




リアルタイムグラフィックスをプログラミングする初心者向けの別の入門記事


私はかつて(迅速に)分子を視覚化する仕事がありました。 たとえば、分子は、次のような一連の球体として簡単に表すことができます。



具体的には、このウイルスは約300万個の原子で構成されています。 彼のモデルは素晴らしいサイトrcsb.orgからダウンロードできます。

これは、シェーダーのトレーニングに最適なトピックです。

まず、OpenGLがどのように呼び出され、シェーダーコードがどのようにリンクするかを示します。

OpenGL helloworld


いつものように、コンパニオンコードのリポジトリを作成しました。 OpenGL自体には、レンダリング用のコンテキストを作成する通常のクロスプラットフォームの方法がないため、ここではGLUTライブラリを使用してウィンドウを作成しますが、実際にはユーザーとのやり取りは行いません。 このチュートリアルのGLUTに加えて、GLUおよびGLEWライブラリが必要です。

これがやかんを描く最も簡単なプログラムです:

非表示のテキスト
#include <GL/glu.h> #include <GL/glut.h> #include <vector> #include <cmath> const int SCREEN_WIDTH = 1024; const int SCREEN_HEIGHT = 1024; const float camera[] = {.6,0,1}; const float light0_position[4] = {1,1,1,0}; void render_scene(void) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity(); gluLookAt(camera[0], camera[1], camera[2], 0, 0, 0, 0, 1, 0); glColor3f(.8, 0., 0.); glutSolidTeapot(.7); glutSwapBuffers(); } void process_keys(unsigned char key, int x, int y) { if (27==key) { exit(0); } } void change_size(int w, int h) { glMatrixMode(GL_PROJECTION); glLoadIdentity(); glViewport(0, 0, w, h); glOrtho(-1,1,-1,1,-1,8); glMatrixMode(GL_MODELVIEW); } int main(int argc, char **argv) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); glutInitWindowPosition(100,100); glutInitWindowSize(SCREEN_WIDTH, SCREEN_HEIGHT); glutCreateWindow("GLSL tutorial"); glClearColor(0.0,0.0,1.0,1.0); glutDisplayFunc(render_scene); glutReshapeFunc(change_size); glutKeyboardFunc(process_keys); glEnable(GL_COLOR_MATERIAL); glEnable(GL_DEPTH_TEST); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glLightfv(GL_LIGHT0, GL_POSITION, light0_position); glutMainLoop(); return 0; } 



理解しましょう。すぐにmain()から始めます。


キーボード処理は最も簡単です。ESCキーを押すと、プログラムを(やや残酷に)終了します。 ウィンドウのジオメトリを変更するとき、OpenGLに投影がまだ直交しており、ウィンドウのフルサイズで座標[-1,1] x [-1,1]の正方形を表示することを伝えます。

render_scene()関数で最も興味深いこと。


その結果、次の図を取得する必要があります。



GLSL helloworld


ここでは、シェーダーを最も簡単に使用するためのソースを見つけることができます。 Githubには非常に便利なバージョン比較ツールがあります。 変更点を確認してください。

画像は次のようになります。



コードに正確に何が追加されましたか? 開始するために、2つの新しいファイルが追加されました:frag_shader.glslおよびvert_shader.glsl。C++ではなくGLSLで記述されています。 これは、グラフィックスカードに直接供給されるシェーダーコードです。 また、main.cppには、これらのシェーダーを使用する必要があることをOpenGLに伝えるバインディングが追加されました。

つまり、prog_hdlrハンドラーが作成され、以前にテキストファイルから読み取られ、コンパイルされた頂点シェーダーとフラグメントシェーダーがそれにリンクされます。

標準のOpenGLを使用して「分子」を描画します


そこで、OpenGLコンテキストを呼び出してシェーダーをリンクする方法を学びました。 それらを脇に置き、何万ものランダムな球体を描画しましょう。 .pdb形式は非常にテキスト形式で非常にシンプルですが、コードをできるだけシンプルに保ちたいので、実際の分子をロードしません。 タスクは次のとおりです。ランダムな色のランダムな球を多数描画してみましょう。

シェーダーを使用せず、glutSolidSphere()を呼び出して1万個の球体を描画するコミットです。

変更を見ることを忘れないでください 。 7つの要素の配列を含むatom配列を追加しました。最初の3つは現在の原子の中心の座標、次に半径、さらに3つの色です。

これは次のような写真です。



個人的には、この写真を見ると痛いです。2つの球の交点は円の弧で、ここにはすべてがありますが、円はありません。 これは、各球体を16個の緯線と16個の子午線、つまり各球体について約500個の三角形で描いたという事実の結果です。 そして、写真の質の低さに加えて、効率に関する直接的な疑問が残っています.1000万個の原子を描画したい場合、50億個の三角形を送信する必要があり、バスの帯域幅に大きな打撃を与え始めます。

シェーダーは役立ちますか?


彼らはできる! シェーダーは照明の変更だけではありませんが、本来はそれを行うことを目的としていました。 CPUとGPU間のデータ転送を最小限に抑えたいため、描画する必要がある各球体に1つの頂点のみを送信します。

古いGLSL#120でコードを書いています。 非常に古いマシンで実行する必要があります。新しいGLSLの構文は少し異なりますが、一般的な考え方はまったく同じです。

同じ領域を描画するコードですが、シェーダーを使用しています。

それでは、アイデアは何ですか?

まず、CPU側で、描画する必要がある各球体に1つの頂点を送信します。
シェーダーを作成しない場合、次の図が表示されます。

非表示のテキスト


さらに頂点シェーダーでは、gl_PointSizeを変更できます。これにより、結果として正方形のセットが得られます。

非表示のテキスト

フラグメントシェーダーは、正方形の各ピクセルに対して実行されることに注意してください!

つまり、すべてが非常に単純になったので、正方形の指定されたピクセルが中心からどれだけ離れているかを検討します。
球体の半径を超えている場合、discardを呼び出します。

非表示のテキスト

各球体の中心を通る平らな紙吹雪のセットを手に入れました。

興味深いことに、各フラグメントの深さを変更する権利があります。

非表示のテキスト


照明を計算するためだけに残っており、そのような写真は最終的に判明します:



それを三角形の球体を描いた絵と比較してください。 画像ははるかに正確で、より高速に描画されます。

ここで、.pdbファイルの読み取り値、画面空間アンビエントオクルージョンを追加する必要があり、この記事のタイトルイメージを取得できます。




シェーダーを使用して球体を描画する方法を理解したいが、glOrthoではなく遠近法を使用したい人には、素晴らしい記事があります。

Source: https://habr.com/ru/post/J253791/


All Articles