記事のディレクトリ
- プロンプト情報
- ハーフエッジデータ構造
- VSのヒント
- 1.三角形メッシュ内の各三角形の面積を計算します(10ポイント)
- 2.三角形メッシュ(20ポイント)の各頂点の法線方向を計算します
- 3. OpenGLを使用して、三角形メッシュ内の各三角形の法線方向を描画します(各三角形の重心で描画します)(20ポイント)
- 4.三角形メッシュのノイズ除去アルゴリズムを実装します(30ポイント)
- 5.(オプション/実行可能かどうか)三角形メッシュに基づく操作を実装します(たとえば、特徴点/エッジ抽出、三角形メッシュにランダムなガウスノイズを追加する、三角形メッシュの各頂点のガウス曲率を見つける、三角メッシュの簡略化、三角メッシュの四辺形化など)(20点)
最終的な宿題には5つのトピックがあり、最後のトピックはオプションです。先生から提供された元のプロジェクトは非常に複雑で、慎重に検討する必要があります。その前に、C ++の構文、特に標準ライブラリの一部の関数と関数を理解する必要があります。私が書いたもの(C ++テンプレート、名前名)を参照できます。 :
テンプレート。
名前空間。
さらに、ドキュメント「final_project割り当て問題2020.docx」とコースウェアPPT「ComputerGraphics 8-2020-Second Semester」を注意深く読み、このプロジェクトの簡単な紹介があります。
構成環境に関するブログもあります:環境構成。
プロンプト情報
a。機能1、2、4、5
その中で、1。2。4. 5の関数宣言がありますmesh/extension/ExKernelT.h
(下のスクリーンショット)。(必須)
で関数の実装をmesh/extension/ExkernelT.cpp
完了してください。
b。機能3
b。3の関数宣言はread_write/read_write.h
関数の実装にありますread_write/read_write.cpp
、(必須)で完了してください
c。コードの再利用に注意する
コード全体をよくお読みください。三角形メッシュ内の各三角形の重心の検出、各三角形の法線方向の検出など、さまざまな基本操作が実装されています。
d。実行中のプログラムのスクリーンショット
次のように:
e。操作インターフェース
マウスの右ボタンをクリックすると、操作インターフェイスが表示され、ライトやその他の描画効果が表示されます。
ハーフエッジデータ構造
定義は次のとおりです。
簡単に言うと、半分には方向があり、片側は2つの半分で構成され、1つのパッチがあります。のみ次の図に示すように、3つの半分に対応します。3
つの黒い半分は青いパッチにのみ属しますが、緑の半分は属しません。半分がどのパッチにも属していない場合、半分は呼び出されます境界の半分、および相関関数(KernelT.h
)を装備:
半分は相互に変換でき、半分①は関数prev_halfedge_handle()
を使用して半分③を取得できます;関数next_halfedge_handle()
を使用して半分②を取得することもできます;または関数を使用しopposite_halfedge_handle()
て半分④を取得することもできます(以下を参照)。 )
頂点は関数を介しhalfedge_handle_
てハーフエッジを取得でき、ハーフエッジは頂点を指します。
残りは一つずつ紹介されません。
VSのヒント
検索
このプロジェクトは複雑すぎて、関数の場所が見つからないことが多いため、「find」関数がより多く使用され、ショートカットキーは==「Ctrl + F」==であり、「プロジェクト内で検索」を使用することがよくあります。多くの場合、予期しないドキュメントに表示されます。
定義を表示
コードの再利用に注意してください。たとえば、教師は辺の長さの計算機能を完了しました:(
マウスをドラッグして機能を選択し、右ボタンをクリックして、[定義の表示]に移動します;)
(次の小さなウィンドウプロンプトが表示されます:)
関数名、入力パラメーターに焦点を当て、パラメータータイプを返します。教師が書いた関数名は、その名前が示すように比較的標準的であり、エッジの長さを計算するために使用され、EdgeHandleタイプのパラメーターを入力する必要があり、返される長さは浮動小数点数です。タイプ。将来的には、「ビュー定義」方式でその意味を明確にする関数や変数などが多数登場する予定です。
(「定義を確認する」ことで実質的なものが見つからない可能性があります。最初の種類の「発見」を使用して解決することをお勧めします)
すべての参照を検索
マウスをドラッグして関数を選択し、右ボタンをクリックして、[すべての参照を検索]に移動し
ます。プロンプトメッセージが下にポップアップ表示されます
。プロジェクトが呼び出す場所を検索します。
1.三角形メッシュ内の各三角形の面積を計算します(10ポイント)
タイトル説明
この関数は次のように宣言されています。
Scalar calc_facet_area(const FacetHandle& _fh); //给定一个三角面片,计算它的面积
ヒント:プログラムの出力に応じて、計算が正しいかどうかを判断できます。たとえば、入力モデルcow.off。すべての三角形の中で、最大の面積を持つ三角形の面積は次のとおりです。の最大面積メッシュは:0.0494426
アルゴリズムのアイデア
三角形の3つの辺の長さを計算する方法を見つけてから、Helenの式を使用します。
変換プロセス変数タイプ:
FacetHandle- > HalfedgeHandle- > EdgeHandle- > Double ;
パッチ->ハーフハンドル->エッジハンドル->辺の長さ
先生に電話してcalc_edge_length
関数を書いてもらいました。
コード例
template<class ExItems>
typename ExKernelT<ExItems>::Scalar
ExKernelT<ExItems>::calc_facet_area(const FacetHandle& _fh)
{
/计算三角形的面积
float a, b, c, p;//a, b, c 为三角形三边长
///得到半边句柄
HalfedgeHandle& half1 = halfedge_handle(_fh); //这样使用变量的引用效率高
HalfedgeHandle& half2 = next_halfedge_handle(half1);
HalfedgeHandle& half3 = next_halfedge_handle(half2);
由半边句柄得到边句柄
EdgeHandle& edge1 = edge_handle(half1);
EdgeHandle& edge2 = edge_handle(half2);
EdgeHandle& edge3 = edge_handle(half3);
由边句柄得到各边长
a = calc_edge_length(edge1);
b = calc_edge_length(edge2);
c = calc_edge_length(edge3);
利用海伦公式求面积s=sqrt(p(p-a)(p-b)(p-c))
Scalar area = 0.0;
p = (a + b + c) / 2.0;
area = sqrt(p*(p - a)*(p - b)*(p - c));
facet_ref(_fh).area_ = area;
return area;
}
コードfacet_ref()
関数を終了し、FacetHandleタイプパラメーターを入力すると、ファセット(パッチ)タイプが返されます。あなたは多くの名前があることがわかります扱うこれはiterator
(イテレータ)のようなもので、ポインタとしても理解できます。
(KernelT.h
)
私はばかげて混乱していますが、ハンドルとイテレータがあります。。。
facet_ref(_fh).area_ = area;
このコード行の意味は次のとおりarea_
です。計算された領域をパッチオブジェクトの属性に割り当てます。
area_
定義は次のとおりです。
関数名の前にある奇妙なものの束に怖がらないでください。山かっこが付いた「<...>」はテンプレートであり、テンプレートに設定したいパラメータと同じです。 2コロン内の「::」は名前空間に関連しています。これは、ここで呼び出したい領域に相当します。したがって、関数名はほんの数文です:(calc_facet_area
実を言うと、この小さなばかを初めて見たときは、結局関数名が何であるかわからない、(〜¯▽¯)〜 )
そしてそれ彼らは臭い長いものが引きずり出されました:
template<class ExItems>
typename ExKernelT<ExItems>::
スカラー
ExKernelT<ExItems>::
calc_facet_area(const
FacetHandle& _fh)
戻り値の型:スカラー
関数名:calc_facet_area
入力方式:FacetHandle
2.三角形メッシュ(20ポイント)の各頂点の法線方向を計算します
タイトル説明
この関数は次のように宣言されています。
inline Normal calc_normal(const VertexHandle& _vh);///计算顶点的法向值
ヒント:正規化に注意してください。
アルゴリズムのアイデア
現在の頂点の周りのパッチの法線を合計し、現在の頂点の法線である平均を取得します。
可能であれば、パッチの面積を重みとして使用するなど、加重平均を設定できます(私はそれを認識していませんでした...)
関数calc_normal
(同じ名前の別の関数)を呼び出して
、顔の法線ベクトルを計算し、顔のイテレーターを入力してFacetHandle
、法線ベクトルを返します。Normal
template <class ExItems>
typename ExKernelT<ExItems>::Normal
ExKernelT<ExItems>::calc_normal(const FacetHandle& _fh) {
assert( _fh.is_valid() );
assert( _fh.idx() < facet_size() );
const HalfedgeHandle& hh = halfedge_handle(_fh);
const HalfedgeHandle& p_hh = prev_halfedge_handle(hh);
const HalfedgeHandle& n_hh = next_halfedge_handle(hh);
const Coord& cd0 = coord( vertex_handle( hh) );
const Coord& cd1 = coord( vertex_handle(p_hh) );
const Coord& cd2 = coord( vertex_handle(n_hh) );
//return ((cd1-cd0)%(cd2-cd1)).normalize();
return ((cd2-cd1)%(cd1-cd0)).normalize();//两个向量叉乘并且单位化 be careful
}
コード例
template <class ExItems>
typename ExKernelT<ExItems>::Normal
ExKernelT<ExItems>::calc_normal(const VertexHandle& _vh) {更新一个顶点的法向
assert( _vh.is_valid());
assert( _vh.idx() < vertex_size() );
Normal norm(1,1,1);///用norm存储求得的法向值
//在此实现
//最简单最不负责的一种做法啦
HalfedgeHandle half1, half2, half3;
FacetHandle face;
half1 = halfedge_handle(_vh);
face = facet_handle(half1);
norm = norm + calc_normal(face);
half2 = next_halfedge_handle(half1); //1.
half2 = opposite_halfedge_handle(half2); //1.//
while (half2 != half1)
{
face = facet_handle(half2);
norm = norm + calc_normal(face);//把每个面的法向加起来
half2 = next_halfedge_handle(half2);
half2 = opposite_halfedge_handle(half2); //1.//
}
3. OpenGLを使用して、三角形メッシュ内の各三角形の法線方向を描画します(各三角形の重心で描画します)(20ポイント)
タイトル説明
この関数は次のように宣言されています。bool ogl_writer2(bool _orient = true, bool _smooth = false);
ヒント:関数を参照することbool ogl_writer(bool _orient = true, bool _smooth = false)
で実装できます。現時点では、各三角形パッチに法線方向を描画するだけで済みます(法線方向は線で表されます。または、に追加することもできます。描かれた線の矢印(方向を表します。キーボードの「m」を押して、面を法線後方に描いた結果を表示します。)
アルゴリズムのアイデア
各三角形のファセットを1つずつ通過し、重心を描画し、重心と到達点から描画された法線ベクトルを使用してglBegin(GL_LINES);
、自動的に線を接続すると、通常のベクトルの表示が完了します。
これは単に描画された法線ベクトルであることに注意してください。含まれるようにogl_writer()
、本体も描かれているが、そうでなければ、空気中に浮遊針の束を取得します。また、描画される法線ベクトルの長さは、それ自体で調整できます。見た目を快適にしたい場合は、整数で割って解くことができます。
関数の呼び出し:calc_centroid()
入力パッチイテレータタイプFacetHandle
、出力Coord
(座標)タイプ。
template<class ExItems>
typename ExKernelT<ExItems>::Coord
ExKernelT<ExItems>::calc_centroid(const FacetHandle& _fh)//求重心
{
HalfedgeHandle& hh = halfedge_handle(_fh);
HalfedgeHandle& n_hh = next_halfedge_handle(hh);
HalfedgeHandle& pre_hh = prev_halfedge_handle(hh);
VertexHandle& vh = vertex_handle(hh);
VertexHandle& n_vh = vertex_handle(n_hh);
VertexHandle& pre_vh = vertex_handle(pre_hh);
return Coord(coord(vh) + coord(n_vh) + coord(pre_vh)) / 3.0;
}
コード例
template <class Mesh>
bool ReaderWriterT<Mesh>::ogl_writer2(bool _orient, bool _smooth)//画出法向量
{
//在里面把三角面片法向画出
HalfedgeHandle half;
Mesh::FacetIterator face_it(mesh_->facet_begin());//初始面片,既然是 "iterator" 那我就把它当作一个指针呗
bool orient;
FacetHandle face;
float blue[] = { 0, 0, 1.0, 0.8 };
reader.ogl_writer(0, 1); //这里加了一个,就把原本的画出来啦
glShadeModel(GL_SMOOTH);
orient = true;
mesh_->update_normals();
for (; face_it != mesh_->facet_end(); face_it++)//遍历所有的面
{
if ((*face_it).status_.is_deleted()) //跳过已经被删除的
{
continue;
}
half = face_it->halfedge_handle_; //当前半边
face = mesh_->facet_handle(half); //所在面片
const VertexHandle& vertex0 = mesh_->vertex_handle(half);//当前半边顶点
glMaterialfv(GL_FRONT, GL_AMBIENT, blue);
glBegin(GL_LINES); //画直线
do {
const VertexHandle& vertex = mesh_->vertex_handle(half);//当前顶点
half = mesh_->next_halfedge_handle(half); //下一个半边
//先画重心,再画法向
glVertex3fv(mesh_->calc_centroid(face));//重心
glVertex3fv(mesh_->calc_centroid(face) + mesh_->normal(face) / 2.0); //重心+法向 -> 上端点 (你给我短一点)
} while (half != face_it->halfedge_handle_);//把当前面片的半边都遍历完
glEnd();
glFlush();
}
return true;
}
ここではショートカットを使用します。関数のogl_writer2()
上に、教師はファイルに保存されている3Dオブジェクトogl_writer()
を描画するために使用される兄弟関数を実装しました.off
。最初に勉強してから模倣してください(私はあなたに言いません、私は2つか3つの行を変えただけです...)
//把这个函数看懂就可以啦,就可以写下边的了
template <class Mesh>
bool ReaderWriterT<Mesh>::ogl_writer(bool _orient, bool _smooth) //画出原图形
{
HalfedgeHandle half;
Mesh::FacetIterator face_it(mesh_->facet_begin());
float green[] = { 0, 1.0, 0.0, 0.8 };
//glShadeModel(GL_FLAT);
glShadeModel(GL_SMOOTH);
int orient = true;//orient 朝向
// (_orient) ? 1 : -1;
mesh_->update_normals();
for (; face_it != mesh_->facet_end(); face_it++)
{
if ((*face_it).status_.is_deleted())
{
continue;
}
half = face_it->halfedge_handle_;
FacetHandle face = mesh_->facet_handle(half);
const VertexHandle& vertex0 = mesh_->vertex_handle(half);
glMaterialfv(GL_FRONT, GL_AMBIENT, green);
glBegin(GL_TRIANGLES);
do {
const VertexHandle& vertex = mesh_->vertex_handle(half);
glNormal3fv(mesh_->normal(face)*orient);
glVertex3fv(mesh_->coord(vertex));
half = mesh_->next_halfedge_handle(half);
} while (half != face_it->halfedge_handle_);
glEnd();
}
return true;
}
ogl_writer()アルゴリズムのアイデア
各パッチをトラバースし、三角形のパッチの各ポイントを描画して使用しglBegin(GL_TRIANGLES);
、自動的に三角形に接続します。
4.三角形メッシュのノイズ除去アルゴリズムを実装します(30ポイント)
タイトル説明
(ラプラシアンスムージング(グローバルまたはローカルラプラシアンスムージング)、グローバルバイラテラルフィルタリング(バイラテラルメッシュノイズ除去 "Shachar Fleishman、Iddo Drori、Daniel Cohen-または:バイラテラルメッシュノイズ除去。ACMTrans。Graph。22(3):950-953( 2003)) "またはメッシュノイズ除去のためのバイラテラルノーマルフィルタリング" Youyi Zheng、Hongbo Fu、Oscar Kin-Chung Au、Chiew-Lan Tai:メッシュノイズ除去のためのバイラテラルノーマルフィルタリング。IEEETrans。Vis。Comput。Graph。17(10): 1521- 1530(2011)」)、ガイド付きバイラテラルフィルタリング(ガイド付きメッシュ通常フィルタリング、「Wangyu Zhang、Bailin Deng、Juyong Zhang、Sofien Bouaziz、Ligang Liu:ガイド付きメッシュ通常フィルタリング。計算グラフ。フォーラム34(7):23 -34(2015) ")、L0メソッド" Lei He、Scott Schaefer:L0最小化によるメッシュノイズ除去。ACMTrans。Graph。32(4):64:1-64:8(2013) "など)
この関数は次のように宣言されています。
void Laplacian_Smoothing();//实现一种三角网格去噪算法
ヒント:実装されている2つのノイズ除去アルゴリズムを参照できます。キーボードの「b」を押して、ノイズ除去の結果を表示します。
アルゴリズムのアイデア
ラプラス変換では、現在の頂点について、その周囲のすべての頂点の座標が合計され、平均化されてから、現在の頂点に割り当てられて、スムーズなノイズ除去効果が得られます。
ここで、現在の頂点の周りのすべての頂点をトラバースするプロセスを理解します。
コード例
template<class ExItems>
void ExKernelT<ExItems>::Laplacian_Smoothing()
{
/请实现自己的去噪算法
int vertex_num = vertex_size();//顶点数量
int iterations, i = 0, j = 0; //iterations : 迭代次数
clock_t t1, t2;
std::cout << "Input laplacian vertex update iterations(5-30次): ";
std::cin >> iterations;
std::cout << "由于迭代,比较耗时!" << std::endl;
std::cout << "Please wait..... " << std::endl;
t1 = clock(); //注意哦
do{
std::vector<Coord> updateVertexPosition; //向量,存储... ...
updateVertexPosition.resize(vertex_num); //调整大小
VertexIterator vertex_it(vertex_begin());
for (i = 0; vertex_it != vertex_end(); vertex_it++)
{
HalfedgeHandle& half = vertex_it->halfedge_handle_; //顶点处半边
HalfedgeHandle half2(opposite_halfedge_handle(half));//对面的半边
j = 0;
do
{
updateVertexPosition[i] += coord(vertex_handle(half2)); //获取顶点坐标
j++;
half2 = opposite_halfedge_handle(prev_halfedge_handle(half2));
} while (opposite_halfedge_handle(half2) != half);
updateVertexPosition[i] /= j;//顶点坐标取平均
i++;
}
i = 0;
for ( vertex_it = vertex_begin(); vertex_it != vertex_end(); vertex_it++)
{
vertex_it->coord_ = updateVertexPosition[i]; //用刚求得的平均值代替原来的,可避免毛刺产生
i++;
}
} while (iterations--);
t2 = clock();
std::cout << "The time of laplacian vertex updating: " << (t2 - t1) * 1.0 / CLOCKS_PER_SEC << "s" << std::endl;
}
実装する前に、教師が完了した他の2つのノイズ除去方法を確認できます。
Mesh_Denoising_FE()
Mesh_Denoising_YouyiZheng()
5.(オプション/実行可能かどうか)三角形メッシュに基づく操作を実装します(たとえば、特徴点/エッジ抽出、三角形メッシュにランダムなガウスノイズを追加する、三角形メッシュの各頂点のガウス曲率を見つける、三角メッシュの簡略化、三角メッシュの四辺形化など)(20点)
タイトル説明
この関数は次のように宣言されています。 void mesh_process();
アルゴリズムのアイデア
ラプラス変換を逆にして、周囲の頂点と現在の頂点座標の間のギャップを拡大しました。これは今でもおなじみの画像です。
緑の「5」は、ユニバーサルである周囲の頂点「j」の数に置き換えられます。
ここで10で割る理由は、画像をより鮮明に見せるために少しノイズを減らすためです。
コード例
template<class ExItems>
void ExKernelT<ExItems>::mesh_process()
{
//放大噪声
int vertex_num = vertex_size();
int iterations, i, j;
clock_t t1, t2;
std::cout << "Input sharpening vertex update iterations(1-30次): ";
std::cin >> iterations;
i = 0;
std::cout << "由于迭代,比较耗时!" << std::endl;
std::cout << "Please wait..... " << std::endl;
t1 = clock();
do{
std::vector<Coord> updateVertexPosition;
updateVertexPosition.resize(vertex_num);
VertexIterator vertex_it(vertex_begin());
for (i = 0; vertex_it != vertex_end(); vertex_it++)
{
HalfedgeHandle& half = vertex_it->halfedge_handle_;
HalfedgeHandle half2(opposite_halfedge_handle(half));
j = 0;
do{
updateVertexPosition[i] +=coord(vertex_handle(half2));//coordinates 坐标
j++;
half2 = prev_halfedge_handle(half2);
half2 = opposite_halfedge_handle(half2);
} while (opposite_halfedge_handle(half2) != half);
updateVertexPosition[i] -= coord(vertex_handle(half)) * j;
updateVertexPosition[i] /= j;
updateVertexPosition[i] *= -1;
i++;
}
i = 0;
for ( vertex_it = vertex_begin(); vertex_it != vertex_end(); vertex_it++)
{
vertex_it->coord_ += updateVertexPosition[i] * 0.1;
//小一点,小一点
i++;
}
} while (iterations--);
t2 = clock();
std::cout << "The time of sharpening vertex updating: " << (t2 - t1) * 1.0 / CLOCKS_PER_SEC << "s" << std::endl;
}
プロジェクトはこの関数を呼び出しません。独自の関数を呼び出してください:
ここでいいchoice == 4
ね
何か欠点があれば訂正してください。