%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2000-2009. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
%%
%% %CopyrightEnd%
%%
-module(itracer).
-export([itracer/0]).
%%%---------------------------------------------------------------------------
%%%
%%% This is a little raytracer.
%%%
%%%---------------------------------------------------------------------------
%%----------------------------------------------------------------------------
%% Constructors.
%%----------------------------------------------------------------------------
%%----------------------------------------------------------------------------
%%
%%----------------------------------------------------------------------------
itracer() ->
C1 = ccreate(),
C2 = set_width(C1,100),
C3 = set_height(C2,100),
C4 = initialize(C3),
Sphere1 = screate(40,vcreate(35,10,0),{1,0,0}),
Sphere2 = screate(35,vcreate(-25,-25,50),{0,1,0}),
PL = traceloop(C4,50,50,[Sphere1,Sphere2]).
%%----------------------------------------------------------------------------
%%
%%----------------------------------------------------------------------------
overflow_prevent(A) when A<1 -> A;
overflow_prevent(_) -> 1.
%%----------------------------------------------------------------------------
%%
%%----------------------------------------------------------------------------
traceloop(Camera,Width,Height,Scene) ->
traceloop(Camera,Width,Height,0,0,Scene,[]).
traceloop(_,_,Height,_,Y,_,PL) when Height=<Y ->
PL;
traceloop(Camera,Width,Height,X,Y,Scene,PL) when Width=<X ->
traceloop(Camera,Width,Height,0,Y+1,Scene,PL);
traceloop(Camera,Width,Height,X,Y,Scene,PL) ->
Ray = ray(Camera,X/Width,Y/Height),
{R1,G1,B1} = traceray(Ray,Scene,1),
R2 = overflow_prevent(R1),
G2 = overflow_prevent(G1),
B2 = overflow_prevent(B1),
P = {trunc(R2*255), trunc(G2*255), trunc(B2*255)},
traceloop(Camera,Width,Height,X+1,Y,Scene,[{X,Y,P}|PL]).
%%----------------------------------------------------------------------------
%%
%%----------------------------------------------------------------------------
traceray(Ray,Scene,Level) ->
Hit = findintersection(Ray,Scene,Level),
case Hit of
nohit -> {0,0,0};
{[T|Ts],Object} -> shaderay(Ray,Scene,Level,T,Object)
end.
%%----------------------------------------------------------------------------
%%
%%----------------------------------------------------------------------------
% Here we loop through all the objects in the scene to find the
% closest intersection.
findintersection(_,[],_) -> nohit;
findintersection(Ray,[Object|Objects],Level) ->
Ts = intersection(Object,Ray),
Hit1 = findintersection(Ray,Objects,Level),
Hit2 = closesthit(Ts,Object,Hit1).
closesthit(nohit,_,nohit) -> nohit;
closesthit(nohit,_,{[T|Ts],Obj}) when T>0 -> {[T|Ts],Obj};
closesthit(nohit,_,_) -> nohit;
closesthit([T|Ts],Obj,nohit) when T>0 -> {[T|Ts],Obj};
closesthit(_,_,nohit) -> nohit;
closesthit([T1|Ts1],Obj1,{[T2|Ts2],Obj2}) when T1>0,T1<T2 -> {[T1|Ts1],Obj1};
closesthit([T1|Ts1],Obj1,{[T2|Ts2],Obj2}) when T2>0,T2<T1 -> {[T2|Ts2],Obj2};
closesthit(_,_,_) -> nohit.
%%----------------------------------------------------------------------------
%%
%%----------------------------------------------------------------------------
shaderay(Ray,Scene,Level,T,Object) ->
Direction = get_direction(Ray),
Origin = get_origin(Ray),
Point = add(Origin, mul(T, Direction)),
Normal = calcnormal(Object, Point),
Diffuse = -dot(Normal, Direction),
ReflectionVector = reflection(Ray,Normal),
NewOrigin = add(Point, mul(0.0001, Normal)),
ReflectionRay = rcreate(NewOrigin,ReflectionVector),
{Red1,Green1,Blue1} = get_color(Object),
if
Level<4, Diffuse>0 ->
{Red2,Green2,Blue2} = traceray(ReflectionRay,Scene,Level+1),
{Diffuse*Red1 + 0.5*Red2,
Diffuse*Green1 + 0.5*Green2,
Diffuse*Blue1 + 0.5*Blue2};
Level<4, Diffuse<0 ->
{0,0,0};
true ->
{0,0,0}
end.
%%----------------------------------------------------------------------------
%% Har nedan foljer bara ett gang testfunktioner....
%%----------------------------------------------------------------------------
-record(camera,{width,height,zoom,position,lookat,up,right,down,corner}).
%%%---------------------------------------------------------------------------
%%%
%%% Useful camera operations.
%%%
%%%---------------------------------------------------------------------------
%%----------------------------------------------------------------------------
%% Constructors.
%%----------------------------------------------------------------------------
ccreate() ->
#camera{width=100, height=100, zoom=256,
position = vcreate(0,0,-256),
lookat = vcreate(0,0,0),
up = vcreate(0,1,0)}.
%%----------------------------------------------------------------------------
%% Selectors and modifiers.
%%----------------------------------------------------------------------------
set_width(C,Width) -> C#camera{width=Width}.
set_height(C,Height) -> C#camera{height=Height}.
set_zoom(C,Zoom) -> C#camera{zoom=Zoom}.
cset_position(C,Pos) -> C#camera{position=Pos}.
set_lookat(C,Lookat) -> C#camera{lookat=Lookat}.
set_up(C,Up) -> C#camera{up=Up}.
get_width(C) -> C#camera.width.
get_height(C) -> C#camera.height.
get_zoom(C) -> C#camera.zoom.
cget_position(C) -> C#camera.position.
get_lookat(C) -> C#camera.lookat.
get_up(C) -> C#camera.up.
%%----------------------------------------------------------------------------
%% Operators.
%%----------------------------------------------------------------------------
initialize(C) ->
Dir = normalize(sub(C#camera.lookat, C#camera.position)),
Up1 = normalize(C#camera.up),
D = dot(Up1, Dir),
Up2 = normalize(sub(Up1, mul(D, Dir))),
Down = mul(-1, Up2),
Right = normalize(cross(Up2,Dir)),
Corner1 = mul(C#camera.zoom, Dir),
Corner2 = add(Corner1, mul(-C#camera.width/2, Right)),
Corner3 = add(Corner2, mul(-C#camera.height/2, Down)),
C2 = C#camera{down=Down, right=Right, corner=Corner3}.
%
% X och Y ska ligga i intervallet [0..1]
%
ray(C,X,Y) ->
Right = mul(C#camera.width*X, C#camera.right),
Down = mul(C#camera.height*Y, C#camera.down),
Point = add(C#camera.corner, add(Right,Down)),
rcreate(C#camera.position,normalize(Point)).
%%----------------------------------------------------------------------------
%% E N D O F F I L E
%%----------------------------------------------------------------------------
-record(vector,{x,y,z}).
%%%---------------------------------------------------------------------------
%%%
%%% Useful vector operations.
%%%
%%%---------------------------------------------------------------------------
%%----------------------------------------------------------------------------
%% Constructors.
%%----------------------------------------------------------------------------
vcreate() ->
#vector{x=0,y=0,z=0}.
vcreate(X,Y,Z) ->
#vector{x=X,y=Y,z=Z}.
%%----------------------------------------------------------------------------
%% Selectors and modifiers.
%%----------------------------------------------------------------------------
set_x(V,X) -> V#vector{x=X}.
set_y(V,Y) -> V#vector{y=Y}.
set_z(V,Z) -> V#vector{z=Z}.
get_x(V) -> V#vector.x.
get_y(V) -> V#vector.y.
get_z(V) -> V#vector.z.
%%----------------------------------------------------------------------------
%% Operators.
%%----------------------------------------------------------------------------
add(A,B) ->
#vector{x=A#vector.x+B#vector.x,
y=A#vector.y+B#vector.y,
z=A#vector.z+B#vector.z}.
sub(A,B) ->
#vector{x=A#vector.x-B#vector.x,
y=A#vector.y-B#vector.y,
z=A#vector.z-B#vector.z}.
mul(T,A) ->
#vector{x=A#vector.x * T,
y=A#vector.y * T,
z=A#vector.z * T}.
dot(A,B) ->
A#vector.x*B#vector.x +
A#vector.y*B#vector.y +
A#vector.z*B#vector.z.
normalize(A) ->
S = 1 / math:sqrt(dot(A,A)),
vcreate(A#vector.x * S, A#vector.y * S, A#vector.z * S).
cross(A,B) ->
#vector{x = A#vector.y*B#vector.z - A#vector.z*B#vector.y,
y = A#vector.z*B#vector.x - A#vector.x*B#vector.z,
z = A#vector.x*B#vector.y - A#vector.y*B#vector.x}.
%%----------------------------------------------------------------------------
%% E N D O F F I L E
%%----------------------------------------------------------------------------
-record(ray,{origin,direction}).
%%%---------------------------------------------------------------------------
%%%
%%% Useful ray stuff.
%%%
%%%---------------------------------------------------------------------------
%%----------------------------------------------------------------------------
%% Constructors.
%%----------------------------------------------------------------------------
rcreate() ->
#ray{origin=vcreate(0,0,0), direction=vcreate(0,0,1)}.
rcreate(Origin,Direction) ->
#ray{origin=Origin, direction=Direction}.
%%----------------------------------------------------------------------------
%% Selectors and modifiers.
%%----------------------------------------------------------------------------
set_origin(R,Origin) -> R#ray{origin=Origin}.
set_direction(R,Direction) -> R#ray{direction=Direction}.
get_origin(R) -> R#ray.origin.
get_direction(R) -> R#ray.direction.
%%----------------------------------------------------------------------------
%%
%%----------------------------------------------------------------------------
reflection(R,N) ->
A = mul(2*dot(N, R#ray.direction), N),
normalize(sub(R#ray.direction, A)).
%%----------------------------------------------------------------------------
%% E N D O F F I L E
%%----------------------------------------------------------------------------
-record(sphere,{radius,position,color}).
%%%---------------------------------------------------------------------------
%%%
%%% Useful sphere operations.
%%%
%%%---------------------------------------------------------------------------
%%----------------------------------------------------------------------------
%% Constructors.
%%----------------------------------------------------------------------------
screate() ->
#sphere{radius=1, position=vcreate(0,0,0), color={1,1,1}}.
screate(Radius,Position,Color) ->
#sphere{radius=Radius, position=Position, color=Color}.
%%----------------------------------------------------------------------------
%% Selectors and modifiers.
%%----------------------------------------------------------------------------
set_radius(S,Radius) -> S#sphere{radius=Radius}.
sset_position(S,Position) -> S#sphere{position=Position}.
set_color(S,Color) -> S#sphere{color=Color}.
get_radius(S) -> S#sphere.radius.
sget_position(S) -> S#sphere.position.
get_color(S) -> S#sphere.color.
%%----------------------------------------------------------------------------
%% Calculates the intersection between a ray and the sphere.
%%----------------------------------------------------------------------------
intersection(S,Ray) ->
SR = sub(S#sphere.position,get_origin(Ray)),
B = dot(SR,get_direction(Ray)),
C = dot(SR,SR),
Root = B*B-C + S#sphere.radius * S#sphere.radius,
if
Root>0 ->
SquareRoot = math:sqrt(Root),
[B-SquareRoot,B+SquareRoot];
true ->
nohit
end.
calcnormal(S,P) ->
normalize(sub(P, S#sphere.position)).
%mul(1/S#sphere.radius, sub(P, S#sphere.position)).
%%----------------------------------------------------------------------------
%% E N D O F F I L E
%%----------------------------------------------------------------------------