Loading [MathJax]/extensions/tex2jax.js

2015年2月26日木曜日

テクスチャ補間方法

GL_TEXTURE_MIN_FILTER, GL_TEXTURE_MAG_FILTER を変えながら tunnel サンプルを動かしてみた。

GL_TEXTURE_MIN_FILTER: GL_NEAREST,
GL_TEXTURE_MAG_FILTER: GL_NEAREST

GL_TEXTURE_MIN_FILTER: GL_LINEAR,
GL_TEXTURE_MAG_FILTER: GL_LINEAR

GL_TEXTURE_MIN_FILTER: GL_NEAREST_MIPMAP_NEAREST(ミップレベル間際近接、テクセル間際近接),
GL_TEXTURE_MAG_FILTER: GL_NEAREST

GL_TEXTURE_MIN_FILTER: GL_NEAREST_MIPMAP_LINEAR(ミップレベル間線形補間、テクセル間最近接),
GL_TEXTURE_MAG_FILTER: GL_NEAREST

GL_TEXTURE_MIN_FILTER: GL_LINEAR_MIPMAP_NEAREST(ミップレベル間最近接、テクセル間線形補間),
GL_TEXTURE_MAG_FILTER: GL_LINEAR

GL_TEXTURE_MIN_FILTER: GL_LINEAR_MIPMAP_LINEAR(ミップレベル間線形補間、テクセル間線形補間),
GL_TEXTURE_MAG_FILTER: GL_LINEAR

本に通り GL_LINEAR_MIPMAP_LINEAR が一番画質がよく、GL_LINEAR_MIPMAP_NEAREST もまあ使えて、GL_NEAREST_MIPMAP_NEAREST と GL_NEAREST_MIPMAP_LINEAR は NG な感じですね。

静止画ではわかりませんが、ミップマップを使わない GL_NEAREST, GL_LINEAR は画面が動く(このサンプルでは奥へ進んでいく)とテクスチャがギラギラ光るようなアーティファクトが出ます。

ktxview が動かない

OpenGL SuperBible 6th Edition のサンプルプログラムの ktxview を動かすには少し修正が必要だった。

--- ktxview.cpp.orig 2015-02-26 00:19:32.007575700 +0900
+++ ktxview.cpp 2015-02-26 00:20:30.933946100 +0900
@@ -61,7 +61,7 @@
     "                                                                               \n"
     "uniform sampler2D s;                                                           \n"
     "                                                                               \n"
-    "uniform float exposure;\n"
+    "layout (location = 0) uniform float exposure;\n"
     "\n"
     "out vec4 color;                                                                \n"
     "                                                                               \n"

テクスチャを float で持たせる意義がわかるサンプルでした。

2015年2月18日水曜日

Parallel NSight をつかってみた

NVIDIA の Parallel Nsight をつかってみた。CUDA プログラムのデバッグができることを売りにしてるけど、OpenGL のシェーダーのデバッグにも使えます。VisualStudio からシェーダーにブレークポイント張って、ステップ実行したり、変数の中身を確認したりできて便利。お察しの通り使うには NVIDIA の GPU が必要です。

uniform block のサンプル

OpenGL Super Bible 6th Edition にはサンプルプログラムがいっぱいついているけど、それでも解説されていることに対応するサンプルがけっこうないことに気づいてきた。なのでいくつか自分で作ってみている。
https://github.com/orchely/cg_lesson/tree/master/opengl

uniform block もそうしたサンプルがないもののひとつ。

There is a complete example of the alignments of various types in the original ARB_uniform_buffer_object extension specification.
とあるだけでサンプルプログラムはついていない。一応そのドキュメントのサンプルを動かしてみることはできたけど、OpenGL 2.x 時代のサンプルでいろいろ今と違うし、uniform block の使い方も少し違うので今風に書き直してみた。
https://github.com/orchely/cg_lesson/blob/master/opengl/11_uniform_block_std_layout/uniform_block_std_layout.cpp?ts=4

まだ OpenGL 自体がよくわかっていないので試行錯誤したけど、なんとかそれっぽく動いた。もとのサンプルでは

        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        gluPerspective(60.0, wf/hf, 0.1, 100.0);
となっていたところを
 projection_matrix = glm::perspective(60.0f, 4.0f / 3.0f, 0.1f, 100.0f);
ではなく
 projection_matrix = glm::perspective(45.0f, 4.0f / 3.0f, 0.1f, 100.0f);
にしないといい感じにならなかったのかはよくわかっていない。

ちなみに行列計算には Super Bible 付属のライブラリではなく、この本を卒業したあとも使い続けられそうな GLM を使いました。

2015年2月16日月曜日

Chapter 5: Data, Buffers

OpenGL Super Bible 6th Edition, Chapter 5: Data, Buffers のメモ

  • バッファは汎用の GPU メモリ上のただのメモリブロック。特に使い方は決まっていない。
  • 名前という名の識別子(実際のところ数値)で識別する。
  • 使用するには
    void glGenBuffers(
        GLsizei n,
        GLuint * buffers);
    で作成したあと
    void glBindBuffer(
        GLenum target,
        GLuint buffer);
    でバインディングポイント(ターゲットと呼ぶこともある)にアタッチする。バインディングポイントは以下のものがある:
    • GL_ARRAY_BUFFER (5章)
    • GL_ATOMIC_COUNTER_BUFFER (5章)
    • GL_COPY_READ_BUFFER (5章)
    • GL_COPY_WRITE_BUFFER (5章)
    • GL_DISPATCH_INDIRECT_BUFFER (10章)
    • GL_DRAW_INDIRECT_BUFFER (7章)
    • GL_ELEMENT_ARRAY_BUFFER (7章)
    • GL_PIXEL_PACK_BUFFER (9章)
    • GL_PIXEL_UNPACK_BUFFER (13章)
    • GL_QUERY_BUFFER (非解説、OpenGL 4.4 で導入)
    • GL_SHADER_STORAGE_BUFFER (5章)
    • GL_TEXTURE_BUFFER (5章)
    • GL_TRANSFORM_FEEDBACK_BUFFER (7章)
    • GL_UNIFORM_BUFFER (5章)
  • OpenGL ではバッファに種類はない。glBindBuffer() した後でも、そのバッファを違うバインディングポイントにバインドすることもできる。
  • 各バッファのメモリはアタッチしたあと
    void glBufferData(
        GLenum target,
        GLsizeiptr size,
        const GLvoid * data,
        GLenum usage);
    で割り当てる。usage 引数は確保した領域をどう使うつもりか OpenGL に対しヒントを与えるためのもの。data は確保したメモリに書き込むデータで NULL でもよい。
  • void glBufferSubData(
        GLenum target,
        GLintptr offset,
        GLsizeiptr size,
        const GLvoid * data);
    で後からデータを書き込むこともできる。
  • void *glMapBuffer(
        GLenum target,
        GLenum access);
    で GPU メモリをメインメモリにマップし、普通のメモリと同様に書き込むこともできる。
  • バッファを一様な値で塗りつぶすには
    void glClearBufferSubData(
        GLenum target,
        GLenum internalformat,
        GLintptr offset,
        GLsizeiptr size,
        GLenum format,
        GLenum type,
        const void * data);
    を使う。
  • バッファ間でコピーを行うには
    void glCopyBufferSubData(
        GLenum readTarget,
        GLenum writeTarget,
        GLintptr readOffset,
        GLintptr writeOffset,
        GLsizeiptr size);
    を使う。コピー元/先ともバインディングポイントで指定するので同じバインディングポイントにバインドしたバッファ間でコピーはできない。一つのバインディングポイントに同時に複数のバッファをバインドすることはできないため。コピーするための一時的なバインド先として GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER バインディングポイントが使用できる。
  • (復習) vertex array object: P20 より
    a vertex array object (VAO), which is an object that represents the vertex fetch stage of the OpenGL pipeline and is used to supply input to the vertex shader.
  • (復習) vertex attribute: シェーダーへのデータ入出力に使うこういうやつ
    // "offset" and "color" are input vertex attributes
    layout (location = 0) in vec4 offset;
    layout (location = 1) in vec4 color;
  • バッファから頂点属性にデータを渡すには、まず、
    void glVertexAttribPointer(
        GLuint index,
        GLint size,
        GLenum type,
        GLboolean normalized,
        GLsizei stride,
        const GLvoid * pointer);
    でどの頂点属性が、バッファ中のどこのデータを使うかを指定する。index は上の layout(location = x) で指定した値。引数名とは裏腹に pointer はバッファ中の開始オフセット。バッファ中の整数データをその整数型の最大値で割って 0.0f - 1.0f の float/doubleに変換してシェーダーに渡す場合は normalized を GL_TRUE にする。次に
    void glEnableVertexAttribArray(GLuint index);
    で拡張点属性が glVertexAttrib*() で渡されたデータではなく、バッファ中のデータを使うようにする。
  • 二つの頂点属性に対してことなるバッファからデータを渡すには、一つ目のバッファを glBindBuffer() した状態で、glVertexAttribPointer(), glEnableVertexAttribArray() を呼び、次に二つ目のバッファを glBindBuffer() した状態で、glVertexAttribPointer(), glEnableVertexAttribArray() を呼ぶ。
  • 二つの頂点属性(例えば座標と色)が交互に並んだバッファからデータをわたすには、その共通のバッファを glBindBuffer() を呼んだ後、stride と pointer を適切に設定して、glVertexAttribPointer() と glEnableVertexAttribArray() をそれぞれ 2 回呼ぶ。

2015年2月11日水曜日

3D CG 座標空間

最近 OpenGL の勉強をしているんだけど、別名がいろいろ出てきて混乱してくるのでまとめみる。

オブジェクト空間

  • 別名: モデリング空間、ローカル空間
  • 次数: 3
  • 原点: どこでもかまわないが、通常は原点を中心に回転させたときに妥当な振る舞いになるよう中心を原点にとる。
  • 各モデルに固有空間。モデルを作成するときにはこの座標系で作業する。

ワールド空間

  • 次数: 3
  • 原点: どこでもかまわない。
  • 全てのオブジェクトを配置するためのグローバルな絶対座標をを提供するための空間。ライティング、物理計算はこの座標で行われることが多い。

カメラ空間

  • 別名: 視野空間 (view space)、視点空間 (eye space)
  • 次数: 3
  • 原点: カメラの位置
  • 原点だけでなく、向きも大事。右手座標系ではカメラが向いている方向を \( -z \) 方向(手前ほど大きな値で奥ほど小さな値)、カメラマンにとっての上と右をそれぞれ \( +y, +x \) 方向にとる。

クリップ空間

  • 別名: 正準視体積空間
  • 次数: 4 (同次座標の \( w \) 成分が \( 1 \) ではなく、意味のある値を持つという意味で)
  • 原点: 中心
  • 最終的に見えるオブジェクトを決定するための空間。OpenGL の場合、この座標空間に変換後 \( -w \le x \le w, -w \le y \le w, -w \le z \le w \) になっている点が最終的に映し出される空間に含まれるとして処理される。つまり視錐台の 6 面が \( x = \pm w, y = \pm w, z = \pm w \) の 6 面になる。

正規化デバイス座標空間

  • 別名: NDC (Normalized Device Coordinate) space
  • 次数: 3 (同次座標の \( w \) 成分は \( 1 \) で、もう意味はない)
  • クリップ空間の各点について \( x, y, z, w \) 成分をそれぞれ \( w \) 成分で割って正規化した点が入っている空間。したがって視錐台の 6 面は \( x = \pm 1, y = \pm 1, z = \pm 1 \) の 6 面になる。

ウィンドウ空間

  • 次数: 3 (2ではない。 \( z \) 成分は深度テストで使うため意味のある値を持っている)
  • 正規化デバイス空間の各点の \( x, y \) 成分を最終的な画像を表示するウィンドウのピクセル単位のサイズに合わせてスケールしたもの。正規化デバイス空間では視錐台に含まれる各点の \( x, y \) 成分は \[ -1 \le x \le 1, \\ -1 \le y \le 1 \] に含まれたがこれが \[ -window\_width / 2 \le x \le window\_width / 2, \\ -window\_height /2 \le y \le window\_height / 2 \] になるようにスケールする。