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
, xz
và yz
như đề xuất trong bài báo (trang 22). Các giá trị Yz
dường như là hợp lý. Sơ đồ sau đây cho thấy Yz
là một hàm của khoảng cách zenital của mặt trời cho độ đục T
5:
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.5
và solarazimuth=0
. zenith
phát triển từ trên xuống (0 đến Pi / 2), azimuth
phá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 .