C++のテンプレートテンプレートパラメータ(誤表記ではない)について。
以下をやりたい。つまりコンテナとテンプレート引数を別にとるようなテンプレートを書きたい。
しかしこれは動かない。
//エラー コンパイルできないtemplate< class Ty, class Type> auto makelistError() { Ty<Type> v; v.push_back(5); v.push_back(6); v.push_back(7); v.push_back(8); return v; }int main() { auto mylist = makelistError<std::vector, int>(); std::cout << "出力" << std::endl; for (auto it = std::begin(mylist); it != std::end(mylist); it++) { std::cout << *it << std::endl; } int i; std::cin >> i; }
template< template<class,class> class Ty, class Type> auto makelist() { Ty<Type, std::allocator<Type> > v;//STLのリスト構造は本来データ型とデータのallocatorの二つをとる v.push_back(5); v.push_back(6); v.push_back(7); v.push_back(8); return v; }int main() { auto mylist = makelist<std::vector, int>(); std::cout << "出力" << std::endl; for (auto it = std::begin(mylist); it != std::end(mylist); it++) { std::cout << *it << std::endl; } int i; std::cin >> i; }
makelistの第一テンプレート引数の意味は、「テンプレート引数を二つとる型」という意味になる。第二引数はそのまま。
vectorの場合、定義が以下のようになっているので、受け取る側もそれに合わせなければならない。

上記プログラムのstd::vectorの所をstd::listにしても正常に動作する。
#pragma once // vcg コア #include <vcg/complex/complex.h> // vcg input output #include <wrap/io_trimesh/import_ply.h> #include <wrap/io_trimesh/export_ply.h> #include <wrap/io_trimesh/import_stl.h> using namespace vcg; using namespace std; ///////////////////////////////////////////////// ///////////////////////////////////////////////// // メッシュデータ型 ///////////////////////////////////////////////// ///////////////////////////////////////////////// class MyFace; class MyVertex; class MyEdge; struct MyUsedTypes : public UsedTypes< Use<MyVertex>::AsVertexType, Use<MyFace>::AsFaceType, Use<MyEdge>::AsEdgeType > {}; class MyVertex : public Vertex< MyUsedTypes, vertex::Coord3f, vertex::Color4b, vertex::Normal3f, vertex::BitFlags, vertex::VEAdj,// needed vertex::Mark> { public: bool m_loopchecked; MyVertex() { m_loopchecked = false; } }; class MyEdge : public vcg::Edge< MyUsedTypes, vcg::edge::VertexRef, vcg::edge::VEAdj,// needed vcg::edge::BitFlags> { public: MyEdge() { } }; class MyFace : public Face < MyUsedTypes, face::VertexRef, face::Normal3f, face::FFAdj,// needed face::BitFlags > {}; class MyMesh : public vcg::tri::TriMesh< vector<MyVertex>, vector<MyFace>, vector<MyEdge> > {};
#pragma once #include <windows.h> #include <GL/gl.h> #include <GL/freeglut.h> #include "nurotate.h" #include "MyMesh.h" //! @brief 穴構成頂点の配列をhole_tとする。 using hole_t = std::vector<int>; //! @brief 穴の配列 std::vector<hole_t> holes;//! @brief 穴を検出する関数 //! @param [in] mesh メッシュデータ //! @param [out] hole 穴構成頂点の格納先 //! @param [in] vid 検索開始地点の頂点ID //! @retval false 穴が見つからなかった //! @retval true 穴が見つかった bool search_near_edge(MyMesh& mesh, hole_t& hole,int vid) { vcg::edge::VEIterator<MyEdge> vfe(&mesh.vert[vid]); for (; !vfe.End(); ++vfe) { MyEdge* e = vfe.E(); // OpenEdge以外は読み飛ばす if (e->IsB() == false) { continue; } //現在検索中の頂点ともう一方に分ける int from, to; if (vcg::tri::Index(mesh, e->cV(0)) == vid) { from = vid; to = vcg::tri::Index(mesh, e->cV(1)); } else { from = vid; to = vcg::tri::Index(mesh, e->cV(0)); } if (hole.size() != 0) { if (hole[0] == to) { hole.push_back(from); return true; } } if (mesh.vert[to].m_loopchecked == true) continue; mesh.vert[from].m_loopchecked = true; hole.push_back(from); if (search_near_edge(mesh, hole, to) == true) { return true; } } return false; }MyMesh m; int width, height; //回転オブジェクト定義 nu::mrotate camr; //! @brief 表示関数 //! @return なし void disp(void) { glClearColor(0.3, 0.3, 0.3, 1); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glViewport(0, 0, width, height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(45,width / (double)height, 0.1, 5); glMatrixMode(GL_MODELVIEW); glEnable(GL_DEPTH_TEST); glEnable(GL_NORMALIZE); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 1); GLfloat lpos[4] = { 0,1,1,1 }; GLfloat ambw[4] = { 0.5,0.5,0.5,1 }; GLfloat color[4] = { 1,1,1,1 }; GLfloat lspec[4] = { 1,1,1,1 }; GLfloat mspec[4] = { 0.5,0.5,0.5,1 }; ///////////////////////////////////////////// glLightfv(GL_LIGHT0, GL_AMBIENT_AND_DIFFUSE, ambw); glLightfv(GL_LIGHT0, GL_SPECULAR, lspec); glLightfv(GL_LIGHT0, GL_POSITION, lpos); ///////////////////////////////////////////// glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color); glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 20.0); glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, mspec); glPushMatrix(); //原点を視線方向に0.5ずらす glTranslated(0, 0, -2); //回転行列を適用 double mat[16]; glMultMatrixd(camr.getmatrix(mat));glPushMatrix(); glScaled(2.0 / m.bbox.Diag(), 2.0 / m.bbox.Diag(), 2.0 / m.bbox.Diag()); { //全ての面を表示 glBegin(GL_TRIANGLES); for (auto& f : m.face) { glNormal3f(f.cN().X(), f.cN().Y(), f.cN().Z()); glVertex3d(f.cV(0)->cP().X(), f.cV(0)->cP().Y(), f.cV(0)->cP().Z()); glVertex3d(f.cV(1)->cP().X(), f.cV(1)->cP().Y(), f.cV(1)->cP().Z()); glVertex3d(f.cV(2)->cP().X(), f.cV(2)->cP().Y(), f.cV(2)->cP().Z()); } glEnd(); } { //全てのエッジを表示 glDisable(GL_LIGHTING); glLineWidth(1); glColor3d(0, 0, 0); for (auto& f : m.face) { glBegin(GL_LINE_LOOP); glVertex3d(f.cV(0)->cP().X(), f.cV(0)->cP().Y(), f.cV(0)->cP().Z()); glVertex3d(f.cV(1)->cP().X(), f.cV(1)->cP().Y(), f.cV(1)->cP().Z()); glVertex3d(f.cV(2)->cP().X(), f.cV(2)->cP().Y(), f.cV(2)->cP().Z()); glEnd(); } } { //見つかった穴だけを表示 glDisable(GL_LIGHTING); glLineWidth(3); glColor3d(0, 0, 1); for (auto& hole : holes) { glBegin(GL_LINE_STRIP); for (auto& v : hole) { glVertex3d( m.vert[v].cP().X(), m.vert[v].cP().Y(), m.vert[v].cP().Z() ); } glEnd(); } } glPopMatrix();glPopMatrix(); glFlush(); } void reshape(int w, int h) { width = w; height = h; //クライアント領域のサイズを指定 //Win32APIの場合はGetClientRectで求めたrightとbottomだがglutではwとh camr.setWindowSize(width, height); //クライアント領域のどこの範囲に描画しているかを指定。 //glViewportで一画面丸々描画しているので、クライアント領域の(0,0)-(width,height)に書いていることになる camr.setRenderRect(0, 0, width, height); disp(); } //マウスクリック時のイベント void mouse(int button, int state, int x, int y) { if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) { camr.dragstart(x, y);//最初の回転軸を決定 } else if (state == GLUT_UP) { camr.dragend();//ドラッグ終了 } glutPostRedisplay(); } //ドラッグ時のイベント void motion(int x, int y) { camr.dragto(x, y);//移動中の回転軸を更新 disp(); } //エントリポイント int main(int argc, char ** argv) { glutInit(&argc, argv); glutInitWindowPosition(100, 50); glutInitWindowSize(400, 300); glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA); glutCreateWindow("MouseRotateSample"); glutDisplayFunc(disp); glutReshapeFunc(reshape); glutMouseFunc(mouse); glutMotionFunc(motion);vcg::tri::io::ImporterPLY<MyMesh>::Open(m, "sample.ply"); vcg::tri::Clean<MyMesh>::RemoveDuplicateVertex(m); vcg::tri::UpdateBounding<MyMesh>::Box(m); vcg::tri::UpdateNormal<MyMesh>::PerFace(m); vcg::tri::UpdateTopology<MyMesh>::FaceFace(m); vcg::tri::UpdateTopology<MyMesh>::AllocateEdge(m); vcg::tri::UpdateTopology<MyMesh>::VertexEdge(m); vcg::tri::UpdateFlags<MyMesh>::VertexBorderFromFaceAdj(m);///////////////////////////////// // 穴検出 /////////////////////// bool ret; for (size_t i = 0; i < m.vert.size(); i++) { //OpenEdgeのフラグが立っていないなら何もしない if (m.vert[i].IsB() == false) continue; //すでにループかどうかの評価をしたフラグが立っていれば何もしない if (m.vert[i].m_loopchecked) continue; m.vert[i].m_loopchecked = true; hole_t hole; ret = search_near_edge(m, hole,i);//未チェックのOpenEdge構成頂点を渡して穴かどうかの評価 if (ret == true) { holes.push_back(hole);//穴一覧を更新 } }glutMainLoop(); return 0; }
以下を参照

4×4行列の積を行うと以下のような結果になる

高精度計算サイト https://keisan.casio.jp/exec/system/1308269580
行列は数学ではi,jの二次元で表現されたりするが、C/C++ではメモリが一次元のため大抵は一次元で扱われる。以下の式で二次元を一次元に変換する

inline int f(const int i, const int k) { return i + 4*k; } //4x4行列の乗算関数 void multMatrix(double* C, double* A, double* B) { for (int i = 0; i < 4; i++) for (int k = 0; k < 4; k++) { C[f(i, k)] = 0.0; for (int j = 0; j < 4; j++) { C[f(i, k)] += A[f(i, j)]*B[f(j, k)]; } } } //描画関数 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); double in[4] = { 0.2,0.1,0.1,1 }; glMatrixMode(GL_MODELVIEW); glLoadIdentity(); double rmat[16]; double tmat[16]; ////////////////////// OpenGLで計算 glPushMatrix(); glRotated(120, 0, 0, 1); glTranslated(0.5, 0, 0); glColor3d(1, 0, 0); glPointSize(15); glBegin(GL_POINTS); glVertex3d(in[0], in[1], in[2]); glEnd(); glPopMatrix(); ////////////////////// 自前実装 ////////////////////// OpenGLで行列作成 glPushMatrix(); glRotated(120, 0, 0, 1); glGetDoublev(GL_MODELVIEW_MATRIX, rmat); glPopMatrix(); glPushMatrix(); glTranslated(0.5, 0, 0); glGetDoublev(GL_MODELVIEW_MATRIX, tmat); glPopMatrix(); //行列のかけ算 double amat[16]; glPushMatrix(); multMatrix(amat, rmat, tmat); glLoadMatrixd(amat); glColor3d(0, 1, 0); glPointSize(10); glBegin(GL_POINTS); glVertex3d(in[0], in[1], in[2]); glEnd(); glPopMatrix(); glFlush(); }


template<typename real_t> inline void multMatrixVec(real_t* const out4, const real_t* const matrix, const real_t* const in4) { int i; for (i = 0; i<4; i++) { out4[i] = in4[0] * matrix[0 * 4 + i] + in4[1] * matrix[1 * 4 + i] + in4[2] * matrix[2 * 4 + i] + in4[3] * matrix[3 * 4 + i]; } }
template<typename real_t> inline void multMatrixVec(real_t* const out4, const real_t* const matrix, const real_t* const in4) { int i; for (i = 0; i < 4; i++) { out4[i] = in4[0] * matrix[0 * 4 + i] + in4[1] * matrix[1 * 4 + i] + in4[2] * matrix[2 * 4 + i] + in4[3] * matrix[3 * 4 + i]; } }//描画関数 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); double in[4] = { 0.1,0.2,0.3,1 }; glMatrixMode(GL_MODELVIEW); glLoadIdentity(); double mat[16]; ////////////////////// OpenGLで描画 glPushMatrix(); glRotated(25, 0, 0, 1); glTranslated(0.5, 0, 0); glGetDoublev(GL_MODELVIEW_MATRIX, mat); //行列を退避 glColor3d(1, 0, 0); glPointSize(15); glBegin(GL_POINTS); glVertex3d(in[0], in[1], in[2]); glEnd(); glPopMatrix(); ////////////////////// 自前実装 double out4[4]; multMatrixVec(out4, mat, in);//退避した行列で座標を変換 glColor3d(0, 1, 0); glPointSize(10); glBegin(GL_POINTS); glVertex3d(out4[0], out4[1], out4[2]); glEnd(); glFlush(); }
球を模倣したい。wireframeなsphereだと表示が鬱陶しくなったりするので円で代用したいような場合に用いる。
今回のプログラムは前回のものに付け足すのでdrawcircle.hppをincludeする。なおbbはbillboardの意。
//! @file drawcircle_bb.hpp #pragma once #include "drawcircle.hpp" //! @brief カメラ行列から視線方向に対して垂直で画面右向きのベクトルを取得する //! @param [out] v3 結果を格納する三次元ベクトル 要素数3 //! @param [in] cameraModelView カメラ行列 //! @return なし inline void getRayRightFromCamera(double* v3, double* cameraModelView) { v3[0] = cameraModelView[0]; v3[1] = cameraModelView[4]; v3[2] = cameraModelView[8]; } //! @brief カメラ行列から視線方向に対して垂直で画面上向きのベクトルを取得する //! @param [out] v3 結果を格納する三次元ベクトル 要素数3 //! @param [in] cameraModelView カメラ行列 //! @return なし inline void getRayUpFromCamera(double* v3, double* cameraModelView) { v3[0] = cameraModelView[1]; v3[1] = cameraModelView[5]; v3[2] = cameraModelView[9]; }; //! @brief 常に正面を向いた円を描く //! @param [in] x 中心座標 //! @param [in] y 中心座標 //! @param [in] z 中心座標 //! @param [in] r 半径 //! @param [in] count 分割数 4 ぐらいがとりあえず適当 //! @param [in] カメラ行列。nullptrを指定した場合はglGetDoublevでOpenGLから取り出す //! @return なし inline void circle_xy_bb(double x, double y, double z, double r, const int count,double* cameramatrix=nullptr) { coord_t center(x, y, z); double vr[3]; double vu[3]; //モデルビュー行列から四角形の頂点を取り出す if (cameramatrix == nullptr) { double model[16]; glGetDoublev(GL_MODELVIEW_MATRIX, model); getRayRightFromCamera(vr, model); getRayUpFromCamera(vu, model); } else { getRayRightFromCamera(vr, cameramatrix); getRayUpFromCamera(vu, cameramatrix); } double a[3] = { vr[0], vr[1], vr[2] }; double b[3] = { vu[0],vu[1], vu[2] }; double c[3] = {-vr[0],-vr[1], -vr[2] }; double d[3] = {-vu[0],-vu[1], -vu[2] }; coord_t p[4]; p[0] = coord_t(a); p[1] = coord_t(b); p[2] = coord_t(c); p[3] = coord_t(d); glBegin(GL_LINE_LOOP); for (int i = 0; i < 4; i++) { int ia = i % 4; int ic = (i + 1) % 4; coord_t a(p[ia].x, p[ia].y, p[ia].z); coord_t c(p[ic].x, p[ic].y, p[ic].z); plot_ab(center, count, r, a, c); } glEnd(); }
#include <iostream> #include <Windows.h> #include <gl/GL.h> #include <gl/GLU.h> #include <gl/freeglut.h> #include <cassert> // freeglut: // http://freeglut.sourceforge.net/ //矢印描画関数 #include "drawarrow.hpp" //円描画関数 #include "drawcircle.hpp" #include "drawcircle_bb.hpp" //ウィンドウの幅と高さ int width, height; double rotate_angle = 0; //描画関数 void disp(void) { glClearColor(0.2, 0.2, 0.2, 1); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glEnable(GL_DEPTH_TEST); glViewport(0, 0, width, height); //カメラの設定 glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(60, width / (double)height, 0.1, 3); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glTranslated(0, 0, -2); //表示用の回転 glRotated(rotate_angle, 0, 1, 0); glRotated(rotate_angle, 1, 0, 0); //表示用に全体を少しずらす glTranslated(0, 0.2, 0.2); //矢印を三つ描画 glLineWidth(2); double center[3] = { 0.3,0,0 }; double tox[3] = { 1 + center[0],0 + center[1],0 + center[2] }; double toy[3] = { 0 + center[0],1 + center[1],0 + center[2] }; double toz[3] = { 0 + center[0],0 + center[1],1 + center[2] }; glColor3d(1, 0, 0); drawArrow(center, tox); glColor3d(0, 1, 0); drawArrow(center, toy); glColor3d(0, 0, 1); drawArrow(center, toz); glLineWidth(1); ////////////////////////////////////// //円を描画 glLineWidth(3); glColor3d(1, 1, 0); circle_xy(center[0], center[1], center[2], 0.5, 3); glColor3d(0, 1, 1); circle_xy_bb(center[0], center[1], center[2], 0.5, 3,nullptr); glFlush(); } //ウィンドウサイズの変化時に呼び出される void reshape(int w, int h) { width = w; height = h; disp(); } void timer(int value) { rotate_angle += 5; disp(); glutTimerFunc(100, timer, 0); } //エントリポイント int main(int argc, char** argv) { glutInit(&argc, argv); glutInitWindowPosition(100, 50); glutInitWindowSize(500, 500); glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA); glutCreateWindow("sample"); glutDisplayFunc(disp); glutReshapeFunc(reshape); glutTimerFunc(1000, timer, 0);//タイマー glutMainLoop(); return 0; }
※2020/09/08追記:正直、以下の方が簡単です。
OpenGLで円を描くプログラム。
普通のやり方だと三角関数を使って正多角形を描くのだけれど、面倒なので、まず正方形を用意し、それをN分割することで円に近づける。
ただし今回、円はXY平面上にある。
//! @file drawcircle.hpp #pragma once #include <Windows.h> #include <gl/GL.h> #include <cmath> //! @brief 座標を管理する構造体 struct coord_t { double x, y, z; coord_t() {} coord_t(double X, double Y, double Z) :x(X), y(Y), z(Z) {} coord_t(double* v3) { x = v3[0]; y = v3[1]; z = v3[2]; } coord_t(const coord_t& src) { x = src.x; y = src.y; z = src.z; } inline bool normalize() { double len = static_cast<double>(sqrt(x * x + y * y + z * z)); if (len < 1e-6) return false; len = 1.0 / len; x *= len; y *= len; z *= len; return true; } inline void mult(const double val) { x *= val; y *= val; z *= val; } }; //! @brief a----c という辺をa--b--cと分割し、a--bを描画する。呼び出し側でGL_LINE_LOOPを使う限りb--cは勝手につながる //! @param [in] offset 円の中心座標 //! @param [in] count 分割数 //! @param [in] r 半径 //! @param [in] a 始点 //! @param [in] c 終点 //! @return なし inline void plot_ab(const coord_t offset, const int count, double r, coord_t a, coord_t c) { a.normalize(); c.normalize(); a.mult(r); c.mult(r); if (count == 0) { glVertex3d(a.x + offset.x, a.y + offset.y, a.z + offset.z); return; } //a----c → a--b--c coord_t b((a.x + c.x) / 2.0, (a.y + c.y) / 2.0, (a.z + c.z) / 2.0); b.normalize(); b.mult(r); if (count == 1) { glVertex3d(a.x + offset.x, a.y + offset.y, a.z + offset.z); glVertex3d(b.x + offset.x, b.y + offset.y, b.z + offset.z); } else { plot_ab(offset, count - 1, r, a, b); plot_ab(offset, count - 1, r, b, c); } } //! @brief OpenGLで円を描く //! @param [in] x 中心座標 //! @param [in] y 中心座標 //! @param [in] z 中心座標 //! @param [in] r 半径 //! @param [in] count 分割数 4 ぐらいがとりあえず適当 //! @return なし inline void circle_xy(double x, double y, double z, double r, const int count) { coord_t center(x, y, z); coord_t p[4]; p[0] = coord_t(-1, -1, 0); p[1] = coord_t(1, -1, 0); p[2] = coord_t(1, 1, 0); p[3] = coord_t(-1, 1, 0); glBegin(GL_LINE_LOOP); for (int i = 0; i < 4; i++) { int ia = i % 4; int ic = (i+1) % 4; coord_t a(p[ia].x, p[ia].y, p[ia].z); coord_t c(p[ic].x, p[ic].y, p[ic].z); plot_ab(center, count, r, a, c); } glEnd(); }
#include <iostream> #include <Windows.h> #include <gl/GL.h> #include <gl/GLU.h> #include <gl/freeglut.h> #include <cassert> // freeglut: // http://freeglut.sourceforge.net/ //矢印描画関数 #include "drawarrow.hpp"
//円描画関数
#include "drawcircle.hpp" //ウィンドウの幅と高さ int width, height; double rotate_angle=0; //描画関数 void disp(void) { glClearColor(0.2, 0.2, 0.2, 1); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glEnable(GL_DEPTH_TEST); glViewport(0, 0, width, height); //カメラの設定 glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(60, width / (double)height, 0.1, 3); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glTranslated(0, 0, -2); //表示用の回転 glRotated(rotate_angle, 0, 1, 0); glRotated(rotate_angle, 1, 0, 0); //矢印を三つ描画 glLineWidth(2); double from[3] = { 0,0,0 }; double tox[3] = { 1,0,0 }; double toy[3] = { 0,1,0 }; double toz[3] = { 0,0,1 }; glColor3d(1, 0, 0); drawArrow(from, tox); glColor3d(0, 1, 0); drawArrow(from, toy); glColor3d(0, 0, 1); drawArrow(from, toz); glLineWidth(1); ////////////////////////////////////// //円を描画 glLineWidth(3); glColor3d(1, 1, 0); circle_xy(0,0,0,0.5,4); glFlush(); } //ウィンドウサイズの変化時に呼び出される void reshape(int w, int h) { width = w; height = h; disp(); } void timer(int value) { rotate_angle += 5; disp(); glutTimerFunc(100, timer, 0); } //エントリポイント int main(int argc, char** argv) { glutInit(&argc, argv); glutInitWindowPosition(100, 50); glutInitWindowSize(500, 500); glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA); glutCreateWindow("sample"); glutDisplayFunc(disp); glutReshapeFunc(reshape); glutTimerFunc(1000, timer, 0);//タイマー glutMainLoop(); return 0; }
本格的な矢印はポリゴン数も計算も大変なので簡易的な矢印を実装する。
なおそろそろ拡張子.hppを割と意識して使うことにする
#pragma once #include <Windows.h> #include <gl/GL.h> namespace numath { typedef double real_t; //! @brief ラジアンを度に変換 //! @param [in] radian ラジアンの値 //! @return 度の値 inline real_t toDegree(const real_t radian) { return radian * 180 / 3.1415926535897932384626; } //! @brief 三次元のベクトルを正規化する //! @param [in,out] pV 三次元ベクトル1 要素数3 //! @retval true 成功 //! @retval false 失敗 inline bool normalize(real_t* const pV) { const int X = 0; const int Y = 1; const int Z = 2; real_t len; real_t& x = pV[X]; real_t& y = pV[Y]; real_t& z = pV[Z]; len = static_cast<real_t>(sqrt(x * x + y * y + z * z)); if (len < static_cast<real_t>(1e-6)) return false; len = static_cast<real_t>(1.0) / len; x *= len; y *= len; z *= len; return true; } //! @brief ベクトルの長さを求める //! @param [in] vec 三次元ベクトル1 要素数3 //! @return vecの長さを表すスカラー値 inline real_t length(real_t const* const vec) { const int X = 0; const int Y = 1; const int Z = 2; return sqrt(vec[X] * vec[X] + vec[Y] * vec[Y] + vec[Z] * vec[Z]); } //! @brief 二つの三次元ベクトルの内積を求める //! @param [in] vec1 三次元ベクトル1 要素数3 //! @param [in] vec1 三次元ベクトル2 要素数3 //! @return ベクトルの内積 inline real_t inner(real_t const* const vec1, real_t const* const vec2) { const int X = 0; const int Y = 1; const int Z = 2; return ((vec1[X]) * (vec2[X]) + (vec1[Y]) * (vec2[Y]) + (vec1[Z]) * (vec2[Z])); } //! @brief 二つの三次元ベクトルの角度を求める //! @param [in] vec1 三次元ベクトル1 要素数3 //! @param [in] vec1 三次元ベクトル2 要素数3 //! @return ベクトルの角度(ラジアン) inline real_t vectorAngle(real_t const* const vec1, real_t const* const vec2) { real_t x = inner(vec1, vec2) / (length(vec1) * length(vec2)); if (x <= -1) x = -0.9999999; if (x >= 1) x = 0.99999999; return acos(x); } //! @brief ベクトルにスカラー値をかける //! @param [in,out] 対象の三次元ベクトル1 要素数3 //! @param scalar スカラー値 //! @return なし inline void vectorMult3(real_t * dst, const real_t scalar) { dst[0] *= scalar; dst[1] *= scalar; dst[2] *= scalar; } //! @brief 始点終点の線分をベクトルに変換 //! @param [out] vec3 結果を格納する三次元ベクトル 要素数3 //! @param [out] from 始点を表す三次元ベクトル 要素数3 //! @param [out] to 終点三次元ベクトル 要素数3 //! @return vec3のポインタ inline real_t* lineToVector3(real_t * const vec3, const real_t * const from, const real_t * const to) { vec3[0] = to[0] - from[0]; vec3[1] = to[1] - from[1]; vec3[2] = to[2] - from[2]; return vec3; } //! @brief 与えられた二つのベクトルの外積を求める //! @param [out] dvec 結果を格納する三次元ベクトル 要素数3 //! @param [in] svec 三次元ベクトル1 要素数3 //! @param [in] svec 三次元ベクトル2 要素数3 //! @return なし inline void outer(real_t * const dvec, real_t const* const svec1, real_t const* const svec2) { const int X = 0; const int Y = 1; const int Z = 2; const real_t& x1 = svec1[X]; const real_t& y1 = svec1[Y]; const real_t& z1 = svec1[Z]; const real_t& x2 = svec2[X]; const real_t& y2 = svec2[Y]; const real_t& z2 = svec2[Z]; dvec[X] = static_cast<real_t>(y1 * z2 - z1 * y2); dvec[Y] = static_cast<real_t>(z1 * x2 - x1 * z2); dvec[Z] = static_cast<real_t>(x1 * y2 - y1 * x2); } //! @brief 与えられたベクトルと垂直なベクトルを計算する //! @param dst13 結果を表す三次元ベクトル 要素数3(一つ目 必須) //! @param dst23 結果を表す三次元ベクトル 要素数3(二つ目 いらないならnullptrを指定) //! @param src3 入力する三次元ベクトル 要素数3 //! @return なし inline void rightAngleVector(real_t * const dst13, real_t * const dst23, const real_t * const src3) { real_t tmp[3] = { 1,0,0 }; //tmpとsrc3の角度0またはそれに限りなく近いなら、別なベクトルを用意 if (toDegree(vectorAngle(tmp, src3)) < 0.1) { tmp[0] = 0; tmp[1] = 1; tmp[2] = 0; } //外積を求める outer(dst13, tmp, src3); if (dst23 != nullptr) { outer(dst23, src3, dst13); } } } //! @brief 矢印を描画 //! @param [in] 矢印の始点 //! @param [out] 矢印の終点 //! @return なし inline void drawArrow(const double* from3, const double* to3) { glBegin(GL_LINES); glVertex3dv(from3); glVertex3dv(to3); glEnd(); numath::real_t v[3]; numath::lineToVector3(v, from3, to3); double len = numath::length(v); numath::real_t p3[3] = { from3[0], from3[1], from3[2] }; numath::vectorMult3(v, 0.8); numath::real_t v1[3], v2[3]; numath::rightAngleVector(v1, v2, v); numath::normalize(v1); numath::normalize(v2); numath::vectorMult3(v1, len * 0.03); numath::vectorMult3(v2, len * 0.03); const double* f = from3; const double* t = to3; glBegin(GL_LINE_STRIP); glVertex3d(f[0] + v[0], f[1] + v[1], f[2] + v[2]); glVertex3d(f[0] + v[0] + v1[0], f[1] + v[1] + v1[1], f[2] + v[2] + v1[2]); glVertex3d(t[0], t[1], t[2]); glEnd(); glBegin(GL_LINE_STRIP); glVertex3d(f[0] + v[0], f[1] + v[1], f[2] + v[2]); glVertex3d(f[0] + v[0] + v2[0], f[1] + v[1] + v2[1], f[2] + v[2] + v2[2]); glVertex3d(t[0], t[1], t[2]); glEnd(); glBegin(GL_LINE_STRIP); glVertex3d(f[0] + v[0], f[1] + v[1], f[2] + v[2]); glVertex3d(f[0] + v[0] - v1[0], f[1] + v[1] - v1[1], f[2] + v[2] - v1[2]); glVertex3d(t[0], t[1], t[2]); glEnd(); glBegin(GL_LINE_STRIP); glVertex3d(f[0] + v[0], f[1] + v[1], f[2] + v[2]); glVertex3d(f[0] + v[0] - v2[0], f[1] + v[1] - v2[1], f[2] + v[2] - v2[2]); glVertex3d(t[0], t[1], t[2]); glEnd(); }
#include <iostream> #include <Windows.h> #include <gl/GL.h> #include <gl/GLU.h> #include <gl/freeglut.h> // freeglut: // http://freeglut.sourceforge.net/ //矢印描画関数 #include "drawarrow.hpp" //ウィンドウの幅と高さ int width, height; double rotate_angle=0; //描画関数 void disp(void) { glClearColor(0.2, 0.2, 0.2, 1); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glEnable(GL_DEPTH_TEST); glViewport(0, 0, width, height); //カメラの設定 glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(60, width / (double)height, 0.1, 3); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glTranslated(0, 0, -2); //表示用の回転 glRotated(rotate_angle, 0, 1, 0); glRotated(rotate_angle, 1, 0, 0); //矢印を三つ描画 glLineWidth(2); double from[3] = { 0,0,0 }; double tox[3] = { 1,0,0 }; double toy[3] = { 0,1,0 }; double toz[3] = { 0,0,1 }; glColor3d(1, 0, 0); drawArrow(from, tox); glColor3d(0, 1, 0); drawArrow(from, toy); glColor3d(0, 0, 1); drawArrow(from, toz); glLineWidth(1); double v = 0.2; 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(); glFlush(); } //ウィンドウサイズの変化時に呼び出される void reshape(int w, int h) { width = w; height = h; disp(); } void timer(int value) { rotate_angle += 5; disp(); glutTimerFunc(100, timer, 0); } //エントリポイント int main(int argc, char** argv) { glutInit(&argc, argv); glutInitWindowPosition(100, 50); glutInitWindowSize(500, 500); glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA); glutCreateWindow("sample"); glutDisplayFunc(disp); glutReshapeFunc(reshape); glutTimerFunc(1000, timer, 0);//タイマー glutMainLoop(); return 0; }

現在管理人はゼノブレイド2をやっていてブログを書く暇がガチで無い(←)。
いい機会なので暫く小物をアップロードしてお茶を濁す。正直どこかに置いておかないと無くしてしまうのでGitHubにでも捨てておきたいのだが色々と思うところがあってできないでいる。
以下はOpenGLの基本のプログラム。いつも某所からコピペをしていた。
#include <iostream> #include <Windows.h> #include <gl/GL.h> #include <gl/GLU.h> #include <gl/freeglut.h> // freeglut: // http://freeglut.sourceforge.net/ //ウィンドウの幅と高さ int width, height; //描画関数 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); 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(); 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); glutCreateWindow("sample"); glutDisplayFunc(disp); glutReshapeFunc(reshape); glutMainLoop(); return 0; }
#include <iostream> #include <Windows.h> #include <gl/GL.h> #include <gl/GLU.h> #include <gl/freeglut.h> // freeglut: // http://freeglut.sourceforge.net/ //ウィンドウの幅と高さ int width, height; double rotate_angle=0; //描画関数 void disp(void) { glClearColor(0.2, 0.2, 0.2, 1); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glViewport(0, 0, width, height); //カメラの設定 glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(60, width / (double)height, 0.1, 3); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glTranslated(0, 0, -2); glRotated(rotate_angle, 0, 1, 0); //回転するならカリングはOffにしたほうがいい。 //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(); glFlush(); } //ウィンドウサイズの変化時に呼び出される void reshape(int w, int h) { width = w; height = h; disp(); } void timer(int value) { rotate_angle += 5; disp(); glutTimerFunc(10, timer, 0); } //エントリポイント int main(int argc, char** argv) { glutInit(&argc, argv); glutInitWindowPosition(100, 50); glutInitWindowSize(500, 500); glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA); glutCreateWindow("sample"); glutDisplayFunc(disp); glutReshapeFunc(reshape); glutTimerFunc(1000, timer, 0);//タイマー glutMainLoop(); return 0; }

次回はもう少しオリジナリティあるのものを載せる
12:00~
1.Object Modeへ行き、新たなParticleを追加する。
2.Particleを以下のように設定

この時、元のFoliage01_colオブジェクトを選択し[H]キーでHiddenにしておく。
3.Weight Paintする。

この時、ペンの種類を選ぶ場所が見当たらない時は、[View]→[Tool Settings]にチェックを入れる
青の部分にはParticleが生じないので、緑~赤の領域が全体を占めるように塗る
4.Object Modeへ戻り、Particleの設定から、Vertex Group→Densityに、今塗りつぶした"Group"を選択する

5.Particleのその他の設定を以下のようにする

最後に、hairの数を5000にする

6.Viewport ShaderをRenderedに設定する

7.Renderを以下のように設定。動画ではSubsurface Scatteringにチェックを付けることになっている。自分の使っているバージョンにはそのチェックがない。

8.Materialへ行き、Materialを一つ追加する。BaseColorを濃い茶色に設定する。

9.Sunを夕暮れ時を想定して急な角度に配置し、色を赤みがける
9.その他、Shadowの設定を以下のように行う
10.cloud_layers_4K.hdrを以下からダウンロードする
https://hdrihaven.com/hdri/?h=cloud_layers
11.World Settingsへ行き、Color→Environment Textureを選択し、上記hdrファイルを設定する。



12.[Shift+A]→[Mesh]→CubeでCubeを配置し、[S][Z]で縦に伸ばす
影を付け、カメラを適切な場所に設置すると木が近くにあるように見える


RenderのLookをHigh Contrastにすると結果が変わる。

08:44
1.[Tab]でEditモードへ行く
2.[Ctrl+R]でループカットを開始
3.ホイールを回し、草のポリゴンを7等分する
4.エッジ選択モードにする
5.Proportional EditingをSharpにする
6.ポリゴンの一番上のエッジを選択し、[G]で曲げる
7.Smooth Shadingに切り替える
[F3]で検索窓でShade Smoothを検索する
※あるいは、Object→Shade Smoothを選択する
09:24~
8.Editモードで[Shift+D]でDuplicateし、すぐ隣に移動する
9.再び画面を二分割し、左側をUV Imageにする
10.[D]でUVを移動し、別の葉に移動する
11.回転・拡大縮小を加え、同様のものを複数設置し、草の株を作る
12.複製などを利用すると比較的早くできる。最後に、originが草の根元にあることを確認する
11:23~
13.[Shift+A]→[Mesh]→Plane で新たなPlaneを追加し、[S]で約6弱の大きさに広げる
14.Sculpt Modeへ移行する
16.DyntopoをOnにする。(チェックを入れると警告が出るので"OK"を押す)

Sculptで地面を盛り上げる

区切りがいいので次は次回