Tôi đang cố gắng thực hiện một thuật toán để tính toán màu trời dựa trên bài báo này (mô hình của Perez). Trước khi tôi bắt đầu lập trình trình đổ bóng, tôi muốn kiểm tra khái niệm trong Mathicala. Đã có một số vấn đề tôi không thể thoát khỏi. Có lẽ ai đó đã thực hiện thuật toán.
Tôi bắt đầu với các phương trình cho độ chói zenital tuyệt đối Yz, xzvà yznhư đề xuất trong bài báo (trang 22). Các giá trị Yzdường như là hợp lý. Sơ đồ sau đây cho thấy Yzlà một hàm của khoảng cách zenital của mặt trời cho độ đục T5:

Hàm gamma (zenith, azimuth, solarzenith, solarazimuth) tính toán góc giữa một điểm với khoảng cách zenital đã cho và góc phương vị và mặt trời tại vị trí đã cho. Chức năng này dường như cũng hoạt động. Sơ đồ sau cho thấy góc này cho solarzenith=0.5và solarazimuth=0. zenithphát triển từ trên xuống (0 đến Pi / 2), azimuthphát triển từ trái sang phải (-Pi đến Pi). Bạn có thể thấy rõ vị trí của mặt trời (điểm sáng, góc trở thành số không):

Hàm Perez (F) và các hệ số đã được thực hiện như được nêu trong bài báo. Thì các giá trị màu Yxy phải là absolute value * F(z, gamma) / F(0, solarzenith). Tôi hy vọng những giá trị đó sẽ nằm trong phạm vi [0,1]. Tuy nhiên, đây không phải là trường hợp của thành phần Y (xem cập nhật bên dưới để biết chi tiết). Dưới đây là một số giá trị mẫu:
{Y, x, y}
{19.1548, 0.25984, 0.270379}
{10.1932, 0.248629, 0.267739]
{20.0393, 0.268119, 0.280024}
Đây là kết quả hiện tại:

Sổ tay Mathicala với tất cả các tính toán có thể được tìm thấy ở đây và phiên bản PDF ở đây .
Có ai có ý tưởng gì về những gì tôi phải thay đổi để có được kết quả tương tự như trong bài báo không?
C như mã
// 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();
}
}
Giải pháp
Như đã hứa, tôi đã viết một bài viết trên blog về việc vẽ lại bầu trời. Bạn có thể tìm thấy nó ở đây .