Skip to content

Instantly share code, notes, and snippets.

@stoeckley
Forked from peterhellberg/gfx-tinykaboom.go
Created August 13, 2019 12:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save stoeckley/9828dd2ea8de4193db2badb23b32d8d0 to your computer and use it in GitHub Desktop.
Save stoeckley/9828dd2ea8de4193db2badb23b32d8d0 to your computer and use it in GitHub Desktop.
tinykaboom with gfx
package main
import (
"image"
"image/color"
"math"
"github.com/peterhellberg/gfx"
)
var (
sphereRadius = 1.5
noiseAmplitude = 0.2
)
func main() {
frame := render(640, 480, math.Pi/3.0)
gfx.SavePNG("/tmp/gfx-tinykaboom-step5.png", frame)
}
func render(width, height int, fov float64) *Frame {
frame := NewFrame(width, height)
fw, fh := float64(width), float64(height)
for j := 0; j < height; j++ {
for i := 0; i < width; i++ {
fi, fj := float64(i), float64(j)
dir := gfx.V3((fi+0.5)-fw/2, -(fj+0.5)+fh/2, -fh/(2*math.Tan(fov/2)))
if hit, ok := sphereTrace(gfx.V3(0, 0, 3), dir.Norm()); ok {
ld := gfx.V3(10, 10, 10).Sub(hit).Norm()
li := math.Max(0.4, ld.Dot(distanceFieldNormal(hit)))
frame.Buffer[i+j*width] = gfx.V3(1, 1, 1).Mul(li)
} else {
frame.Buffer[i+j*width] = gfx.V3(0.1, 0.1, 0.1)
}
}
}
return frame
}
func signedDistance(p gfx.Vec3) float64 {
d := math.Sin(16*p.X) * math.Sin(16*p.Y) * math.Sin(16*p.Z) * noiseAmplitude
return p.SqLen() - (sphereRadius + d)
}
func sphereTrace(orig, dir gfx.Vec3) (gfx.Vec3, bool) {
pos := orig
for i := 0; i < 128; i++ {
d := signedDistance(pos)
if d < 0 {
return pos, true
}
pos = pos.Add(dir.Mul(math.Max(d*0.1, 0.01)))
}
return pos, false
}
func distanceFieldNormal(pos gfx.Vec3) gfx.Vec3 {
const eps = 0.1
d := signedDistance(pos)
nx := signedDistance(pos.Add(gfx.V3(eps, 0, 0))) - d
ny := signedDistance(pos.Add(gfx.V3(0, eps, 0))) - d
nz := signedDistance(pos.Add(gfx.V3(0, 0, eps))) - d
return gfx.V3(nx, ny, nz).Norm()
}
type Frame struct {
Buffer []gfx.Vec3
bounds image.Rectangle
gfx.Float64Scaler
}
func NewFrame(w, h int) *Frame {
return &Frame{
Buffer: make([]gfx.Vec3, w*h),
bounds: gfx.IR(0, 0, w, h),
Float64Scaler: gfx.NewLinearScaler().Domain(0, 1).Range(0, 255),
}
}
func (f *Frame) At(x, y int) color.Color {
v := f.Buffer[x+y*f.bounds.Max.X]
return color.NRGBA{
uint8(f.ScaleFloat64(v.X)),
uint8(f.ScaleFloat64(v.Y)),
uint8(f.ScaleFloat64(v.Z)),
255,
}
}
func (f *Frame) Bounds() image.Rectangle {
return f.bounds
}
func (f *Frame) ColorModel() color.Model {
return color.NRGBAModel
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment