/
Renderer.scala
128 lines (101 loc) · 4.24 KB
/
Renderer.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
package timetrace
import java.io.File
import timetrace.camera.Camera
import org.apache.spark.SparkConf
import org.apache.spark.SparkContext
import timetrace.math.Vector4
import org.apache.spark.rdd.RDD
import timetrace.photon.Photon
import timetrace.light.Light
import org.apache.spark.broadcast.Broadcast
import java.awt.image.BufferedImage
import javax.imageio.ImageIO
import java.io.ByteArrayOutputStream
import java.io.FileOutputStream
import timetrace.camera.DefaultStillCamera
import timetrace.light.SinglePulsePointLight
import timetrace.shape.Plane
import timetrace.math.Vector3
import timetrace.light.StaticPointLight
import timetrace.material.WhiteDiffuseMaterial
object Renderer {
private val PHOTON_SCATTERING_PARTITIONS = 1000
def main(args: Array[String]): Unit = {
val camera: Camera = DefaultStillCamera
val scene = new Scene( //
List(Thing(new Plane(Vector3(0.0, 1.0, 0.0).normalize(), -1.0), WhiteDiffuseMaterial)), //
List(new SinglePulsePointLight(Vector3(0.0, 1.0, 0.0), Color.WHITE, 0.0, 1.0)))
val downscale = 4
val job = new RenderJob(scene, camera, 20.0, 100000, 1920 / downscale, 1080 / downscale, 100, null, 10.0, 1.0 / 1.8)
render(job)
}
def render(job: RenderJob): Unit = {
val sparkConf = new SparkConf().setAppName("timetrace").setMaster("local[*]")
val sparkContext = new SparkContext(sparkConf)
try {
val photons: RDD[Photon] = sparkContext //
.parallelize(1 to PHOTON_SCATTERING_PARTITIONS, PHOTON_SCATTERING_PARTITIONS) //
.flatMap(generatePhotonBatch(job))
val photonMap: Broadcast[PhotonMap] = sparkContext.broadcast(buildPhotonMap(photons.collect))
val frames: RDD[Frame] = sparkContext.parallelize(0 to job.frameCount).map(renderFrame(job, photonMap))
//frames.saveAsObjectFile("var/frames")
val images: Iterator[(Int, Array[Byte])] = frames.map(convertToImageFile(job)).toLocalIterator
for (i <- images) {
val filename = f"var/frame_${i._1}%06d.png"
println(s"Saving ${filename}")
val out = new FileOutputStream(filename)
out.write(i._2)
out.close()
}
} finally {
sparkContext.stop
}
}
def buildPhotonMap(photons: Array[Photon]) = {
PhotonMap.build(photons)
}
def renderFrame(job: RenderJob, photonMap: Broadcast[PhotonMap])(n: Int): Frame = {
println(s"Rendering frame ${n}, found ${photonMap.value.photons.length} photons.")
val raytracer: Raytrace = new Raytrace(job.scene)
val t: Double = n.toDouble * (job.maxT / job.frameCount)
println(s"Rendering t=${t}")
val averageHalfSizeInPixels: Double = (job.widthInPixels + job.heightInPixels) / 4.0;
def iToR(i: Int, max: Int) = (i - ((max - 1) / 2.0)) / averageHalfSizeInPixels
val pixels: Seq[Color] =
for {
yi <- 0 until job.heightInPixels
y = iToR(yi, job.heightInPixels)
xi <- 0 until job.widthInPixels
x = iToR(xi, job.widthInPixels)
ray = job.camera.generateRay(x, y, t)
} yield {
raytracer.raytrace(ray)
}
val pixelsArray: Array[Color] = pixels.toArray
new Frame(n, job.widthInPixels, job.heightInPixels, pixelsArray)
}
def generatePhotonBatch(job: RenderJob)(n: Int): Seq[Photon] = {
val raytracer: Raytrace = new Raytrace(job.scene)
val photonsToGenerate = job.photonCount / PHOTON_SCATTERING_PARTITIONS
Iterator.continually(raytracer.generatePhotons()).flatten.take(photonsToGenerate).toSeq
}
def convertToImageFile(job: RenderJob)(frame: Frame): (Int, Array[Byte]) = {
val image = new BufferedImage(frame.width, frame.height, BufferedImage.TYPE_INT_ARGB);
def exposeAndGamma(v: Double): Int = {
val r: Double = Math.pow(1.0 - Math.exp(-v * job.exposure), job.encodingGamma) * 256;
Math.max(0, Math.min(Math.round(r).toInt, 255))
}
def colorToRgb(c: Color): Int = {
255 << 24 |
exposeAndGamma(c.red) << 16 |
exposeAndGamma(c.green) << 8 |
exposeAndGamma(c.blue)
}
for (y <- 0 until frame.height; x <- 0 until frame.width) {
image.setRGB(x, frame.height - y - 1, colorToRgb(frame.pixels(y * frame.width + x)))
}
val buffer = new ByteArrayOutputStream()
ImageIO.write(image, "png", buffer)
(frame.number, buffer.toByteArray())
}
}