Eclairage en OpenGL. Matériaux et éclairage Source lumineuse ponctuelle en opengl

Eh bien, messieurs. Nous avons beaucoup appris sur OpenGL ces derniers temps, notamment sur la façon de contrôler la caméra, de travailler avec des textures et de travailler avec des modèles. Il est temps de parler de quelque chose de beaucoup plus intéressant, à savoir l'éclairage. Ce sujet est intéressant, car il n'y a rien de prêt pour travailler avec la lumière dans OpenGL, tout doit être écrit indépendamment sur les shaders. Dans le cadre de cette note, nous considérerons l'éclairage Phong. C'est un sujet assez vaste, nous ne parlerons donc que de éclairage. Dans la façon dont ils sont faits ombres, devra le comprendre une autre fois.

Enregistrer et utiliser les normales

Avant de passer directement à l'éclairage, nous avons besoin d'une chose telle que les normales.

Nous savons déjà que les modèles ont des sommets et des coordonnées UV correspondant à ces sommets. Pour créer un éclairage, nous avons besoin de plus d'informations sur les modèles, à savoir les normales. Une normale est un vecteur unitaire correspondant à un sommet (ou, alternativement, un polygone, mais ce n'est pas notre cas). Quel rôle jouent les normales dans la mise en œuvre de l'éclairage, nous l'apprendrons ci-dessous. Pour l'instant, il suffit de dire que les normales sont en effet très importantes. Par exemple, grâce à eux, les surfaces paraissent plus lisses et vous pouvez distinguer une boule d'un polyèdre convexe régulier comme un icosaèdre. Et puisque les normales sont si importantes, nous devons apprendre à les préserver lors de la conversion des modèles Blender dans notre propre format.

Les changements correspondants sont plutôt anodins. Nous obtenons les normales de la même manière que nous avons obtenu les coordonnées des sommets et les coordonnées UV :

// partie du corps de la procédure importéModelCreate

pour (entier non signé j = 0 ; j< face.mNumIndices ; ++ j) {
index entier non signé = face.mIndices[ j] ;
aiVector3D pos = mesh-> mVertices[ index] ;
aiVector3D uv = mesh-> mTextureCoords[ 0 ] [index] ;
aiVector3D normal = mesh-> mNormals[ index] ;

VerticesBuffer[ verticesBufferIndex++ ] = pos.x ;
verticesBuffer[ verticesBufferIndex++ ] = pos.y ;
verticesBuffer[ verticesBufferIndex++ ] = pos.z ;
verticesBuffer[ verticesBufferIndex++ ] = normal.x ;
verticesBuffer[ verticesBufferIndex++ ] = normal.y ;
verticesBuffer[ verticesBufferIndex++ ] = normal.z ;
verticesBuffer[ verticesBufferIndex++ ] = uv.x ;
verticesBuffer[ verticesBufferIndex++ ] = 1.0f - uv.y ;
}

La procédure d'optimisation du modèle change de manière similaire. Et dans la procédure modelLoad, au lieu de deux tableaux d'attributs, nous en avons maintenant besoin de trois :

// partie du corps de la procédure modelLoad

GlBindVertexArray(modèleVAO) ;
glEnableVertexAttribArray(0) ;
glEnableVertexAttribArray(1 ) ;
glEnableVertexAttribArray(2) ;

GlBindBuffer(GL_ARRAY_BUFFER, modèleVBO) ;
glBufferData(GL_ARRAY_BUFFER, header->verticesDataSize, verticesPtr,
GL_STATIC_DRAW);

GLsizei foulée = 8 * sizeof(GLfloat) ;
glVertexAttribPointer(0 , 3 , GL_FLOAT, GL_FALSE, foulée, nullptr) ;
glVertexAttribPointer(1 , 3 , GL_FLOAT, GL_FALSE, foulée,
(const void * ) (3 * sizeof (GLfloat) ) ) ;
glVertexAttribPointer(2 , 2 , GL_FLOAT, GL_FALSE, foulée,
(const void * ) (6 * sizeof (GLfloat) ) ) ;

De plus, nous avons en plus besoin d'une variable uniforme de matrice M :

GLint uniformM = getUniformLocation(programId, "M" ) ;

// ...

GlUniformMatrix4fv(uniformM, 1 , GL_FALSE, & towerM[ 0 ] [ 0 ] ) ;

... pour faire pivoter correctement la normale dans l'espace dans le vertex shader :

# cœur de la version 330

Layout(location = 0 ) in vec3 vertexPos;
layout(location = 1 ) in vec3 vertexNorm;
layout(location = 2 ) in vec2 vertexUV;

uniforme mat4 MVP ;
tapis uniforme4M;

out vec2 fragmentUV ;
sortie vec3 fragmentNormal ;
sortie vec3 fragmentPos ;

void main() (
fragmentUV = vertexUV ;
fragmentNormal = (M * vec4 (vertexNorm, 0 ) ) . xyz ;
fragmentPos = (M * vec4 (vertexPos, 1 ) ) . xyz ;

gl_Position = MVP * vec4 (vertexPos, 1 ) ;
}

Enfin, le fragment shader prend une normale interpolée sur trois sommets :

// ...

void main() (

// ...
}

De cette manière simple, nous obtenons les normales pour fragments.

Qu'est-ce que l'éclairage Phong

Comme indiqué, l'éclairage dans OpenGL est écrit sur les shaders par le programmeur lui-même. Il est clair qu'il existe plus d'une façon de mettre en œuvre cet éclairage, chacune avec son propre degré de réalisme et ses besoins en ressources. Et chaque méthode peut toujours avoir un nombre infini d'implémentations spécifiques. D'après ma compréhension, un éclairage en temps réel efficace et réaliste est toujours un domaine de recherche actif. Dans le cadre de cette note, nous nous pencherons sur l'éclairage Phong, qui est à la fois assez réaliste et facile à mettre en place.

Il est important de comprendre la différence entre les concepts suivants :

  • L'ombrage Gouraud est lorsque vous calculez l'illumination de chaque sommet, et l'illumination des fragments entre eux est interpolée;
  • Ombrage Phong - lorsque l'éclairage est calculé séparément pour chaque fragment;
  • L'éclairage Phong ou le modèle de réflexion Phong est une méthode d'éclairage spécifique discutée dans cette note et qui peut être utilisée à la fois dans l'ombrage Gouraud et l'ombrage Phong;

Il n'est pas surprenant que l'ombrage Phong et l'éclairage Phong soient souvent confondus, et dans certains tutoriels, vous pouvez lire des bêtises comme "L'idée de l'ombrage Phong est d'utiliser trois composants ..." ce qui vous fait immédiatement sérieusement douter de l'autorité. de la personne qui a écrit ce tutoriel.

Autant que je sache, l'ombrage Gouraud n'est presque jamais utilisé dans les applications modernes, l'ombrage Phong est préféré à la place. Dans le cadre de cet article, nous utiliserons également l'ombrage Phong, c'est-à-dire que l'éclairage sera calculé séparément pour chaque fragment. La méthode d'éclairage spécifique que nous utiliserons est l'éclairage Phong. Cette méthode est la suivante.

Selon différentes formules, trois composantes d'éclairage sont calculées :

  • Éclairage de fond (éclairage ambiant) - une imitation de la lumière qui a atteint un point donné après réflexion par d'autres objets. Lors du calcul de l'éclairage d'arrière-plan, ni les normales ni la position actuelle de la caméra ne sont prises en compte ;
  • Éclairage diffus - lumière provenant d'une source qui est diffusée après avoir atteint un point donné. Selon l'angle sous lequel la lumière tombe, l'éclairage devient plus fort ou plus faible. Les normales sont prises en compte ici, mais pas la position de la caméra ;
  • Éclairage réfléchi (éclairage spéculaire) - lumière d'une source réfléchie après avoir atteint un point donné. La lumière réfléchie est visible si elle pénètre dans la caméra. Par conséquent, les normales et la position de la caméra sont prises en compte ici ;

Les résultats sont ensuite additionnés, ce qui donne un éclairement total.

Pour le rendre encore plus intéressant, il existe différentes sources lumineuses. Évidemment, le soleil dans la rue et une lampe de poche dans le noir éclairent la scène de manière très différente. Pour commencer, nous considérerons la source la plus simple - la lumière directionnelle.

Lumière directionnelle

La lumière directionnelle est une imitation d'une source lumineuse infiniment distante. Prenons, par exemple, le Soleil. Le soleil est très loin de la terre. Ainsi, à la surface de la Terre, il est possible de considérer avec une grande précision tous les rayons lumineux du Soleil comme parallèles. La lumière directionnelle caractérise sa direction, sa couleur, ainsi que quelques coefficients dont nous aurons besoin ci-dessous :

structDirectionalLight(
sens vec3 ;

couleur vec3 ;
float ambientIntensity ;
float diffuseIntensity ;
float specularIntensity ;
} ;

Dans le code fragment shader, nous définissons la procédure calcDirectionalLight, qui sera utilisée quelque chose comme ceci :

dans vec3 fragmentPos ;
caméraPos vec3 uniforme ;
uniformDirectionalLight directionnelleLight ;

// ...

void main() (
// normal doit être corrigé après interpolation
vec3 normal = normalize(fragmentNormal) ;


lumière directionnelle) ;

// ...
}

Considérez la mise en œuvre de la procédure.

vec4 calcDirectionalLight(vec3 normal, vec3 fragmentToCamera,
lumière directionnelle) (
vec4 ambientColor = vec4 (light. color , 1 ) * light. ambientIntensity ;

// ...
}

Tout d'abord, la première composante, l'éclairage de fond, est calculée. Il s'agit simplement de la couleur de la lumière émise multipliée par l'intensité de la lumière de fond. Jusqu'ici, tout est simple.

// ...

float diffuseFactor = max (0.0 , dot (normal, - light. direction ) ) ;
vec4 diffuseColor = vec4 (light. color , 1 ) * light. diffuseIntensity
* facteur diffus ;

// ...

Eclairage diffus. La variable diffuseFactor est le cosinus de l'angle entre la normale au fragment et le vecteur dirigé du fragment vers la source lumineuse. Si la lumière est incidente perpendiculairement à la surface, l'angle est nul. Le cosinus de cet angle est égal à un et l'éclairement est maximal (voir article Wikipédia sur la loi de Lambert). Lorsque l'angle augmente, le cosinus diminue et devient égal à zéro si la lumière se déplace parallèlement à la surface. Si le cosinus est négatif, alors la source de lumière se trouve quelque part derrière la surface et elle n'est pas éclairée, nous transformons donc les valeurs négatives en zéro avec max(0.0, ...) . En plus de l'angle sous lequel la lumière tombe, diffuseIntensity est également pris en compte.

// ...
vec3 lightReflect = normalize (reflect (light. direction , normal) ) ;
flottant Facteur spéculaire = pow(
max (0.0 , dot (fragmentToCamera, lightReflect) ) ,
matériauSpéculaireFacteur
) ;
vec4 specularColor = lumière. specularIntensity * vec4 (light.color , 1 )
* materialSpecularIntensity * specularFactor;
// ...

Eclairage indirect. La variable lightReflect est un vecteur unitaire qui spécifie la direction de la lumière réfléchie. La variable specularFactor est calculée de la même manière que diffuseFactor, sauf que cette fois elle prend en compte le cosinus de l'angle entre la direction dans laquelle la lumière a été réfléchie et la direction du fragment vers la caméra. Si cet angle est nul, la lumière réfléchie vole directement dans la caméra et l'éblouissement sur la surface est maximal. Si l'angle est grand, aucun éblouissement ne doit être visible. Ici, materialSpecularFactor est une variable uniforme. Plus il est grand, plus la zone d'éblouissement à la surface de l'objet est petite. La variable materialSpecularIntensity est également utilisée, qui détermine la luminosité des hautes lumières. Notez que ce sont toutes des propriétés matérielles, pas des propriétés lumineuses. Par exemple, le métal réfléchit la lumière et est donc éblouissant. Et l'arbre ne réfléchit pas la lumière, et vous ne voyez jamais d'éblouissement sur les arbres (bien sûr, si la surface est sèche, etc.).

Dans le code ci-dessus, la lumière a une propriété specularIntensity. Mais il ne doit être utilisé qu'à des fins de débogage, pour accentuer les reflets d'une source de lumière particulière. Dans la version finale du code, ce coefficient doit soit être égal à un, soit être complètement éliminé du code.

Enfin, les trois composants sont ajoutés et le résultat est renvoyé :

// ...

return ambientColor + diffuseColor + specularColor;
}

Pas si difficile, non ?

Source lumineuse ponctuelle

Une source ponctuelle de lumière est, par exemple, une ampoule allumée. La lumière de l'ampoule est dirigée dans toutes les directions. Par conséquent, une source lumineuse ponctuelle n'est pas caractérisée par la direction de la lumière, mais est caractérisée par la position de la source dans l'espace :

structPointLumière(
position vec3 ;

couleur vec3 ;
float ambientIntensity ;
float diffuseIntensity ;
float specularIntensity ; // à des fins de débogage, doit être défini sur 1.0
} ;

L'éclairage d'une source lumineuse ponctuelle est facilement calculé à l'aide de la procédure calcDirectionalLight déjà existante :

vec4 calcPointLight(vec3 normal, vec3 fragmentToCamera,
lumière PointLight) (
vec3 lightDirection = normalize (fragmentPos - light. position ) ;
distance flottante = longueur (fragmentPos - light. position ) ;
float pointFactor = 1.0 / (1.0 + pow (distance , 2 ) ) ;

Lumière directionnelle tempLumière directionnelle = Lumière directionnelle(
directionlumière,
lumière. couleur ,
lumière. Intensité ambiante,
lumière. diffuseIntensity ,
lumière. specularIntensity
) ;
return pointFactor * calcDirectionalLight(normal, fragmentToCamera,
tempDirectionalLight) ;
}

Ayant les coordonnées d'un fragment et d'une source lumineuse, on peut facilement calculer la direction de la lumière vers un fragment donné grâce à la différence des vecteurs. Le facteur pointFactor reflète le fait que la lumière est atténuée avec le carré de la distance à sa source (conformément à la formule de dépendance de la surface d'une sphère sur le rayon). Lors du calcul du pointFactor dans le diviseur, un supplémentaire est ajouté pour empêcher la possibilité de diviser par zéro. Après cela, tout est calculé de la même manière que pour la lumière directionnelle.

Projecteur (projecteur)

Un exemple de cette source de lumière est une lampe de poche. Il est similaire à une source lumineuse ponctuelle, sauf qu'il a en plus une direction et un angle d'influence (coupure):

structure SpotLight (
sens vec3 ;
position vec3 ;
coupure flottante ;

couleur vec3 ;
float ambientIntensity ;
float diffuseIntensity ;
float specularIntensity ; // à des fins de débogage, doit être défini sur 1.0
} ;

Procédure pertinente :

vec4 calcSpotLight(vec3 normal, vec3 fragmentToCamera,
Projecteur) (
vec3 spotLightDirection = normalize (fragmentPos - light. position ) ;
float spotAngleCos = dot (spotLightDirection, light. direction ) ;
atténuation flottante = (1.0 - 1.0 * (1.0 - spotAngleCos) /
(1.0 - lumière. coupure) );
float spotFactor = float(spotAngleCos > light. cutoff ) * atténuation ;

PointLumière tempPointLumière = PointLumière(
lumière. position ,
lumière. couleur ,
lumière. Intensité ambiante,
lumière. diffuseIntensity ,
lumière. ambientIntensity
) ;
return spotFactor * calcPointLight(normal, fragmentToCamera,
tempPointLight);
}

La direction de la lumière est calculée exactement de la même manière que pour une source ponctuelle. Le cosinus de l'angle entre cette direction et la direction spécifiée dans les propriétés de la lumière elle-même est ensuite calculé. A l'aide de l'expression float(spotAngleCos > light.cutoff) la lumière est fortement écrêtée à l'angle spécifié. Le multiplicateur d'atténuation ajoute lisse l'atténuation de la lumière lorsque les fragments s'éloignent de la direction de la lumière spécifiée dans les propriétés de la source. Après cela, tous les calculs sont réduits à des calculs pour une source lumineuse ponctuelle.

Correction gamma

L'ensemble de la procédure principale dans le fragment shader ressemble à ceci :

void main() (
// normal doit être corrigé après interpolation
vec3 normal = normalize(fragmentNormal) ;
vec3 fragmentToCamera = normalize(cameraPos - fragmentPos) ;

vec4 directColor = calcDirectionalLight(normal, fragmentToCamera,
lumière directionnelle) ;
vec4 pointColor = calcPointLight(normal, fragmentToCamera,
pointLumière);
vec4 spotColor = calcSpotLight(normal, fragmentToCamera, spotLight) ;
vec4 linearColor = texture(textureSampler, fragmentUV) *
(vec4 (materialEmission, 1 ) + directColor +
pointColor + spotColor) ;

vec4 gamma = vec4 (vec3 (1.0 / 2.2 ) , 1 ) ;
color = pow (linearColor, gamma) ; // couleur corrigée gamma
}

Ne faites pas trop attention aux émissions matérielles. Ce n'est qu'une autre propriété du matériau qui lui ajoute sa propre lueur. De nombreux objets brillent d'eux-mêmes. Prenez les mêmes ampoules qui servent de source de lumière pour d'autres objets. Nous devrions pouvoir les voir dans l'obscurité totale, même si les ampoules ne sont pas éclairées par une autre source de lumière, n'est-ce pas ?

Ce qui mérite vraiment l'attention, c'est la correction gamma, qui consiste à élever toutes les composantes lumineuses à la puissance 1/2,2. Jusqu'à présent, nous avons travaillé dans un espace colorimétrique linéaire basé sur l'hypothèse qu'une couleur avec une luminosité de 1,0 est deux fois plus lumineuse qu'une couleur avec une luminosité de 0,5. Le problème est que l'œil humain perçoit la luminosité de manière non linéaire. Par conséquent, pour obtenir un éclairage réaliste, il est nécessaire d'effectuer une correction gamma après tous les calculs dans l'espace linéaire.

Gardez à l'esprit que lors de l'enregistrement d'une image, les éditeurs graphiques modernes effectuent également une correction gamma. Par conséquent, avant d'utiliser des textures, cette correction gamma doit être annulée. Heureusement, ce n'est pas difficile.

Il suffit de remplacer toutes les constantes dans le code de chargement de texture :

GL_COMPRESSED_RGBA_S3TC_DXT1_EXT
GL_COMPRESSED_RGBA_S3TC_DXT3_EXT
GL_COMPRESSED_RGBA_S3TC_DXT5_EXT

GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT
GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT
GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT

… respectivement. Cela indiquera qu'une correction gamma a été appliquée à l'image, qui doit être annulée. OpenGL s'occupera du reste.

Dans les applications réelles, le paramètre gamma (nous avons gamma = 2,2) est mieux placé dans les paramètres du programme afin que l'utilisateur, s'il le souhaite, puisse l'ajuster légèrement pour l'adapter à son moniteur.

Conclusion

Il est temps de regarder les images !

Ici, nous voyons les différents composants d'éclairage. De gauche à droite, de haut en bas : fond, diffus, réfléchi, les trois ensemble. Comme vous pouvez le voir, le modèle de tore a été ajouté à la scène. En raison de la disposition complexe des normales, ce modèle est recommandé pour les tests d'éclairage.

Diverses sources lumineuses. De gauche à droite, de haut en bas : lumière blanche directionnelle, lumière ponctuelle rouge, spot bleu, les trois ensemble.

Je note à nouveau qu'une même méthode d'éclairage peut avoir des implémentations différentes. Par exemple, vous pouvez rendre les propriétés du matériau couleur ambiante, diffuse et spéculaire, ce qui vous permettra de dessiner des objets rouges qui diffusent du vert et ont des reflets bleus. Dans certaines implémentations de l'éclairage Phong, j'ai vu l'éclairage de fond être calculé une fois plutôt que pour chaque source lumineuse. J'ai également vu des implémentations où la lumière d'une source ponctuelle n'était pas simplement atténuée proportionnellement au carré de la distance à celle-ci (d * d), mais selon une formule plus générale (dans le style de A + B * d + C * d * d). Quelqu'un fait de l'intensité ambiante et de l'intensité diffuse une propriété non seulement de la source lumineuse, mais aussi du matériau. Je ne suis pas sûr, cependant, à quel point tout cela a à voir avec le réalisme de l'éclairage. Mais pour les devoirs, tu peux jouer avec tout ça.

OpenGL utilise le modèle d'éclairage Phong, selon lequel la couleur d'un point est déterminée par plusieurs facteurs : les propriétés du matériau et de la texture, la valeur de la normale à ce point, et la position de la source lumineuse et de l'observateur. Pour un calcul correct de l'illumination en un point, les normales unitaires doivent être utilisées, cependant, des commandes comme glScale..() peuvent changer la longueur des normales. Pour prendre cela en compte, le mode de normalisation déjà mentionné est utilisé, qui est activé en appelant la commande glEnable(GL_NORMALIZE).

OpenGL permet de définir trois paramètres qui déterminent les règles générales d'application du modèle d'éclairage. Ces paramètres sont passés à la fonction glLightModel() et à certaines de ses modifications. Les commandes suivantes sont utilisées pour définir les paramètres d'éclairage globaux :

void glLightModel(GLenum pname, GLenum param)

void glLightModelv(GLenum pname, const GLtype *params)

L'argument pname détermine quel paramètre de modèle d'éclairage sera configuré et peut prendre les valeurs suivantes :

GL_LIGHT_MODEL_LOCAL_VIEWER - Si le point de vue est local ou distant. OpenGL calcule les réflexions spéculaires en utilisant le "vecteur intermédiaire" h=s+v décrit précédemment. Les vraies directions de s et v sont différentes pour chaque sommet de maillage. Si la source de lumière est directionnelle, alors le vecteur de valeur s est constant, mais v varie toujours d'un sommet à l'autre. La vitesse de rendu augmente si le vecteur v est également rendu constant pour tous les sommets. Par défaut, OpenGL utilise v=(0,0,1), avec le vecteur pointant vers l'axe z positif dans les coordonnées de la caméra. En même temps, vous pouvez forcer le pipeline graphique à calculer la vraie valeur du vecteur v pour chaque sommet en exécutant l'instruction :

GlLightModel(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE);

Le paramètre param doit être booléen et spécifie la position de l'observateur. S'il est FALSE, la direction de la vue est considérée comme parallèle à l'axe z, quelle que soit la position dans les coordonnées de la vue. Si c'est TRUE, alors l'observateur est à l'origine du système de coordonnées de la vue. Cela peut améliorer la qualité de l'éclairage, mais le rend plus difficile à calculer. Valeur par défaut : FAUX. ;

GL_LIGHT_MODEL_TWO_SIDE - si les deux côtés du polygone sont peints correctement. Le paramètre param doit être un booléen et contrôle le mode de calcul de l'éclairement pour les faces avant et arrière. Si c'est FAUX, alors l'éclairement est calculé uniquement pour les faces avant. S'il est égal à TRUE, le calcul est également effectué pour les faces arrière. Valeur par défaut : FAUX. Chaque face polygonale du modèle a deux côtés. Lors de la modélisation, vous pouvez considérer l'intérieur et l'extérieur. Il est habituel de lister ces sommets dans le sens antihoraire lorsqu'ils sont vus de l'extérieur de l'objet. La plupart des objets filaires sont des corps solides délimitant un espace, de sorte que les concepts d'extérieur et d'intérieur sont bien définis. Pour de tels objets, la caméra ne peut observer que la surface extérieure de chaque face (à moins, bien sûr, que la caméra ne soit à l'intérieur de l'objet). Avec la suppression correcte des surfaces invisibles, la surface interne de chaque face est cachée à l'œil par une face plus proche.

OpenGL n'a pas de concept de "intérieur" et "dehors", il ne peut distinguer que les "faces avant" et les "faces non avant". Une face est une face avant si ses sommets sont répertoriés dans le sens antihoraire, dans l'ordre dans lequel l'œil les voit. Vous pouvez inverser cet ordre en utilisant la fonction glFrontFace(GL_CW), qui spécifie qu'une face est avant uniquement si ses sommets sont listés dans le sens des aiguilles d'une montre. Pour un objet qui délimite un espace, tous les bords que l'œil voit sont faciaux, et OpenGL les dessine et les remplit correctement. Les faces non tournées sont également dessinées dans OpenGL, mais elles sont finalement cachées derrière les faces tournées les plus proches. Vous pouvez accélérer le processeur en désactivant le rendu OpenGL des faces non tournées. Cela utilise le code suivant :

glCullFace(GL_BACK);

glActive(GL_CULL_FACE);.

La situation est différente dans Erreur : Source de référence introuvable, b, qui affiche une boîte avec un bord supprimé. Comme précédemment, les flèches indiquent l'ordre dans lequel les sommets de chaque face sont envoyés au pipeline graphique. Trois des faces visibles ne se font pas face. Par défaut, OpenGL ne peut pas peindre correctement les faces. Pour peindre correctement des faces non tournées, OpenGL doit être instruit à l'aide de l'opérateur glLightModel(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE). Lorsque cette commande est exécutée, OpenGL modifie la direction des normales de toutes les faces non faciales afin qu'elles pointent vers le spectateur, après quoi l'ombrage est effectué correctement. Le remplacement de GL_TRUE par GL_FALSE désactive cette option. Les visages dessinés avec OpenGL ne projettent aucune ombre, de sorte que tous les visages sans visage reçoivent la même lumière de la source, même s'il y a un autre visage entre eux et la source de lumière ;

GL_LIGHT_MODEL_AMBIENT - couleur globale de la lumière d'arrière-plan. Le paramètre params doit contenir quatre nombres entiers ou réels qui définissent la couleur de la lumière de fond même en l'absence de lumières spécifiques. Pour une scène donnée, vous pouvez définir une lumière d'arrière-plan globale indépendante de toute source spécifique. Pour créer un tel éclairage, définissez sa couleur à l'aide des commandes suivantes :

GLfloat amb = (0,2, 0,3, 0,1, 1,0);

glLightModelfv(GL_LIGHT_MODEL_AMBIENT, amb);

Ce code donne une couleur au rétroéclairage (0.2, 0.3, 0.1). La valeur par défaut est (0.2, 0.2, 0.2, 0.1), donc la lumière de fond est toujours présente à moins que vous ne la changiez intentionnellement. Définir la source d'arrière-plan sur une valeur différente de zéro rend les objets de la scène visibles même si vous n'avez activé aucune fonction d'éclairage ;

GL_LIGHT_MODEL_COLOR_CONTROL séparation de la composante de couleur spéculaire. Pour les calculs d'éclairage conventionnels, les composants de fond, diffus, spéculaire et émissif sont calculés et simplement additionnés. Par défaut, le placage de texture est appliqué après l'éclairage, de sorte que les reflets spéculaires peuvent sembler atténués ou que la texture peut sembler différente. Le prochain appel à glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL,GL_SEPARATE_SPECULAR_COLOR) OpenGL sépare le calcul de la couleur spéculaire de l'application. L'éclairage génère alors deux couleurs pour chaque sommet : une couleur initiale constituée des composantes de luminance non réfléchies, et une seconde couleur qui est la somme des composantes de luminance spéculaire. Lors du rendu des textures, seule la première couleur est combinée avec les couleurs de la texture. Une fois l'opération de texturation terminée, la deuxième couleur est ajoutée à la combinaison finale des premières composantes de couleur et de texture. Les objets éclairés et texturés avec une séparation des couleurs spéculaires sont généralement plus visibles et ont des reflets spéculaires plus visibles. Pour revenir aux paramètres par défaut, vous devez appeler glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL,GL_SINGLE_COLOR). Après cela, encore une fois, la couleur d'origine sera composée de toutes les composantes de couleur : diffuse, diffuse, d'émission et spéculaire. Les composants d'éclairage ne sont pas ajoutés après la texturation.

Partie 1. Préparation

Pour étudier l'éclairage, vous avez besoin de:

  • OpenGL;
  • surabondance;
  • EDI, même si vous pouvez utiliser gedit ou blocs de code;
  • compilateur, par exemple, gcc pour Linux et minw Pour les fenêtres;
Partie 2. Exemple de programme simple

Prenons un exemple de programme qui utilise l'éclairage.

Code:

/*http://site, isaer*/ #include #inclure #inclure void init() ( glClearColor(0.3, 0.3, 0.3, 1.0); glEnable(GL_LIGHTING); glLightModelf(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE); glEnable(GL_NORMALIZE); ) void remodeler(int largeur, int hauteur) ( glViewport(0, 0, largeur, hauteur); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(-1.2, 1.2, -1.2, 1.2, -1, 1); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); ) void init_l() ( float light0_diffuse = (0.4, 0.7, 0.2); float light0_direction = (0.0, 0.0, 1.0, 0.0); glEnable(GL_LIGHT0); glLightfv(GL_LIGHT0, GL_DIFFUSE, light0_diffuse); glLightfv(GL_LIGHT0, GL_POSITION, light0_direction); ) void display() ( glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); init_l(); GLfloat x, y; glBegin(GL_QUADS); glNormal3f(0.0, 0.0, -1.0); for (x = -1.0; x< 1.0; x += 0.005) { for (y = -1.0; y < 1.0; y += 0.005) { glVertex3f(x, y, 0.0); glVertex3f(x, y + 0.005, 0.0); glVertex3f(x + 0.005, y + 0.005, 0.0); glVertex3f(x + 0.005, y, 0.0); } } glEnd(); glDisable(GL_LIGHT0); glutSwapBuffers(); } int main(int argc, char** argv) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); glutInitWindowPosition(50, 100); glutInitWindowSize(500, 500); glutCreateWindow("Light"); init(); glutDisplayFunc(display); glutReshapeFunc(reshape); glutMainLoop(); }

Partie 3. Analyser le code dans l'exemple

Les commentaires sont donnés par le symbole // - "slasher".

Code:


L'éclairage est initialisé ici

Code:


C'est là que tout le dessin a lieu.

Code:


Partie 4. Étudier l'éclairage

Tout d'abord, activez le calcul d'éclairage avec la commande glActive(GL_LIGHTING). Ensuite, vous devez déverrouiller la source de lumière avec la commande glActiver(GL_LIGHT). GL_LIGHT ne peut prendre que 8 valeurs (au moins dans OpenGL 2.1), c'est-à-dire GL_LIGHT0..GL_LIGHT7.

Maintenant, nous devons créer une source de lumière. Chaque lumière a ses propres paramètres par défaut, par exemple, si vous déverrouillez simplement 2 lumières GL_LIGHT0 et GL_LIGHT1, alors seulement 0 sera visible, car elle a des paramètres par défaut différents du reste (tous les autres ont les mêmes).

Les sources lumineuses ont plusieurs paramètres, à savoir : la couleur, la position et la direction.

La commande utilisée pour spécifier tous les paramètres de lumière est glLumière*(). Il prend trois arguments : l'ID de la lumière, le nom de la propriété et la valeur souhaitée pour celle-ci.

Code:


Sinon, c'est la seule valeur.

Par exemple:

Code:

/*http://site, isaer*/ glLightf(GL_LIGHT0, GL_GL_SPOT_CUTOFF, 180);

Voici une liste des valeurs Nom Glénum.

Il se lit comme suit : la première ligne est le nom du paramètre, la seconde est la valeur par défaut et la troisième est une explication. Si vous voyez quelque chose comme (1.0,1.0,1.0,1.0) ou (0.0,0.0,0.0,1.0), cela signifie que la première parenthèse est la valeur par défaut pour la source nulle, et la deuxième parenthèse est la valeur par défaut pour le repos:

Code:


Exemple d'éclairage :

Code:

/*http://site, isaer*/ float light_ambient = (0.0,0.0,0.0,1.0); float light_diffuse = (1.0,1.0,1.0,1.0); float light_specular = (1.0,1.0,1.0,1.0); float light_position = (1.0,1.0,1.0,0.0); glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient); glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse); glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular); glLightfv(GL_LIGHT0, GL_POSITION, light_position);

Partie 5. Nous étudions les paramètres de la lumière

1. Couleur

diffuser

Le paramètre GL_DIFFUSE est probablement celui qui correspond le mieux à ce que vous avez l'habitude d'appeler "couleur claire". Il définit la couleur RGBA de la lumière diffuse qu'une seule source de lumière ajoute à la scène.

Ambiant

Le paramètre GL_AMBIENT affecte la couleur de la surbrillance spéculaire sur l'objet. Dans le monde réel, des objets comme une bouteille en verre ont un reflet spéculaire qui correspond à la couleur de la lumière (souvent blanche).

Spéculaire

Le paramètre GL_SPECULAR affecte l'intensité de la surbrillance spéculaire sur les objets.

2. Poste

Paramètre GL_POSITION(x, y, z, w) a 3 valeurs de position et une indiquant quelle source de lumière sera utilisée.

Les 3 premiers paramètres (x, y, z) sont explicites et le 4ème paramètre (w) spécifie s'il faut utiliser l'infini ou la lumière ponctuelle. Si la valeur w = 0, alors la source de lumière est infiniment éloignée (quelque chose comme le soleil). Si w = 1, alors cette source de lumière est un point (quelque chose comme une ampoule).

Si w = 0, alors les 3 premiers paramètres sont un vecteur du centre du système de coordonnées (0,0,0).

3. Projecteur

GL_SPOT_DIRECTION- la direction du projecteur.

GL_SPOT_EXPONENT est la concentration du faisceau lumineux.

GL_SPOT_CUTOFF est la largeur angulaire du faisceau lumineux.

La seule chose qui doit être clarifiée est que la position doit être w = 1.

4. Affaiblissement

Si vous devez atténuer l'intensité de la lumière à partir du centre (c'est-à-dire plus loin du centre, le gradateur), vous devez alors ajuster les paramètres : GL_CONSTANT_ATTENUATION, GL_LINEAR_ATTENUATION, GL_QUADRATIC_ATTENUATION.

Mais ce n'est pas très pratique, vous pouvez donc utiliser la formule :

Code:


Le rayon est défini du centre à la fin.

Si vous avez besoin de réduire l'intensité globale, vous pouvez modifier le paramètre att.

Partie 6. Choisir un modèle d'éclairage

Le concept OpenGL d'un modèle d'éclairage est divisé en 4 composants :

  • intensité globale de la lumière de fond ;
  • si la position du point de vue est considérée comme locale à la scène ou infiniment distante ;
  • si le calcul de l'éclairement doit être fait différemment pour les faces avant et arrière des objets ;
  • si la couleur spéculaire doit être séparée du fond et diffuse et superposée à l'objet après les opérations de texturation.
glModèleLumière*() est une commande utilisée pour définir tous les paramètres du modèle d'éclairage. Il peut prendre deux arguments : le nom du paramètre de modèle d'éclairage en tant que constante et une valeur pour ce paramètre.

Code:


Vous pouvez maintenant créer de superbes sources de lumière.

Travaux d'années. Volochine Maximilien. LA VALEUR DU POÈTE. 1. Éditez le poème comme le texte d'une dépêche outre-mer : Sécheresse, clarté, pression - chaque mot est en alerte.

Lettre après lettre à tailler sur une pierre dure et exiguë : Plus les mots sont avares, plus leur force est intense. La charge volontaire de la pensée est égale aux strophes silencieuses.

Effacer les mots "Beauté", "Inspiration" du dictionnaire - Jargon moyen des rimes Au poète - compréhension : Vérité, construction, plan, équivalence, concision et exactitude. Dans un métier sobre et tendu - l'inspiration et l'honneur du poète : Dans une substance sourde-muette, aiguisez la vigilance transcendantale. Volochine M.A. Bibliothèque: Bibliothèque Publique Universelle Scientifique Régionale d'Orel. I.A. Bounine. - M.,; Oeuvres choisies : En 2 volumes.

M., ; Fumée rouge : Contes. - M.,; Gladyshev de reconnaissance: Contes. - M.,; Échelon; Inévitabilité : Romans. Il a fait beaucoup de traductions de poètes mari et oudmourte. De temps en temps, il s'essaya aussi à la prose. Op. Maximilian Aleksandrovich Voloshin () est l'un des plus grands poètes du premier tiers du XXe siècle. C'est un artiste talentueux, un parolier aux multiples facettes, qui est passé des poèmes symbolistes et ésotériques à la poésie civile-journalistique et scientifique-philosophique, en passant par les prédilections anthroposophiques - à «l'idéal de la Cité de Dieu».

L'édition proposée permet au lecteur de se familiariser non seulement avec les meilleures œuvres poétiques de Volochine, mais également avec ses œuvres les plus intéressantes sur l'esthétique, la prose des mémoires, le journalisme et les lettres liées aux événements dramatiques de la vie des pays. Auteur. Volochine Maximilien. Tous les poèmes de l'auteur. Travail. La prouesse d'un poète. 2. Étoiles. Créez des collections sélectionnées d'auteurs et de poèmes!

Discutez avec des personnes partageant les mêmes idées ! Rédigez des avis, participez à des duels poétiques et à des concours ! Rejoignez les meilleurs ! Merci d'avoir rejoint Poembuk ! Un e-mail avec les données d'accès au compte a été envoyé à votre adresse e-mail !

Vous devez vous connecter dans les 24 heures. Sinon, le compte sera supprimé ! Les utilisateurs enregistrés bénéficient de nombreux avantages : Publiez de la poésie - réalisez votre talent ! Créez des collections sélectionnées d'auteurs et de poèmes! Discutez avec des personnes partageant les mêmes idées ! Rédigez des critiques, participez à des duels poétiques et à des concours !. Maximilien Volochine. Description. Maximilien Aleksandrovitch Volochine est l'un des plus grands poètes du premier tiers du XXe siècle.

C'est un artiste talentueux, un parolier aux multiples facettes, qui est passé des poèmes symbolistes et ésotériques à la poésie civile-journalistique et scientifique-philosophique, en passant par les prédilections anthroposophiques - à "l'idéal de la Cité de Dieu". L'édition proposée permet au lecteur de se familiariser non seulement avec les meilleures œuvres poétiques de Volochine, mais également avec ses œuvres les plus intéressantes sur l'esthétique, la prose des mémoires, le journalisme et les lettres liées au théâtre.

Oeuvres et lettres choisies. M. A. Volochine. Prix. frotter. Maximilien Aleksandrovitch Volochine est l'un des plus grands poètes du premier tiers du XXe siècle. C'est un artiste talentueux, un parolier aux multiples facettes, qui est passé des poèmes symbolistes et ésotériques à la poésie civile-journalistique et scientifique-philosophique, en passant par les prédilections anthroposophiques - à "l'idéal de la Cité de Dieu".

Voloshin M.A., Valor of the poet: Selected works and letters. série : New Library of Russian Classics : copie obligatoire Parade, g., p., Description du livre. Maximilian Aleksandrovich Voloshin () est l'un des plus grands poètes du premier tiers du XXe siècle. C'est un artiste talentueux, un parolier aux multiples facettes, qui est passé des poèmes symbolistes et ésotériques à la poésie civile-journalistique et scientifique-philosophique, en passant par les prédilections anthroposophiques - à «l'idéal de la Cité de Dieu».

Catégories Navigation des articles

Dans cette leçon, nous allons apprendre à éclaircir et ombrager nos modèles 3D. Voici une liste de ce que nous allons apprendre :

  • Comment rendre un objet plus lumineux lorsqu'il est proche d'une source de lumière.
  • Comment faire des réflexions lorsque nous voyons de la lumière réfléchie sur un objet (éclairage spéculaire)
  • Comment faire apparaître un objet un peu sombre lorsque la lumière n'est pas directement sur l'objet (éclairage diffus)
  • Éclairage de scène (éclairage ambiant)
  • Ombres. Ce sujet mérite une leçon séparée (ou des leçons, sinon même des livres).
  • Miroir réflexion (ex. eau)
  • sous la surfacediffusion (par exemple, comme la cire)
  • Anisotrope matériaux (peint métal, par exemple)
  • Ombrage basé sur des processus physiques pour imiter encore mieux la réalité.
  • Blocage de la lumière (Occlusion ambiante si quelque chose bloque la lumière, elle devient plus sombre)
  • Réflexion de couleur (un tapis rouge fera apparaître un plafond blanc légèrement rougeâtre)
  • Transparence
  • Illumination globale (en principe, tout ce que nous avons indiqué ci-dessus peut être appelé ce terme)

En d'autres termes, l'éclairage et l'ombrage les plus simples.

Normales

Dans la dernière leçon, nous avons travaillé avec des normales, mais sans trop comprendre pourquoi elles sont nécessaires.

Normales du triangle

La normale à un plan est un vecteur unitaire perpendiculaire à ce plan.

La normale à un triangle est un vecteur unitaire dirigé perpendiculairement au triangle. La normale est très simplement calculée à partir du produit croisé des deux côtés du triangle (si vous vous en souvenez, le produit croisé de deux vecteurs nous donne un vecteur perpendiculaire aux deux) et normalisée : sa longueur est fixée à un.

Voici le pseudocode de calcul normal :

triangle(v1, v2, v3)
côté1=v2-v1
côté2=v3-v1
triangle.normal = vectProduct(côté1, côté2).normalize()

Normale au sommet

C'est la normale introduite pour la commodité des calculs. Il s'agit de la normale combinée des normales des triangles entourant le sommet donné. C'est très pratique, car dans les vertex shaders, nous avons affaire à des sommets, pas à des triangles. En tout cas, dans En OpenGL, nous n'avons presque jamais affaire à des triangles.

haut v1, v2, v3, ....
triangle tr1, tr2, tr3 // ils utilisent tous le sommet v1
v1.normal = normaliser(tr1.normal + tr2.normal + tr3.normal)

Utilisation des normales aux sommets dans OpenGL

L'utilisation des normales dans OpenGL est très simple. Une normale n'est qu'un attribut de sommet, tout comme la position, la couleur ou les coordonnées UV... Il n'y a donc rien de nouveau à apprendre... même notre simple fonction loadOBJ charge déjà les normales.

GLint tampon normal ;
glGenBuffers(1, &normalbuffer);

glBufferData(GL_ARRAY_BUFFER, normals.size() * sizeof(glm::vec3), &normals, GL_STATIC_DRAW);

// Troisième tampon d'attribut : normales
glEnableVertexAttribArray(2);

glBindBuffer(GL_ARRAY_BUFFER, normalbuffer);
glVertexAttribPointer(
2, // attribut
3, // taille
GL_FLOAT, //taper
GL_FALSE, // normalisé ?
0, // étape
(vide*)0 // décalage du tampon
);

Et c'est suffisant pour commencer :


éclairage diffus

L'importance de la normale de surface

Lorsqu'un faisceau lumineux frappe une surface, la plus grande partie est réfléchie dans toutes les directions. C'est ce qu'on appelle la "composante diffuse". Nous verrons le reste des composants un peu plus tard.

Après la chute du faisceau, la surface réfléchit la lumière de différentes manières, en fonction de l'angle sous lequel ce faisceau tombe sur la surface. Si le faisceau tombe perpendiculairement à la surface, alors il est concentré sur une petite surface, s'il est tangentiel, alors il est dispersé sur une surface beaucoup plus grande :


Du point de vue de l'infographie, la couleur d'un pixel est très dépendante de la différence entre les angles de la direction de la lumière et la normale à la surface.


//
//
float cosTheta = point(n,l);

Dans ce code, "n" est la normale et "l" est le vecteur unitaire qui va de la surface à la lumière (et non l'inverse, bien que cela puisse sembler déroutant)

Attention au signe

Parfois, notre formule ne fonctionnera pas. Par exemple, lorsque la lumière est derrière le triangle, n et l seront opposés, donc n.l sera négatif. Et à la fin, nous aurons une sorte de couleur négative, et par conséquent, une sorte de non-sens. Par conséquent, nous convertirons tous les nombres négatifs en 0 en utilisant la fonction de serrage.

// Cosinus de l'angle entre la direction normale et la direction de la lumière
// 1 - si la lumière est perpendiculaire au triangle
// 0 - si la lumière est parallèle au triangle
// 0 - si la lumière est derrière le triangle
float cosTheta = clamp(dot(n, l), 0.1);
couleur = LightColor * cosTheta ;

Couleur du matériau

Bien sûr, la couleur de l'objet doit dépendre beaucoup de la couleur du matériau. La lumière blanche a trois composants - rouge, bleu et vert. Lorsque la lumière frappe une surface rouge, les composantes verte et bleue sont absorbées et le rouge est réfléchi.



Nous pouvons modéliser cela avec une simple multiplication :

couleur = MaterialDiffuseColor * LightColor * cosTheta ;

modelage léger

Supposons que nous ayons une source lumineuse ponctuelle qui émet de la lumière dans toutes les directions, comme une bougie.

Avec une telle source lumineuse, le niveau d'éclairement de la surface dépendra de la distance à la source lumineuse : plus elle est éloignée, plus elle est sombre. Cette dépendance se calcule ainsi :

couleur = MaterialDiffuseColor * LightColor * cosTheta / (distance*distance);

Bientôt, nous aurons besoin d'un paramètre supplémentaire pour contrôler le niveau d'intensité lumineuse - la couleur de la lumière, mais pour l'instant, supposons que nous ayons une ampoule blanche d'une certaine puissance (par exemple, 60 watts).

couleur = MaterialDiffuseColor * LightColor * LightPower * cosTheta / (distance*distance);

Mettre tous ensemble

Pour que ce code fonctionne, nous avons besoin d'un certain ensemble de paramètres (couleurs et puissance) et d'un code supplémentaire.

MaterialDiffuseColor - nous pouvons le prendre directement à partir de la texture.

LightColor et LightPower devront être définis dans le shader à l'aide d'un uniforme GLSL.

CosTheta dépendra des vecteurs n et l. Il peut être calculé pour n'importe lequel des espaces, l'angle sera le même. Nous utiliserons l'espace de la caméra, car il est très facile de calculer la position de la source lumineuse ici :

// Fragment normal dans l'espace caméra
vec3 n = normaliser(Normal_cameraspace);
// Direction de la lumière (du fragment à la source lumineuse
vec3 l = normaliser(LightDirection_cameraspace);

Normal _cameraspace et LightDirection _cameraspace sont calculés dans le vertex shader et transmis au fragment shader pour un traitement ultérieur :

// Position du vertex dans l'espace caméra : MVP * position
gl_Position= MVP * vec4(vertexPosition_modelspace,1);
// Position du sommet dans l'espace univers : M * position
Position_worldspace = (M * vec4(vertexPosition_modelspace,1)).xyz ;
// Vecteur qui vient du hautcaméra dans l'espace caméra
// Dans l'espace caméra, la caméra est à la position (0,0,0)
vec 3 vertexPosition _ espace caméra = ( V * M * vec 4( vertexPosition _ espace modèle ,1)). xyz ;
EyeDirection_cameraspace = vec3(0,0,0) - vertexPosition_cameraspace ;
// Un vecteur qui va du sommet à la source de lumière dans l'espace de la caméra.
//Matrice M omis, puisqu'il est singulier dans cet espace.
vec3 LightPosition_cameraspace = (V * vec4(LightPosition_worldspace,1)).xyz ;
LightDirection_cameraspace = LightPosition_cameraspace +
EyeDirection_cameraspace ;
// Normal pics V espace appareils photo
Normal_cameraspace = (V * M * vec4(vertexNormal_modelspace,0)).xyz ; // Sera travail seul V volume cas , Quand matrice des modèles Pas changements son taille .

À première vue, le code peut sembler plutôt complexe et déroutant, mais en fait, il n'y a rien de nouveau ici qui n'était pas dans la leçon 3 : Matrices. J'ai essayé de donner des noms significatifs à chaque variable afin qu'il vous soit facile de comprendre ce qui se passe et comment.

Assurez-vous d'essayer !!!

M et V sont les matrices Model et View qui sont transmises au shader, tout comme notre bon vieux MVP.

Temps de test

Je vous ai dit tout ce qu'il faut savoir pour faire un éclairage diffus. Allez-y, essayez-le.

Résultat

Avec un seul composant diffus, nous obtenons une telle image ici (pardonnez-moi pour les textures laides).



Cela semble être mieux qu'avant, mais il manque encore beaucoup de choses. Le problème est particulièrement visible avec les pièces non éclairées. L'arrière de la tête de notre cher singe Suzanne est complètement noir (nous avons utilisé clamp() après tout).

Éclairage ambiant

L'éclairage ambiant est une pure tricherie.

L'arrière de la tête de Suzanne ne doit pas être complètement noir, car dans la vraie vie, la lumière de la lampe doit tomber sur le mur, le sol, le plafond, en refléter partiellement et éclairer la partie ombrée de l'objet.

Cependant, cela est trop coûteux en temps de calcul pour le faire en temps réel. Et c'est pourquoi nous allons ajouter une composante constante. C'est comme si l'objet lui-même émettait de la lumière pour ne pas être complètement noir.

vec3 MaterialAmbientColor = vec3(0.1,0.1,0.1) * MaterialDiffuseColor ;
couleur=
// Ambiant éclairage : simuler indirect éclairage
MatériauAmbientColor +
// diffuser : " couleur " l'objet lui-même
MaterialDiffuseColor * LightColor * LightPower * cosTheta /
(distance*distance);

Résultat

C'est là que ça s'améliore un peu. Vous pouvez jouer avec les coefficients (0,1, 0,1, 0,1) pour essayer d'obtenir le meilleur résultat.



Lumière réfléchie

La partie de la lumière qui est réfléchie est principalement réfléchie vers le faisceau réfléchi vers la surface.



Comme on peut le voir sur la figure, la lumière réfléchie forme une tache lumineuse. Dans certains cas, lorsque la composante diffuse est nulle, cette tache lumineuse est très très étroite (toute la lumière est complètement réfléchie dans une direction) et on obtient un miroir.

(cependant, bien que vous puissiez modifier les paramètres pour obtenir un miroir, dans notre cas, il ne prendra en compte que la réflexion de notre source de lumière. Il se révélera donc être un miroir étrange)


// vecteur de regard (vers la caméra)
vec3 E = normaliser(EyeDirection_cameraspace);
//Direction dans laquelle le triangle réfléchit la lumière
vec 3 R = refléter (- je , n );
// Cosinus de l'angle entre le vecteur de vue et le vecteur de réflexion ajusté à zéro si nécessaire
// - Regardez directement le reflet -> 1
// - Nous regardons quelque part dans l'autre sens -\u003e< 1
float cosAlpha = clamp(dot(E,R), 0.1);
couleur=
// Éclairage ambiant : simulez l'éclairage indirect
MatériauAmbientColor +
// diffuser : " couleur " l'objet lui-même
MaterialDiffuseColor * LightColor * LightPower * cosTheta /
(distance*distance) ;
// Reflected : reflets réfléchis comme un miroir
MaterialSpecularColor * LightColor * LightPower * pow(cosAlpha,5) /

Dans la prochaine leçon, nous analyserons comment nous pouvons accélérer le rendu de notre VBO.