初歩的レンダラー
目次
概要
今回は、Unityとは全く関係がない内容になりますが、最近流行りのレイトレーシングの初歩を学ぼう! という事でC++でレイトレーシング レンダラーを作って行きたいと思います。
レイトレーシングとは?
最近、RTXがリアルタイムレイトレーシングが出来ると話題になっていますよね。 大体のゲームは、ラスタライズという方法で描画していましたが、RTXではラスタライズと一部レイトレーシングで描画しています。 一部でしかレイトレーシングが使われないのは、本来1フレームを描画するのは数時間、何日もかかる処理なので、それをリアルタイムで描画するのはまだ今の性能では無理な話です。なので、ガラスや水面などレイトレーシングが適してる所だけレイトレーシングで描画しているので、なので一部レイトレーシングなのです。
レイトレーシング ON OFF比較
RTXデモ動画
Battlefield V: Official GeForce RTX Trailer Battlefield V, NVIDIA RTX Ray Tracing, And GeForce RTX Combine To Deliver Next-Gen Graphics in Games – See It In Action In Our Exclusive Trailer
レイトレーシングの描画流れ
現実世界では、光源から光が放射して物体(以下、「オブジェクト」と言う。)などに反射、屈折などをして、人間の目に光が入り光の周波数によって色を認識しています。 レイトレーシングはその逆の流れで計算をしています。理由は、光源の光のごく一部しか目に入らないので、光源の光を全部計算すると無駄が多くなってしまうからです。
レイトレーシング実装
今回のブログでは、レイトレーシング法で簡単な反射のみ実装していきます。
1.レイの生成
std::vector<Ray> rays; for (int i = 0; i < getScreen.w * getScreen.h; i++) { double x = getScreen.GetWidth(i) - getScreen.w * 0.5; double y = getScreen.GetHigh(i) - getScreen.h * 0.5; rays.push_back(Ray(getScreen.cameraRay.o, getScreen.cameraRay.d + V(x * getScreen.pov, y * getScreen.pov, 0))); }
raysにこれから計算するレイをプッシュバックしてます。 x,yは for文の i に対してスクリーン上のピクセルかを計算しています。x,yでカメラの方向をズラし、それをレイとしてプッシュバックしています。 例) iが getScreen.w * 0.5 + getScreen.h * 0.5、つまりシーンの中心の時、x、yは0になります。その時はカメラの方向をそのままレイとしてプッシュバックします。
応用として、視野角も後々考えないといけませんね。
2.レイの反射
光源とオブジェクト間での反射を考えます。
std::optional<HitInfo> RayHit(Screen &getScreen, const Ray &getRay, double rayPower) { //再帰中止 if (rayPower < 0.01) { return std::nullopt; } for (int s = 0; s < getScreen.spheres.size(); s++) { double dotA = Dot(getRay.d, getRay.d); double dotB = -2 * Dot(getRay.d ,getScreen.spheres[s].p - getRay.o); double dotC = Dot(getScreen.spheres[s].p - getRay.o, getScreen.spheres[s].p - getRay.o) - std::pow(getScreen.spheres[s].r,2); double D_4 = (std::pow(dotB, 2) - 4 * dotA*dotC) / 4; double t1 = (-dotB / 2 + std::pow(D_4, 0.5)) / dotA; double t2 = (-dotB / 2 - std::pow(D_4, 0.5)) / dotA; if (t1 >= DBL_EPSILON) { V Q1 = V(getRay.o + t1 * getRay.d); HitInfo hit; hit.hitObject = getScreen.spheres[s]; hit.position = Q1; hit.hitObjectNormal = Normalize(Q1 - getScreen.spheres[s].p); hit.dot = Dot(hit.position, hit.hitObject.p) / Magnitude(hit.position) / Magnitude(hit.hitObject.p); hit.ray = Ray(hit.position, Normalize(getRay.d) + hit.hitObjectNormal); //光源の場合、直ちに光を反映 if (hit.hitObject.emission.Power() > 0) { hit.color = hit.dot* hit.hitObject.emission; return hit; } auto hitRecursive = RayHit(getScreen , Ray(hit.position, Normalize(getRay.d) + hit.hitObjectNormal),rayPower*hit.dot); if (hitRecursive) { if (hitRecursive.value().hitObject.emission.Power() > 0) { hit.color = hitRecursive.value().dot * hitRecursive.value().hitObject.emission; } else { hit.color = hitRecursive.value().color * hit.dot; } } else { //色の影響がごくわずかである //あるいは、rayが接触しなかった hit.color = Color(0,0,0); } return hit; } } //結果衝突を検出出来なかった return std::nullopt; }
反射を繰り返し、ピクセルの色を決定する時の色の影響率が低いレイは再帰の途中で停止してます。
3.画像ファイルの書き出し
今回はPPMファイルを出力しています。PPMファイルの中身はテキストで書かれています。全てのピクセルがテキストで表現できるので、余計なライブラリを使わずに簡単に書き出しが出来ます。
計算結果
上手くいきました👍
りぽじとりー
ノード型シェーダーエディタOSS
初めに
今回は、OSS(オープンソースソフトウェア)のお題で簡単なシェーダーエディタを作ってみました。
環境
Unity 2018.2.14f1
ノードの決め事
計算結果を毎回変数に格納して、その計算結果を後のコードに使える状態にします。
例)
float add1 = 1 + 2;
float add2 = add1 + 7;
ダメな例)
float add2 = add1 + 7;
float add1 = 1 + 2;
1行目でadd1にアクセスしますが、定義前のadd1にはアクセス出来ません。
定義前の変数にアクセスしない様、今回は深さ優先探索を使いノードの書き出し順を決めていきます。
下の画像は深さ優先探査の図です。折り返した時に定義するとします。
ノード処理
fixed4をfixedに分解するノードを「ExportColor」と言うことにします。
下の図のコメントはシェーダーの書き出し部分です。
処理の流れ
ExportColorノードのインポートとして接続されたノードの変数(f4)を取得し、ExportColorで決めた変数名で定義します。また、インポート先とエクスポート先の変数の型を確認する必要があります。理由は、仮にExportColorのインポートノードがfixed4以外時のエラーを回避するためです。fixed3の変数のアルファ値をアクセスしますとエラー、想定外の処理が起きます。
ID決定
その他にもExportColorノードが複数あった場合、定義変数が重複しエラーが出るので、ノード毎にIDを割り当てて定義変数に追加します。IDは上で書いた深さ優先探索でノードで訪れた順にします。
リポジトリー
例
お、とりあえず書き出し上手くいった pic.twitter.com/HTjLRUp08m
— Penguin (@Penguin_iceice) 2018年10月24日
お、いい感じ👍 pic.twitter.com/puAS1f0iYZ
— Penguin (@Penguin_iceice) 2018年10月25日
いい感じ👍 pic.twitter.com/0oMvDGxJ5E
— Penguin (@Penguin_iceice) 2018年10月25日
初ハッカソンに出ました!!
大学のVR部としてMashup Awards 2017に出場しました!
作品のデモ動画
【会津ハッカソン】#ma_2017
— MashupAwards@11/13〆切 (@mashupaward) 2017年10月29日
④VsR 〜魔法アクションVR〜 / Wizardshttps://t.co/RSJJD9KPCP
デモ。魔法はなぜか出なかったけど、リープモーションで手はトラックできた pic.twitter.com/onZUqL1SQD
デモ中にリープモーションによる攻撃が出ないというアクシデントがありました💦
プレゼンのデモの前に焦ってビルドしたのが良くなかったですね。
これからは気を付けますw
受賞!!
Mashup Awards 2017で「データスパイダー賞」を受賞しました!!
【会津ハッカソン】#ma_2017
— MashupAwards@11/13〆切 (@mashupaward) 2017年10月29日
データスパイダー賞!
副賞はamazonギフト券
いつか自分たちが世の中にこれを出したいというコメントが素晴らしかった。プロダクトマネージャーとして pic.twitter.com/q75OHpCjoy
このVRコンテンツをデジゲー博に向けて作って行きたいと思います!
プログラムによる分居モデル
分居モデルをC言語で作って遊んでみました。プログラムが少し甘い所があり、空所が少ないと無限ループします。
ルール
始めに各色をランダムに配置し、次に1ドットの周りのドットが中心のドットとどれほど色違いであるか計算して、あまりにも色違いであったら空いてる所(黒)に移動する、これを全てのドットが満足するまでループさせます。