github.com/egonelbre/exp@v0.0.0-20240430123955-ed1d3aa93911/render/cosmic2.go (about) 1 package main 2 3 import ( 4 "image" 5 "image/color" 6 "image/png" 7 "math" 8 mr "math/rand" 9 "os" 10 11 m "github.com/go-gl/mathgl/mgl64" 12 ) 13 14 var ( 15 iterations int = 17 16 magic float64 = 0.53 17 volSteps int = 20 18 stepSize float64 = 0.1 19 zoom float64 = 0.800 20 tile float64 = 0.850 21 speed float64 = 0.010 22 brightness float64 = 0.0015 23 darkMatter float64 = 0.300 24 distFading float64 = 0.730 25 saturation float64 = 0.850 26 frequencyVariation float64 = 1.3 27 sparsity float64 = 0.5 28 ) 29 30 var cos = math.Cos 31 var sin = math.Sin 32 33 // https://www.shadertoy.com/view/XlfGRj 34 func Cosmic1(img *image.RGBA) { 35 size := img.Bounds().Size() 36 for y := 0; y < size.Y; y++ { 37 for x := 0; x < size.X; x++ { 38 // coords & direction 39 uv := m.Vec2{float64(x)/float64(size.X) - 0.5, float64(y)/float64(size.Y) - 0.5} 40 uv = m.Vec2{uv.X(), uv.Y() * float64(size.Y) / float64(size.X)} 41 dir := m.Vec3{uv[0] * zoom, uv[1] * zoom, 1.0} 42 from := m.Vec3{1, 0.5, 0.5} 43 44 // volumetric rendering 45 s := 0.1 46 fade := 1.0 47 v := m.Vec3{} 48 for r := 0; r < volSteps; r++ { 49 var p m.Vec3 50 p = from.Add(m.Vec3{s, s, s}) 51 p = m.Vec3{p.X() * dir.X(), p.Y() * dir.Y(), p.Z() * dir.Z()}.Mul(.5) 52 p = m.Vec3{tile, 0, 0}.Sub(m.Vec3{math.Mod(p.X(), tile*2), p.Y(), p.Z()}) 53 p = m.Vec3{math.Abs(p.X()), math.Abs(p.Y()), math.Abs(p.Z())} 54 var a, pa float64 55 for i := 0; i < iterations; i++ { 56 mv := p.Dot(p) - magic 57 p = m.Vec3{p.X() / mv, p.Y() / mv, p.Z() / mv} 58 a += math.Abs(p.Len() - pa) 59 pa = p.Len() 60 } 61 dm := math.Max(0, darkMatter-a*a*.001) 62 a *= a * a 63 if r > 6 { 64 fade *= 1 - dm 65 } 66 v = m.Vec3{v.X() + fade, v.Y() + fade, v.Z() + fade} 67 abf := a * brightness * fade 68 v = m.Vec3{v.X() + s*abf, v.Y() + s*s*abf, v.Z() + s*s*s*s*abf} 69 fade *= distFading 70 s += stepSize 71 } 72 73 //color adjust 74 z := v.Len() 75 v = v.Add(m.Vec3{ 76 lerp(z, v.X(), saturation), 77 lerp(z, v.Y(), saturation), 78 lerp(z, v.Z(), saturation)}) 79 c := m.Vec4{v.X() * .01, v.Y() * .01, v.Z() * 01, 1} 80 color := color.RGBA{toUint8(c.X()), toUint8(c.Y()), toUint8(c.Z()), toUint8(c.W())} 81 82 img.SetRGBA(x, y, color) 83 } 84 } 85 } 86 87 func toUint8(in float64) uint8 { 88 v := int(in * 256) 89 if v >= 256 { 90 return 255 91 } else if v <= 0 { 92 return 0 93 } 94 return uint8(v) 95 } 96 97 func lerp(v0, v1 float64, t float64) float64 { 98 return (1.0-t)*v0 + t*v1 99 } 100 101 // https://casual-effects.blogspot.com/2013/08/starfield-shader.html 102 // problem: output image is always the same mono color 103 func Cosmic2(img *image.RGBA) { 104 size := img.Bounds().Size() 105 var resolution = m.Vec2{float64(size.X), float64(size.Y)} 106 var invResolution = m.Vec2{resolution.Y(), resolution.X()} 107 108 rrt := float64(mr.Intn(size.X)) 109 rt := .5 + (rrt/3)/float64(size.X)*2 110 var rotate = m.Mat2{math.Cos(rt), math.Sin(rt), -(math.Sin(rt)), math.Cos(rt)} 111 112 for y := 0; y < size.Y; y++ { 113 for x := 0; x < size.X; x++ { 114 uv := m.Vec2{float64(size.X)*resolution.X() - 0.5, float64(size.Y)*resolution.Y() - 0.5*(resolution.Y()*invResolution.X())} 115 dir := m.Vec3{(uv.X() * zoom) * rotate.At(0, 0), uv.Y() * zoom, 1.0 * rotate.At(1, 0)} 116 s := 0.1 117 fade := 0.01 118 var c m.Vec3 119 var o m.Vec3 120 for r := 0; r < volSteps; r++ { 121 pp := m.Vec3{float64(x) + dir.X()*(s*0.5), float64(y) + dir.Y()*(s*0.5), dir.Z() * (s * 0.5)} 122 fv := func(in float64) float64 { 123 return math.Abs(frequencyVariation - math.Mod(in, (frequencyVariation*2))) 124 } 125 p := m.Vec3{fv(pp.X()), fv(pp.Y()), fv(pp.Z())} 126 prevLen := 0.0 127 a := 0.0 128 for i := 0; i < iterations; i++ { 129 p = m.Vec3{math.Abs(p.X()), math.Abs(p.Y()), math.Abs(p.Z())} 130 p = p.Mul(1.0/p.Dot(p) + (-sparsity)) 131 l := p.Len() 132 a += math.Abs(l - prevLen) 133 prevLen = l 134 } 135 a *= a * a 136 tw := m.Vec3{s, s * s, s * s * s} 137 tw.Mul((a*brightness + 1.0)) 138 tw.Mul(fade) 139 c = m.Vec3{c.X() + tw.X(), c.Y() + tw.Y(), c.Z() + tw.Z()} 140 fade *= distFading 141 s += stepSize 142 } 143 c = m.Vec3{math.Min(c.X(), 1.2), c.Y(), c.Z()} 144 intensity := math.Min((c.X() + c.Y() + c.Z()), 0.7) 145 sgn := m.Vec2{float64(x) + 1.0, float64(y) + 1.0} 146 sgn.Mul(2.0) 147 sgn.Sub(m.Vec2{1, 1}) 148 gradient := m.Vec2{intensity * sgn.X(), intensity * sgn.Y()} 149 cutoff := math.Max(math.Max(gradient.X(), gradient.Y())-0.1, 0.0) 150 c.Mul(math.Max(1.0-cutoff*6.0, 0.3)) 151 c = m.Vec3{lerp(c.X(), o.X(), intensity) - .004, lerp(c.Y(), o.Y(), intensity), lerp(c.Z(), o.Z(), intensity)} 152 o = c 153 154 clr := color.RGBA{toUint8(c.X()), toUint8(c.Y()), toUint8(c.Z()), toUint8(1)} 155 img.SetRGBA(x, y, clr) 156 } 157 } 158 } 159 160 func main() { 161 m := image.NewRGBA(image.Rect(0, 0, 640, 480)) 162 Cosmic1(m) 163 f, err := os.Create("main.png") 164 check(err) 165 check(png.Encode(f, m)) 166 check(f.Close()) 167 } 168 169 func check(err error) { 170 if err != nil { 171 panic(err) 172 } 173 }