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  }