arusu0629のブログ

Swift初心者エンジニア

WebGLについて⑥

【前回】
arusu0629.hatenablog.com

※この章は少し長い内容となっております...mm

シェーダの記述と基礎

WebGLではプログラマブルシェーダの一種であるシェーダ言語が実装されている。それがGLSL(OpenGL Shading Language)である

GLSLを使って2種類のシェーダ、頂点シェーダとフラグメントシェーダを記述することが出来る。原則的に先に頂点シェーダが呼ばれます

頂点シェーダは、頂点に関する情報のすべてを渡すことが出来る
例えば、頂点の位置情報や、頂点が持つ法線、テクスチャ座標、頂点の色など、頂点に関する全てをシェーダに渡すことが可能

次にフラグメントシェーダは画面にどんな色を出力すればよいのかを決めることが出来る
画面上のピクセルそれぞれに対して、最終的に出力される色を操作してくれる

【ミニまとめ】
頂点シェーダは頂点に関する情報を、フラグメントシェーダは画面上の色の情報をそれぞれ処理するもの

■ものすごく単純な頂点シェーダの記述例
attribute vec3 position;

void main(void) {
    // 組み込み変数gl_Positionに頂点データを渡す
    gl_Position = position
}

attributeという修飾子を付けて宣言された変数(上記の場合はposition)が頂点の情報をシェーダ側で受け取る変数になる。つまりWebGLのプログラム側でpositionという名前をつけ、あらかじめデータを仕込んでおきシェーダに渡す


頂点シェーダは頂点に関する処理をするため、座標変換も頂点シェーダの仕事の1つです
どこまでを頂点シェーダに任せるかは自由ですが、WebGLのプログラム側でモデル・ビュー・プロジェクションのそれぞれの行列を生成して掛け合わせておき、最終的に出来上がった座標変換行列を頂点シェーダに渡すのが一般的

■座標変換行列をシェーダに渡す記述例
attribute vec3 position;
uniform mat4 mvpMatrix;

void main(void) {
    gl_Position = mvpMatrix * position;
}

uniform修飾子全ての頂点に対して一律に処理される情報を渡すことが可能になる
ここで出てきているmvpMatrixはモデル・ビュー・プロジェクションそれぞれの変換行列をかけ合わせた座標変換行列。その変換行列をWebGL側からGLSLに渡してあげる

attribute, uniformの2つの修飾子に加えて、varying修飾子があります
このvarying修飾子の使いみちは頂点シェーダとフラグメントシェーダの橋渡しです

例えば頂点シェーダが持つ頂点の色情報をフラグメントシェーダで使用したい場合などに使われます。(以下例)

頂点シェーダ
attribute vec4 position;
attribute vec4 color;
uniform mat4 mvpMatrix;
varying vec4 vColor

void main(void) {
    vColor = color;
    gl_Position = mvpMatrix * position;
}


続いてvarying修飾子付きの変数vColorを受け取るフラグメントシェーダ

フラグメントシェーダ
varying vec4 vColor

void main(void) {
    // フラグメントシェーダの組み込み変数gl_FragColorに色情報を入力する
    gl_FragColor = vColor;
}


このように頂点シェーダが持つ頂点の色情報をフラグメントシェーダに渡す際にvarying修飾子付きの変数を用いる。頂点シェーダで必須だったgl_Positionへのデータの代入と似たような形で、フラグメントシェーダにおいてはgl_FragColorにデータを入力する

【まとめ】

  • 頂点シェーダとフラグメントシェーダはGLSLを使って記述でき、基本的に双方がセットの形で使用する
  • シェーダ内部では必ずmain関数を用意してシェーダの行う処理を記述する
  • WebGL側から頂点の位置情報や色情報、座標変換行列などをシェーダに渡す際にattribute, uniformなどの特別な修飾子付きの変数を用意する必要がある
  • そして頂点シェーダからフラグメントシェーダにデータを渡す際にはvarying修飾子付きの変数を用意することで値を渡すことが出来る
  • 頂点シェーダは組み込み変数であるgl_Positionへのデータ代入が必須であり、またフラグメントシェーダはgl_FragColorに出力する色のデータを基本的に代入する必要がある


【続き】 arusu0629.hatenablog.com

WebGLについて⑤

【前回】 arusu0629.hatenablog.com

コンテキストの初期化

コンテキストの取得から初期化まで

  1. htmlソース要素をgetElementByIdを使って取得

  2. 取得したcanvas要素を使ってWebGLコンテキストを取得する。これをするにはgetContextメソッドを利用する

  3. 画面をクリアするにはclearメソッドを使い、引数には画面上の色をクリアするので組み込み定数のCOLOR_BUFFER_BITのみを指定する


上記例のJavaScriptコード

script.js

onload = function(){
    // canvasエレメントを取得
    var c = document.getElementById('canvas')
    // 画面サイズを適当に決める
    c.width = 500
    c.height = 300

    // webglコンテキストを取得
    var gl = c.getContext('webgl') || c.getContext('experimental-webgl');

    // canvasを黒でクリア(初期化)する
    gl.clearColor(0.0, 0.0, 0.0, 1.0);
    gl.clear(gl.COLOR_BUFFER_BIT);
};

このJavaScriptコードをhtml側で読み込めばブラウザで500×300の黒い領域が表示される(添付図参照)

sample.html

<html>
  <head>
    <title>WebGL TEST</title>
    <script src="script.js" type="text/javascript"></script>
  </head>
  <body>
    <canvas id="canvas"></canvas>
  </body>
</html>


【添付図】 f:id:arusu0629:20180331182731p:plain

【続き】 arusu0629.hatenablog.com

WebGLについて④

【前回】 arusu0629.hatenablog.com

頂点ポリゴン

WebGLでは基本的に次のいずれかしか描画することが出来ない

  • 線分
  • 三角形

基本的にWebGLでは三角形を用いて画面上に何かを描画する
そしてその三角形こそがポリゴン。ポリゴンは3つの頂点を結んで描かれる三角形です。そのため1枚のポリゴンを描画するのに3つの頂点を必要とする

  • ポリゴンが少ない場合
    →描画するモデルがカクカク

  • ポリゴンが多い場合
    →精細なモデルを表現することが出来る
    →しかしポリゴンの数だけ頂点の数も倍々で増えるため座標計算などの負荷が大きくなる

3Dレンダリングではこのポリゴンの数をどこまで増やすかで見た目のクオリティが決まり、一方で座標計算の負荷が大きくなるトレード・オフの関係なため、ポリゴン数の決定に頭を悩ませられる

頂点を結ぶ順序とカリング

頂点を結んでポリゴンを描画する際に頂点同士の線をつなぐ順序が大切になってくる。時計回りで結ぶのか、もしくは反時計回りで結ぶのか

どうしてかと言うとレンダリングする際に正面と真反対の裏側は見えないため、描画する必要がない。そのため3Dレンダリングの世界では見えないものは描画しない仕組みを使って負荷を軽減している。この裏面にあるものは描画しない仕組みをカリングと呼ぶ

では、ポリゴンの表と裏をどうやって判断しているかと言うと、頂点を結ぶ順序を判断基準に使っている
時計回りの順序で頂点を結んだポリゴンを裏、逆の場合は表、といった具合に裏表を判断している

【まとめ】
三角形のことをポリゴンと呼び、そのポリゴンを表すのに3つの点(頂点)を結んで表現する
ポリゴンには頂点を結ぶ順序によって表裏が存在し、それによってカリングを行うことが可能になる

【続き】 arusu0629.hatenablog.com

WebGLについて③

【前回】 arusu0629.hatenablog.com

行列(マトリックス)の基礎知識
行列の必要性

3Dレンダリングする際に非常に多くの計算が必要になる
どんなモデルをどこに、どんな角度で、どんな大きさで存在するのか、それを描画したいモデルの数分計算する必要がある
行列は数字を詰め合わせたもののようなイメージで、1つの行列の中にたくさんの情報を詰め込むことができる
それはつまり、モデル座標変換・ビュー座標変換・プロジェクション座標変換の一連の座標変換を1つの行列で全ての座標変換の結果をまとめて処理することが出来る

一般的な3Dレンダリングの世界では4×4の行列を用いるのが一般的

モデル変換行列

→3Dモデルの位置拡大縮小の有無回転しているかどうかなどを定義する

ビュー変換行列

カメラの位置やカメラの向き、そしてカメラがどこを見ているか(注視点)を1つの行列で定義する

プロジェクション変換

ディスプレイの縦横の比率(アスペクト比)視野角などを定義する

【まとめ】
3Dレンダリングを行う上で、行列は欠かすことのできない存在。なぜならば3Dレンダリングを行う際に非常にたくさんの計算を要するため、それを実現するために行列は適した道具なため
各座標変換(モデル、ビュー、プロジェクション)を行列として定義して、それらの行列を掛け合わせて座標変換を実現する(行列の掛け算は掛け合わせる順序によって結果が変わるため注意が必要)

【続き】 arusu0629.hatenablog.com

WebGLについて②

【前回】 arusu0629.hatenablog.com

レンダリングのための下準備

固定機能パイプライン
→3Dレンダリングを行う一連の処理の流れ(①で出てきた3つの座標変換をしてくれる)

しかしWebGLには固定機能パイプラインが存在しない
つまり座標変換を全て記述する必要があり、その仕組こそがシェーダである

プログラマブルシェーダ
プログラマが自前で記述できる仕組みのことで以下の2種類がある

頂点シェーダ(バーテックスシェーダ)
→ポリゴンの頂点情報を扱う

フラグメントシェーダ(ピクセルシェーダ)
→描画される際のピクセルの情報を扱う

WebGLでは固定機能パイプラインが存在しないため、上記2つの頂点シェーダとフラグメントシェーダの2つを用意する必要がある

【続き】 arusu0629.hatenablog.com

WebGLについて①

少し前から3Dについての勉強をしたくて、最近は決まってやることが無かったので、ボチボチ勉強していこうと思います。 (デザインではなくプログラミングについてです)

勉強方法は会社の先輩に教えていただいた下記サイトを読んで、自分が大事だなとかよく分からなかった箇所をメモして、ここに振り返り用にアウトプットする感じでやっていこうかなと

wgld.org

3D描画の基礎知識

座標系

OpenGL(WebGL)は右手座標系

一方でDirectX左手座標系

左手座標系と右手座標系の違いはZ座標(奥行き)の取り扱い方の違い

左手座標系では奥側がマイナスで手前に行くほどプラスの値

右手座標系はこれの逆で奥側がプラスで手前に行くほどマイナスの値を取る

3つの座標変換
  1. モデル変換(DirectXではワールド変換と呼ばれる)
    被写体となる物体が3次元空間のどの位置にあるのかを定義するための座標変換

  2. ビュー変換
    カメラの位置、向きを定義する。カメラの位置やその向けられている方角を決めるために行われる座標変換

  3. プロジェクション変換
    3次元空間のどの領域を撮影するかなどを定義する。横に幅広く描画するのか、もしくは縦長の映像を撮影するのかなど。一般的にカメラの場合はレンズの前にあるものを全て撮影し、どのくらいの遠さまでを映すかは意識しない。しかし、プログラムでは一番手前はここまで、一番遠くはここまで、と領域を決めて処理する

まとめ

プログラムが3次元空間を描画する時、最終的に2次元データに変換する必要がある。WebGLDirectXでZ座標の扱いが異なる。3次元空間の情報を2次元の情報に変換するには、モデル、ビュー、プロジェクション変換が組み合わさって最終的にディズプレイに描画される

【続き】 arusu0629.hatenablog.com

「プログラマの数学」を読んで

サボりぐせが早速出てしまいました...!
が、気にせずに書いていきます。

2018年の目標として「1ヶ月に1冊本を読む」という目標を立てていて、 今のところはリーダブルコードしか読めてません(笑)

2冊目として読んでいたプリンシプルオブプログラミングは半分ほど読んでちょっと飽きてしまっていて、ちょうど数学ガールでお馴染みの結城さんの本が新しく出るということでプログラマの数学」を読んで少し整理するために振り返ろうと思います。

※基本的には読みながらノートにメモした言葉を連連と書くだけです。

1章 ゼロの物語

メモ:大きな問題は小さな「まとまり」に分けて解け
→問題全体をいきなり解くのではなく、小さな「まとまり」に分けることから始めることが大切。

2章 論理

メモ:「だぶり」がないことー排他的であること
論理和論理積など大学生に学んだことを思い出しました。論理表を書くことの大切さやベン図、カルノー図など実際に図解することで理解が深まりました。
またカルノー図は複雑な条件式を簡略化することが出来る方法だとこの本を読んで気付いたので、これからの仕事でも活かすことが出来そうだなと思いました。

3章 余剰

メモ:同期性を見抜く。
   余剰は同期性を活用するための道具。
   余剰を使って、大きな数の問題を小さな数の問題に落とし込む
   オセロで通信
  「正確な把握」よりも「的確な分類」の方が役に立つ場合もある

→余剰も日頃からよく使っているモノかな、と思いました。例えば奇数や偶数判定時に余剰をしばしば使っています。

if (x % 2 == 1) // 奇数
if (x % 2 == 0) // 偶数

本書ではもっと大きな枠として「同期性」について繰り返し説明していたような気がします。余剰を使うことでグルーピングすることができたり大きな問題を小さくすることが出来たりと...!
「オセロで通信」はコラム的な形で紹介されており、個人的にすごい面白かった内容なので多分メモってたんだと思います(笑)是非皆さんに読んでほしいです!

4章 数学的帰納法

5章 順列・組み合わせ

→メモがありませんでした...(汗)
もう1度ちゃんと読み直さないといけないかもしれないです...

6章 再帰

メモ:数えるとはー整数との対応付け
→あるものを数えるときに「0,1,2,3,...」とそれぞれのものに整数の「ラベル」のようなものを割り当てることを「数える」という行為であると解釈しました。後の章に出てくる内容で「カウンタブル」などに通ずる話だなーと全部を読んで思いました。

再帰はプログラムを書くときにも偶に出てきたりすると思いますが、自分はこの再帰を使うことがなかなか苦手なので、この章をもう少し深く理解して仕事に活かせるようになったら良いなと思いました。

再帰を扱うにはこれまでに出てきた、小さな問題に分けてみたり、パターンを見抜いたり、規則性、同期性がないかを見つけて初めて再帰だと気付くと思うので、つくづくこの本は今までの話がどの章にも絡んでくる重要な事柄だと感じました。

あと「組み合わせは順序を考えて重複度」で割るという考え方も新しかったです。
今までは順列や組み合わせを公式として覚えているだけだったので...

7章 指数的な爆発

→メモがありませんでした...(2回目汗)
ただ最近読んだ内容なので、覚えている内容としては倍倍には気をつけろ!ということです。
有名なコラムでもありますが、厚さ1mmの紙をどんどん折っていくと数十回では月まで届くようなものすごい大きな厚さになってしまうという話を例にあげていました。

また逆にその指数的な爆発を味方にすることができれば強力な武器にもなると!
本書ではバイナリサーチ(2分探索)について触れていました。
指数は数が大きくなるのでそれを扱いやすくすために「対数」という話にも触れていました。どんどん学生時代に習った内容がより分かりやすく、具体例をもとに説明されているので読んでて凄く楽しかったです。学生時代にこういう風に習っておけば...と数学ガールを読んだ時のような思いになりました(笑)

8章 計算不可能な問題

メモ:「カウンタブル」ー「集合の要素が有限個であるか、または集合のすべての要素を1以上の整数と一対一に対応付けられる」集合のこと。
→正直この章は1番理解が浅いのであと2,3回はこの章を読まないといけないと思っています...(汗)
計算不可能とは答えがなかったり、計算にものすごく時間がかかるという意味ではないらしく、「絶対に解けない問題」と解釈したほうが良いんでしょうかね...!

もう1つのメモとして「任意のプログラムの振る舞いを調べる問題はプログラムで解くことが出来ない」も印象的というか読んでいてほんの少しだけ理解できた気がします。

プログラムを書いていると時々誤って無限ループ(=処理が一生終わらない)になってしまうコードを書いてしまうことがあり、それをプログラマートライアンドエラーで解決します。
しかし、その解決を別のプログラムが行うことはできないみたいな例だった気がします!(ちゃんと理解できていないので間違って解釈していたらすみません...)

仮に上記のことが出来てしまうと現在未解決の数学問題を解けてしまうことになると背理法の考え方で矛盾が生じるとも話していました。
とにかくあと数回は読み直さないと理解できなかったです!(笑)

9章 プログラマの数学

メモ:問題の構造を見ぬき、それをシンプルに表現し、一貫性のあるルールにまとめる。
→この1文に本書の全てが表現されているな、と思いました!

確かに日常的なプログラマーの仕事では難しい数学の知識や数式が出てくることはめったにないと思います。普通は義務教育の範囲内で履修する内容で解くことが出来る問題だらけですが、色々なしがらみを持って(コロコロ変わる仕様や無理なスケジュールなど...)問題を複雑にしている気がします。

その問題をいかにシンプルに解くか、シンプルに解くにはどうしたら良いか、大きな問題を小さくしたり、構造を見抜いて規則性や同期性を見つけて一般化することが出来たりと...!そういうことが大事なんじゃないかと。

またそうやって解く1つの方法として「実際に小さな数字(=問題)に当てはめてみて解いてみる」という方法も本書ではよく登場していて、普段自分がよく行う手段だなーと思って、自分がやっている解決のアプローチはあながち間違いではないと思ったりもしました(笑)
そこから一般化する事ができれば汎用的にその問題を解くことが出来る!(汎用的というと語弊があるかもしれませんが...)

この章はまとめの章となっていて、各章について数行で振り返りをしていました。この章を読むだけでも十分理解できるというか、簡単に読むことが出来る内容だったので凄くよくまとめられていると思いました。(全章を読んだ前提があるかもしれませんが...)



最後に

結城さんの本は数学ガールもそうですが、先生と生徒の会話や面白いコラム、最初は何を言っているか分からない名言のような言葉(=ちゃんと内容に関連している)が個人的には凄く楽しくて、スラスラ読める理由なのかなーと思いました!