Нормаль из координат треугольника Объяснения и Реализующая Функция

Решил сделать карту высот, Все просто и понятно, одна картинка чёрно-белая формирует рельеф, вторая цветная формирует цвет. введите сюда описание изображения Но вот вечная проблема нормалей, помню несколько лет для моделей искал тему, нашёл кусок кода вставил, заработало но верно ли и каким образом до конца не был уверен. Для карты высот такой же вопрос, возможно так тоже можно было бы решить, но немного хочу разобраться и получить 100 процентной рабочий способ. Не привязывая к конкретно моей программе хочу получить общее понятное и кратное объяснения без заумных математических фраз, как почитать нормали по трем координатам, не забываем правило построения точек по часовой стрелке, это соблюдено. Дополнительно хотелось бы в ответе получить функцию в которой на входе ми отправляем три точки, а на выходе получаем нормаль. Заранее спасибо, очень поможете. C++.

Уточнение: нормаль по сути общая для всех трех точек, так как центр нормали всегда в центре координат 0,0,0, а точки находиться в одной плоскости. Точки задаться трема координатами x,y,z

Для тех кто просто посмотреть, нормали нужны для расчета освещения.

Нормаль выглядит как на картинке, но у меня для каждого треугольника (или для каждой точки) своя нормаль.

//===================================== После комментариев KoVadim и Stranger in the Q Вот что получилось, присутствует некий кубизм хотя у каждой верни с одинаковыми координатами только одна нормаль, но думаю разберусь. Блгодарен

введите сюда описание изображения

введите сюда описание изображения

К сожалению вопрос не решён, нормали совершенно не корректно рассчитаны. Что странно, на плоскости вроде все ок, но не на холмах. Там просто в бок.

введите сюда описание изображения введите сюда описание изображения

Не уверен что поможет но вот код

namespace USMod
{
    
    class HeighMap
    {
    public:
        //Моделька
        MeshCreater Mesh;

//Модель линий для нормалей
        MeshCreater NormalMesh;


        //только при загрузке
        glm::vec3 Scalse;
        glm::vec3 Translate;
        //Массив с точками итд
        MeshCreater::VertexOne **masiv;


        //Ширина глубина
        float height, width;

        HeighMap()
        {
            height = 0;
            width = 0;
        }

        MeshCreater::v3 glmToV3(glm::vec3 v)
        {
            MeshCreater::v3 a;
            a.x = v.x;
            a.y = v.y;
            a.z = v.z;
            return a;
        }
        
        MeshCreater::v2 glmToV2(glm::vec2 v)
        {
            MeshCreater::v2 a;
            a.x = v.x;
            a.y = v.y;
            return a;
        }


        //Загрузить картинку
        bool Load(std::string url,ShaderL &sh,glm::vec3 scale = glm::vec3(1,0.57,1))
        {
            Scalse = scale;
            //Файл с высотой
            FIBITMAP  *dib1 = FreeIMloadImage(url.c_str());
            if (!dib1)
            {
                return false; //ошибка
            }
            Mesh.SH = sh;
            NormalMesh.SH = sh;






            //темп
            int x, y;




            //Удалить массив если бил MeshCreater::VertexOne **masiv;
    
            for (int i = 0; i < width; i++)
                delete []masiv[i];
            delete []masiv;


            //Для загрузки  текстур
            RGBQUAD rgbquad;
            FREE_IMAGE_TYPE type;
            BITMAPINFOHEADER *header;

            type = FreeImage_GetImageType(dib1);
            height = FreeImage_GetHeight(dib1);
            width = FreeImage_GetWidth(dib1);



            //Инит массив MeshCreater::VertexOne **masiv;
            masiv = new MeshCreater::VertexOne*[width];
            for (int i = 0; i < width; i++)
                masiv[i] = new MeshCreater::VertexOne[height];




            //С картинки в масив
            header = FreeImage_GetInfoHeader(dib1);
            int scanLineWidh = ((3 * (int)width) % 4 == 0) ? 3 * (int)width : ((3 * (int)width) / 4) * 4 + 4;
            unsigned char * texels = (GLubyte*)calloc(height*scanLineWidh, sizeof(GLubyte));
            for (x = 0; x < width; x++)
                for (y = 0; y < height; y++)
                {

                    FreeImage_GetPixelColor(dib1, x, y, &rgbquad);
                    masiv[x][y].v.y = ((GLubyte*)&rgbquad)[1];
                }
            free(texels);
            FreeImage_Unload(dib1);
            //=====================================

            //Сместить в центр сцены
            Translate.x = -width / 2;
            Translate.z = -height / 2;

//Заполнить вершины текстур координаты и нормали
            for (x = 0; x < width - 1; x++)
                for (y = 0; y < height - 1; y++)
                {
                    glm::vec3 a, b, c, d;//Точки
                    a = glm::vec3(x, masiv[x][y].v.y, y);
                    b = glm::vec3(x + 1, masiv[x + 1][y + 1].v.y, y + 1);
                    c = glm::vec3(x + 1, masiv[x + 1][y].v.y, y);
                    d = glm::vec3(x, masiv[x][y + 1].v.y, y + 1);


                    //Применить трансформации 
                    a = a * scale + Translate;
                    b = b * scale + Translate;
                    c = c * scale + Translate;
                    d = d * scale + Translate;


                    glm::vec2 atx, btx, ctx, dtx;//Текстур корденаты
                    atx = glm::vec2((float)x / (float)width, (float)y / (float)-height);
                    btx = glm::vec2(((float)x + 1.0f) / (float)width, (((float)y + 1.0f) / (float)-height));
                    ctx = glm::vec2(((float)x + 1.0f) / (float)width, (float)y / (float)-height);
                    dtx = glm::vec2(((float)x) / (float)width, ((float)y + 1.0f) / (float)-height);


                    //a
                    masiv[x][y].v = glmToV3(a);
                    masiv[x][y].t = glmToV2(atx);
                    {
                        glm::vec3 s = glm::normalize(glm::cross(c-a, c-b));
                        masiv[x][y].n = glmToV3(s);
                    }
                    
                    //b     *scale + Translate
                    masiv[x+1][y+1].v = glmToV3(b);
                    masiv[x+1][y+1].t = glmToV2(btx);
                    masiv[x+1][y+1].n = masiv[x][y].n;



                    //c
                    masiv[x + 1][y].v = glmToV3(c);
                    masiv[x + 1][y].t = glmToV2(ctx);
                    masiv[x + 1][y].n = masiv[x][y].n;
                    

                    //d
                    masiv[x][y + 1].v = glmToV3(d);
                    masiv[x][y + 1].t = glmToV2(dtx);
                    {
                        glm::vec3 s = glm::normalize(glm::cross(a - b, a - b));
                        masiv[x][y+1].n = glmToV3(s);
                    }


                } //Массив заполнен

            

            NormalMesh.begin();

    //Обнулить меш
            Mesh.begin();
            for (x = 0; x < width - 1; x++)
                for (y = 0; y < height - 1; y++)
                {
                    //c
                    Mesh.Nomal3f(masiv[x + 1][y].n);
                    Mesh.TextCord2f(masiv[x + 1][y].t);
                    Mesh.Vertex3f(masiv[x + 1][y].v);

                    //b
                    Mesh.Nomal3f(masiv[x + 1][y + 1].n);
                    Mesh.TextCord2f(masiv[x + 1][y + 1].t);
                    Mesh.Vertex3f(masiv[x + 1][y + 1].v);

                    //a
                    Mesh.Nomal3f(masiv[x][y].n);
                    Mesh.TextCord2f(masiv[x][y].t);
                    Mesh.Vertex3f(masiv[x][y].v);



                    NormalMesh.Vertex3f(masiv[x][y].v);

                    glm::vec3 p2;
                    p2.x = masiv[x][y].v.x+ masiv[x][y].n.x;
                    p2.y = masiv[x][y].v.y+ masiv[x][y].n.y;
                    p2.z = masiv[x][y].v.z+ masiv[x][y].n.z;
                    NormalMesh.Vertex3f(p2);



                    //a
                    Mesh.Nomal3f(masiv[x][y].n);
                    Mesh.TextCord2f(masiv[x][y].t);
                    Mesh.Vertex3f(masiv[x][y].v);


                    //b
                    Mesh.Nomal3f(masiv[x + 1][y + 1].n);
                    Mesh.TextCord2f(masiv[x + 1][y + 1].t);
                    Mesh.Vertex3f(masiv[x + 1][y + 1].v);

                    //d
                    Mesh.Nomal3f(masiv[x][y + 1].n);
                    Mesh.TextCord2f(masiv[x][y + 1].t);
                    Mesh.Vertex3f(masiv[x][y + 1].v);



                }

            Mesh.end(); //Компилировать графику


            NormalMesh.end();



        }



        void Draw()
        {
            Mesh.draw();
            NormalMesh.draw(GL_LINES);
        }

    };
}

Отмечу что источники света вроде работают, но расчет скорее всего работает от позиции точки, а я то думал что за глюки со светом, направление не работает из за некорректных нормалей.

введите сюда описание изображения

Проверил методику расчета и она верна, значит я где то намудрил, буду разбираться. Проверил на едином треугольнике с постоянно подвижными точками. Все ок. введите сюда описание изображения

glm::vec3 A, B, C;
    glm::vec3 NormalA;

    //Анимировать точки
    A = glm::vec3(-5, sin(glfwGetTime())*3, 5);  
    B = glm::vec3(-5, sin(glfwGetTime()/2)*3, -5);
    C = glm::vec3(5, cos(glfwGetTime())*3,5);

    NormalA = glm::normalize(glm::cross(C - A, B - A));

    TesterNormal.begin();
    Tester.begin();

    
    Tester.Nomal3f(NormalA);

    Tester.TextCord2f(0,1); Tester.Vertex3f(A);
    Tester.TextCord2f(0, 0);  Tester.Vertex3f(B);
    Tester.TextCord2f(1, 1); Tester.Vertex3f(C);

    

    

    TesterNormal.Vertex3f(A);
    TesterNormal.Vertex3f(A + NormalA);


    Tester.end();
    TesterNormal.end();

    

    Tester.draw();
    TesterNormal.draw(GL_LINES);

В любом случаи расчет нормали normalize(cross(a-b,a-c)) рабочий, Буду копаться в своем коде.

Всем спасибо победа! введите сюда описание изображения


Ответы (0 шт):