From 038145d3e5bb406df9b597b94a55b46977867c47 Mon Sep 17 00:00:00 2001 From: Crizomb Date: Sun, 28 Sep 2025 00:47:22 +0200 Subject: [PATCH 1/2] Realtime + refactor --- src/main.go | 134 ++++++++++++++++++++++++++++++++++++++++++++ src/ray_marching.go | 105 ---------------------------------- 2 files changed, 134 insertions(+), 105 deletions(-) create mode 100644 src/main.go diff --git a/src/main.go b/src/main.go new file mode 100644 index 0000000..4315a09 --- /dev/null +++ b/src/main.go @@ -0,0 +1,134 @@ +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 = 4 + +const MAX_DIST = 1000.0 +const MAX_STEP = 200 +const EPS = 0.1 +const PI = math.Pi + +const SOFT_SHADOW_COEFF = 32 + +var lightPos Vector3 +var scene SDF + +// radius, theta, phi +var screenSpherical Vector3 +var screenPhysicalSize float64 +var cameraPos Vector3 + +var sphere TranslatedSDF + +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{0, 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) + scene = SmoothUnionSDF{sphere, plane, 2.5} + // 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) + 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 + 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() + } +} diff --git a/src/ray_marching.go b/src/ray_marching.go index 0dc2551..d7b806f 100644 --- a/src/ray_marching.go +++ b/src/ray_marching.go @@ -6,51 +6,8 @@ package main import ( "math" - "runtime" - "sync" - - rl "github.com/gen2brain/raylib-go/raylib" ) -const MAX_DIST = 1000.0 -const MAX_STEP = 1000 -const EPS = 0.01 -const WIDTH = 500 -const HEIGHT = 500 -const PI = math.Pi - -const SOFT_SHADOW_COEFF = 32 - -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 - - sphereMat := RED_MAT - sphereMat.specularFac = 0.5 - sphere := TranslatedSDF{Sphere{10, sphereMat}, Vector3{0, 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} - - scene = SmoothUnionSDF{sphere, plane, 2.5} - // scene = UnionSDF{plane, sphere} - -} - func phongShading(point Vector3, normal Vector3, mat Material) Vector3 { lightVec := (lightPos.Sub(point)).Normalized() reflectLight := Reflect(lightVec, normal) @@ -102,65 +59,3 @@ func rayMarch(origin Vector3, direction Vector3) (bool, Vector3) { } return false, GREY } - -func genImage() *rl.Image { - screenCartesian, _, thetaVec, phiVec := screenSpherical.SphericalToCartesian() - screenCenter := cameraPos.Add(screenCartesian) - - image := rl.GenImageColor(WIDTH, HEIGHT, rl.Black) - - 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 func(y0, y1 int) { - 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) - 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}) - } - } - }(startY, endY) - } - - wg.Wait() - return image -} - -func main() { - rl.InitWindow(WIDTH, HEIGHT, "raymarching") - texture := rl.LoadTextureFromImage(genImage()) - - for !rl.WindowShouldClose() { - rl.BeginDrawing() - rl.ClearBackground(rl.Black) - rl.DrawTexture(texture, 0, 0, rl.White) - rl.EndDrawing() - } -} From 30ba975eb53287e3569d6590774b977160923309 Mon Sep 17 00:00:00 2001 From: Crizomb Date: Sun, 28 Sep 2025 00:52:01 +0200 Subject: [PATCH 2/2] small adjusts --- src/main.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main.go b/src/main.go index 4315a09..1868e3b 100644 --- a/src/main.go +++ b/src/main.go @@ -13,16 +13,16 @@ import ( rl "github.com/gen2brain/raylib-go/raylib" ) -const WIDTH = 200 -const HEIGHT = 200 +const WIDTH = 100 +const HEIGHT = 100 const TEXTURE_SCALE = 4 -const MAX_DIST = 1000.0 -const MAX_STEP = 200 +const MAX_DIST = 500.0 +const MAX_STEP = 100 const EPS = 0.1 const PI = math.Pi -const SOFT_SHADOW_COEFF = 32 +const SOFT_SHADOW_COEFF = 16 var lightPos Vector3 var scene SDF