package main // RIGHT : x // FORWARD : Y // UP : Z import ( "fmt" "math" "runtime" "sync" rl "github.com/gen2brain/raylib-go/raylib" ) const WIDTH = 200 const HEIGHT = 200 const TEXTURE_SCALE = 2 const MAX_DIST = 500.0 const MAX_STEP = 100 const EPS = 0.1 const PI = math.Pi const SOFT_SHADOW_COEFF = 16 var lightPos Vector3 var scene SDF // radius, theta, phi var screenSpherical Vector3 var screenPhysicalSize float64 var cameraPos Vector3 func init() { lightPos = Vector3{100, -400, 400} cameraPos = Vector3{0, -10, 25} screenSpherical = Vector3{10, PI/2 + 0.2, PI / 2} screenPhysicalSize = 5 } func update(t float64) { sphereMat := RED_MAT sphereMat.specularFac = 0.5 sphere := TranslatedSDF{Sphere{10, sphereMat}, Vector3{-15, 100, 10}} sphere2 := TranslatedSDF{Sphere{10, sphereMat}, Vector3{15, 100, 10}} // boxMat := BLUE_MAT // box := TranslatedSDF{Box{Vector3{10, 2, 10}, boxMat}, Vector3{0, 100, 10}} // sphere := RepeatSDF{Sphere{20, sphereMat}, Vector3{50, 50, 50}} plane := Plane{Vector3{0, 0, 1}, WHITE_GREY_GRID_MAT} sphere.translate.Z = 20 * math.Sin(t) sphere2.translate.Z = 20 * math.Sin(t*0.5) scene = SmoothUnionSDF{SmoothUnionSDF{sphere, plane, 2.5}, sphere2, 2.5} // scene = UnionSDF{UnionSDF{sphere, plane}, sphere2} // scene = UnionSDF{plane, sphere} } func compute_pixel(y0 int, y1 int, wg *sync.WaitGroup, screenCenter Vector3, phiVec Vector3, thetaVec Vector3, image *rl.Image) { defer wg.Done() for pixelY := y0; pixelY < y1; pixelY++ { for pixelX := range WIDTH { sx := screenPhysicalSize * (float64(pixelX)/WIDTH - 0.5) sy := screenPhysicalSize * (float64(pixelY)/HEIGHT - 0.5) origin := screenCenter.Add(phiVec.Scale(-sx)).Add(thetaVec.Scale(sy)) direction := (origin.Sub(cameraPos)).Normalized() hit, colorVec := rayMarch(origin, direction, 5) var fr, fg, fb float64 if hit { fr, fg, fb = colorVec.Unpack() } else { fr, fg, fb = 0, 0, 0 } fr, fg, fb = Clamp(fr, 0, 255), Clamp(fg, 0, 255), Clamp(fb, 0, 255) r, g, b := uint8(fr), uint8(fg), uint8(fb) rl.ImageDrawPixel(image, int32(pixelX), int32(pixelY), rl.Color{R: r, G: g, B: b, A: 255}) } } } func genImage() *rl.Image { screenCartesian, _, thetaVec, phiVec := screenSpherical.SphericalToCartesian() screenCenter := cameraPos.Add(screenCartesian) image := rl.GenImageColor(WIDTH, HEIGHT, rl.Black) // numWorkers := 1 numWorkers := runtime.NumCPU() var wg sync.WaitGroup wg.Add(numWorkers) bandHeight := HEIGHT / numWorkers for worker := range numWorkers { startY := worker * bandHeight endY := startY + bandHeight if worker == numWorkers-1 { endY = HEIGHT } go compute_pixel(startY, endY, &wg, screenCenter, phiVec, thetaVec, image) } wg.Wait() return image } func main() { rl.InitWindow(WIDTH*TEXTURE_SCALE, HEIGHT*TEXTURE_SCALE, "raymarching") var texture rl.Texture2D time := rl.GetTime() t := 0.0 for !rl.WindowShouldClose() { new_time := rl.GetTime() dt := new_time - time t += dt * 0.4 time = new_time update(t) fmt.Println("FPS ", 1/dt) rl.BeginDrawing() rl.ClearBackground(rl.Black) texture = rl.LoadTextureFromImage(genImage()) rl.DrawTextureEx(texture, rl.Vector2{X: 0, Y: 0}, 0.0, TEXTURE_SCALE, rl.White) rl.EndDrawing() } }