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

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

ただいまの
回答率

87.48%

OpenGL フォンの反射モデルのライティングを設定したい。

解決済

回答 3

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 1,726
退会済みユーザー

退会済みユーザー

タイトル通りですが(フォンの反射モデル)ライティングを実装するにはどうしたらいいのでしょうか? 

1,////コメント部の内部の頂点属性に法線を設定すると思いますがこの値はどういう計算をすればいいのか知りたいです。
2, // カメラ位置(ワールド) コードですがカメラ座標を転置するいうことをするそうですがこれはどうすればいいのでしょうか?

イメージ説明

API: OpenGL
ライブラリ glm
OS: Windows10

//コンストラクタ
Mesh::Mesh(Game* g, const char* texture, std::vector<Game::VertexAttribute> vert, std::vector<unsigned int> idx) : Transform()
{
    Onwer = g;
    key = KeyState::Rotate;         //
    keyMode = KeyState::Rotate;     //現在のモード


    sh_basic_draw = new Shader();
    sh_Phong_draw = new Shader();

    sh_basic_draw->LoadShader("Basic.vert", "Basic.frag");
    sh_Phong_draw->LoadShader("Phong.vert", "Phong.frag");




    //シェーダーでの画面描画配列を計算
    mMove = glm::vec3(0,0,-7);
    mScale = glm::vec3(1,1,1);

    setMove(mMove);
    setRotate_X(0);
    setRotate_Y(0);
    setRotate_Z(0);
    setScale(mScale);


    vertex = vert;        //頂点
    index = idx;        //インデックス

//    printf("%d\n",index.size());

    //VAO
    glGenVertexArrays(1, &vao);
    glBindVertexArray(vao);

    //VBO
    glGenBuffers(1, &vbo);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, std::size(vertex) * sizeof(Game::VertexAttribute), std::data(vertex) ,GL_STATIC_DRAW);

    //頂点座標
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Game::VertexAttribute), NULL);    
    glEnableVertexAttribArray(0);


    //UV座標    
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Game::VertexAttribute), (void*)(sizeof(GLfloat) * 3));
    glEnableVertexAttribArray(1);

    //法線
    glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(Game::VertexAttribute), (void*)(sizeof(GLfloat) * 5));
    glEnableVertexAttribArray(2);

    //IBO
    glGenBuffers(1, &ibo);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, std::size(index) * sizeof(unsigned int),std::data(index),GL_STATIC_DRAW);

}

//メッシュデータを設定

    //頂点属性
    struct VertexAttribute {

        GLfloat Position[3];    //頂点座標
        GLfloat uv[2];            //UV座標

        GLfloat Normal[3];        //法線

    };

//法線を計算する
glm::vec3 norm(float P1[],float P2[],float P3[])
{

    glm::vec3 v0, v1, v2;
    glm::vec3 vv1, vv2;

    v0.x = P1[0];
    v0.y = P1[1];
    v0.z = P1[2];

    v1.x = P2[0];
    v1.y = P2[1];
    v1.z = P2[2];

    v2.x = P3[0];
    v2.y = P3[1];
    v2.z = P3[2];

    vv1 = v0 - v1;
    vv2 = v2 - v0;

    glm::vec3 r = vv1 * vv2;

    glm::normalize(r);

    return r;

}

//コンストラクタ
Game::Game()
{
    mIsRunLoop = true;

    srand((unsigned int)time(0));

    //立方体のメッシュ情報
    //前
    vertex.push_back(VertexAttribute{ -1,1,0   ,0,0 , 0,0,0 });
    vertex.push_back(VertexAttribute{ -1,-1,0  ,0,1 , 0,0,0 });
    vertex.push_back(VertexAttribute{ 1,-1,0   ,1,1 , 0,0,0 });
    vertex.push_back(VertexAttribute{ 1,1,0    ,1,0 , 0,0,0 });
    //省略


    struct PoyNorm
    {
        glm::vec3 Position[3];
        glm::vec3 Norm;
    };


    PoyNorm pm[12];

    glm::vec3 a1 = norm(vertex.at(0).Position, vertex.at(1).Position, vertex.at(2).Position);
    pm[0].Norm = a1;
    pm[0].Position[0] = glm::vec3(vertex.at(0).Position[0], vertex.at(0).Position[1], vertex.at(0).Position[2]);
    pm[0].Position[1] = glm::vec3(vertex.at(1).Position[0], vertex.at(1).Position[1], vertex.at(1).Position[2]);
    pm[0].Position[2] = glm::vec3(vertex.at(2).Position[0], vertex.at(2).Position[1], vertex.at(2).Position[2]);

    glm::vec3 a2 = norm(vertex.at(0).Position, vertex.at(3).Position, vertex.at(2).Position);
    pm[1].Norm = a2;
    pm[1].Position[0] = glm::vec3(vertex.at(0).Position[0], vertex.at(0).Position[1], vertex.at(0).Position[2]);
    pm[1].Position[1] = glm::vec3(vertex.at(3).Position[0], vertex.at(3).Position[1], vertex.at(3).Position[2]);
    pm[1].Position[2] = glm::vec3(vertex.at(2).Position[0], vertex.at(2).Position[1], vertex.at(2).Position[2]);
    //省略

    glm::vec3 p = glm::vec3();//面法線を足す
    for (int i = 0; i < 24; i++)
    {      
        for (int j = 0; j < 12; j++)
        {        

            for (int k = 0; k < 3; k++) {
                if (glm::vec3(vertex.at(i).Position[0], vertex.at(i).Position[1], vertex.at(i).Position[2]) == pm[j].Position[k])
                {
                    p += pm[j].Norm;
                    break;
                }
            }
        }

        glm::vec3 p2 =  glm::normalize(p);

        vertex.at(i).Normal[0] = p2.x;
        vertex.at(i).Normal[1] = p2.y;
        vertex.at(i).Normal[2] = p2.z;

        p = glm::vec3();

        printf("%f , %f , %f  \n",vertex.at(i).Normal[0], vertex.at(i).Normal[1], vertex.at(i).Normal[2]);
    }

    //インデックスバッファ
    //省略

     //地面のメッシュ情報

     //頂点、UV
     Ground.push_back(VertexAttribute{ -1,1,0       ,0,0 });
     Ground.push_back(VertexAttribute{ -1,-1,0      ,0,1 });
     Ground.push_back(VertexAttribute{ 1,-1,0       ,1,1 });
     Ground.push_back(VertexAttribute{ 1,1,0        ,1,0 });

     //インデックスを設定
     Ground_index.push_back(0);
     Ground_index.push_back(1);
     Ground_index.push_back(2);

     Ground_index.push_back(0);
     Ground_index.push_back(3);
     Ground_index.push_back(2);
}

//3D空間をシュミレートするための行列を計算
void Mesh::CreateDrawMatrix()
{
    //ワールド行列
    glm::mat4x4 t = getMove() * getScale() * getRotate();
    memcpy(worldMatrix, glm::value_ptr(t), sizeof(float) * 16);

    //printf("あああ\n");


    float m[16];
    memcpy(m, glm::value_ptr(Onwer->camera->getViewMatrix()),sizeof(float) * 16);


    //Uniformリソースをプログラムに設定 シェーダプログラム
    //glUniformMatrix4fv(glGetUniformLocation(ShaderProgram, "worldMatrix"), 1, GL_FALSE, worldMatrix);
    //glUniformMatrix4fv(glGetUniformLocation(ShaderProgram, "viewMatrix"), 1, GL_FALSE, viewMatrix);
    //glUniformMatrix4fv(glGetUniformLocation(ShaderProgram, "viewMatrix"), 1, GL_FALSE, m);

    //画面描画
    sh_basic_draw->SetUniform_4m("worldMatrix", worldMatrix);
    sh_basic_draw->SetUniform_4m("viewMatrix", m);

    //ライティング描画

    //.vert
    sh_Phong_draw->SetUniform_4m("uWorldTransform", worldMatrix);
    sh_Phong_draw->SetUniform_4m("uViewProj", m);

    //.frag

    //並行光源用の構造体
    struct DirectionLight
    {
        glm::vec3 mDirection;        //光の方向
        glm::vec3 mDiffuseColor;    //拡散反射色
        glm::vec3 mSpecColor;        //鏡反射色
    };

    struct DirectionLight mDirLight;
    mDirLight.mDirection = glm::vec3(0, -0.7, -0.7);
    mDirLight.mDiffuseColor = glm::vec3(0, 1, 0);
    mDirLight.mSpecColor = glm::vec3(0.5, 1, 0.5);


    glm::vec3 mAmbientLight = glm::vec3(0.2,0.2,0.2);//環境光の強さ


    sh_Phong_draw->SetFloatUniform_3f("uCameraPos", Onwer->camera->getPosition());// カメラ位置(ワールド)

    sh_Phong_draw->SetFloatUniform_3f("uAmbientLight", mAmbientLight);// 環境光の強さ

    sh_Phong_draw->SetFloatUniform_3f("uDirLight.mDirection", mDirLight.mDirection);//光の方向

    sh_Phong_draw->SetFloatUniform_3f("uDirLight.mDiffuseColor", mDirLight.mDiffuseColor);//拡散反射色

    sh_Phong_draw->SetFloatUniform_3f("uDirLight.mSpecColor", mDirLight.mSpecColor);//鏡反射色




    /*
    sh_Phong_draw->SetFloatUniform_3f("inPosition", m);        //座標
    sh_Phong_draw->SetFloatUniform_3f("inNormal", m);        //法線
    sh_Phong_draw->SetFloatUniform_2f("inTexcoord", m);        //UV座標
    */



}
/*****************************************************************************************************************
* ライティング  .vert
******************************************************************************************************************/


#version 400

uniform mat4 uWorldTransform;
uniform mat4 uViewProj;

layout(location = 0) in vec3 inPosition;    //座標
layout(location = 2) in vec3 inNormal;        //法線
layout(location = 1) in vec2 inTexCoord;    //テクスチャ座標

out vec2 fragTexCoord;
out vec3 fragNormal;
out vec3 fragWorldPos;

void main()
{
    vec4 pos = vec4(inPosition,1.0);
    pos = pos * uWorldTransform;
    fragWorldPos = pos.xyz;

    gl_Position = pos * uViewProj;

    fragNormal = (vec4(inNormal,0.0f) * uWorldTransform).xyz;
    fragTexCoord = inTexCoord;
}
/*****************************************************************************************************************
* ライティング  .frag
******************************************************************************************************************/

#version 400

in vec2 fragTexCoord;
in vec3 fragNoraml;
in vec3 fragWorldPos;

out vec4 outColor;

uniform sampler2D uTexture;


//並行光源用の構造体
struct DirectionLight
{
    vec3 mDirection;        //光の方向

    vec3 mDiffuseColor;        //拡散反射色

    vec3 mSpecColor;        //鏡反射色
};

//ライティング用 uniform
uniform vec3 uCameraPos;

uniform float uSpecPower;

uniform vec3 uAmbientLight;

uniform DirectionLight uDirLight;


void main()
{
    vec3 N = normalize(fragNoraml);
    vec3 L = normalize(-uDirLight.mDirection);

    vec3 V = normalize(-uCameraPos - fragWorldPos);

    vec3 R = normalize(reflect(-L,N));

    vec3 Phong = uAmbientLight;
    float NdotL = dot(N,L);

    if(NdotL > 0)
    {
        vec3 Diffuse = uDirLight.mDiffuseColor * NdotL;
        vec3 Specular = uDirLight.mSpecColor * pow(max(0.0,dot(R,V)),uSpecPower);
        Phong += Diffuse + Specular;
    }

    outColor = texture(uTexture,fragTexCoord) * vec4(Phong,1.0f);
}
  • 気になる質問をクリップする

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • txty

    2020/11/09 18:16

    unityやってたみたいですけどunityがらみなのですか。何に法線つかうのですか。

    glVertex3f()を使っていいならglBeigin(hoge);glNormal3f(hoge,hoge,hoge);
    glTexCoord2f(hoge,hoge);glVertex3f(hoge,hoge,hoge);glEnd();の語順でいいと思いますけど。

    glNormal3f(,,);につかうのでは。

    参考サイト
    http://web.wakayama-u.ac.jp/~wuhy/GSS/06.htmlです
    (なお.objは正規化されているという情報があるため正規化の計算をするか知らないです。)
    自力でなんとかするしかないですね。)

    キャンセル

  • 退会済みユーザー

    退会済みユーザー

    2020/11/09 18:26

    シェーダーを使ってプログラムしたいのですが。面法線は算出出来ますが頂点法線も必要なのでしょか?
    ライティングをしたいです。

    キャンセル

  • txty

    2020/11/09 18:37 編集

    シェーダーわからないから無理。たぶん左側の三角形しかつかったことないです。あってるかわからないのでこれは、ごめんなさいです。というか上の面法線が左の図形になっているような。違うのかもしれませんが。

    キャンセル

回答 3

+1

こちらの情報が参考になるかと思います。
面法線と頂点法線_3DCG

なお、平面の場合、頂点法線と面法線は同一となります。
描画対象が曲面の場合に、頂点法線が異なることになります。

以下、大雑把に、頂点法線の概念を説明します。

現在の主流の3Dモデルは、2次元で例えると、円を正八角形などの多角形で疑似的に現しているようなものです。

正八角形の各辺の中心点は、円上でもその辺と平行な直線で接します。この時、円の中心と辺の中心点を通る直線は、直角に交わり、これを法線と呼びます。
これが3Dの場合、面法線です。

これに対し、各辺の頂点の場合、頂点と円の中心点を結ぶ直線は、直角には交わりません。
ですが、本来の円周上の点では、これも法線となります。
中心点の法線のままで影を付けると、角に見えてしまいますが、この法線を設定して影の調整を行い、丸く見えるようにします。
これが頂点法線です。

なお、頂点法線は、その性質上、3Dモデルの原型の形状に依存しますので、3Dモデルからだけでは算出できません。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/11/11 13:22

    見てみます。

    キャンセル

  • 2020/11/11 13:26

    fanaさん、適切なワードのご提示、ありがとうございます。
    txtyさん、既に質問者のご迷惑になっているように思えますので、ご自分で調査していただき、それでも不明な点があれば、その際に改めてご自分の質問としてご投稿ください。

    キャンセル

  • 2020/11/11 13:52 編集

    頂点法線も必要かと聞かれたものでしたから。このスレッドでは最後にするけど、http://web.wakayama-u.ac.jp/~wuhy/GSS/06.htmlの6.3節は面法線の計算式ですか。
    頂点法線の計算式ですか。あと、普通は左回りが正ですよね。式が間違ってる気がしてきた。後、
    追記しておく。https://wgld.org/d/contribution/a002.htmlを見る限り、面法線らしいですね。

    キャンセル

checkベストアンサー

0

※シェーディングに関して全般的に「diffuse specular ambient」といった語に関して,一度先に調べると良いのではないかと.

(1)計算すべき法線とは?

行うシェーディング技法に依るので,まず「どのようなアルゴリズムでシェーディングを行うのか」という話が先に存在しないと無意味です.
ざっくり,

  • フラットシェーディングであれば,三角形の3頂点に同じ法線(面の法線)を与える
  • スムーズシェーディングであれば,各頂点の法線は,その頂点を共有する複数の面の法線の合成(平均みたいな)方向を持たせる

ということになるかと.

(2)法線をどう使うんですか?

そもそも用いるシェーディングアルゴリズムの計算に"法線"が必要だから用意するわけなので,"法線"を用意し始めた時点で本来自明であるハズなのですが……
具体的な計算式については「ランバート則」とか「Phongの反射モデル」とかでググると見つかるかと思います.

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/11/11 17:11 編集

    質問ですが一番簡単なシェーディングはどれでしょうか?また面法線と頂点法線法線の計算は毎フレーム行うのでしょうか?

    キャンセル

  • 2020/11/11 18:34

    上述のように,まずは,{フラットシェーディングで,拡散反射の計算だけを実装}するのが簡単だと思います.

    > 法線の計算は毎フレーム行うのでしょうか?

    テクスチャ座標等と同じような固定のデータになるでしょうから最初だけで良いでしょう.

    キャンセル

  • 2020/11/11 19:37

    カメラ座標はどの値を入れればいいのでしょうか?

    キャンセル

0

法線の取得の仕方ですが、一番簡単な方法は下記の通りです。

  1. 頂点3点を取得
  2. 0番から1番,0番から2番のベクトルV1,V2を計算する
  3. V1とV2で外積Cを計算。
  4. Cを正規化

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

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

  • ただいまの回答率 87.48%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る