Beispiel #1
0
func BloomFilter(img [][]geometry.Vec3, depth int) [][]geometry.Vec3 {
	data := make([][]geometry.Vec3, len(img))
	for i, _ := range data {
		data[i] = make([]geometry.Vec3, len(img[0]))
	}

	const box_width = 2
	factor := geometry.Float(1.0 / math.Pow(2*box_width+1, 2))

	source := img
	for iteration := 0; iteration < depth; iteration++ {
		for y := box_width; y < len(img)-box_width; y++ {
			for x := box_width; x < len(img[0])-box_width; x++ {
				var colour geometry.Vec3
				for dy := -box_width; dy <= box_width; dy++ {
					for dx := -box_width; dx <= box_width; dx++ {
						colour.AddInPlace(source[y+dy][x+dx])
					}
				}
				data[y][x] = colour.Mult(factor)
			}
		}
		fmt.Printf("\rPost Processing %3.0f%%   \r", 100*float64(iteration)/float64(depth))
		source, data = data, source
	}
	return source
}
Beispiel #2
0
func MonteCarloPixel(results chan Result, scene *geometry.Scene, diffuseMap /*, causticsMap*/ *kd.KDNode, start, rows int, rand *rand.Rand) {
	samples := Config.NumRays
	var px, py, dy, dx geometry.Float
	var direction, contribution geometry.Vec3

	for y := start; y < start+rows; y++ {
		py = scene.Height - scene.Height*2*geometry.Float(y)/geometry.Float(scene.Rows)
		for x := 0; x < scene.Cols; x++ {
			px = -scene.Width + scene.Width*2*geometry.Float(x)/geometry.Float(scene.Cols)
			var colourSamples geometry.Vec3
			if x >= Config.Skip.Left && x < scene.Cols-Config.Skip.Right &&
				y >= Config.Skip.Top && y < scene.Rows-Config.Skip.Bottom {
				for sample := 0; sample < samples; sample++ {
					dy, dx = geometry.Float(rand.Float32())*scene.PixH, geometry.Float(rand.Float32())*scene.PixW
					direction = geometry.Vec3{
						px + dx - scene.Camera.Origin.X,
						py + dy - scene.Camera.Origin.Y,
						-scene.Camera.Origin.Z,
					}.Normalize()

					contribution = Radiance(geometry.Ray{scene.Camera.Origin, direction}, scene, diffuseMap /*causticsMap,*/, 0, 1.0, rand)
					colourSamples.AddInPlace(contribution)
				}
			}
			results <- Result{x, y, colourSamples.Mult(1.0 / geometry.Float(samples))}
		}
	}
}
Beispiel #3
0
func ClosestIntersection(shapes []*geometry.Shape, ray geometry.Ray) (*geometry.Shape, geometry.Float) {
	var closest *geometry.Shape
	bestHit := geometry.Float(math.Inf(+1))
	for _, shape := range shapes {
		if hit := shape.Intersects(&ray); hit > 0 && hit < bestHit {
			bestHit = hit
			closest = shape
		}
	}
	return closest, bestHit
}
Beispiel #4
0
func DiffusePhoton(scene []*geometry.Shape, emitter *geometry.Shape, ray geometry.Ray, colour geometry.Vec3, result chan<- PhotonHit, alpha geometry.Float, depth int, rand *rand.Rand) {
	if geometry.Float(rand.Float32()) > alpha {
		return
	}
	if shape, distance := ClosestIntersection(scene, ray); shape != nil {
		impact := ray.Origin.Add(ray.Direction.Mult(distance))

		if depth == 0 && emitter == shape {
			// Leave the emitter first
			nextRay := geometry.Ray{impact, ray.Direction}
			DiffusePhoton(scene, emitter, nextRay, colour, result, alpha, depth, rand)
		} else {
			normal := shape.NormalDir(impact).Normalize()
			reverse := ray.Direction.Mult(-1)
			outgoing := normal
			if normal.Dot(reverse) < 0 {
				outgoing = normal.Mult(-1)
			}
			strength := colour.Mult(alpha / (1 + distance))
			result <- PhotonHit{impact, strength, ray.Direction, uint8(depth)}

			if shape.Material == geometry.DIFFUSE {
				// Random bounce for color bleeding
				u := normal.Cross(reverse).Normalize().Mult(geometry.Float(rand.NormFloat64() * 0.5))
				v := u.Cross(normal).Normalize().Mult(geometry.Float(rand.NormFloat64() * 0.5))
				bounce := geometry.Vec3{
					u.X + outgoing.X + v.X,
					u.Y + outgoing.Y + v.Y,
					u.Z + outgoing.Z + v.Z,
				}
				bounceRay := geometry.Ray{impact, bounce.Normalize()}
				bleedColour := colour.MultVec(shape.Colour).Mult(alpha / (1 + distance))
				DiffusePhoton(scene, shape, bounceRay, bleedColour, result, alpha*0.66, depth+1, rand)
			}
			// Store Shadow Photons
			shadowRay := geometry.Ray{impact, ray.Direction}
			DiffusePhoton(scene, shape, shadowRay, geometry.Vec3{0, 0, 0}, result, alpha*0.66, depth+1, rand)
		}
	}
}
Beispiel #5
0
func EmitterSampling(point, normal geometry.Vec3, shapes []*geometry.Shape, rand *rand.Rand) geometry.Vec3 {
	incomingLight := geometry.Vec3{0, 0, 0}

	for _, shape := range shapes {
		if !shape.Emission.IsZero() {
			// It's a light source
			direction := shape.NormalDir(point).Mult(-1)
			u := direction.Cross(normal).Normalize().Mult(geometry.Float(rand.NormFloat64() * 0.3))
			v := direction.Cross(u).Normalize().Mult(geometry.Float(rand.NormFloat64() * 0.3))

			direction.X += u.X + v.X
			direction.Y += u.Y + v.Y
			direction.Z += u.Z + v.Z
			ray := geometry.Ray{point, direction.Normalize()}

			if object, distance := ClosestIntersection(shapes, ray); object == shape {
				incomingLight.AddInPlace(object.Emission.Mult(direction.Dot(normal) / (1 + distance)))
			}
		}
	}
	return incomingLight
}
Beispiel #6
0
func PhotonChunk(scene []*geometry.Shape, traceFunc RayFunc, shape *geometry.Shape, factor, start, chunksize int, result chan<- PhotonHit, done chan<- bool, rand *rand.Rand) {
	for i := 0; i < chunksize; i++ {
		longitude := (start*chunksize + i) / factor
		latitude := (start*chunksize + i) % factor

		//fmt.Println("Lo La:", longitude, latitude)

		sign := -2.0*float64(longitude%2.0) + 1.0
		phi := 2.0 * math.Pi * float64(longitude) / float64(factor)
		theta := math.Pi * float64(latitude) / float64(factor)

		//fmt.Println("S, T, P:", sign, theta, phi)

		x, y, z := math.Sin(theta)*math.Cos(phi),
			sign*math.Cos(theta),
			math.Sin(theta)*math.Sin(phi)

		direction := geometry.Vec3{geometry.Float(x), geometry.Float(y), geometry.Float(z)}
		ray := geometry.Ray{shape.Position, direction.Normalize()}
		traceFunc(scene, shape, ray, shape.Emission, result, 1.0, 0, rand)
	}
	done <- true
}
Beispiel #7
0
func CorrectColour(x geometry.Float) geometry.Float {
	return geometry.Float(math.Pow(float64(x), 1.0/Config.GammaFactor)*255 + 0.5)
}
Beispiel #8
0
func Render(scene geometry.Scene) image.Image {
	img := image.NewNRGBA(image.Rect(0, 0, scene.Cols, scene.Rows))
	pixels := make(chan Result, 128)

	workload := scene.Rows / Config.Chunks

	startTime := time.Now()
	globals /*, caustics*/ := GenerateMaps(scene.Objects)
	fmt.Println(" Done!")
	//fmt.Printf("Diffuse Map depth: %v Caustics Map depth: %v\n", globals.Depth(), caustics.Depth())
	fmt.Printf("Diffuse Map depth: %v\n", globals.Depth())
	fmt.Printf("Photon Maps Done. Generation took: ")
	stopTime := time.Now()
	PrintDuration(stopTime.Sub(startTime))
	fmt.Println()

	startTime = time.Now()
	for y := 0; y < scene.Rows; y += workload {
		go MonteCarloPixel(pixels, &scene, globals /*caustics,*/, y, workload, rand.New(rand.NewSource(rand.Int63())))
	}

	// Write targets for after effects
	data := make([][]geometry.Vec3, scene.Rows)
	peaks := make([][]geometry.Vec3, scene.Rows)
	for i, _ := range data {
		data[i] = make([]geometry.Vec3, scene.Cols)
		peaks[i] = make([]geometry.Vec3, scene.Cols)
	}

	// Collect results
	var so_far time.Duration
	var highest, lowest geometry.Vec3
	highValue, lowValue := geometry.Float(0), geometry.Float(math.Inf(+1))
	numPixels := scene.Rows * scene.Cols
	for i := 0; i < numPixels; i++ {
		// Print progress information every 500 pixels
		if i%500 == 0 {
			fmt.Printf("\rRendering %6.2f%%", 100*float64(i)/float64(scene.Rows*scene.Cols))
			so_far = time.Now().Sub(startTime)
			remaining := time.Duration((so_far.Seconds()/float64(i))*float64(numPixels-i)) * time.Second
			fmt.Printf(" (Time Remaining: ")
			PrintDuration(remaining)
			fmt.Printf(" at %0.1f pps)                \r", float64(i)/so_far.Seconds())
		}
		pixel := <-pixels

		if low := pixel.colour.Abs(); low < lowValue {
			lowValue = low
			lowest = pixel.colour
		}
		if high := pixel.colour.Abs(); high > highValue {
			highValue = high
			highest = pixel.colour
		}
		data[pixel.y][pixel.x] = pixel.colour.CLAMPF()
		peaks[pixel.y][pixel.x] = pixel.colour.PEAKS(0.8)
	}
	fmt.Println("\rRendering 100.00%")

	bloomed := BloomFilter(peaks, Config.BloomFactor)

	for y := 0; y < len(data); y++ {
		for x := 0; x < len(data[0]); x++ {
			colour := data[y][x].Add(bloomed[y][x])
			colour = CorrectColours(colour).CLAMP()
			img.SetNRGBA(x, y, color.NRGBA{uint8(colour.X), uint8(colour.Y), uint8(colour.Z), 255})
		}
	}
	stopTime = time.Now()
	clearLine()
	fmt.Println("\rDone!")
	fmt.Printf("Brightest pixel: %v intensity: %v\n", highest, highValue)
	fmt.Printf("Dimmest pixel: %v intensity: %v\n", lowest, lowValue)

	// Print duration
	fmt.Printf("Rendering took ")
	PrintDuration(stopTime.Sub(startTime))
	fmt.Println()

	return img
}
Beispiel #9
0
func main() {
	flag.Parse()

	rand.Seed(*seed)

	gorender.Config.NumRays = *rays
	//gorender.Config.Caustics = *caustics
	gorender.Config.BloomFactor = *bloom
	gorender.Config.MinDepth = *mindepth
	gorender.Config.GammaFactor = *gamma

	gorender.Config.Skip.Top = *skipTop
	gorender.Config.Skip.Left = *skipLeft
	gorender.Config.Skip.Right = *skipRight
	gorender.Config.Skip.Bottom = *skipBottom

	wantedCPUs := *cores
	if wantedCPUs < 1 {
		wantedCPUs = 1
	}
	fmt.Printf("Running on %v/%v CPU cores\n", wantedCPUs, runtime.NumCPU())
	runtime.GOMAXPROCS(wantedCPUs)

	if wantedCPUs > *chunks {
		log.Print("Warning: chunks setting is lower than the number of cores - not all cores will be used")
	}

	if *rows%*chunks != 0 {
		log.Fatal("The images height needs to be evenly divisible by chunks")
	}

	gorender.Config.Chunks = *chunks

	if *cpuprofile != "" {
		cpupf, err := os.Create(*cpuprofile)
		fmt.Println("Writing CPU profiling information to file:", *cpuprofile)
		if err != nil {
			log.Fatal(err)
		}
		pprof.StartCPUProfile(cpupf)
		defer pprof.StopCPUProfile()
	}

	if *memprofile != "" {
		fmt.Println("Writing Memory profiling information to file:", *memprofile)
	} else {
		runtime.MemProfileRate = 0
	}

	file, err := os.OpenFile(*output, os.O_CREATE|os.O_WRONLY, 0666)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("Rendering %vx%v sized image with %v rays per pixel to %v\n", *cols, *rows, *rays, *output)

	// "Real world" frustrum
	height := geometry.Float(2.0)
	width := height * (geometry.Float(*cols) / geometry.Float(*rows)) // Aspect ratio?
	angle := math.Pi * geometry.Float(*fov) / 180.0

	scene := geometry.ParseScene(*input, width, height, angle, *cols, *rows)
	img := gorender.Render(scene)

	if err = png.Encode(file, img); err != nil {
		log.Fatal(err)
	}

	if *memprofile != "" {
		mempf, err := os.Create(*memprofile)
		if err != nil {
			log.Fatal(err)
		}
		pprof.WriteHeapProfile(mempf)
		defer mempf.Close()
	}
}
Beispiel #10
0
func Radiance(ray geometry.Ray, scene *geometry.Scene, diffuseMap /*, causticsMap*/ *kd.KDNode, depth int, alpha float64, rand *rand.Rand) geometry.Vec3 {

	if depth > Config.MinDepth && rand.Float64() > alpha {
		return geometry.Vec3{0, 0, 0}
	}

	if shape, distance := ClosestIntersection(scene.Objects, ray); shape != nil {
		impact := ray.Origin.Add(ray.Direction.Mult(distance))
		normal := shape.NormalDir(impact).Normalize()
		reverse := ray.Direction.Mult(-1)

		contribution := shape.Emission
		outgoing := normal
		if normal.Dot(reverse) < 0 {
			outgoing = normal.Mult(-1)
		}

		if shape.Material == geometry.DIFFUSE {
			var /*causticLight,*/ directLight geometry.Vec3

			/*nodes := causticsMap.Neighbors(impact, 0.1)
			for _, e := range nodes {
				photon := causticPhotons[e.Position]
				dist := photon.Location.Distance(impact)
				light := photon.Photon.Mult(outgoing.Dot(photon.Incomming.Mult(-1 / math.Pi * (1 + dist))))
				causticLight.AddInPlace(light)
			}
			if len(nodes) > 0 {
				causticLight = causticLight.Mult(1.0 / float64(len(nodes)))
			}*/

			directLight = EmitterSampling(impact, normal, scene.Objects, rand)

			u := normal.Cross(reverse).Normalize().Mult(geometry.Float(rand.NormFloat64() * 0.5))
			v := u.Cross(normal).Normalize().Mult(geometry.Float(rand.NormFloat64() * 0.5))

			bounceDirection := geometry.Vec3{
				u.X + outgoing.X + v.X,
				u.Y + outgoing.Y + v.Y,
				u.Z + outgoing.Z + v.Z,
			}
			bounceRay := geometry.Ray{impact, bounceDirection.Normalize()}
			indirectLight := Radiance(bounceRay, scene, diffuseMap /*causticsMap,*/, depth+1, alpha*0.9, rand)
			dot := outgoing.Dot(reverse)
			diffuseLight := geometry.Vec3{
				(shape.Colour.X * (directLight.X + indirectLight.X) /*+ causticLight.X*/) * dot,
				(shape.Colour.Y * (directLight.Y + indirectLight.Y) /*+ causticLight.Y*/) * dot,
				(shape.Colour.Z * (directLight.Z + indirectLight.Z) /*+ causticLight.Z*/) * dot,
			}

			return contribution.Add(diffuseLight)

		}
		if shape.Material == geometry.SPECULAR {
			reflectionDirection := ray.Direction.Sub(normal.Mult(2 * outgoing.Dot(ray.Direction)))
			reflectedRay := geometry.Ray{impact, reflectionDirection.Normalize()}
			incomingLight := Radiance(reflectedRay, scene, diffuseMap /*causticsMap,*/, depth+1, alpha*0.99, rand)
			return incomingLight.Mult(outgoing.Dot(reverse))
		}

		if shape.Material == geometry.REFRACTIVE {
			var n1, n2 float64
			if normal.Dot(outgoing) < 0 {
				// Leave the glass
				n1, n2 = GLASS, AIR
			} else {
				n1, n2 = AIR, GLASS
			}

			factor := n1 / n2
			cosTi := float64(normal.Dot(reverse))
			sinTi := math.Sqrt(1 - cosTi*cosTi) // sin² + cos² = 1
			sqrt := math.Sqrt(math.Max(1.0-math.Pow(factor*sinTi, 2), 0))
			// Rs
			top := n1*cosTi - n2*sqrt
			bottom := n1*cosTi + n2*sqrt
			Rs := math.Pow(top/bottom, 2)
			// Rp
			top = n1*sqrt - n2*cosTi
			bottom = n1*sqrt + n2*cosTi
			Rp := math.Pow(top/bottom, 2)

			R := (Rs*Rs + Rp*Rp) / 2.0

			// Approximate:
			R = math.Pow((n1-n2)/(n1+n2), 2)
			// SmallPT formula
			//R = R + (1 - R) * math.Pow(1 - cosTi, 5)
			T := 1.0 - R

			if math.IsNaN(R) {
				fmt.Printf("into: %v, sqrt: %v\n", n2 > n1, sqrt)
				fmt.Printf("cos: %v, sin: %v\n", cosTi, sinTi)
				fmt.Printf("n1: %v, n2: %v\n", n1, n2)
				fmt.Printf("Top: %v, Bottom: %v\n", top, bottom)
				fmt.Printf("Rs: %v, Rp: %v\n", Rs, Rp)
				fmt.Printf("R: %v, T: %v\n", R, T)
				panic("NAN!")
			}

			totalReflection := false
			if n1 > n2 {
				maxAngle := math.Asin(n2 / n1)
				actualAngle := math.Asin(sinTi)

				if actualAngle > maxAngle {
					totalReflection = true
				}
				totalReflection = totalReflection
			}

			if totalReflection {
				reflectionDirection := ray.Direction.Sub(outgoing.Mult(2 * outgoing.Dot(ray.Direction)))
				reflectedRay := geometry.Ray{impact, reflectionDirection.Normalize()}
				return Radiance(reflectedRay, scene, diffuseMap /*causticsMap,*/, depth+1, alpha*0.9, rand)
			} else {
				reflectionDirection := ray.Direction.Sub(outgoing.Mult(2 * outgoing.Dot(ray.Direction)))
				reflectedRay := geometry.Ray{impact, reflectionDirection.Normalize()}
				reflectedLight := Radiance(reflectedRay, scene, diffuseMap /*causticsMap,*/, depth+1, alpha*0.9, rand).Mult(geometry.Float(R))

				nDotI := float64(normal.Dot(ray.Direction))
				trasmittedDirection := ray.Direction.Mult(geometry.Float(factor))
				term2 := factor * nDotI
				term3 := math.Sqrt(1 - factor*factor*(1-nDotI*nDotI))

				trasmittedDirection = trasmittedDirection.Add(normal.Mult(geometry.Float(term2 - term3)))
				transmittedRay := geometry.Ray{impact, trasmittedDirection.Normalize()}
				transmittedLight := Radiance(transmittedRay, scene, diffuseMap /*causticsMap,*/, depth+1, alpha*0.9, rand).Mult(geometry.Float(T))
				return reflectedLight.Add(transmittedLight).Mult(outgoing.Dot(reverse))
			}
		}
		panic("Material without property encountered!")
	}

	return geometry.Vec3{0, 0, 0}
}