2015年3月5日木曜日

プログラミング言語 C++ 第 1 章

値段も厚さもすごいけど、今メインで使っているのが C++ なので買わずにいられなかった。

まずは「第1章 本書の読み進め方」を読んだ。気になった点として:

  • C99 の可変長配列は意図的に C++11 には取り込まれなかった。
  • 1980年の "C with Classes" には既にタスクライブラリがあった(!)が、C++11 で並行処理ライブラリが復活するまで 30 年もかかった!
  • 例外指定が obsolete になってた。
  • (メジャーなコンパイラは実装してないのでお目にかかったことはないけど)外部テンプレートも obsolete になってた。
  • iostream に Norihiro Kumagai さんという日本人がからんでる。(シャープの人?)
  • コンセプトなるものが C++17 で標準入りを目指している。
  • counted_ptr なんてあったの!?
  • Linux の主要部分が C++ で書かれているというのはちょっと違うんじゃないでしょうか?

旧版と比べると(今の所)訳がまっとうになってる感じで安心。

2015年3月4日水曜日

GLSL のコンパイルエラーメッセージにファイル名を含める

ソースコードの頭に #line ディレクティブでファイル名を書いておくと:

#version 430 core
#line 3 "append.fs.glsl"

layout(binding = 0, offset = 0) uniform atomic_uint fill_counter;
layout(binding = 0, r32i) coherent uniform uimage2D heap_pointer;

struct list_item {
 vec4 color;
 float depth;
 int facing;
 uint next;
};

layout(binding = 0, std430) buffer list_item_block {
 list_item item[];
}

in VS_OUT {
 vec4 pos;
 vec4 color;
} fs_in;

void main(void)
{
 ivec2 P = ivec2(gl_FragCoord.xy);
 uint index = atomicCounterIncrement(fill_counter);
 uint old_head = imageAtomicExchange(heap_pointer, P, index);
 item[index].color = fs_in.color;
 item[index].depth = gl_FragCoord.z;
 item[index].facing = gl_FontFacing ? 1 : 0;
 item[index].nex = old_head;
}

glGetShaderInfoLog() の出力にファイル名が出るようになる。

append.fs.glsl(5) : error C1318: can't apply layout(r32i) to image type "uimage2D"
append.fs.glsl(18) : error C0000: syntax error, unexpected reserved word "in" at token "in"
append.fs.glsl(21) : error C0000: syntax error, unexpected '}' at token "}"

#line の次の数値は #line ディレクティブの次の行がソースコード中の何行目に当たるかを指定するもの。

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 回呼ぶ。