レイトレースベンチを移植中
とりあえず知っている範囲の構文を使ってやっつけで書いてみた。
まだ未完成だけど更新。
あたらしい言語なのでわりとデバッグに苦労してしまった。
最大の落とし穴はlists:nthが1originなこと。VBかw
あとは3要素ベクトルの操作をリスト内包表記で書こうとして9要素のベクトルになったりとか
いろいろと嵌った。
地雷はすべて自ら踏む主義なのである意味順調。
とりあえずは、球の法線計算とシーンの球を増やす部分を書き上げていったん完成させる方向で。
それと前回書いたdib32.erlの一部が間違っていたので修正。
BITMAPINFOHEADER::biBitCountを4と書いていたが32と書かないといけない。
ToDoとしては、Erlang的な連想配列のようなものの(Record?))使い方と
内積を簡潔に表記する方法を調べる。
-module(renderer). -export([compatible/3, rendering/4]). -import(dib32). % export %------------------------------------------------------------% % レイトレースベンチ互換の動作 compatible(F, Size, Level)-> Scene=create_scene(Level), dib32:write_file(F, Size, Size, lists:map(fun(PIXEL) -> sampling(PIXEL, Scene, 4, ray_generator(Size, Size, Size)) end, create_screen(Size, Size))). % 指定サイズでレンダリングして保存する rendering(F, W, H, SamplingLevel)-> Scene=create_scene(), dib32:write_file(F, W, H, lists:map(fun(PIXEL) -> sampling(PIXEL, Scene, SamplingLevel, ray_generator(W, H, W)) end, create_screen(W, H))). % サンプリング %------------------------------------------------------------% % 幅と高さを指定してピクセル座標の配列を取得する create_screen(W, H)-> [{X, Y}||X<-generate_list(W), Y<-generate_list(H)]. % 指定ピクセルをサンプリング % ピクセルの中央をサンプリング sampling({PIXEL_X, PIXEL_Y}, Scene, 1, GET_RAY)-> ray_trace(GET_RAY(PIXEL_X+0.5, PIXEL_Y+0.5), Scene); % オーバーサンプリング(N x N倍サンプリングして平均する) sampling({PIXEL_X, PIXEL_Y}, Scene, N, GET_RAY)-> average_color( lists:map(fun({X, Y})->ray_trace(GET_RAY(X, Y), Scene) end, over_sampling({PIXEL_X, PIXEL_Y}, N)) ). % オーバーサンプリング位置を生成 over_sampling({PIXEL_X, PIXEL_Y}, N)-> D=1.0/N, [{X, Y}|| X<-append_times(N, D, [PIXEL_X]), Y<-append_times(N, D, [PIXEL_Y])]. % スクリーン位置XYに対応するレイを作成する関数を返す ray_generator(W, H, Z)-> fun (X, Y)-> [ {orig, [0.0, 0.0, -4.0]}, % 視点 {dir, normalize([X-W/2, H/2-Y, -Z])} % 方向 ] end. % 指定されたピクセルにレイを飛ばしRGB値を得る ray_trace(Ray, Scene)-> %io:write(Ray), io:format("~n"), case intersect(Ray, get_value(Scene, objects)) of % 交差せず false -> [0, 0, 0, 0]; % 交差した Intersection -> shading(Intersection, get_value(Scene, lights)) end. % シーン %------------------------------------------------------------% % シーンを作成する create_scene()-> [ {objects, [ {sphere, [{center, [0, -1.0, 1.0]}, {radius, 1.0}]} % 球 ]}, {lights, [ {directional_light, [dir, normalize([-1.0, -3.0, 2.0])]} % 光源 ]} ]. create_scene(Level)-> create_scene(). % ToDo % 交差判定 %------------------------------------------------------------% % 球の交差判定 intersect(Ray, {sphere, Sphere})-> [SX, SY, SZ]=get_value(Sphere, center), [OX, OY, OZ]=get_value(Ray, orig), % 視点から球の中心へのベクトル Vec=[SX-OX, SY-OY, SZ-OZ], % レイが球に再接近する点と視点の距離 B=dot_product(Vec, get_value(Ray, dir)), % レイが球に再接近する点と球の中心との距離の二乗 R=get_value(Sphere, radius), case B*B-sqnorm(Vec)+R*R of Det when Det<0 -> {false}; % 遠い。交差しない Det -> {hit, [{distance, 10}]} end; % リストの交差判定 intersect(Ray, Objects)-> case [Intersection||{hit, Intersection}<- lists:map(fun(Object)-> intersect(Ray, Object) end, Objects)] of [] -> false; Result -> lists:nth(1, lists:sort(fun(A, B)->get_value(A, distance) < get_value(B, distance) end, Result)) end. % 輝度計算 %------------------------------------------------------------% shading(Intersection, Lights)-> [255, 255, 255, 255]. % ベクトル %------------------------------------------------------------% % ベクトルの正規化 normalize(Vec)-> [N/norm(Vec) || N<-Vec]. % ベクトルの大きさ norm(Vec)-> math:sqrt(sqnorm(Vec)). % 大きさの二乗 sqnorm(Vec)-> lists:sum([N*N || N<-Vec]). % 内積 dot_product([LX, LY, LZ], [RX, RY, RZ])-> LX*RX+LY*RY+LZ*RZ. % その他 %------------------------------------------------------------% % {atom, Param}のリストからatomの一致するものをゲットする get_value([], _)-> false; get_value([H|T], Key)-> {K, V}=H, if K==Key -> V; true -> get_value(T, Key) end. % 0からN-1のリストを作る generate_list(N)-> generate_list(N-1, []). generate_list(0, L)-> [0|L]; generate_list(N, L)-> generate_list(N-1, [N|L]). % 指定回数Dを加算した配列をつくる append_times(0, _, L)-> L; append_times(N, D, [H|T])-> append_times(N-1, D, [H+D|[H|T]]). % RGBAを積算して平均を得る average_color(L)-> average_color(L, [0, 0, 0, 0], 0). average_color([[R, G, B, A]|T], [SUMR, SUMG, SUMB, SUMA], N)-> average_color(T, [SUMA+R, SUMG+G, SUMB+B, SUMA+A], N+1); average_color([], [SUMR, SUMG, SUMB, SUMA], N)-> [SUMR div N, SUMG div N, SUMB div N, SUMA div N].
15> renderer:compatible("tmp.bmp", 200, 1). ok
なんとか円が表示されるところまでこぎつけた。