質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.46%
OpenGL

OpenGLは、プラットフォームから独立した、デスクトップやワークステーション、モバイルサービスで使用可能な映像処理用のAPIです。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

3DCG

コンピュータの演算により、3次元空間の仮想物体を、2次元平面上で表現する手法である。

Q&A

0回答

1093閲覧

OBJローダーで面法線から頂点法線を求める方法

退会済みユーザー

退会済みユーザー

総合スコア0

OpenGL

OpenGLは、プラットフォームから独立した、デスクトップやワークステーション、モバイルサービスで使用可能な映像処理用のAPIです。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

3DCG

コンピュータの演算により、3次元空間の仮想物体を、2次元平面上で表現する手法である。

0グッド

0クリップ

投稿2021/10/29 06:38

編集2021/10/29 10:19

提示コードですがコメント部内部のコードなのですがobjファイルにある面法線から頂点法線を求める方法が知りたいです。以下のように考えて実装してみたのですが提示画像のように正しい頂点法線を実装できません。これはどういったアルゴリズムなのでしょうか? objファイルはブレンダーで出力しました。

知りたいこと

面法線から頂点法線を算出したい。

確認したこと

Phongのシェーディングを行っているのですがシェーダーコードは問題ありません。
また頂点データ等を正しくopenglに渡せていることを確認しました。
objファイルのデータは面法線

Github: https://github.com/Shigurechan/GL/tree/14b9ed0e2d87eafaae0b3b11fd42660b7d3f07b7

イメージ説明

mode.cpp
// ##################################### .objファイル読み込み ##################################### void FrameWork::D3::LoadObj(const char *fileName, ObjFile &attribute) { ObjFile obj; std::vector<int> vertexIndex; std::vector<int> uvIndex; std::vector<int> normalIndex; std::vector<glm::vec3> vertex; std::vector<glm::vec2> uv; std::vector<glm::vec3> normal; FILE *file = fopen(fileName, "r"); if (file == NULL) { std::cerr << ".OBJファイルが開けません: " << fileName << std::endl; assert(0); } else { while (true) { char line[500]; int res = fscanf(file, "%s", line); if (res == EOF) { break; } if (strcmp(line, "v") == 0) { glm::vec3 vert; fscanf(file, "%f %f %fn", &vert.x, &vert.y, &vert.z); vertex.push_back(vert); } else if (strcmp(line, "vt") == 0) { glm::vec2 u; fscanf(file, "%f %fn", &u.x, &u.y); uv.push_back(u); } else if (strcmp(line, "vn") == 0) { glm::vec3 norm; fscanf(file, "%f %f %fn", &norm.x, &norm.y, &norm.z); normal.push_back(norm); } else if (strcmp(line, "f") == 0) { unsigned int v[3], u[3], n[3]; int matches = fscanf(file, "%d/%d/%d %d/%d/%d %d/%d/%dn", &v[0], &u[0], &n[0], &v[1], &u[1], &n[1], &v[2], &u[2], &n[2]); vertexIndex.push_back(v[0]); vertexIndex.push_back(v[1]); vertexIndex.push_back(v[2]); uvIndex.push_back(u[0]); uvIndex.push_back(u[1]); uvIndex.push_back(u[2]); normalIndex.push_back(n[0]); normalIndex.push_back(n[1]); normalIndex.push_back(n[2]); } } ////////////////////////////////////////////////////////////////////////////////////// for (unsigned int i = 0; i < vertexIndex.size(); i++) { unsigned int vi = vertexIndex[i]; unsigned int ui = uvIndex[i]; unsigned int ni = normalIndex[i]; glm::vec3 v = vertex[vi - 1]; glm::vec2 u = uv[ui - 1]; glm::vec3 n = normal[ni - 1]; VertexAttribute attrib; attrib.position[0] = v.x; attrib.position[1] = v.y; attrib.position[2] = v.z; attrib.uv[0] = u.x; attrib.uv[1] = u.y; attrib.normal[0] = n.x; attrib.normal[1] = n.y; attrib.normal[2] = n.z; obj.attribute.push_back(attrib); } ///////////////////////////////////////////////////////////////////////////////////// } attribute = obj; }
Cube.obj
# Blender v2.93.5 OBJ File: '' # www.blender.org mtllib Cube.mtl o Cube v 1.000000 1.000000 -1.000000 v 1.000000 -1.000000 -1.000000 v 1.000000 1.000000 1.000000 v 1.000000 -1.000000 1.000000 v -1.000000 1.000000 -1.000000 v -1.000000 -1.000000 -1.000000 v -1.000000 1.000000 1.000000 v -1.000000 -1.000000 1.000000 vt 0.875000 0.500000 vt 0.625000 0.750000 vt 0.625000 0.500000 vt 0.375000 1.000000 vt 0.375000 0.750000 vt 0.625000 0.000000 vt 0.375000 0.250000 vt 0.375000 0.000000 vt 0.375000 0.500000 vt 0.125000 0.750000 vt 0.125000 0.500000 vt 0.625000 0.250000 vt 0.875000 0.750000 vt 0.625000 1.000000 vn 0.0000 1.0000 0.0000 vn 0.0000 0.0000 1.0000 vn -1.0000 0.0000 0.0000 vn 0.0000 -1.0000 0.0000 vn 1.0000 0.0000 0.0000 vn 0.0000 0.0000 -1.0000 usemtl Material s off f 5/1/1 3/2/1 1/3/1 f 3/2/2 8/4/2 4/5/2 f 7/6/3 6/7/3 8/8/3 f 2/9/4 8/10/4 6/11/4 f 1/3/5 4/5/5 2/9/5 f 5/12/6 2/9/6 6/7/6 f 5/1/1 7/13/1 3/2/1 f 3/2/2 7/14/2 8/4/2 f 7/6/3 5/12/3 6/7/3 f 2/9/4 4/5/4 8/10/4 f 1/3/5 3/2/5 4/5/5 f 5/12/6 1/3/6 2/9/6

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

fana

2021/10/29 07:05

> objファイルにある面法線 ググった感じだと, ファイルのフォーマット的には 面毎に単一の法線が定められるような形ではない ように見えますが. f から始まる行には 頂点毎に法線データのindexが書かれている みたいですから, 単にそのindexが指し示すデータを用いればよいだけなのではないでしょうか(やるとしても単位化くらいでは). あなたの所望する向きを向いた法線データ値を用意するのは OBJファイル を作る側の仕事なのではないでしょうか. (ファイルを読んだ側で n += どうのこうの とか計算するのではなくて)
退会済みユーザー

退会済みユーザー

2021/10/29 07:06

はい。最初はただnormalIndexの値を参照していたのですがそれでは陰影がおかしくなるのでやり方を変更してみました。
fana

2021/10/29 07:11

相手にしているファイルのフォーマットは上述のとおりと思われるのですが 「ファイルが示す情報をそのまま用いない形の あなた特有の何らかのやり方」を模索しているのであれば, それがどういう話なのか? というのは,残念ながらあなたしか知らないわけです. であれば,何が正しくて何が違うのかを他者に問うても無駄なのではないでしょうか.
退会済みユーザー

退会済みユーザー

2021/10/29 07:13

すいませんでした。
fana

2021/10/29 07:24

なんとなく思う懸念点を述べておきます. 立方体の頂点の個数は? と訊かれたら,私は「8個」と答えますが, 内容が立方体なOBJファイルでも頂点座標データが8個かどうかは不明であるように思えます. (そこはOBJファイルを出力する側の都合に依ると思う) であれば,複数の面が「同一の」頂点を共有するのかどうか? というのは,OBJファイルから読み込んだindex値だけからでは必ずしも判定できない……ように思えます. (そういう話も問題現象の要因のひとつになってはいる可能性もあるかな? とか)
退会済みユーザー

退会済みユーザー

2021/10/29 07:27

なるほど。もし8個だった場合どういう方法で法線を設定したらいいのでしょうか?
fana

2021/10/29 07:32 編集

OBJファイルがうまいこと頂点座標データが8個しかない状態であって, 且つ,各面が単一の法線を有するようなデータなのだとしても…… 四角い面が 2つの三角形に分割したデータ とされている場合, 各頂点座標データを共有する面の個数 は三角形への分割具合によって変わってくるでしょう. (ある1つの四角い面についてのみ考えれば,2つの三角形によって参照される頂点と,1つの三角形にしか参照されない頂点 がある) なので,【「同一の頂点」を共有している三角形群】を特定できたとしても,「それら三角形群の法線の平均」みたいなのを単純に計算してしまうと,うまくない結果になるでしょう.
退会済みユーザー

退会済みユーザー

2021/10/29 07:32

つまりこの場合 頂点法線を計算する必要があるということでしょうか?
fana

2021/10/29 07:38

最も簡単なのは,【あなたのプログラムで面倒な話を扱わないこと】だと思います. ファイルを読むならば,ファイルに書かれていたデータを素直に使う. > あなたの所望する向きを向いた法線データ値を用意するのは OBJファイル を作る側の仕事 というのが最も明確な話ではないでしょうか.
退会済みユーザー

退会済みユーザー

2021/10/29 07:39

> OBJファイル を作る側の仕事 なので blenderを確認しましたが法線は出力しています。 ということはプログラム側の問題なのですがこれはなぜでしょうか?
fana

2021/10/29 07:48

何はともあれ【ファイルの読み込み処理を書いたならば,まともに読めているのかどうかをデバッグしましょうや】っていう. テストに用いているOBJファイルが立方体であればそのデータ量もたかが知れているでしょうから, 「読み込み処理の各段階における変数の値がこうなっているハズ」というのを追いながらでもデバッグできるでしょう. そういうのをひたすらちまちまと確認するしかないでしょう. それがデバッグです.
退会済みユーザー

退会済みユーザー

2021/10/29 09:21

読めているかどうかを確認しました。
fana

2021/10/29 09:30

とりあえず Cube.obj から読み込んだ通りの法線のまま使えばいいんじゃない?
退会済みユーザー

退会済みユーザー

2021/10/29 09:31

それも試したのですが似たような現象が起きます。
fana

2021/10/29 09:42

(1)シェーダを使うためのコード 「三角形の3つの頂点に関して,頂点の座標と法線のデータがあれば,シェーダは描画できる」 すなわち, {座標1,座標2,座標3,法線1,法線2,法線3}というデータを用意したならば三角形を描画でき, さらにこのデータを複数個用意したならば,複数の三角形で構成されたモデルを描画できる --- (2) OBJ ファイルを読み込む実装 OBJファイルを読み込んで 各三角形について,{座標1,座標2,座標3,法線1,法線2,法線3}というデータを得る. (その Cube.obj を読んだならば,各三角形の{法線1,法線2,法線3}は全て同じ値になるが) --- 上記(1)も(2)もできているのであれば,(2)のデータを(1)に与えたら終了だよね.
退会済みユーザー

退会済みユーザー

2021/10/29 09:54

> (2)のデータを(1) に与えたのですが似たような描画にになります。 提示コードを更新しました。
fana

2021/10/29 10:12 編集

何度も同じことを言っている気がするけど, {座標1,座標2,座標3,法線1,法線2,法線3}というデータを あなたが(ハードコーディングでも何でもいいから)三角形12個分用意したならば,妥当に立方体を描画できる(1)が存在するのか? っていう話なのね. まずこれが最低限確認されるべき. ↓ で,この三角形12個分の(ハードコーティングか何かされている)データの値を,「もしも Cube.OBJ から正常に読み込まれたならばこういう値になるハズ」っていう値に変更したらどうなるの? っていうのが2点目の確認事項ね. ↓ ここまでがOKならば, OBJファイルを読み込むコードによって作り上げられた三角形12個分のデータ と, 前記2点目の時点で手作業で用意したデータ とが一致するならば, 何も問題なく立方体が描画されるハズだよね.(だって(1)側に渡されるデータは全く同一なのだもの)
退会済みユーザー

退会済みユーザー

2021/10/29 10:12 編集

誤投稿です
fana

2021/10/29 10:14

上記2点目までが実際に確認できて初めて 【「シェーダコード と それを用いるcppコード の組」には問題ないハズだ】と言える. (少なくとも Cube.obj 相当の立方体を描画することにおいては)
退会済みユーザー

退会済みユーザー

2021/10/29 10:15

はい。立方体が描画できるのですが。法線がおかしいのです。これはなぜでしょうか?同じやり方で法線を設定しているのに表示がおかしくなる原因がわかりません。
fana

2021/10/29 10:16

そしたらもう,あとは単純な話. > OBJファイルを読み込むコードによって作り上げられた三角形12個分のデータ と, > 前記2点目の時点で手作業で用意したデータ とが一致する のか否か? 一致しないなら,「OBJファイルからデータを読み出す ~ (1)にデータを渡す」の経路のどこかがおかしい.
退会済みユーザー

退会済みユーザー

2021/10/29 10:26

blenderが原因という可能はあるんでしょうか?
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだ回答がついていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.46%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問