# ray tracing example # modified from Artem Yashin's project Console3D # MIT License # Copyright (c) 2021 Artem Yashin # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. use module.libmat; use std.runtime; use std.math; func() { # allocate more spaces runtime.gc.extend("str", 8); runtime.gc.extend("vec", 8); }(); # alias var (max, min, sqrt, sin, cos, abs) = ( math.max, math.min, math.sqrt, math.sin, math.cos, math.abs ); var (vec2, vec3) = ( libmat.vec2.new, libmat.vec3.new ); var (vec2add, vec2sub, vec2mul, vec2div, vec2len) = ( libmat.vec2.add, libmat.vec2.sub, libmat.vec2.mul, libmat.vec2.div, libmat.vec2.len ); var (vec3add, vec3sub, vec3mul, vec3div, vec3neg, vec3norm, vec3len, vec3dot) = ( libmat.vec3.add, libmat.vec3.sub, libmat.vec3.mul, libmat.vec3.div, libmat.vec3.neg, libmat.vec3.norm, libmat.vec3.len, libmat.vec3.dot ); var (rotateX, rotateY, rotateZ) = ( libmat.vec3.rx, libmat.vec3.ry, libmat.vec3.rz, ); var use_raw = func() { vec2 = func(x, y) {return [x,y];} vec2add = func(v1, v2) {return [v1[0]+v2[0],v1[1]+v2[1]];} vec2sub = func(v1, v2) {return [v1[0]-v2[0],v1[1]-v2[1]];} vec2mul = func(v1, v2) {return [v1[0]*v2[0],v1[1]*v2[1]];} vec2div = func(v1, v2) {return [v1[0]/v2[0],v1[1]/v2[1]];} vec2len = func(v) {var (x,y)=(v[0],v[1]); return sqrt(x*x+y*y);} vec3 = func(x, y, z) {return [x,y,z];} vec3add = func(v1, v2) {return [v1[0]+v2[0],v1[1]+v2[1],v1[2]+v2[2]];} vec3sub = func(v1, v2) {return [v1[0]-v2[0],v1[1]-v2[1],v1[2]-v2[2]];} vec3mul = func(v1, v2) {return [v1[0]*v2[0],v1[1]*v2[1],v1[2]*v2[2]];} vec3div = func(v1, v2) {return [v1[0]/v2[0],v1[1]/v2[1],v1[2]/v2[2]];} vec3neg = func(v) {return [-v[0],-v[1],-v[2]];} vec3len = func(v) {var (x,y,z)=(v[0],v[1],v[2]); return sqrt(x*x+y*y+z*z);} vec3norm = func(v) {var t=vec3len(v); return vec3div(v,[t,t,t]);} vec3dot = func(a, b) { return a[0]*b[0] + a[1]*b[1] + a[2]*b[2]; } rotateX = func(a, angle) { return [ a[0], a[2] * sin(angle) + a[1] * cos(angle), a[2] * cos(angle) - a[1] * sin(angle) ]; } rotateY = func(a, angle) { return [ a[0] * cos(angle) - a[2] * sin(angle), a[1], a[0] * sin(angle) + a[2] * cos(angle) ]; } rotateZ = func(a, angle) { return [ a[0] * cos(angle) - a[1] * sin(angle), a[0] * sin(angle) + a[1] * cos(angle), a[2] ]; } } var clamp = func(value, _min, _max) { return max(min(value, _max), _min); } var sign = func(a) { return (0 < a) - (a < 0); } var step = func(edge, x) { return x > edge; } var vec3abs = func(v) { return [abs(v[0]), abs(v[1]), abs(v[2])]; } var vec3sign = func(v) { return [sign(v[0]), sign(v[1]), sign(v[2])]; } var vec3step = func(edge, v) { return [ step(edge[0], v[0]), step(edge[1], v[1]), step(edge[2], v[2]) ]; } var vec3reflect = func(rd, n) { var d = vec3dot(n, rd); return vec3sub(rd, vec3mul(n, vec3mul([2, 2, 2], [d, d, d]))); } var sphere = func(ro, rd, r) { var b = vec3dot(ro, rd); var c = vec3dot(ro, ro) - r * r; var h = b * b - c; if (h < 0.0) return [-1.0, -1.0]; h = sqrt(h); return [-b - h, -b + h]; } var box = func(ro, rd, boxSize, outNormal) { var m = vec3div([1.0, 1.0, 1.0], rd); var n = vec3mul(m, ro); var k = vec3mul(vec3abs(m), boxSize); var t1 = vec3sub(vec3neg(n), k); var t2 = vec3add(vec3neg(n), k); var tN = max(max(t1[0], t1[1]), t1[2]); var tF = min(min(t2[0], t2[1]), t2[2]); if (tN>tF or tF<0.0) return [-1.0, -1.0]; var yzx = [t1[1], t1[2], t1[0]]; var zxy = [t1[2], t1[0], t1[1]]; var tmp = vec3mul( vec3mul(vec3neg(vec3sign(rd)), vec3step(yzx, t1)), vec3step(zxy, t1) ); outNormal[0] = tmp[0]; outNormal[1] = tmp[1]; outNormal[2] = tmp[2]; return [tN, tF]; } var plane = func(ro, rd, p, w) { return -(vec3dot(ro, p) + w) / vec3dot(rd, p); } var main = func(frame) { var height = 15*2; var width = int(height*1/0.618)*2; var aspect = width/height; var pixelAspect = 11.0/24.0; var gradient = split("", " .:!/r(l1Z4H9W8$"); var gradientSize = size(gradient)-1; var screen = []; setsize(screen, width*height); var light = vec3norm([-0.5, 0.5, -1.0]); var spherePos = [0, 3, 0]; var vec2_2_2 = [2, 2]; var vec2_1_1 = [1, 1]; var vec3_000 = [0, 0, 0]; var vec3_00n1 = [0, 0, -1]; var vec3_111 = [1, 1, 1]; print("\e[2J"); var stamp = maketimestamp(); for (var t = 0; t < frame; t += 1) { stamp.stamp(); for (var i = 0; i < width; i += 1) { for (var j = 0; j < height; j += 1) { var uv=vec2sub(vec2mul(vec2div([i,j],[width,height]),vec2_2_2),vec2_1_1); uv[0]*=aspect*pixelAspect; var ro=[-6,0,0]; var rd=vec3norm([2,uv[0],uv[1]]); ro=rotateY(ro,0.25); rd=rotateY(rd,0.25); ro=rotateZ(ro,t*0.03); rd=rotateZ(rd,t*0.03); var diff=1; for (var k=0;k<5;k+=1) { var minIt=99999; var intersection=sphere(vec3sub(ro,spherePos),rd,1); var n=vec3_000; var albedo=1; if (intersection[0]>0) { var itPoint=vec3add(vec3sub(ro,spherePos),vec3mul(rd,[intersection[0],intersection[0],intersection[0]])); minIt=intersection[0]; n=vec3norm(itPoint); } var boxN=[0,0,0]; intersection=box(ro,rd,vec3_111,boxN); if (intersection[0]>0 and intersection[0]0 and intersection[0]