• Remix
  • Share
  • New Entry

on 9 Nov 2023
  • 17
  • 102
  • 0
  • 7
  • 1938
drawframe(1)
function drawframe(xx)
% Inspiration: https://www.gabrielgambetta.com/computer-graphics-from-scratch/reflection.html
%% Scene Setup
vs = [1.5 1]; % viewport size
p = 1; % projection plane
O = [0 0 0];
c = eye(3); % Camera Rotation
Cw = 120; Ch = 80;
cnvs = zeros(Cw+1,Ch+1,3); % Canvas of pixels
b = Sphere([8/47*xx-196/47 -1 3],1,[0 0 255],500,0.2);
r = Sphere([2 0 4],1,[255 0 0],500,0.3);
g = Sphere([-2 0 4],1,[0 255 0],10,0.4);
y = Sphere([0 -5001 0],5000,[0 255 255],1000,0.5);
sp = [r g b y];
l1 = Light('amb',0.2,[],[]);
l2 = Light('pt',0.6,[2 1 0],[]);
l3 = Light('dir',0.2,[],[1 4 4]);
ls = [l1 l2 l3];
%%
for i = 1:Cw+1 % For each column of pixels
x = i-1-Cw/2;
slc = zeros(1,Ch,3);
for j = 1:Ch+1 % For each row of pixels
y = j-1-Ch/2;
D = C2V(x,y,Cw,Ch,vs,p);
D = (c*D')';
color = TR(O,D,1,inf,sp,ls,3);
slc(1,j,:) = rot90(color);
end
cnvs(i,:,:) = slc;
end
% Show the canvas, upsample by a factor of 2
imshow(imresize(rot90(uint8(cnvs)),2));
axis equal;
end
%%
function D = C2V(x,y,Cw,Ch,V,d)
% Canvas to Viewport
D = [x*V(1)/Cw, y*V(2)./Ch, d];
end
function c = TR(O,D,ti,ta,sp,ls,rd)
% Trace Ray
% rd: recursion depth
[cs,ct] = CI(O,D,ti,ta,sp);
if isempty(cs)
c = [0 0 0];
return
else
P = O + ct*D; % Compute intersection
N = P-cs.m; % Compute sphere normal at intersection
N = N/norm(N);
c = cs.c*CL(P,N,ls,-D,cs.s,ta,sp);
end
r = cs.rf;
if ~(rd<=0 || r<=0)
R = RR(-D,N);
rc = TR(P,R,0.001,Inf,sp,ls,rd-1); % reflected color
c = c*(1-r)+rc*r;
end
end
function [s,t] = CI(O,D,t_min,t_max,sp)
% CI: ClosestIntersection
t = inf; % Distance to shere
s = []; % Sphere
for i = 1:length(sp)
[t1,t2] = IRS(O, D, sp(i));
if t1>t_min && t1<t_max && t1 < t
t = t1;
s = sp(i);
end
if t2>t_min && t2<t_max && t2 < t
t = t2;
s = sp(i);
end
end
end
function i = CL(P,N,ls,V,s,ta,sp)
% Compute Lighting
i = 0;
for l = 1:length(ls)
if strcmp(ls(l).t,'amb') % Ambient light
i = i+ls(l).i;
else
if strcmp(ls(l).t,'pt') % Point light
L = ls(l).p - P;
else
L = ls(l).d; % Diffuse light
end
% Shadow Check
ss = CI(P,L,0.001,ta,sp);
if ~isempty(ss)
continue
end
% Diffuse
n_dot_l = dot(N,L);
if n_dot_l>0
i = i + ls(l).i*n_dot_l/(norm(N)*norm(L));
end
% Specular
if s ~= -1
R = RR(L,N);
r_dot_v = dot(R,V);
if r_dot_v > 0
i = i + ls(l).i*(r_dot_v/(norm(R)*norm(V)))^s;
end
end
end
end
end
function r = RR(R,N)
% Reflect Ray
r = 2*N*dot(N,R)-R;
end
function [t1,t2] = IRS(O,D,s)
% IntersectRaySphere
C = s.m;
r = s.r;
OC = O - C;
k1 = dot(D, D);
k2 = 2*dot(OC, D);
k3 = dot(OC, OC) - r*r;
d = k2*k2 - 4*k1*k3; % discriminant
if d < 0
t1 = inf;
t2 = inf;
else
t1 = (-k2 + sqrt(d)) / (2*k1);
t2 = (-k2 - sqrt(d)) / (2*k1);
end
end
function l = Light(t,i,p,d)
% t: type (amb: ambient, pt: point, dir: directional)
% i: intensity
% p: position
% d: direction
l.t = t;
l.i = i;
l.p = p;
l.d = d;
end
function s = Sphere(cntr,r,col,spec,ref)
% cntr: center
% r: radius
% col: color
% spec: spectral
% ref: reflective
s.m = cntr;
s.r = r;
s.c = col;
s.s = spec;
s.rf = ref;
end
Animation
Remix Tree