🛡️

韓国軍のPCで「ソフトウェアレンダリング」してみた

に公開
5
38

はじめに : 真っ黒なコンソール画面と、グラフィックスへの渇望

私は現在、韓国で兵役の義務を遂行中の現役軍人です。
目前には、簡単なウェブサーフィン、ネット講義用として支給された共用PCがあります。

しかし、私はこのPCで派手な3Dグラフィックスを画面に表示させることはできません。
軍隊の厳格なセキュリティシステムが、未知のプログラムによる 「ウィンドウ生成(Window Creation)」や 「ドライバへのアクセス」を完全に遮断しているからです。

この記事は、色んな制約を越え、C++の標準ライブラリ(STL)だけでグラフィックス数学ライブラリとソフトウェアレンダラーをゼロから実装した記録であります。

開発環境:「サイバー知識情報房」という名のダンジョン

CyberKnowledgeRoom
(※ セキュリティ規定により実際の撮影が不可能なため、AI生成画像でイメージを再現しました。)

日本のエンジニアの方々には馴染みがないかもしれないが、韓国の軍隊には兵士が学習や余暇のために使用する共用PCルーム「サイバー知識情報房(サジバン)」が存在します。ここは開発者にとって、最悪でありながら最高のダンジョンだと思います。

以前、この環境でOpenGLを用いた自作レンダラーに挑戦したことがあります。しかし、結果は虚しいものでした。
真っ黒なコンソール画面に表示される Build Success という文字だけを見て、「たぶん、内部ではレンダリングされているはずだ…」と想像するしかなかったのです。
グラフィックスパイプラインの理解にはなりましたが、やはり自分の目で結果を見られないのは悔しいものでした。

「自分の目で、自分が作ったピクセルを見てみたい」

この負けん気が、私をLow-Levelグラフィックスの世界、そして「ソフトウェアレンダリング」へと導きました。

逃げた先に、楽園があった

OpenGLレンダラーを実装しながら、実質的にレンダリング結果を出すことが今の環境では難しいと感じると、自然に軍内での学習の方向性をもっと概念的なところに集中するようになりました。
Modern C++とグラフィックス数学を深く理解するため、GLMのような数学ライブラリをゼロから実装し始めました。名付けて 『ShikaMath』 です。

  • Vector3, Matrix4x4 クラスの実装
  • 内積・外積、正規化、各種変換行列の計算
  • SIMDによる最適化:CPUだけで計算を行うため、SSE命令セットを活用してベクトル演算を高速化

単なる「車輪の再発明」で終わらせないため、メモリレイアウトには特にこだわりました。
以下のように alignas(16) でアライメントを強制し、union を活用してスカラーとレジスタの両方にゼロオーバーヘッドでアクセスできる構造にしています。

// ShikaMath/Vector3.h (Preview)
namespace Shika {
   // SIMD演算のために16バイトアライメントを強制
   struct alignas(16) Vector3 {
      public : 
         union {
            // 直感的なアクセスのための匿名構造体
            struct {float x, y, z;};
 
            // SIMDレジスタ (__m128)
            __m128 v;

            // パディングを含む配列アクセス用
            float e[4];
         };

      // ... (コンストラクタや演算子オーバーロードの実装はGitHubへ)
   };
}

このように、低レベルなメモリ管理を意識することで、普段エンジン任せにしていた「計算コスト」の重みを肌で感じることができました。 行列演算の最適化(Linear Combination)など、より詳細な実装コードはGitHubリポジトリで公開しています。

https://github.com/SHIKA-gfx/ShikaMath

ウィンドウがないなら、ファイルで焼けばいい

数学ライブラリーがある程度の品揃えを整えていくうちに、再びレンダリングに対する欲が生じ始めました。
画面(Window)にピクセルを打てないなら、ハードディスクにファイルとして書き出せばいいという結論に至りました。
それでソフトウェアレンダリングパイプラインを構築し、結果をPPM形式で出力することにしました。

  • なぜPPMなのか?
    PPMならヘッダーがテキスト(P3など)で構成されているため、std::ofstream と文字列操作だけで画像を生成可能
  • パイプラインの構築
    GPUが行っている処理を、CPU上でそのまま模倣しました。
    • Vertex Processing: ShikaMathを使い、頂点を World → View → Projection 空間へ変換
    • Rasterization: 変換された頂点を2D画面座標へマッピングし三角形を塗りつぶす。
    • Output: 計算された色情報をPPMフォーマットのテキストとしてファイルに保存。

仮想スクリーン『Canvas』の実装

メモリ上に仮想的なスクリーン(フレームバッファ)を用意し、それを画像ファイルとしてダンプするクラス Canvas を実装しました。

1. メモリ上のピクセル配列
GPUのフレームバッファの代わりに、std::vector<Color> を使用してピクセルデータを管理します。 2次元の画像データ(x, y)を1次元の配列インデックスに変換してアクセスする、非常にシンプルな構造です。

// Canvas.h (一部抜粋)
class Canvas {
private: 
    int width;
    int height;
    std::vector<Color> pixels; // これが仮想的な画面となる

public:
    Canvas(int w, int h) : width(w), height(h) {
        pixels.resize(w * h, Color::Black());
    }

    // (x, y) 座標を 1次元インデックス (y * width + x) に変換して書き込む
    void PutPixel(int x, int y, const Color& color) {
        if (x < 0 || x >= width || y < 0 || y >= height) return;
        pixels[y * width + x] = color;
    }
    
    // ...
};

2. PPM(Portable Pixel Map)フォーマットによる出力
PPMはヘッダーからピクセルデータまで全て「テキスト」で記述できるため、C++標準の std::ofstream さえあれば実装可能です。

// Canvas.h : 画像出力ロジック
bool SaveToPPM(const std::string& filename) {
    std::ofstream ofs(filename);
    if (!ofs.is_open()) return false;

    // PPMヘッダーの書き込み (P3 = テキスト形式カラー画像)
    ofs << "P3\n" << width << " " << height << "\n255\n";

    // ピクセルデータの書き込み
    for (const auto& p : pixels) {
        // 0.0~1.0 (float) の色情報を 0~255 (int) に変換
        int ir = static_cast<int>(std::clamp(p.r, 0.0f, 1.0f) * 255.99f);
        int ig = static_cast<int>(std::clamp(p.g, 0.0f, 1.0f) * 255.99f);
        int ib = static_cast<int>(std::clamp(p.b, 0.0f, 1.0f) * 255.99f);
    
        ofs << ir << " " << ig << " " << ib << "\n";
    }

    ofs.close();
    return true;
}

このように、制約だらけの環境でも 「標準ライブラリだけで完結させる」 というアプローチを取ることで、レンダリング結果を可視化することに成功しました。

結果:闇の中で咲いた、最初の三角形

コードをビルドし実行すると、フォルダに output.ppm が生成されました。 Web上のPPMビューアにファイルをドラッグ&ドロップして開きました。

PPMresult

久しぶりにみたレンダリングされた三角形は、とても嬉しかったです。
コンソールの黒い画面の裏側で、数字の羅列として計算されていたグラフィックスが、ついに実体を持って現れた瞬間でした。

結論:制約は成長の触媒である

軍隊という特殊な環境は、私から「便利なツール」を奪ったが、代わりに「原理への探求心」を与えてくれました。

もし私が快適な自宅で、UnityやUnreal Engineだけを使っていたら、行列の4行目がなぜ「平行移動(Translation)」を担当するのか、ラスタライザがどうやって三角形をピクセルに変えるのか、これほど深く悩むことはなかったかもしれないと思います。

私はもうすぐ除隊しますが、
この「底」から積み上げた基礎を武器に、より広いグラフィックスエンジニアリングの世界へ飛び込もうとしてます。
軍隊だからこそできなかったことも多いですが、軍隊だからこそ学べたこともたくさんありました。これからも、目の前に制約や障害物があったとしても、それを成長の機会に変えていきたいと考えています。

38

Discussion

ひよつくひよつく

制約は成長の触媒であるという意見、とても共感します。
転役までに情報通信大隊に見つからないことをお祈りしておきますw

Shika-gfxShika-gfx

共感していただき嬉しいです。制約があったからこそ、ここまで没頭できたのだと思います。情報通信大隊には……見つからないようにうまく逃げ切りますw

Nknight AMAMIYANknight AMAMIYA

斬新すぎるタイトルに引き寄せられてみてしまいました...w
お体にはきおつけて、これからも遠くで応援しています by 日本の学生より

Shika-gfxShika-gfx

読んでいただきありがとうございます!
限られた環境ですが、除隊するまでセキュリティ担当に怒られない範囲で、これからも技術を楽しみたいと思います!

SangunSangun

懐かしい~
同じく韓国人で今は日本でお仕事しています。
兵役終わって10年ぐらい経ちますが、自分はあそこでfacebookばっかりしました。
めっちゃいい記事で、刺激をもらいました!
どうか終わるまでセキュリティ的なトラブルなく、無事に전역してください!

ログインするとコメントできます
38