J'essaie d'implémenter un algorithme pour calculer la couleur du ciel sur la base de cet article (modèle de Perez). Avant de commencer à programmer le shader, je voulais tester le concept dans Mathematica. Il y a déjà des problèmes dont je ne peux pas me débarrasser. Peut-être que quelqu'un a déjà implémenté l'algorithme.
J'ai commencé avec des équations pour les luminances zénitales absolues Yz
, xz
et yz
comme proposé dans l'article (page 22). Les valeurs de Yz
semblent raisonnables. Le diagramme suivant montre Yz
en fonction de la distance zénitale du soleil pour une turbidité T
de 5:
La fonction gamma (zénith, azimut, solarzenith, solarazimuth) calcule l'angle entre un point avec la distance zénitale donnée et l'azimut et le soleil à la position donnée. Cette fonction semble également fonctionner. Le diagramme suivant montre cet angle pour solarzenith=0.5
et solarazimuth=0
. zenith
croît de haut en bas (0 à Pi / 2), azimuth
croît de gauche à droite (-Pi à Pi). Vous pouvez voir clairement la position du soleil (la tache lumineuse, l'angle devient nul):
La fonction de Perez (F) et les coefficients ont été mis en œuvre comme indiqué dans l'article. Ensuite, les valeurs de couleur Yxy devraient être absolute value * F(z, gamma) / F(0, solarzenith)
. Je m'attends à ce que ces valeurs soient dans la plage [0,1]. Cependant, ce n'est pas le cas pour le composant Y (voir la mise à jour ci-dessous pour plus de détails). Voici quelques exemples de valeurs:
{Y, x, y}
{19.1548, 0.25984, 0.270379}
{10.1932, 0.248629, 0.267739]
{20.0393, 0.268119, 0.280024}
Voici le résultat actuel:
Le cahier Mathematica avec tous les calculs peut être trouvé ici et la version PDF ici .
Quelqu'un at-il une idée de ce que je dois changer pour obtenir les mêmes résultats que dans le document?
C comme le code
// this function returns the zenital Y component for
// a given solar zenital distance z and turbidity T
float Yz(float z, float T)
{
return (4.0453 * T - 4.9710)*tan( (4.0f/9-T/120)*(Pi-2*z) ) - 0.2155 * T + 2.4192
}
// returns zenital x component
float xz(float z, float T)
{
return //matrix calculation, see paper
}
// returns zenital y component
float yz(float z, float T)
{
return //matrix calculation, see paper
}
// returns the rgb color of a Yxy color
Color RGB(float Y, float x, float y)
{
Matrix m; //this is a CIE XYZ -> RGB conversion matrix
Vector v;
v.x = x/y*Y;
v.y = Y;
v.z = (1-x-y)/y*Y;
v = M * v; //matrix-vector multiplication;
return Color ( v.x, v.y, v.z );
}
// returns the 5 coefficients (A-E) for the given turbidity T
float[5] CoeffY(float T)
{
float[5] result;
result[0] = 0.1787 * T - 1.4630;
result[1] = -0.3554 * T + 0.4275;
...
return result;
}
//same for Coeffx and Coeffy
// returns the angle between an observed point and the sun
float PerezGamma(float zenith, float azimuth, float solarzenith, float solarazimuth)
{
return acos(sin(solarzenith)*sin(zenith)*cos(azimuth-solarazimuth)+cos(solarzenith)*cos(zenith));
}
// evalutes Perez' function F
// the last parameter is a function
float Perez(float zenith, float gamma, float T, t->float[5] coeffs)
{
return (1+coeffs(T)[0] * exp(coeffs(T)[1]/cos(zenith)) *
(1+coeffs(T)[2] * exp(coeffs(T)[3]*gamma) +
coeffs(T)[4]*pow(cos(gamma),2))
}
// calculates the color for a given point
YxyColor calculateColor(float zenith, float azimuth, float solarzenith, float solarazimuth, float T)
{
YxyColor c;
float gamma = PerezGamma(zenith, azimuth, solarzenith, solarazimuth);
c.Y = Yz(solarzenith, T) * Perez(zenith, gamma, T, CoeffY) / Perez(0, solarzenith, T, CoeffY);
c.x = xz(solarzenith, T) * Perez(zenith, gamma, T, Coeffx) / Perez(0, solarzenith, T, Coeffx);
c.y = yz(solarzenith, T) * Perez(zenith, gamma, T, Coeffy) / Perez(0, solarzenith, T, Coeffy);
return c;
}
// draws an image of the sky
void DrawImage()
{
for(float z from 0 to Pi/2) //zenithal distance
{
for(float a from -Pi to Pi) //azimuth
{
YxyColor c = calculateColor(zenith, azimuth, 1, 0, 5);
Color rgb = RGB(c.Y, c.x, c.y);
setNextColor(rgb);
}
newline();
}
}
Solution
Comme promis, j'ai écrit un article de blog sur le rendu du ciel. Vous pouvez le trouver ici .