Advertisement
Guest User

Untitled

a guest
Jun 26th, 2023
45
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 11.83 KB | None | 0 0
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title>Camera position demo - Computer Graphics from scratch</title>
  5. </head>
  6. <body>
  7. <div class="centered">
  8. <canvas id="canvas" width=600 height=600 style="border: 1px grey solid"></canvas>
  9. </div>
  10.  
  11. <script>
  12. // ======================================================================
  13. // Low-level canvas access.
  14. // ======================================================================
  15.  
  16. var canvas = document.getElementById("canvas");
  17. var canvas_context = canvas.getContext("2d");
  18. var canvas_buffer = canvas_context.getImageData(0, 0, canvas.width, canvas.height);
  19. var canvas_pitch = canvas_buffer.width * 4;
  20.  
  21. // The PutPixel() function.
  22. var PutPixel = function(x, y, color) {
  23. x = canvas.width / 2 + x;
  24. y = canvas.height / 2 - y - 1;
  25.  
  26. if (x < 0 || x >= canvas.width || y < 0 || y >= canvas.height) {
  27. return;
  28. }
  29.  
  30. var offset = 4 * x + canvas_pitch * y;
  31. canvas_buffer.data[offset++] = color[0];
  32. canvas_buffer.data[offset++] = color[1];
  33. canvas_buffer.data[offset++] = color[2];
  34. canvas_buffer.data[offset++] = 255; // Alpha = 255 (full opacity)
  35. }
  36.  
  37. // Displays the contents of the offscreen buffer into the canvas.
  38. var UpdateCanvas = function() {
  39. canvas_context.putImageData(canvas_buffer, 0, 0);
  40. }
  41.  
  42.  
  43. // ======================================================================
  44. // Linear algebra and helpers.
  45. // ======================================================================
  46.  
  47. // Conceptually, an "infinitesimally small" real number.
  48. var EPSILON = 0.001;
  49.  
  50. // Dot product of two 3D vectors.
  51. var DotProduct = function (v1, v2) {
  52. return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2];
  53. }
  54.  
  55. // Length of a 3D vector.
  56. var Length = function (vec) {
  57. return Math.sqrt(DotProduct(vec, vec));
  58. }
  59.  
  60. // Multiplies a scalar and a vector.
  61. var MultiplySV = function (k, vec) {
  62. return [k * vec[0], k * vec[1], k * vec[2]];
  63. }
  64.  
  65. // Multiplies a matrix and a vector.
  66. var MultiplyMV = function (mat, vec) {
  67. var result = [0, 0, 0];
  68.  
  69. for (var i = 0; i < 3; i++) {
  70. for (var j = 0; j < 3; j++) {
  71. result[i] += vec[j] * mat[i][j];
  72. }
  73. }
  74.  
  75. return result;
  76. }
  77.  
  78. // Computes v1 + v2.
  79. var Add = function (v1, v2) {
  80. return [v1[0] + v2[0], v1[1] + v2[1], v1[2] + v2[2]];
  81. }
  82.  
  83. // Computes v1 - v2.
  84. var Subtract = function (v1, v2) {
  85. return [v1[0] - v2[0], v1[1] - v2[1], v1[2] - v2[2]];
  86. }
  87.  
  88. // Clamps a color to the canonical color range.
  89. var Clamp = function (vec) {
  90. return [
  91. Math.min(255, Math.max(0, vec[0])),
  92. Math.min(255, Math.max(0, vec[1])),
  93. Math.min(255, Math.max(0, vec[2]))
  94. ];
  95. }
  96.  
  97. // Computes the reflection of v1 respect to v2.
  98. var ReflectRay = function (v1, v2) {
  99. return Subtract(MultiplySV(2 * DotProduct(v1, v2), v2), v1);
  100. }
  101.  
  102.  
  103. // ======================================================================
  104. // A raytracer with diffuse and specular illumination, shadows and reflections,
  105. // arbitrary camera position and orientation.
  106. // ======================================================================
  107.  
  108. // A Sphere.
  109. var Sphere = function (center, radius, color, specular, reflective, refractive, refractive_index) {
  110. this.center = center;
  111. this.radius = radius;
  112. this.color = color;
  113. this.specular = specular;
  114. this.reflective = reflective;
  115. this.refractive = refractive;
  116. this.refractive_index = refractive_index; // add this
  117. }
  118.  
  119. // A Light.
  120. var Light = function (ltype, intensity, position) {
  121. this.ltype = ltype;
  122. this.intensity = intensity;
  123. this.position = position;
  124. }
  125.  
  126. Light.AMBIENT = 0;
  127. Light.POINT = 1;
  128. Light.DIRECTIONAL = 2;
  129.  
  130. // Scene setup.
  131. var viewport_size = 1;
  132. var projection_plane_z = 1;
  133. var camera_position = [3, 0, 1];
  134. var camera_rotation = [
  135. [0.7071, 0, -0.7071],
  136. [0, 1, 0],
  137. [0.7071, 0, 0.7071]
  138. ];
  139. var background_color = [0, 0, 0];
  140. var spheres = [
  141. new Sphere([0, -1, 3], 1, [255, 0, 0], 500, 0.2, true, 1.9),
  142. new Sphere([2, 0, 4], 1, [0, 0, 255], 500, 0.3, false, 1.0),
  143. new Sphere([-2, 0, 4], 1, [0, 255, 0], 10, 0.4, false, 1.0),
  144. new Sphere([0, -5001, 0], 5000, [255, 255, 0], 1000, 0.5, false, 1.0)
  145. ];
  146.  
  147. var lights = [
  148. new Light(Light.AMBIENT, 0.2),
  149. new Light(Light.POINT, 0.6, [2, 1, 0]),
  150. new Light(Light.DIRECTIONAL, 0.2, [1, 4, 4])
  151. ];
  152. var recursion_depth = 3;
  153.  
  154. // Converts 2D canvas coordinates to 3D viewport coordinates.
  155. var CanvasToViewport = function (p2d) {
  156. return [
  157. p2d[0] * viewport_size / canvas.width,
  158. p2d[1] * viewport_size / canvas.height,
  159. projection_plane_z
  160. ];
  161. }
  162.  
  163. // Computes the intersection of a ray and a sphere. Returns the values
  164. // of t for the intersections.
  165. var IntersectRaySphere = function (origin, direction, sphere) {
  166. var oc = Subtract(origin, sphere.center);
  167.  
  168. var k1 = DotProduct(direction, direction);
  169. var k2 = 2 * DotProduct(oc, direction);
  170. var k3 = DotProduct(oc, oc) - sphere.radius * sphere.radius;
  171.  
  172. var discriminant = k2 * k2 - 4 * k1 * k3;
  173. if (discriminant < 0) {
  174. return [Infinity, Infinity];
  175. }
  176.  
  177. var t1 = (-k2 + Math.sqrt(discriminant)) / (2 * k1);
  178. var t2 = (-k2 - Math.sqrt(discriminant)) / (2 * k1);
  179. return [t1, t2];
  180. }
  181.  
  182. var ComputeLighting = function (point, normal, view, specular) {
  183. var intensity = 0;
  184. var length_n = Length(normal); // Should be 1.0, but just in case...
  185. var length_v = Length(view);
  186.  
  187. for (var i = 0; i < lights.length; i++) {
  188. var light = lights[i];
  189. if (light.ltype == Light.AMBIENT) {
  190. intensity += light.intensity;
  191. } else {
  192. var vec_l, t_max;
  193. if (light.ltype == Light.POINT) {
  194. vec_l = Subtract(light.position, point);
  195. t_max = 1.0;
  196. } else { // Light.DIRECTIONAL
  197. vec_l = light.position;
  198. t_max = Infinity;
  199. }
  200.  
  201. // Shadow check.
  202. var blocker = ClosestIntersection(point, vec_l, EPSILON, t_max);
  203. if (blocker) {
  204. continue;
  205. }
  206.  
  207. // Diffuse reflection.
  208. var n_dot_l = DotProduct(normal, vec_l);
  209. if (n_dot_l > 0) {
  210. intensity += light.intensity * n_dot_l / (length_n * Length(vec_l));
  211. }
  212.  
  213. // Specular reflection.
  214. if (specular != -1) {
  215. var vec_r = ReflectRay(vec_l, normal);
  216. var r_dot_v = DotProduct(vec_r, view);
  217. if (r_dot_v > 0) {
  218. intensity += light.intensity * Math.pow(r_dot_v / (Length(vec_r) * length_v), specular);
  219. }
  220. }
  221. }
  222. }
  223.  
  224. return intensity;
  225. }
  226.  
  227. // Find the closest intersection between a ray and the spheres in the scene.
  228. var ClosestIntersection = function (origin, direction, min_t, max_t) {
  229. var closest_t = Infinity;
  230. var closest_sphere = null;
  231.  
  232. for (var i = 0; i < spheres.length; i++) {
  233. var ts = IntersectRaySphere(origin, direction, spheres[i]);
  234. if (ts[0] < closest_t && min_t < ts[0] && ts[0] < max_t) {
  235. closest_t = ts[0];
  236. closest_sphere = spheres[i];
  237. }
  238. if (ts[1] < closest_t && min_t < ts[1] && ts[1] < max_t) {
  239. closest_t = ts[1];
  240. closest_sphere = spheres[i];
  241. }
  242. }
  243.  
  244. if (closest_sphere) {
  245. return [closest_sphere, closest_t];
  246. }
  247. return null;
  248. }
  249.  
  250. // Computes the refracted ray direction.
  251. var RefractRay = function (ray, normal, refractive_index) {
  252. var cosi = -Math.max(-1, Math.min(1, DotProduct(ray, normal)));
  253. var etai = 1,
  254. etat = refractive_index;
  255. var n = normal;
  256. if (cosi < 0) {
  257. cosi = -cosi;
  258. [etai, etat] = [etat, etai];
  259. n = MultiplySV(-1, normal);
  260. }
  261. var eta = etai / etat;
  262. var k = 1 - eta * eta * (1 - cosi * cosi);
  263. return k < 0 ?
  264. [0, 0, 0] :
  265. Add(MultiplySV(eta, ray), MultiplySV((eta * cosi - Math.sqrt(k)), n));
  266. }
  267.  
  268. // Traces a ray against the set of spheres in the scene.
  269. var TraceRay = function (origin, direction, min_t, max_t, depth) {
  270. var intersection = ClosestIntersection(origin, direction, min_t, max_t);
  271. if (!intersection) {
  272. return background_color;
  273. }
  274.  
  275. var closest_sphere = intersection[0];
  276. var closest_t = intersection[1];
  277.  
  278. var point = Add(origin, MultiplySV(closest_t, direction));
  279. var normal = Subtract(point, closest_sphere.center);
  280. normal = MultiplySV(1.0 / Length(normal), normal);
  281.  
  282. var view = MultiplySV(-1, direction);
  283. var lighting = ComputeLighting(point, normal, view, closest_sphere.specular);
  284. var local_color = MultiplySV(lighting, closest_sphere.color);
  285.  
  286. if (depth <= 0) {
  287. return local_color;
  288. }
  289.  
  290. var reflected_color = [0, 0, 0];
  291. if (closest_sphere.reflective > 0) {
  292. var reflected_ray = ReflectRay(view, normal);
  293. reflected_color = TraceRay(point, reflected_ray, EPSILON, Infinity, depth - 1);
  294. }
  295.  
  296. var refracted_color = [0, 0, 0];
  297. if (closest_sphere.refractive) {
  298. var refracted_ray = RefractRay(view, normal, closest_sphere.refractive_index);
  299. refracted_color = TraceRay(point, refracted_ray, EPSILON, Infinity, depth - 1);
  300. }
  301.  
  302. return Add(MultiplySV(1 - closest_sphere.reflective - closest_sphere.refractive, local_color),
  303. Add(MultiplySV(closest_sphere.reflective, reflected_color),
  304. MultiplySV(closest_sphere.refractive, refracted_color)));
  305. }
  306.  
  307.  
  308.  
  309.  
  310.  
  311. //
  312. // Main loop.
  313. //
  314. for (var x = -canvas.width/2; x < canvas.width/2; x++) {
  315. for (var y = -canvas.height/2; y < canvas.height/2; y++) {
  316. var direction = CanvasToViewport([x, y])
  317. direction = MultiplyMV(camera_rotation, direction);
  318. var color = TraceRay(camera_position, direction, 1, Infinity, recursion_depth);
  319. PutPixel(x, y, Clamp(color));
  320. }
  321. }
  322.  
  323. UpdateCanvas();
  324.  
  325.  
  326. </script>
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement