本站標題列的底圖其實是傳說中的Julia Set,經過美工軟體處理過的結果。其實產生本blog圖形的程式碼用完就丟了,相關的參數也不太記得,所以我自己也沒辦法產生一模一樣的圖形了。
這裡提供一個非常簡單產生此圖形的方法。執行結果應該類似於下圖 (滑鼠移近放大)
我說「類似」的意思是,就像所有的泡麵包裝一樣,圖片僅供參考,實際上我留了一手,下面的程式碼跑不出這麼細緻的結果,有興趣自己嘗試改改看吧。
#include <cfloat>
#include <complex>
#include <windows.h>
using namespace std;
const complex<double> c(-0.777, 0.123);
const double rMax = 1.6, rMin = -1.6;
const double iMax = 1.2, iMin = -1.2;
const int graphWidth = 800;
const int graphHeight = 600;
int EscapeTime(complex<double> z, int iterMax, double thresh)
{
for (int i = 0; i < iterMax; ++i) {
z = z*z + c;
if (norm(z) > thresh) return i;
}
return iterMax;
}
void DrawJulia(HDC hdc, int width, int height)
{
const double rStep = (rMax-rMin) / width;
const double iStep = (iMin-iMax) / height;
double r = rMin, i = iMax;
for (int y = 0; y < height; ++y, i += iStep) {
r = rMin;
for (int x = 0; x < width; ++x, r += rStep) {
complex<double> z(r, i);
int depth = EscapeTime(z, 255, DBL_MAX);
SetPixel(hdc, x, y, RGB(depth/2, depth, depth));
}
}
}
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrev, char* cmd, int mode)
{
HDC hdc = GetDC(0);
DrawJulia(hdc, graphWidth, graphHeight);
Sleep(3000);
ReleaseDC(0, hdc);
}
理論上只要隨便寫個擁有HDC的視窗就可以套用上面的DrawJulia函數,當然最懶人的方法就是直接抓desktop的DC。此外將SetPixel(...)這行改為直接寫入圖檔應該也不會很難。
為了節省篇幅,上面直接套用std::complex示意。不過就實作上的觀點來說,這樣的效率不太好,如果仔細觀察norm函數的所作所為,就會發現其中部分計算和下一次迴圈中的z*z有所重複,等於多做了不必要的浮點數乘法。而用norm(z) > thresh來判斷是否發散雖然簡單,但稱不上有效率。另外逐點用SetPixel也會因頻繁的調用GDI而使效能降低,一個簡單的方法是先寫入記憶體,一次update整條橫列或整個區塊。我剛開始學程式的時候還在用486的電腦,那時候隨便跑個Mandelbrot Set都動輒幾分鐘,這些細節會造成明顯感覺得到的時間差。
不過這些基本的加速都還比不上利用演算法猜測近似值,以演算法搭配現在的硬體,一流的碎形軟體甚至能在全螢幕達到瞬間render。
除了演算速度外,另一個非常重要的地方就是render的方式,整個圖形的美觀與否全看render得好不好。這裡使用的RGB(depth/2, depth, depth),有興趣可以行修改,更好的做法是事先算好顏色陣列,直接查表即可。除此之外還必須搭配平滑演算法,才不會得到破碎的邊界,這點我比較沒有涉獵。
最後,有興趣的人可以嘗試改改看
const complex c(-0.777, 0.122);
例如(-0.727, 0.189)和(0.285, 0.01)都還蠻好看的。
(0.285, 0.013)
(-0.501, -0.523)
(-0.8, 0.2)
留言列表