package main // A very simple, badly written (sorry, I just found the language) ray tracer // by gynvael.coldwind//vx // http://gynvael.coldwind.pl // // Assume the code is on a BSD-style licence ;> // // Compile: // cpp t2.go | grep -v '^#' > t2.out.go // 6g t2.out.go // 6l t2.out.6 // import ( "os"; "fmt"; "math"; "image"; "image/png" ) const ResX = 1024; const ResY = 768; const MaxPrimCount = 64; const MaxLightCount = 10; const MaxThreads = 4; type Vector3D struct { x, y, z, w float64; } #define V3DNew(a,b,c,d) var a Vector3D; a.x = b; a.y = c; a.z = d; #define V3DAdd(a,b) { a.x += b.x; a.y += b.y; a.z += b.z; } #define V3DSub(a,b) { a.x -= b.x; a.y -= b.y; a.z -= b.z; } #define V3DMov(a,b) { a.x = b.x; a.y = b.y; a.z = b.z; } #define V3DMul(a,b) { a.x *= b; a.y *= b; a.z *= b; } #define V3DMulV3D(a,b) { a.x *= b.x; a.y *= b.y; a.z *= b.z; } #define V3DSet(a,b,c,d) { a.x = b; a.y = c; a.z = d; } #define V3DCross(a,b,c) { a.x = b.y * c.z - b.z * c.y; \ a.y = b.z * c.x - b.x * c.z; \ a.z = b.x * c.y - b.y * c.x; } #define V3DDot(a,b) (a.x*b.x + a.y*b.y + a.z*b.z) #define V3DLength(a) (math.Sqrt(a.x*a.x+a.y*a.y+a.z*a.z)) #define V3DSqrLength(a) (a.x*a.x+a.y*a.y+a.z*a.z) #define V3DNorm(a) { _l := float64(1.0 / V3DLength(a)); \ a.x *= _l; a.y *= _l; a.z *= _l; } #define V3DPrint(a) { printf("%f %f %f\n", a.x, a.y, a.z); } type Material struct { Specular, Diffusive, Reflective float64; Color Vector3D; } type PrimSphere struct { Position Vector3D; Radius float64; m *Material; } type Ray struct { Direction, Origin Vector3D; } type Light struct { Position, Color Vector3D; } // My global struct type GlobalsStr struct { img *image.RGBA; PrimitiveCount int; PrimitiveList [MaxPrimCount]*PrimSphere; LightCount int; LightList [MaxLightCount]*Light; } var Globals GlobalsStr func main() { fmt.Printf("Simple RT by gynvael.coldwind//vx (http://gynvael.coldwind.pl)\n"); fmt.Printf("Creating scene...\n"); Globals.img = image.NewRGBA(ResX, ResY); var Mirror, Green, Red Material; V3DSet(Mirror.Color, 0.6, 0.6, 0.6); Mirror.Specular = 0.3; Mirror.Diffusive = 0.2; Mirror.Reflective = 0.8; V3DSet(Green.Color, 0.1, 1.0, 0.1); Green.Specular = 0.1; Green.Diffusive = 0.3; Green.Reflective = 0.4; V3DSet(Red.Color, 1.0, 0.1, 0.1); Red.Specular = 0.1; Red.Diffusive = 0.3; Red.Reflective = 0.4; var SpherePosMap string; SpherePosMap = "........." ".ggg....." ".g...rrr." ".g.g.r.r." ".ggg.rrr." "........."; SpherePosMap = SpherePosMap; p := 0; var SpherePos Vector3D; for j := 0; j < 6; j++ { for i := 0; i < 9; i++ { m := &Mirror; z := float64(2.0); sn := math.Sin(float64(i+j)) * 0.8; switch { case SpherePosMap[p] == 'g': z += -0.5; /*- sn * 0.4;*/ m = &Green; case SpherePosMap[p] == 'r': z += -0.5; /*- sn * 0.4;*/ m = &Red; default: z += sn; } V3DSet(SpherePos, -2.0 + float64(i) * 0.5, 1.25 - float64(j) * 0.5, z); AddSphere(&SpherePos, 0.25, m); p++; } } V3DNew(LightPos, 0.0, 0.0, 0.0); V3DNew(LightColor, 2.0, 2.0, 2.0); AddLight(&LightPos, &LightColor); fmt.Printf("Rendering...\n"); // Start some threads c := make(chan int); for i := 0; i < MaxThreads; i++ { fmt.Printf("[%d] Thread start\n", i); go Render(Globals.img, MaxThreads, i, c); } // Now wait for 8 threads to finish for i := 0; i < MaxThreads; i++ { fmt.Printf("[%d] Thread finished\n", <-c); } fmt.Printf("Writing test.png image...\n"); fd, err:= os.Open("test.png", os.O_WRONLY | os.O_CREATE, 0666); if(err != nil) { fmt.Printf("error: %s\n", err.String()); return } png.Encode(fd, Globals.img); fmt.Printf("Done.\n"); } func AddLight(Pos, Color *Vector3D) { if Globals.LightCount < MaxLightCount { l := new(Light); V3DMov(l.Position, Pos); V3DMov(l.Color, Color); Globals.LightList[Globals.LightCount] = l; Globals.LightCount++; } } func AddSphere(Pos *Vector3D, Rad float64, m *Material) { if Globals.PrimitiveCount < MaxPrimCount { p := new(PrimSphere); V3DMov(p.Position, Pos); p.Radius = Rad; p.m = m; Globals.PrimitiveList[Globals.PrimitiveCount] = p; Globals.PrimitiveCount++; } } func (this *PrimSphere) Intersect(ray *Ray, dist *float64) int { var v_precalc Vector3D; var det_precalc, b, det, i1, i2 float64; V3DMov(v_precalc, ray.Origin); V3DSub(v_precalc, this.Position); det_precalc = this.Radius * this.Radius - V3DDot(v_precalc,v_precalc); b = - V3DDot(v_precalc,ray.Direction); det = b*b + det_precalc; retval := int(0); if(det > 0) { det = math.Sqrt(det); i1 = b - det; i2 = b + det; switch { case i2 > 0 && i1 < 0: retval = -1; // -1 *dist = i2; case i2 > 0 && i1 >= 0: retval = 1; *dist = i1; } } return retval; } func (this *PrimSphere) Normal(Ret, pos *Vector3D) { V3DMov(Ret, pos); V3DSub(Ret, this.Position); f := float64(1.0 / this.Radius); V3DMul(Ret, f); V3DNorm(Ret); } func Trace(RetVector *Vector3D, ray *Ray, refl_depth int) { V3DNew(color, 0.02, 0.1, 0.17); //V3DNew(color, 0.0, 0.0, 0.0); dist := float64(1000000000.0); var prim *PrimSphere = nil; for i := int(0); i < Globals.PrimitiveCount; i++ { var temp_dist float64; p := Globals.PrimitiveList[i]; res := p.Intersect(ray, &temp_dist); if res == 0 { continue; } if temp_dist < dist { prim = p; dist = temp_dist; //result = ret; } } dist = dist; prim = prim; if prim == nil { V3DMov(RetVector, color); return; } // Get the point of intersecion var pi Vector3D; V3DMov(pi, ray.Direction); V3DMul(pi, dist); V3DAdd(pi, ray.Origin); var prim_color Vector3D; V3DMov(prim_color, prim.m.Color); //V3DSet(RetVector, 1.0, 1.0, 1.0); for i := 0; i < Globals.LightCount; i++ { lightiter := Globals.LightList[i]; var L, N Vector3D; V3DMov(L, lightiter.Position); V3DSub(L, pi); V3DNorm(L); prim.Normal(&N, &pi); if prim.m.Diffusive > 0.0 { dot := V3DDot(L,N); if dot > 0.0 { diff := dot * prim.m.Diffusive; var color_add Vector3D; V3DMov(color_add, lightiter.Color); V3DMulV3D(color_add, prim_color); V3DMul(color_add, diff); V3DAdd(color, color_add); //color += ((lightiter)->Color * prim_color) * diff; } } if prim.m.Specular > 0.0 { // I think this formula is messed up... // to change later var R1, R Vector3D; // R = L - N * L.Dot(N) * 2.0l; V3DMov(R1, N); R2 := V3DDot(L,N) * 2.0; V3DMul(R1, R2); V3DMov(R, L); V3DSub(R, R1); dot := V3DDot(ray.Direction,R); if dot > 0 { //dot = pow(dot, 20.0); dot *= dot; dot *= dot; dot *= dot;// dot *= dot; spec := dot * prim.m.Specular; var color_add Vector3D; V3DMov(color_add, lightiter.Color); V3DMul(color_add, spec); V3DAdd(color, color_add); } } refl := prim.m.Reflective; if refl > 0.0 && refl_depth < 4 { // Vector3D N = prim->Normal(pi); var N, R, R1, newPi Vector3D; prim.Normal(&N, &pi); // Vector3D R = ray.Direction - N * (ray.Direction.Dot(N) * 2.0); V3DMov(R, ray.Direction); V3DMov(R1, N); R2 := V3DDot(ray.Direction, N) * 2.0; V3DMul(R1, R2); V3DSub(R, R1); // Vector3D rcol( 0, 0, 0 ); V3DNew(rcol, 0.0, 0.0, 0.0); // var dist float64; // Ray tempr(pi + R * 0.0001, R); V3DMov(newPi, R); V3DMul(newPi, 0.0001); V3DAdd(newPi, pi); var tempr Ray; V3DMov(tempr.Origin, newPi); V3DMov(tempr.Direction, R); // rcol = Trace(tempr, refl_depth + 1); Trace(&rcol, &tempr, refl_depth+1); // color += rcol * refl * prim_color; V3DMul(rcol, refl); V3DMulV3D(rcol, prim_color); V3DAdd(color, rcol); } } V3DMov(RetVector, color); } func Render(img *image.RGBA, ThreadCount, ThreadId int, c chan int) { V3DNew(CameraPos, 0.0, 0.0, -5.0); var WX1, WX2, WY1, WY2 float64 = -2.0, 2.0, 1.5, -1.5; DX := float64((WX2 - WX1) / float64(img.Width())); DY := float64((WY2 - WY1) / float64(img.Height())); SX := WX1; SY := WY1 + DY * float64(ThreadId); var x, y int; for y = ThreadId; y < img.Height(); y += ThreadCount { SX = WX1; for x = 0; x < img.Width(); x++ { V3DNew(CameraTarget, SX, SY, 0.0); var ray Ray; V3DMov(ray.Origin, CameraPos); V3DMov(ray.Direction, CameraTarget); V3DSub(ray.Direction, ray.Origin); V3DNorm(ray.Direction); var color Vector3D; Trace(&color, &ray, 0); // float RGB -> unsigned int RGB var r, g, b int; r = int(color.x * 255.0); g = int(color.y * 255.0); b = int(color.z * 255.0); switch { case r > 255: r = 255; case r < 0: r = 0; } switch { case g > 255: g = 255; case g < 0: g = 0; } switch { case b > 255: b = 255; case b < 0: b = 0; } var cl image.RGBAColor; cl.R = uint8(r); cl.G = uint8(g); cl.B = uint8(b); cl.A = 255; img.Pixel[y][x] = cl; // iter SX += DX; } SY += DY * float64(ThreadCount); } // Send the thread id c <-ThreadId; }