OpenGL.Shader: 7-Learning Light-Normal Vector

OpenGL.Shader: 7-Learning Light-Normal Vector

 

L'éclairage occupe une part très importante d'OpenGL. La simulation de l'éclairage est devenue un sujet de recherche majeur dans le domaine informatique. On peut voir l'impact de ce domaine, qui se traduit non seulement par l'amélioration progressive de la vision du jeu, mais aussi dans les domaines du cinéma, de l'imagerie par ordinateur (CGI) et d'autres domaines.

Habituellement, selon le type, différentes sources de lumière peuvent être divisées dans les groupes suivants:

Lumière ambiante La lumière ambiante
semble venir de toutes les directions et tout dans la scène est éclairé au même degré. Ceci est similaire à la lumière que nous obtenons d'une grande source lumineuse égale, comme le ciel. La lumière ambiante peut également être utilisée pour imaginer l'effet de rebond sur de nombreux objets avant que la lumière n'atteigne nos yeux. Les ombres ne sont jamais peintes à travers la lumière ambiante. Dans le noir.

La lumière directionnelle
semble provenir d'une seule direction, et la source lumineuse semble être dans un endroit très primitif. Ceci est similaire à la lumière que nous obtenons du soleil ou de la lune.

La lumière ponctuelle
est la lumière projetée d'un endroit à proximité, et la densité de la lumière diminue avec la distance. Cela convient pour représenter des sources lumineuses à proximité, qui projettent leur lumière dans toutes les directions, comme une ampoule ou une bougie.

Le spot est
similaire au spot, mais avec une restriction, il ne peut se projeter que dans une direction spécifique. C'est comme le type de lumière que nous obtenons d'une lampe de poche ou d'un projecteur.

Il peut également être divisé en deux catégories selon la manière dont la lumière est émise à la surface de l'objet:

La réflexion diffuse
fait référence à la diffusion de la lumière de manière égale dans toutes les directions et convient aux matériaux qui n'ont pas de surfaces polies, comme les tapis ou les murs mixtes externes. Ces surfaces similaires semblent avoir de nombreux points d'observation différents.

Réflexion spéculaire La réflexion spéculaire
émet plus intensément dans une certaine direction et convient aux matériaux polis ou brillants, comme le métal lisse ou une voiture qui vient d'être cirée. 2

 

 

Ces sources de lumière ne sont pas directement simulées dans OpenGL. Au lieu de cela, la plupart des jeux et applications simplifient les choses et se rapprochent du fonctionnement de la lumière à un niveau supérieur, plutôt que de la simuler directement. Cela introduira un autre concept important - le vecteur normal .

Imaginez: si une surface plane fait face à la lumière, elle est naturellement la plus lumineuse. Bien sûr, il existe des plans matériels qui peuvent réfléchir la lumière, et l'intensité de la lumière réfléchie est liée à l'angle que vous observez, mais cet article ne les présentera pas. Nous utilisons des vecteurs normaux pour indiquer l'orientation du plan.Dans l'implémentation spécifique, chaque point aura un vecteur normal. Le vecteur dit normal est un vecteur tridimensionnel perpendiculaire au plan, comme le montre la figure ci-dessous.

La figure montre deux méthodes de représentation vectorielle normale. À gauche, un vecteur normal pour chaque point de chaque polygone et à droite un vecteur normal pour chaque point. Le vecteur normal du point partagé est ce point en tout La somme des vecteurs normaux sur le plan. Le vecteur normal doit toujours être normalisé en un vecteur unitaire. Dans les exemples de cet article, la méthode de gauche est utilisée.

Ici, nous sommes basés sur le cube qui a été utilisé tout le temps, en ajoutant un vecteur normal aux données originales du cube (position, texture).


class CubeIlluminate {
public:
    struct V3N3T2 {
        float x, y, z; //位置坐标
        float nx, ny, nz; //法向量
        float u,v; //纹理坐标
    };
public:
    GLuint                  mCubeSurfaceTexId;
    V3N3T2                  _data[36];

    void        init(const CELL::float3 &halfSize, GLuint tex)
    {
        V3N3 verts[] =
        {
                {+halfSize.x, -halfSize.y, +halfSize.z, 0.0f,  -1.0f, 0.0f,  0.0f,0.0f},
                {-halfSize.x, -halfSize.y, +halfSize.z, 0.0f,  -1.0f, 0.0f,  1.0f,0.0f},
                {-halfSize.x, -halfSize.y, -halfSize.z, 0.0f,  -1.0f, 0.0f,  1.0f,1.0f},

                {-halfSize.x, -halfSize.y, +halfSize.z, 0.0f,  0.0f,  +1.0f, 0.0f,0.0f},
                {+halfSize.x, -halfSize.y, +halfSize.z, 0.0f,  0.0f,  +1.0f, 1.0f,1.0f},
                {+halfSize.x, +halfSize.y, +halfSize.z, 0.0f,  0.0f,  +1.0f, 0.0f,1.0f},

                {+halfSize.x, +halfSize.y, -halfSize.z, +1.0f, 0.0f,  0.0f,  1.0f,0.0f},
                {+halfSize.x, +halfSize.y, +halfSize.z, +1.0f, 0.0f,  0.0f,  1.0f,1.0f},
                {+halfSize.x, -halfSize.y, +halfSize.z, +1.0f, 0.0f,  0.0f,  0.0f,1.0f},

                {-halfSize.x, +halfSize.y, +halfSize.z, 0.0f,  +1.0f, 0.0f,  1.0f,0.0f},
                {+halfSize.x, +halfSize.y, +halfSize.z, 0.0f,  +1.0f, 0.0f,  0.0f,1.0f},
                {+halfSize.x, +halfSize.y, -halfSize.z, 0.0f,  +1.0f, 0.0f,  0.0f,0.0f},

                {-halfSize.x, +halfSize.y, +halfSize.z, -1.0f, 0.0f,  0.0f,  0.0f,1.0f},
                {-halfSize.x, -halfSize.y, -halfSize.z, -1.0f, 0.0f,  0.0f,  0.0f,0.0f},
                {-halfSize.x, -halfSize.y, +halfSize.z, -1.0f, 0.0f,  0.0f,  1.0f,0.0f},

                {-halfSize.x, +halfSize.y, +halfSize.z, 0.0f,  0.0f,  +1.0f, 0.0f,1.0f},
                {-halfSize.x, -halfSize.y, +halfSize.z, 0.0f,  0.0f,  +1.0f, 1.0f,0.0f},
                {+halfSize.x, +halfSize.y, +halfSize.z, 0.0f,  0.0f,  +1.0f, 1.0f,1.0f},

                {+halfSize.x, -halfSize.y, -halfSize.z, 0.0f,  -1.0f, 0.0f,  1.0f,1.0f},
                {+halfSize.x, -halfSize.y, +halfSize.z, 0.0f,  -1.0f, 0.0f,  0.0f,1.0f},
                {-halfSize.x, -halfSize.y, -halfSize.z, 0.0f,  -1.0f, 0.0f,  0.0f,0.0f},

                {+halfSize.x, -halfSize.y, -halfSize.z, 0.0f,  0.0f,  -1.0f, 1.0f,1.0f},
                {-halfSize.x, -halfSize.y, -halfSize.z, 0.0f,  0.0f,  -1.0f, 0.0f,0.0f},
                {+halfSize.x, +halfSize.y, -halfSize.z, 0.0f,  0.0f,  -1.0f, 1.0f,0.0f},

                {+halfSize.x, -halfSize.y, -halfSize.z, +1.0f, 0.0f,  0.0f,  1.0f,0.0f},
                {+halfSize.x, +halfSize.y, -halfSize.z, +1.0f, 0.0f,  0.0f,  1.0f,1.0f},
                {+halfSize.x, -halfSize.y, +halfSize.z, +1.0f, 0.0f,  0.0f,  0.0f,1.0f},

                {-halfSize.x, +halfSize.y, -halfSize.z, 0.0f,  0.0f,  -1.0f, 1.0f,0.0f},
                {+halfSize.x, +halfSize.y, -halfSize.z, 0.0f,  0.0f,  -1.0f, 0.0f,1.0f},
                {-halfSize.x, -halfSize.y, -halfSize.z, 0.0f,  0.0f,  -1.0f, 0.0f,0.0f},

                {-halfSize.x, +halfSize.y, -halfSize.z, -1.0f, 0.0f,  0.0f,  0.0f,0.0f},
                {-halfSize.x, -halfSize.y, -halfSize.z, -1.0f, 0.0f,  0.0f,  1.0f,0.0f},
                {-halfSize.x, +halfSize.y, +halfSize.z, -1.0f, 0.0f,  0.0f,  1.0f,1.0f},

                {-halfSize.x, +halfSize.y, -halfSize.z, 0.0f,  +1.0f, 0.0f,  0.0f,0.0f},
                {-halfSize.x, +halfSize.y, +halfSize.z, 0.0f,  +1.0f, 0.0f,  1.0f,1.0f},
                {+halfSize.x, +halfSize.y, -halfSize.z, 0.0f,  +1.0f, 0.0f,  0.0f,1.0f},
        };
        memcpy(_data, verts, sizeof(verts));

        mCubeSurfaceTexId = tex;
    }

    // ... ...
};

Compte tenu de la grande quantité de données, la structure personnalisée V3N3T2 représente la quantité de données d'un point, puis tous les trois groupes de V3N3T2 forment une surface triangulaire et le reste n'est plus verbeux. Il convient de noter que le vecteur normal ne représente que la direction, pas les informations de position, de sorte que le vecteur normal doit essayer d'utiliser des données normalisées. Les données ci-dessus sont projetées dans l'espace cartésien comme indiqué sur la figure

Prenons d'abord le point (1, -1, 1) des quatre points sur le côté gauche comme exemple, le rouge est le vecteur normal (1, 0, 0) correspondant au côté gauche horizontalement vers la droite;
remplacé par la surface inférieure, le même point (1, -1, 1) par exemple, le jaune est le vecteur normal (0, -1, 0) correspondant à la face inférieure verticalement;
puis remplacez-le par la face avant, qui est le même point (1, -1, 1), bleu La couleur est le vecteur normal (0, 0, 1) correspondant à la face avant et le plan horizontal est orienté vers l'extérieur;

La description ci-dessus atteste encore une fois des connaissances théoriques: 1) Le vecteur normal représente la direction, pas la position; 2) Le vecteur normal est un vecteur normalisé, à savoir Math.sqrt (x * x + y * y + z * z) = 1; 3 ) Le vecteur normal est déterminé en fonction de la surface et la direction est perpendiculaire au plan. Il peut y avoir plusieurs vecteurs normaux pour les points communs de plusieurs surfaces.

 

Le vecteur normal est essentiellement introduit ici. Passons directement au groupe de programmes shader.

class CubeIlluminateProgram : public ShaderProgram
{
public:
    GLint       _mvp;
    GLint       _lightDir;
    GLint       _lightColor;
    GLint       _lightDiffuse;
    GLint       _texture;
    GLint       _position;
    GLint       _normal;
    GLint       _uv;
public:
    virtual void    initialize()
    {
        const char* vs  =  "#version 320 es\n\
                            uniform mat4   _mvp;\n\
                            uniform vec3   _lightDir;\n\
                            uniform vec3   _lightColor;\n\
                            uniform vec3   _lightDiffuse;\n\
                            in      vec3   _position;\n\
                            in      vec3   _normal;\n\
                            in      vec2   _uv;\n\
                            out     vec2   _outUV;\n\
                            out     vec4   _outComposeColor;\n\
                            void main()\n\
                            {\n\
                                _outUV                =   _uv; \n\
                                float lightStrength   =   max(dot(_normal, -_lightDir), 0.0); \n\
                                _outComposeColor =   vec4(_lightColor * lightStrength + _lightDiffuse, 1);\n\
                                gl_Position      =   _mvp * vec4(_position,1.0);\n\
                            }";

        const char* fs =   "#version 320 es\n\
                            precision mediump float;\n\
                            in      vec4        _outComposeColor;\n\
                            in      vec2        _outUV;\n\
                            uniform sampler2D   _texture;\n\
                            out     vec4        _fragColor;\n\
                            void main()\n\
                            {\n\
                                vec4    color   =   texture(_texture,_outUV);\n\
                                _fragColor      =   color * _outComposeColor;\n\
                            }";

        programId   =   ShaderHelper::buildProgram(vs, fs);
        _mvp        =   glGetUniformLocation(programId,  "_mvp");
        _lightDir   =   glGetUniformLocation(programId,  "_lightDir");
        _lightColor =   glGetUniformLocation(programId,  "_lightColor");
        _lightDiffuse = glGetUniformLocation(programId,  "_lightDiffuse");

        _position   =   glGetAttribLocation(programId,   "_position");
        _normal     =   glGetAttribLocation(programId,   "_normal");
        _uv         =   glGetAttribLocation(programId,   "_uv");

        _texture    =   glGetUniformLocation(programId,  "_texture");
    }

    virtual void    begin()
    {
        glEnableVertexAttribArray(_position);
        glEnableVertexAttribArray(_normal);
        glEnableVertexAttribArray(_uv);
        glUseProgram(programId);
    }
    virtual void    end()
    {
        glDisableVertexAttribArray(_position);
        glDisableVertexAttribArray(_normal);
        glDisableVertexAttribArray(_uv);
        glUseProgram(0);
    }
};

Analysez d'abord le programme vertex shader:

#version 320 es
uniform mat4   _mvp; // 模型视图投影矩阵
uniform vec3   _lightDir; // 光源方向 只是一个方向
uniform vec3   _lightColor; // 环境光源颜色
uniform vec3   _lightDiffuse; // 漫反射 模拟材质补光用
in      vec3   _position; // 顶点位置属性
in      vec3   _normal; // 顶点法向量
in      vec2   _uv; // 纹理坐标
out     vec2   _outUV; // 输出片元着色器纹理坐标
out     vec4   _outComposeColor; // 输出的混合光
void main()
{
     _outUV                =   _uv;
     float lightStrength   =   max(dot(_normal, -_lightDir), 0.0);
     _outComposeColor =   vec4(_lightColor * lightStrength + _lightDiffuse, 1);
     gl_Position      =   _mvp * vec4(_position,1.0);
}

La deuxième ligne de code  intensité lumineuse = vecteur normal * vecteur source de lumière après normalisation inverse  . D'après la formule mathématique, l'intensité lumineuse est le produit scalaire du vecteur normal et du vecteur source de lumière inversement normalisé. Quelle est la source de lumière après naturalisation inversée? Regardez l'image ci-dessous, si facile.

Il existe également des tutoriels web qui inversent directement le vecteur normal d'entrée, ce qui est cohérent du point de vue des opérations mathématiques, mais ce n'est pas facile à comprendre en théorie. Je ne recommande pas cette façon. Il est normal de savoir que c'est le cas et de comprendre certaines des actions des autres.

Après avoir obtenu l'intensité lumineuse, la troisième ligne de code vec4 (_lightColor * lightStrength + _lightDiffuse, 1), intensité lumineuse * valeur de couleur de la lumière ambiante, à ce moment a réellement l'effet d'éclairage, plus la réflexion diffuse _lightDiffuse est d'empêcher l'endroit sans intensité lumineuse Il devient complètement noir, car il ne correspond pas à la situation réelle. Il existe de nombreux contenus de réflexion diffuse qui peuvent être étendus, tels que le calcul de l'intensité lumineuse de la réflexion diffuse en fonction de la texture du matériau, l'ajout de votre propre valeur de couleur, etc.

version 320 es
precision mediump float;
in      vec4        _outComposeColor;
in      vec2        _outUV;
uniform sampler2D   _texture;
out     vec4        _fragColor;
void main()
{
     vec4    color   =   texture(_texture,_outUV);
     _fragColor      =   color * _outComposeColor;
}

Ensuite, venez au programme de shader de fragment, le programme de shader est relativement simple, extrayez la valeur de couleur de texture en fonction des coordonnées de texture, puis multipliez la sortie de couleur de lumière mélangée à partir du sommet en fonction de la valeur de couleur de texture, et vous avez terminé.

 

Enfin, combiné avec le programme shader, complétez la méthode de rendu de CubeIlluminate.

void        render(Camera3D& camera)
{
    sprogram.begin();
    CELL::matrix4   matModel(1);
    CELL::matrix4   vp = camera.getProject() * camera.getView();
    CELL::matrix4   mvp = (vp * matModel);
    glUniformMatrix4fv(sprogram._mvp, 1, GL_FALSE, mvp.data());
    glActiveTexture(GL_TEXTURE0);
    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D,  mCubeSurfaceTexId);
    glUniform1i(sprogram._texture, 0);
    glUniform3f(sprogram._lightDiffuse, 0.1f, 0.1f, 0.1f); // 漫反射 环境光
    glUniform3f(sprogram._lightColor, 1.0f, 1.0f, 1.0f); // 定向光源的颜色
    glUniform3f(sprogram._lightDir, // 定向光源的方向   直接使用摄像头到观察点的方向
                static_cast<GLfloat>(camera._dir.x),
                static_cast<GLfloat>(camera._dir.y),
                static_cast<GLfloat>(camera._dir.z));
    glVertexAttribPointer(static_cast<GLuint>(sprogram._position), 3, GL_FLOAT, GL_FALSE,
                          sizeof(CubeIlluminate::V3N3), &_data[0].x);
    glVertexAttribPointer(static_cast<GLuint>(sprogram._normal),   3, GL_FLOAT, GL_FALSE,
                          sizeof(CubeIlluminate::V3N3), &_data[0].nx);
    glVertexAttribPointer(static_cast<GLuint>(sprogram._uv),       2, GL_FLOAT, GL_FALSE,
                          sizeof(CubeIlluminate::V3N3), &_data[0].u);
    glDrawArrays(GL_TRIANGLES, 0, 36);
    sprogram.end();
}

 

Lien du projet de démonstration https://github.com/MrZhaozhirong/NativeCppApp -> LightRenderer.cpp CubeIlluminate.hpp CubeIlluminateProgram.hpp 

Enfin, voici l'effet normal de lumière le plus simple:

Je suppose que tu aimes

Origine blog.csdn.net/a360940265a/article/details/90679025
conseillé
Classement