U16_NEXT_UNSAFEを使ってutf16文字列を一文字ずつ処理する。
#include <cstdio> #pragma warning(disable:4996) #include <unicode/utf16.h> #include <locale> int main() { std::setlocale(LC_CTYPE, ""); const char16_t* str = u"い👨👦は"; const char16_t* p = str; int i = 0; while (p[i]) { bool bs = U16_IS_SURROGATE(p[i]);//サロゲートペアかどうか int iback = i; char32_t u32c; U16_NEXT_UNSAFE(p, i,u32c); if (bs==false) { if (p[iback] == 0x200d) {//サロゲートペアではないが、結合文字 printf("[%x] Combine string\n", p[iback]); } else {//一文字 printf("%s [%x] (%lc) -> %x\n", " true ", p[iback], p[iback], u32c ); } } else {//サロゲートペア printf("%s [%x,%x] -> %x ", " false", U16_LEAD(u32c),//u32からサロゲートペアを求める U16_TRAIL(u32c), u32c ); printf(" * [%x,%x]", p[iback], p[iback + 1]);//u16のサロゲートペアを表示 puts(""); } } }
DreamStudioは今話題の、、流行りのAIでキーワードから画像を生成するサービスで、現時点でかなり性能の高いものらしい。人類の歴史が動く!!と興奮する者さえいる。
AIを作る場合、今主流のDeepLearningなんかだと、たくさん教えるほど精度が上がる。資料が少ないものほど精度が落ちる。つまり資料(ネット上に落ちていた画像)の量と質で結果に偏りが生じるはず。
利用にはユーザー登録が必要。
https://beta.dreamstudio.ai/
登録出来たらログインし、画面下のテキストフィールドにキーワードを入力して[Dream]ボタンを押す。
率直に言うと、
1.景観、植物などはそのまま使っても問題ないほど優れたものが出てくる。
2.動物などは時々はずれが出る。
3.人間は結構気持ち悪いのが生成される。
4.開発者の文化圏の影響がなんとなくわかる
まずは「tree」。かなり綺麗。大体はそのまま使ってしまえそうなものが出てくる。
「owl」フクロウについて詳しくないのでどこかおかしくてもよくわからない。
「woman blonde」ここまで整ったものが出来上がる確率は低いが、それでも結構きれいなものが出てくる。というかなんか見たことある気がする。
ただし次で言うように、日本人の脳はブロンドの見分けがそんなに得意ではないので、おかしくても気づいていないだけかもしれない。
「woman asian」blondeとの差が傾向として出てしまう…。
日本人であれば、脳がアジア系の顔の見分けに最適化されているはずなので、どうしても粗が見えやすくなるはず。
「man asian」男性で整った顔が出る確率は結構低い。つまりそういうことorz。
「Manju」饅頭。たこ焼きの間違いでは。
「Hamburger」さすがにこいつの精度は高い。
「onigiri」どう見ても巻き寿司。
「rice ball」イメージと違う。嘘は言ってない、みたいな結果になる。
「butter」バターの精度は高い。
単語だけでやってみたが、多分ある程度長い文で出したほうがずっと面白いものができる。
「christmas night tree in snow」
ただし、人間、動物、昆虫などはメンタルが強くないならお勧めできない。あたりを引くまでに何度も生成を繰り返すことになるが悪夢になりかねない。
あと集合体恐怖症への配慮はない。これは本当にきつい。
著者は今ゼノブレイド3をやっていてガチで記事を書く時間がないのだがそもそもネタがなくてやばい。
まずはモデルを読み込む。
[Shift+A]→[Volume]→[Empty] でボリュームを追加。
モディファイアの設定で、Objectにモデルを選択する。
上記方法でボリュームを作った後、これをメッシュ化する。
[Shift+A]→[Mesh]→[Cube]で立方体を追加する。
Cubeに対してVolume to Meshモディファイアを適用。
ObjectにVolumeを選択し、Applyする。
このチュートリアルは解像度が低いこともあり正確に数字が取れない(し、取れてもたいていうまくいかない)のでざっくりとやってみる。
まず以下が基本となる。他はすべて、以下のコピーとなる。
上をコピーして、色を変える。さらにMappingのLocationを6.5にする。
そしてそれをAdd Shaderする。
上記二つをコピーし、計四つにする。Voronoi Textureのsizeを60に設定。他はいじらない。
Voronoiのscaleが60のものを一つだけコピーし、Locationを変更。あと色を変更。
上記をコピーし三つ作成する。scaleを上から120,180,320に設定。
結果
OpenGLの描画内にFreeType2で簡単に文字列を描画できるFTGLだが改行がやや面倒。
FTGLのFTPixmapFontオブジェクトからBBoxメンバ関数を呼び出す。
この関数はテキストを与えるとそのテキストのバウンディングボックスを返すので、これで一行の高さが分かる。
ただし、このサイズは単位がピクセルらしいので、表示中のワールド座標系に変換してやる必要がある。
#include <iostream> #include <Windows.h> #include <gl/GL.h> #include <gl/GLU.h> #include <gl/freeglut.h> #include <FTGL/ftgl.h> #pragma comment(lib,"opengl32.lib") #pragma comment(lib,"freeglut.lib") #pragma comment(lib,"ftgl.lib") //ウィンドウの幅と高さ int width, height;
// FTGLを管理しやすくするクラス struct CFtglObject { const char* FONT_PATHNAME = "C:\\Windows\\Fonts\\msgothic.ttc"; FTPixmapFont* g_pFont; unsigned long g_ulFontSize; // フォントサイズ ~CFtglObject() { delete g_pFont; } // フォントの初期化 void init(const unsigned long fontsize) { g_ulFontSize = fontsize; if (!g_pFont) { g_pFont = new FTPixmapFont(FONT_PATHNAME); if (g_pFont->Error()) { delete g_pFont; g_pFont = nullptr; } else { g_pFont->FaceSize(g_ulFontSize); } } } // FTGLで文字列を描画 void print(const std::wstring& wstr, float x, float y) { if (g_pFont) { glRasterPos2f(x, y); g_pFont->Render(wstr.c_str()); } } FTPixmapFont& get_font() { return *g_pFont; } };
CFtglObject ftglo; //描画関数 void disp(void) { glViewport(0, 0, width, height); glClearColor(0.2, 0.2, 0.2, 1); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(-1, 1, -1, 1, 1, -1); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glEnable(GL_CULL_FACE); double v = 0.7; glBegin(GL_QUADS); glColor3d(0, 0, 1); glVertex2d(-v, -v); glColor3d(1, 0, 1); glVertex2d(v, -v); glColor3d(1, 1, 1); glVertex2d(v, v); glColor3d(0, 1, 1); glVertex2d(-v, v); glEnd(); glLineWidth(2); glBegin(GL_LINES); glColor3d(1, 1, 0); glVertex2d(-v, 0); glColor3d(1, 1, 1); glVertex2d(v, 0); glColor3d(1, 1, 0); glVertex2d(0, -v); glColor3d(1, 1, 1); glVertex2d(0, v); glEnd(); ///////////////////////////////// ///////////////////////////////// ///////////////////////////////// const wchar_t* text; text = L"いろはに"; ftglo.print(text, 0, 0); FTBBox bbox = ftglo.get_font().BBox(text); float BoxHeightPixel = bbox.Upper().Yf() - bbox.Lower().Yf();//文字列の高さを求める // BoxHeightの単位がピクセルらしいので、これを現在の文字幅に修正する。 // 現在は -1 ~ 1の範囲を0~height-1ピクセルで表示しているので、 float ratioh = 2.f/height; float pixelh = ratioh * BoxHeightPixel; text = L"ほへと"; ftglo.print(text, 0, pixelh); glFlush(); } //ウィンドウサイズの変化時に呼び出される void reshape(int w, int h) { width = w; height = h; disp(); } //エントリポイント int main(int argc, char** argv) { glutInit(&argc, argv); glutInitWindowPosition(100, 50); glutInitWindowSize(500, 500); glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA); ftglo.init(32); glutCreateWindow("sample"); glutDisplayFunc(disp); glutReshapeFunc(reshape); glutMainLoop(); return 0; }
二次元の、ある点が三角形の内側にあるか外側にあるかを判定するコードを以前紹介したのだが、点が三角形の境界ぎりぎりにある場合問題を起こすことがある。
判定方法はs,tを計算し、それが0~1に収まっていれば内側と判断するので、この範囲を0.1~0.9などに変更すると誤差を指定することができるようになる。
//https://suzulang.com/2d-triangle-in-out-check/ template<typename real_t> inline bool isInTheTriangle( real_t px, real_t py, real_t p0x, real_t p0y, real_t p1x, real_t p1y, real_t p2x, real_t p2y ) { real_t Area = 0.5 * (-p1y * p2x + p0y * (-p1x + p2x) + p0x * (p1y - p2y) + p1x * p2y); real_t s = 1.0 / (2.0 * Area) * (p0y * p2x - p0x * p2y + (p2y - p0y) * px + (p0x - p2x) * py); real_t t = 1.0 / (2.0 * Area) * (p0x * p1y - p0y * p1x + (p0y - p1y) * px + (p1x - p0x) * py); real_t e = 0.05; real_t tmin = 0.0 - e; real_t tmax = 1.0 + e; if ( (tmin < s) && (s < tmax) && (tmin < t) && (t < tmax) && (tmin < (1 - s - t)) && ((1 - s - t) < tmax) ) { return true; } else { return false; } }
auto p0 = glm::vec3(-0.913841, 0.999223, 0); auto p1 = glm::vec3(0.032665, -0.787554, 0); auto p2 = glm::vec3(0.853559, 0.974719, 0); glBegin(GL_TRIANGLES); glVertex2fv(glm::value_ptr(p0)); glVertex2fv(glm::value_ptr(p1)); glVertex2fv(glm::value_ptr(p2)); glEnd(); glPointSize(5); glBegin(GL_POINTS); for (float x = -2.f; x < 2.f; x += 0.1f) { for (float y = -2.f; y < 2.f; y += 0.1f) { bool hit = isInTheTriangle( x, y, p0.x, p0.y, p1.x, p1.y, p2.x, p2.y ); if (hit) glColor3d(1, 0, 0); else glColor3d(0.7, 0.7, 0.7); glVertex2f(x,y); } } glEnd();
ICUライブラリでのutf16の扱い方を調べていてマクロがあることを知ったのでいくつか列挙しておきたい。
ICU 71.1:utf16.h File Reference
https://unicode-org.github.io/icu-docs/apidoc/dev/icu4c/utf16_8h.html
utf16文字からコードポイントを得る
#pragma warning(disable:4996) #include <iostream> #include <unicode/utf16.h> int main() { const char16_t* u16c = u"あいう"; char32_t u32c; FILE* fp = fopen("test.txt", "w"); U16_GET_UNSAFE(u16c, 0, u32c); fwrite(&u32c, 4, 1, fp); U16_GET_UNSAFE(u16c, 2, u32c); fwrite(&u32c, 4, 1, fp); fclose(fp); }
与えられたコードポイントが、utf16で何要素になるかを返す。返却値は 1 または 2。
std::u32string cc; // U16_LENGTH 与えられたu32がu16で何要素になるか int len; cc = U"a"; len = U16_LENGTH(cc[0]); printf("--- %d\n", len); // 1 utf16一文字 cc = U"あ"; len = U16_LENGTH(cc[0]); printf("--- %d\n", len); // 1 utf16一文字 cc = U"👨👩👧👦"; len = U16_LENGTH(cc[0]); printf("--- %d\n", len); // 2 結合文字の最初の一文字 cc = U"𠀋"; len = U16_LENGTH(cc[0]); printf("--- %d\n", len); // 2 サロゲートペア cc = U"𠀋𡚴"; len = U16_LENGTH(cc[0]); printf("--- %d\n", len); // 2 最初の一文字がサロゲートペア
U16_IS_SINGLE(c) utf16文字cが、サロゲートペアでなければtrue。
U16_IS_SURROGATE(c) utf16文字cが、サロゲートペアであればtrue。
U16_IS_SURROGATE_LEAD(c) utf16文字cが、上位サロゲートであればtrue。ただし、cがサロゲートペアであることが分かっている必要がある。
U16_IS_SURROGATE_TRAIL(c) utf16文字cが、下位サロゲートであればtrue。ただし、cがサロゲートペアであることが分かっている必要がある。
U16_IS_SINGLE | U16_IS_SURROGATE | U16_IS_SURROGATE_LEAD | U16_IS_SURROGATE_TRAIL | |
u"a"[0] | true | false | true | false |
u"あ"[0] | true | false | true | false |
u"👨👩👧👦"[0] | false | true | true | false |
u"𠀋"[0] | false | true | true | false |
u"a"[1] | true | false | true | false |
u"あ"[1] | true | false | true | false |
u"👨👩👧👦"[1] | false | true | false | true |
u"𠀋"[1] | false | true | false | true |
コードポイントから上位サロゲート(lead)、下位サロゲート(trail)を算出する
#pragma warning(disable:4996) #include <iostream> #include <unicode/utf16.h> int main() { // UTF32 //char32_t u32c = U'あ'; char32_t u32c = U'𠀋'; // char32->char16に変換したとき何文字になるかをチェック if (U16_LENGTH(u32c) == 2){
// utf16に変換したときサロゲートペアになる場合は上下サロゲートを算出 char16_t aa[2]; aa[0] = U16_LEAD(u32c); // 上位サロゲート aa[1] = U16_TRAIL(u32c); // 下位サロゲート // ファイル出力 FILE* fp = fopen("test.txt", "w"); fwrite(aa, 2, 2, fp); fclose(fp);
} else {
//サロゲートペアに変換する必要がなければコードユニット値がutf16文字
char16_t u16 = char16_t(u32c); // ファイル出力 FILE* fp = fopen("test.txt", "w"); fwrite(&u16, 2, 1, fp); fclose(fp);
} }
まず私は動画編集は全く詳しくない。あと今回は半分雑談。
アマレココ
http://www.amarectv.com/download_amarecco4.htm
AMV4
http://www.amarectv.com/buy.htm
アマレココはH.264やUtVideoコーデックで録画もできるが、AMV4(有料)をインストールしておかないとロゴが入る。だからAMV4の購入は必須。
ただしAMV4は「入れてさえおけば」いいのでH.264などが使ったほうが動画編集ソフトで読み込めて勝手が良い。AMV4に対応している編集ソフトは多くない(知らない)。
だから私はずっと「アマレココ」 + 「AMV4」 + 「x264VFW」でデスクトップをキャプチャしていたのだが、PCを初期化したので環境を整えなおして録画してみたところうまく動作しなくなった。
なお動画サイズを640x480などのサイズにするとうまくいくので多分走査線的なものがずれている。
H.264を使わずにAMV4で録画する。
AMV4で録画してからH.264に変換するためにAviUtlを使うが、x264guiExを導入しなければいけない。
AviUtl
http://spring-fragrance.mints.ne.jp/aviutl/
x264guiEx
https://github.com/rigaya/x264guiEx/releases
x264guiExを導入すればH.264に変換できる。
glmの、ある点に最も近い線分上の点を求める関数。
公式マニュアル:
https://glm.g-truc.net/0.9.9/api/a00310.html
static float z = 3.f; { z -= 0.1; glm::vec3 p(1, z, 0); //どこか頂点 glm::vec3 a(0, -2, 0);//線分の始点 glm::vec3 b(0, 2, 0); //線分の終点 // 点に最も近い直線上の点を求めるのに必要 // #include <glm/gtx/closest_point.hpp> glm::vec3 kk = glm::closestPointOnLine(p, a, b); glLineWidth(3);//線分 glBegin(GL_LINES); glVertex3fv(glm::value_ptr(a)); glVertex3fv(glm::value_ptr(b)); glEnd(); glPointSize(7);//どこか頂点 glColor3d(1, 0, 0); glBegin(GL_POINTS); glVertex3fv(glm::value_ptr(p)); glEnd(); glPointSize(7);//線分上の点 glColor3d(0, 1, 0); glBegin(GL_POINTS); glVertex3fv(glm::value_ptr(kk)); glEnd(); }
import bpy # マテリアルにワイヤフレームを追加する def set_wireframe(mat): nodetree = mat.node_tree nodes = nodetree.nodes MaterialOutput = nodes['Material Output'] # MaterialOutputにつながっているノードを取得。 # backup -- (wireframe) -- MaterialOutput と挟み込む backup = MaterialOutput.inputs[0].links[0].from_node # ワイヤフレームに必要なノードを追加 wireframe = nodes.new('ShaderNodeWireframe') diffuse = nodes.new('ShaderNodeBsdfDiffuse') mixnode = nodes.new('ShaderNodeMixShader') # ワイヤフレームの色を黒に設定 diffuse.inputs["Color"].default_value[0] =0 diffuse.inputs["Color"].default_value[1] =0 diffuse.inputs["Color"].default_value[2] =0 # mix , diffuse , wireframe の三つをつなげる nodetree.links.new(wireframe.outputs[0],mixnode.inputs[0]) nodetree.links.new(diffuse.outputs[0],mixnode.inputs[2]) # mixにこれまで指定されていたノードをつなぐ nodetree.links.new(backup.outputs[0],mixnode.inputs[1]) # mixをMaterialOutputへ繋げる nodetree.links.new(mixnode.outputs[0],MaterialOutput.inputs[0]) # 位置調整 # MaterialOutputを少し右へずらし、その位置にmixを入れ、同じ位置にdiffuseとwireframeを入れる MaterialOutput.location.x = MaterialOutput.location.x + 200 mixnode.location.x = MaterialOutput.location.x - 200 wireframe.location.x = mixnode.location.x diffuse.location.x = mixnode.location.x # mix,wireframe,diffuseの高さを変えてそれぞれ配置する mixnode.location.y = MaterialOutput.location.y wireframe.location.y = mixnode.location.y + 200 diffuse.location.y = mixnode.location.y - 200 # ワイヤフレームの線の太さを設定 wireframe.inputs["Size"].default_value = 0.05 set_wireframe( bpy.context.active_object.material_slots["Material"].material)