Exemple #1
0
func renderer(
	width, height int,
	renderedImages chan *hdrimage.Image,
	scene *scene.Scene,
	seed int64,
	totalSamples int,
	threads int,
	quiet bool,
) {
	randomGen := random.New(seed)

	sampleCounter := rpc.NewSampleCounter(totalSamples)
	var bar *progress.ProgressBar
	if !quiet {
		bar = progress.StartProgressBar(totalSamples, "rendering samples")
	}

	wg := sync.WaitGroup{}
	wg.Add(threads)

	defer func() {
		wg.Wait()
		if !quiet {
			bar.Done()
		}
	}()

	for i := 0; i < threads; i++ {
		go func(seed int64) {
			defer wg.Done()

			raytracer := raytracer.Raytracer{
				Scene:  scene,
				Random: random.New(seed),
			}

			image := hdrimage.New(width, height)
			image.Divisor = 0

			for {
				if sampleCounter.Dec(1) == 0 {
					renderedImages <- image
					return
				}
				raytracer.Sample(image)
				if !quiet {
					bar.Add(1)
				}
			}
		}(randomGen.NewSeed())
	}
}
Exemple #2
0
// JoinSamples combines samples into a single image
func JoinSamples(
	renderedImages <-chan *hdrimage.Image,
	width int,
	height int,
) *hdrimage.Image {
	averageImage := hdrimage.New(width, height)
	averageImage.Divisor = 0
	for image := range renderedImages {
		averageImage.Add(image)
		averageImage.Divisor += image.Divisor
	}
	return averageImage
}
Exemple #3
0
// Sample works like StoreSample(), but instead of storing the image internally,
// returns a new image.
func (cr *ConcurrentRaytracer) Sample(settings *SampleSettings) (*hdrimage.Image, error) {
	unit := <-cr.units

	if unit.raytracer.Scene == nil {
		return nil, fmt.Errorf("N/A scene")
	}
	image := hdrimage.New(settings.Width, settings.Height)
	image.Divisor = 0

	for i := 0; i < settings.SamplesAtOnce; i++ {
		unit.raytracer.Sample(image)
	}

	cr.units <- unit

	return image, nil
}
Exemple #4
0
// StoreSample renders the scene into an internal image. You can (and should)
// call it multiple times, in parallel, and when you're finished you can get
// the merged samples with GetImage(). StoreSample will block if the parallel
// calls exceed the parallelSamples value, and wait for other samples to finish.
func (cr *ConcurrentRaytracer) StoreSample(settings *SampleSettings) error {
	unit := <-cr.units
	if unit.raytracer.Scene == nil {
		return fmt.Errorf("N/A scene")
	}

	if unit.image == nil {
		unit.image = hdrimage.New(settings.Width, settings.Height)
		unit.image.Divisor = 0
	}

	for i := 0; i < settings.SamplesAtOnce; i++ {
		unit.raytracer.Sample(unit.image)
	}

	cr.units <- unit
	return nil
}
Exemple #5
0
// GetImage collects all samples up to this moment (and waits for those that
// are currently being rendered to finish), and merges them.
// StoreSample() can be called during calling this function, and will block until
// it finishes. Next samples will start from zero (e.g. the base image is reset)
func (cr *ConcurrentRaytracer) GetImage() *hdrimage.Image {
	var mergedSamples *hdrimage.Image

	units := cr.getAllUnits()
	for _, unit := range units {
		if mergedSamples == nil {
			mergedSamples = unit.image
		} else if unit.image != nil {
			mergedSamples.Add(unit.image)
			mergedSamples.Divisor += unit.image.Divisor
		}
		unit.image = nil
	}
	cr.pushAllUnits(units)

	if mergedSamples == nil {
		return hdrimage.New(0, 0)
	}
	return mergedSamples
}
Exemple #6
0
func runRender(c *cli.Context) error {
	scenePath, image := getArguments(c)
	quiet := c.GlobalBool("quiet")

	if !quiet {
		log.Printf(
			"will render %d samples of %s to %s of size %dx%d with %d threads",
			c.Int("total-samples"),
			scenePath, image,
			c.Int("width"), c.Int("height"),
			c.Int("max-jobs"),
		)
	}

	width, height := c.Int("width"), c.Int("height")
	totalSamples := c.Int("total-samples")
	threads := c.Int("max-jobs")

	renderedImages := make(chan *hdrimage.Image)

	scene, err := scene.LoadFromFile(scenePath)
	if err != nil {
		return fmt.Errorf("can't open scene: %s", err)
	}
	scene.Init()

	go func() {
		renderer(width, height, renderedImages, scene, 42, totalSamples, threads, quiet)
		close(renderedImages)
	}()

	averageImage := hdrimage.New(width, height)
	averageImage.Divisor = 0
	for currentImage := range renderedImages {
		averageImage.Add(currentImage)
		averageImage.Divisor += currentImage.Divisor
	}

	return savePng(averageImage, image)
}