Skip to content

Instantly share code, notes, and snippets.

@1xch
Created June 23, 2016 20:05
Show Gist options
  • Save 1xch/3cd7c90353e8990bab7992fb6b8e089c to your computer and use it in GitHub Desktop.
Save 1xch/3cd7c90353e8990bab7992fb6b8e089c to your computer and use it in GitHub Desktop.
package cosmic
import (
"fmt"
"math"
"math/rand"
"time"
cr "github.com/Laughs-In-Flowers/ifriit/lib/art/color"
"github.com/Laughs-In-Flowers/ifriit/lib/art/image"
mt "github.com/Laughs-In-Flowers/ifriit/lib/art/math"
)
// https://www.shadertoy.com/view/XlfGRj
type Cosmic struct {
VolumeSteps, Iterations int
Zoom, Speed, Tile, Search float64
Magic, Brightness, Saturation float64
DarkMatter, DistanceFading, StepSize float64
seedCoordinatesSet bool
seedCoordinates [2]float64
rotationSet bool
rotation [2]mt.M2
directionSet bool
direction mt.V3
fromSet bool
from mt.V3
debug bool
reuse bool
}
func NewCosmic(steps,
iterations int,
zoom,
speed,
tile,
search,
magic,
brightness,
saturation,
dark,
fading,
step float64,
debug,
reuse bool) *Cosmic {
return &Cosmic{
steps,
iterations,
zoom,
speed,
tile,
search,
magic,
brightness,
saturation,
dark,
fading,
step,
false,
[2]float64{},
false,
[2]mt.M2{},
false,
mt.V3{},
false,
mt.V3{},
debug,
reuse,
}
}
var DefaultCosmic = NewCosmic(
20,
17,
0.8,
0.010,
0.85,
1000000,
0.53,
0.0015,
0.850,
0.300,
0.730,
0.1,
false,
false,
)
const cosmicString = `Cosmic Shader
-------------
VolumeSteps: %d
Iterations: %d
Zoom: %f
Speed: %f
Tile: %f
Search: %f
Magic: %f
Brightness: %f
Saturation: %f
DarkMatter: %f
DistanceFading: %f
StepSize: %f
SeedCoordinates: %v
Rotation: %v
Direction: %v
From: %v
Debug: %t
Reuse: %t
---------
`
func (c *Cosmic) String() string {
return fmt.Sprintf(cosmicString,
c.VolumeSteps,
c.Iterations,
c.Zoom,
c.Speed,
c.Tile,
c.Search,
c.Magic,
c.Brightness,
c.Saturation,
c.DarkMatter,
c.DistanceFading,
c.StepSize,
c.seedCoordinates,
c.rotation,
c.direction,
c.from,
c.debug,
c.reuse,
)
}
func (c *Cosmic) Render(m image.Layer) {
size := m.Bounds().Size()
res := mt.V2{float64(size.X), float64(size.Y)}
for y := 0; y < size.Y; y++ {
for x := 0; x < size.X; x++ {
p := mt.V2{float64(x), float64(y)}
uv := mt.V2{float64(x) / res.X, float64(y) / res.Y}
color := c.Shade(p, uv, res)
m.Set(x, y, color.RGBA())
}
}
if c.debug {
fmt.Println(c.String())
}
if !c.reuse {
c.Reset()
}
}
func (c *Cosmic) currentSeedCoords() (float64, float64) {
if !c.seedCoordinatesSet {
c.seedCoordinates[0] = float64(rand.Intn(int(c.Search)))
c.seedCoordinates[1] = float64(rand.Intn(int(c.Search)))
c.seedCoordinatesSet = true
}
return c.seedCoordinates[0], c.seedCoordinates[1]
}
func rot(v float64) mt.M2 {
return mt.M2{math.Cos(v), math.Sin(v), -math.Sin(v), math.Cos(v)}
}
func (c *Cosmic) Rotation(resolution mt.V2) (mt.M2, mt.M2) {
if !c.rotationSet {
mx, my := c.currentSeedCoords()
rot1 := rot(.5 + mx/resolution.X*2)
rot2 := rot(.8 + my/resolution.Y*2)
c.rotation[0] = rot1
c.rotation[1] = rot2
}
return c.rotation[0], c.rotation[1]
}
func (c *Cosmic) Direction(uv mt.V2, rot1, rot2 mt.M2) mt.V3 {
if !c.directionSet {
dir := mt.V3{(uv.X * c.Zoom), (uv.Y * c.Zoom), 1}
dr := rot1.Mul2x1(mt.V2{dir.X, dir.Y})
dr1 := rot2.Mul2x1(dr)
c.direction = mt.V3{dr1.X, dr1.Y, dir.Z}
}
return c.direction
}
func (c *Cosmic) From(rot1, rot2 mt.M2) mt.V3 {
if !c.fromSet {
from := mt.V3{1, 0.5, 0.5}
fr := rot1.Mul2x1(mt.V2{from.X, from.Y})
fr1 := rot2.Mul2x1(fr)
c.from = mt.V3{fr1.X, fr1.Y, from.Z}
}
return c.from
}
func (c *Cosmic) tileFold(in mt.V3) mt.V3 {
t1 := mt.V3{c.Tile, c.Tile, c.Tile}
t2 := mt.V3{math.Mod(in.X, c.Tile*2), math.Mod(in.Y, c.Tile*2), math.Mod(in.Z, c.Tile*2)}
return mt.V3{math.Abs(t1.X - t2.X), math.Abs(t1.Y - t2.Y), math.Abs(t1.Z - t2.Z)}
}
func lerp(a, b, p float64) float64 { return (1.0-p)*a + p*b }
func (c *Cosmic) Shade(p, uv, res mt.V2) cr.Color {
uv.Y *= res.Y / res.X
rot1, rot2 := c.Rotation(res)
dir := c.Direction(uv, rot1, rot2)
from := c.From(rot1, rot2)
TIME := float64(time.Now().UTC().UnixNano())*c.Speed + 0.25
from.Add(mt.V3{TIME * 2, TIME, -2})
s := float64(0.1)
fade := float64(1)
v := mt.V3{}
for r := 0; r < c.VolumeSteps; r++ {
p := from.Add(dir.Scale(s * 0.5))
p = c.tileFold(p)
var pa, a float64
for i := 0; i < c.Iterations; i++ {
p = p.Abs().Scale(1.0 / p.Len2()).Offset(-c.Magic)
a += math.Abs(p.Len() - pa)
}
dm := math.Max(0, c.DarkMatter-a*a*.001)
a *= a * a
if r > 6 {
fade *= 1 - dm
}
v = v.Offset(fade)
v = v.Add(mt.V3{s, s * s, s * s * s * s}.Scale(a * fade * c.Brightness))
fade *= c.DistanceFading
s += c.StepSize
}
lv := v.Len()
v = mt.V3{lerp(lv, v.X, c.Saturation), lerp(lv, v.Y, c.Saturation), lerp(lv, v.Z, c.Saturation)}
v = v.Scale(0.01)
return cr.C3(v.X, v.Y, v.Z)
}
func (c *Cosmic) Reset() {
c.seedCoordinatesSet = false
c.rotationSet = false
c.directionSet = false
c.fromSet = false
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment