Skip to content

Commit

Permalink
add auxiliary PNG file renderer
Browse files Browse the repository at this point in the history
  • Loading branch information
soypat committed Aug 29, 2024
1 parent 8c56094 commit 057dfcd
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 50 deletions.
55 changes: 5 additions & 50 deletions examples/image/image.go
Original file line number Diff line number Diff line change
@@ -1,24 +1,17 @@
package main

import (
"image"
"image/color"
"image/png"
"fmt"
"log"
"os"

"github.com/chewxy/math32"
"github.com/soypat/glgl/math/ms1"
"github.com/soypat/glgl/math/ms2"
"github.com/soypat/glgl/math/ms3"
"github.com/soypat/gsdf"
"github.com/soypat/gsdf/glbuild"
"github.com/soypat/gsdf/gleval"
"github.com/soypat/gsdf/glrender"
"github.com/soypat/gsdf/gsdfaux"
)

const size = 256
const dim = 20
const filename = "circle.png"

func scene() (glbuild.Shader2D, error) {
s, err := gsdf.NewCircle(dim)
Expand All @@ -38,51 +31,13 @@ func scene() (glbuild.Shader2D, error) {
}

func main() {
img := image.NewRGBA(image.Rect(0, 0, 2*size, size))
renderer, err := glrender.NewImageRendererSDF2(4096, colorConversion)
if err != nil {
log.Fatal(err)
}
s, err := scene()
if err != nil {
log.Fatal(err)
}

sdf, err := gleval.NewCPUSDF2(s)
err = gsdfaux.RenderPNGFile(filename, s, 1080, nil)
if err != nil {
log.Fatal(err)
}
err = renderer.Render(sdf, img, nil)
if err != nil {
log.Fatal(err)
}
fp, err := os.Create("circle.png")
if err != nil {
log.Fatal(err)
}
err = png.Encode(fp, img)
if err != nil {
log.Fatal(err)
}
}

func colorConversion(d float32) color.Color {
d /= dim * 2
var one = ms3.Vec{1, 1, 1}
var c ms3.Vec
if d > 0 {
c = ms3.Vec{0.9, 0.6, 0.3}
} else {
c = ms3.Vec{0.65, 0.85, 1.0}
}
c = ms3.Scale(1-math32.Exp(-6*math32.Abs(d)), c)
c = ms3.Scale(0.8+0.2*math32.Cos(150*d), c)
max := 1 - ms1.SmoothStep(0, 0.01, math32.Abs(d))
c = ms3.InterpElem(c, one, ms3.Vec{max, max, max})
return color.RGBA{
R: uint8(c.X * 255),
G: uint8(c.Y * 255),
B: uint8(c.Z * 255),
A: 255,
}
fmt.Println("PNG file rendered")
}
71 changes: 71 additions & 0 deletions gsdfaux/gsdfaux.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,18 @@ import (
"bytes"
"errors"
"fmt"
"image"
"image/color"
"image/png"
"io"
"os"

"time"

"github.com/chewxy/math32"
math "github.com/chewxy/math32"
"github.com/soypat/glgl/math/ms1"
"github.com/soypat/glgl/math/ms3"
"github.com/soypat/gsdf"
"github.com/soypat/gsdf/glbuild"
"github.com/soypat/gsdf/gleval"
Expand Down Expand Up @@ -132,3 +138,68 @@ func stopwatch() func() time.Duration {
return time.Since(start)
}
}

// RenderPNGFile renders a 2D SDF as an image and saves result to a PNG file with said filename.
// The image width is sized automatically from the image height argument to preserve SDF aspect ratio.
// If a nil color conversion function is passed then one is automatically chosen.
func RenderPNGFile(filename string, s glbuild.Shader2D, picHeight int, colorConversion func(float32) color.Color) error {
bb := s.Bounds()
sz := bb.Size()
if colorConversion == nil {
colorConversion = ColorConversionInigoQuilez(bb.Diagonal() / 3)
}
pixPerUnit := float64(picHeight) / float64(sz.Y)
picWidth := int(pixPerUnit * float64(sz.X))
img := image.NewRGBA(image.Rect(0, 0, picWidth, picHeight))
renderer, err := glrender.NewImageRendererSDF2(max(4096, picWidth), colorConversion)
if err != nil {
return err
}
sdf, err := gleval.NewCPUSDF2(s)
if err != nil {
return err
}
err = renderer.Render(sdf, img, nil)
if err != nil {
return err
}
fp, err := os.Create(filename)
if err != nil {
return err
}
defer fp.Close()
err = png.Encode(fp, img)
if err != nil {
return err
}
fp.Sync()
return nil
}

// ColorConversionInigoQuilez creates a new color conversion using [Inigo Quilez]'s style.
// A good value for characteristic distance is the bounding box diagonal divided by 3.
//
// [Inigo Quilez]: https://iquilezles.org/articles/distfunctions2d/
func ColorConversionInigoQuilez(characteristicDistance float32) func(float32) color.Color {
inv := 1. / characteristicDistance
return func(d float32) color.Color {
d *= inv
var one = ms3.Vec{X: 1, Y: 1, Z: 1}
var c ms3.Vec
if d > 0 {
c = ms3.Vec{X: 0.9, Y: 0.6, Z: 0.3}
} else {
c = ms3.Vec{X: 0.65, Y: 0.85, Z: 1.0}
}
c = ms3.Scale(1-math32.Exp(-6*math32.Abs(d)), c)
c = ms3.Scale(0.8+0.2*math32.Cos(150*d), c)
max := 1 - ms1.SmoothStep(0, 0.01, math32.Abs(d))
c = ms3.InterpElem(c, one, ms3.Vec{X: max, Y: max, Z: max})
return color.RGBA{
R: uint8(c.X * 255),
G: uint8(c.Y * 255),
B: uint8(c.Z * 255),
A: 255,
}
}
}

0 comments on commit 057dfcd

Please sign in to comment.