1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296 | function I = sloray
% SLORAY Traçado de raio básico para fins educativos.
% output:
% I: imagem RGB (matriz double MxNx3 com valores entre 0-1)
% Referências:
% [1] K. Suffern. Ray Tracing from the Ground Up. 2007.
% [2] A. Watt, M. Watt. Advanced Animation and Rendering Techniques. 1992.
% [3] P. Heckbert. Writing a Ray Tracer. In: A. Glassner, An Introduction to Ray Tracing. 1989
% EXEMPLO DE USO:
% tic, I=sloray; toc, figure, imshow(I)
% SloRay - Slow Raytracer
% Autores:
% Augusto Valente (avalente arr_oba dca ponto fee ponto unicamp ponto br)
% Wallace Loos (wloos arr_oba dca ponto fee ponto unicamp ponto br)
% Junho/2011
% tamanho da imagem em pixels
% - largura
res_w = 128;
% - altura
res_h = res_w*3/4; % (usando aspect ratio 1.333)
% tamanho do plano de imagem centrado na origem (coords. do "mundo")
% - largura
win_w = 4;
% - altura
win_h = win_w*3/4;
% posicao do observador
ray_o = [0 0 2];
% "epsilon factor": [1], 16.4
kEps = 1e-5;
% iluminacao ambiente ([2], cap. 2)
Ia_ka = [.2 .2 .2];
% máximo nível de recursão
MAX_DEPTH = 5;
% mínimo peso de um raio para levá-lo em conta na recursão
MIN_WEIGHT = 1e-3;
% cor de fundo
BACKGROUND_COLOR = [0 0 0];
% estatística
stat_max_depth = 0;
% posição no eixo z do plano de imagem
Z_IMAGE_PLANE = 0;
% posicoes dos pixels no plano de imagem
pixel_w = win_w/res_w;
pixel_h = win_h/res_h;
c_x = -win_w/2+pixel_w/2:pixel_w:win_w/2-pixel_w/2;
c_y = win_h/2-pixel_h/2:-pixel_h:-win_h/2+pixel_h/2;
% c_x = linspace(-win_w/2,win_w/2,res_w);
% c_y = linspace(win_h/2,-win_h/2,res_h);
% imagem
I = zeros(res_h, res_w, 3);
% estrutura "objs" com objetos da cena
% campos:
% - "parms": cell-array com 2 parametros geometricos
% --> esfera: {centro raio}
% --> plano: {ponto normal}
% - "cor": função que recebe uma posição e retorna uma cor RGB (usado como coeficiente kd do modelo de Phong)
% - "krl": coeficiente de especularidade do modelo de Phong (local)
% - "ns": coeficiente de concentração da especularidade do modelo de Phong (local)
% - "krg": coeficiente de especularidade global
% - "intersec": função que recebe parâmetros do raio e do objeto e testa a intersecção
% 3 objetos:
% esfera especular azul (R = 1, centro = (-2, 0, 4))
% plano lambertiano com textura xadrez (normal = (.5, 1, 0), ponto = (0, -3, -4))
% esfera lambertiana verde (R = 0.5, centro = (1, 0, -4))
objs = struct( ...
'parms', {{[-2 0 -4] 1}, {[0 -3 -4] [.5 1 0]/norm([.5 1 0])}, {[1 0 -4] .5}}, ...
'cor', {@cor1, @checker, @cor2}, ...
'krl', {.9, 0, 0}, ...
'ns', {40, 0, 0}, ...
'krg', {.3, 0, 0}, ...
'intersec', {@intersec_sphere, @intersec_plane, @intersec_sphere}, ...
'normal', {@normal_sphere, @normal_plane, @normal_sphere});
% estrutura com fontes de luz pontuais
% campos:
% - "pos": posição
% - "I": intensidade RGB
lights = struct('pos', {[10 10 -4]},'I',{[245,255,250]/255});
% lights = struct('pos', {[10 10 -4] [0 20 -8]},'I',{[245,255,250]/255,[245,255,250]/255});
h_wait = waitbar(0,'aguarde...');
% traça 1 raio por pixel
for ii=1:res_w
for jj=1:res_h
% direção do raio
ray_d = [c_x(ii) c_y(jj) Z_IMAGE_PLANE]-ray_o;
ray_d = ray_d / norm(ray_d);
% começa recursão e seta pixel da imagem
I(jj,ii,:) = trace_ray(ray_d, ray_o, 1, 1);
end
waitbar(ii/res_w,h_wait)
end
close(h_wait)
disp(['Nível de recursão máximo: ' num2str(stat_max_depth) '/' num2str(MAX_DEPTH)])
function cor = trace_ray(ray_d, ray_o, depth, weight)
% TRACE_RAY Traça um raio e determina sua contribuição em cor.
% input:
% ray_d: direção do raio (vetor 1x3)
% ray_o: origem do raio (vetor 1x3)
% depth: nível de recursão
% weight: fator de atenuação da energia após reflexões
% output:
% cor: cor RGB (vetor 1x3)
if depth > MAX_DEPTH || weight < MIN_WEIGHT, cor = BACKGROUND_COLOR; return; end
if depth > stat_max_depth, stat_max_depth = depth; end
% pega hit mais perto e faz shading
[obj_hit, t] = ray_hit(ray_d, ray_o);
if obj_hit
cor = shade(ray_d, ray_o, obj_hit, t, depth, weight);
else
cor = BACKGROUND_COLOR;
end
end
function [obj_hit, t] = ray_hit(ray_d, ray_o)
% RAY_HIT Testa interseções para um dado raio
% com todos os objetos da struct global 'objs'.
% input:
% ray_d: direção do raio (vetor 1x3)
% ray_o: origem do raio (vetor 1x3)
% output:
% obj_hit: índice do objeto interceptado (0 c.c.)
% t: satisfaz a equação P_intersec = ray_o + t * ray_d
obj_hit = 0;
t = inf;
for k=1:length(objs)
[hit, t1] = objs(k).intersec(ray_d, ray_o, objs(k).parms);
if hit && t1 < t
t = t1;
obj_hit = k;
end
end
end
function [hit, t] = intersec_sphere(ray_d, ray_o, obj_parms)
% INTERSEC_SPHERE Interseção raio-esfera. ref: [1], p. 58
% input:
% ray_d: direção do raio (vetor 1x3)
% ray_o: origem do raio (vetor 1x3)
% obj_parms: cell-array 1x2 contendo centro (vetor 1x3) e raio (escalar)
% output:
% hit: flag de interseção
% t: satisfaz a equação P_intersec = ray_o + t * ray_d
obj_c = obj_parms{1};
obj_r = obj_parms{2};
a = dot(ray_d,ray_d);
b = 2*dot(ray_o - obj_c, ray_d);
c = dot( ray_o - obj_c, ray_o - obj_c ) - obj_r^2;
delta = b^2-4*a*c;
t = inf;
hit = false;
if delta >= 0
t = (-b-sqrt(delta)) / (2*a);
if t > kEps
hit = true;
return
end
t = (-b+sqrt(delta)) / (2*a);
if t > kEps
hit = true;
return
end
end
end
function [hit, t] = intersec_plane(ray_d, ray_o, obj_parms)
% INTERSEC_PLANE Interseção raio-plano. ref: [1], p. 56
% input:
% ray_d: direção do raio (vetor 1x3)
% ray_o: origem do raio (vetor 1x3)
% obj_parms: cell-array 1x2 contendo um ponto (vetor 1x3) e a normal
% (vetor 1x3) do plano
% output:
% hit: flag de interseção
% t: satisfaz a equação P_intersec = ray_o + t * ray_d
a = obj_parms{1};
n = obj_parms{2};
t = dot(a - ray_o, n) / dot(ray_d, n);
hit = false;
if t > kEps
hit = true;
end
end
function cor = cor1(~)
% COR1 Gera verde RGB.
cor = [0 1 0];
end
function cor = cor2(~)
% COR1 Gera azul RGB.
cor = [0 0 1];
end
function cor = checker(P)
% CHECKER Gera textura quadriculada usando floor(). ref: [1], p. 674
% input:
% P: ponto no espaço (vetor 1x3)
% output:
% cor: cor RGB (vetor 1x3)
P = P - 0.000187453738; % evita que o plano caia em região flat
size = .2; % lado dos quadrados
if mod(sum(floor(P/size)),2) == 0, cor = [139,90,0]/255;
else cor = [238,154,0]/255;
end
end
function cor = shade(ray_d, ray_o, obj_hit, t, depth, weight)
% SHADE Calcula cor em uma interseção com objeto.
% input:
% ray_d: direção do raio (vetor 1x3)
% ray_o: origem do raio (vetor 1x3)
% obj_hit: índice do objeto em questão
% t: satisfaz a equação P_intersec = ray_o + t * ray_d
% depth: nível de recursão
% weight: fator de atenuação da energia após reflexões
% output:
% cor: cor RGB (vetor 1x3)
obj = objs(obj_hit);
P = ray_o + t * ray_d; % ponto de interseção
n = obj.normal(obj, P); % normal à superfície
cor = Ia_ka.*obj.cor(P);
% determina contribuição de cada fonte de luz
for l=1:length(lights)
L = lights(l).pos - P; % shadow feeler
dist = norm(L); % distância à fonte de luz
dot_L_n = dot(L/dist,n);
% verifica se há oclusão
[obj_hit, t] = ray_hit(L/dist, P);
if (obj_hit && t < dist) % ver [1], fig. 16.8
continue;
end
if dot_L_n > 0
% modelo de Phong
cor = cor + lights(l).I .* obj.cor(P) * dot_L_n; % [2], eq. 2.5
end
% direção especular utilizada no modelo de Phong
R_light = 2*dot_L_n*n - L/dist;
dot_R_V = dot(R_light,-ray_d);
if dot_R_V > 0
% modelo de Phong
cor = cor + lights(l).I .* obj.krl * dot_R_V^obj.ns; % [2], eq. 2.5
end
end
% direção do raio refletido
R = ray_d - 2*dot(ray_d,n)*n;
% contribuição do raio refletido (recursão)
cor = cor + weight*obj.krg*trace_ray(R, P, depth+1, weight*obj.krg);
end
function n = normal_sphere(obj, P)
% NORMAL_SPHERE Normal à superfície da esfera no ponto.
% input:
% obj: item da struct global 'objs'
% P: ponto na superfície da esfera (vetor 1x3)
% output:
% n: normal
% parâmetros da estrutura:
% obj.parms{1}: centro da esfera (vetor 1x3)
% obj.parms{2}: raio da esfera
% n = (P - C) / R (vetor unitário do centro C para o ponto P)
n = (P - obj.parms{1}) / obj.parms{2};
end
function n = normal_plane(obj, ~)
% NORMAL_PLANE Normal ao plano.
% input:
% obj: item da struct global 'objs'
% output:
% n: normal
% parâmetros da estrutura:
% obj.parms{2}: normal unitária ao plano (vetor 1x3)
n = obj.parms{2};
end
end
|