Skip to content

Commit

Permalink
Merge pull request #20 from sraaphorst/chapter14
Browse files Browse the repository at this point in the history
Chapter 14 completed.
  • Loading branch information
sraaphorst authored Jan 21, 2023
2 parents 4fa56c0 + 8ed00a5 commit b9bf227
Show file tree
Hide file tree
Showing 74 changed files with 993 additions and 406 deletions.
Binary file added output/ch14_world.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes.
8 changes: 6 additions & 2 deletions src/main/kotlin/apps/ch07_world.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import scene.World
import shapes.Sphere
import java.io.File
import kotlin.math.PI
import kotlin.system.measureTimeMillis

fun main() {
val multipleLights = false
Expand Down Expand Up @@ -87,6 +88,9 @@ fun main() {
Camera(1000, 500, PI / 3, t)
}

val canvas = camera.render(world)
canvas.toPPMFile(File("output/ch07_world.ppm"))
val elapsed = measureTimeMillis {
val canvas = camera.render(world)
canvas.toPPMFile(File("output/ch07_world.ppm"))
}
println("Time elapsed: ${elapsed / 1000.0} s")
}
8 changes: 6 additions & 2 deletions src/main/kotlin/apps/ch08_shadowpuppet.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import scene.World
import shapes.Sphere
import java.io.File
import kotlin.math.PI
import kotlin.system.measureTimeMillis

fun main() {
val wrist = run {
Expand Down Expand Up @@ -77,6 +78,9 @@ fun main() {
Camera(1200, 600, PI/6, t)
}

val canvas = camera.render(world)
canvas.toPPMFile(File("output/ch08_shadowpuppet.ppm"))
val elapsed = measureTimeMillis {
val canvas = camera.render(world)
canvas.toPPMFile(File("output/ch08_shadowpuppet.ppm"))
}
println("Time elapsed: ${elapsed / 1000.0} s")
}
8 changes: 6 additions & 2 deletions src/main/kotlin/apps/ch09_world.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import shapes.Plane
import shapes.Sphere
import java.io.File
import kotlin.math.PI
import kotlin.system.measureTimeMillis

fun main() {
val multipleLights = false
Expand Down Expand Up @@ -88,6 +89,9 @@ fun main() {
Camera(1000, 500, PI / 3, t)
}

val canvas = camera.render(world)
canvas.toPPMFile(File("output/ch09_world.ppm"))
val elapsed = measureTimeMillis {
val canvas = camera.render(world)
canvas.toPPMFile(File("output/ch09_world.ppm"))
}
println("Time elapsed: ${elapsed / 1000.0} s")
}
8 changes: 6 additions & 2 deletions src/main/kotlin/apps/ch10_world.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import shapes.Plane
import shapes.Sphere
import java.io.File
import kotlin.math.PI
import kotlin.system.measureTimeMillis

fun main() {
val world = run {
Expand Down Expand Up @@ -95,6 +96,9 @@ fun main() {
Camera(2500, 1250, PI /3, t)
}

val canvas = camera.render(world)
canvas.toPPMFile(File("output/ch10_world.ppm"))
val elapsed = measureTimeMillis {
val canvas = camera.render(world)
canvas.toPPMFile(File("output/ch10_world.ppm"))
}
println("Time elapsed: ${elapsed / 1000.0} s")
}
8 changes: 6 additions & 2 deletions src/main/kotlin/apps/ch11_world.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import shapes.Plane
import shapes.Sphere
import java.io.File
import kotlin.math.PI
import kotlin.system.measureTimeMillis

fun main() {
val wallMaterial = run {
Expand Down Expand Up @@ -113,6 +114,9 @@ fun main() {
Camera(2400, 1200, 1.152, t)
}

val canvas = camera.render(world)
canvas.toPPMFile(File("output/ch11_world.ppm"))
val elapsed = measureTimeMillis {
val canvas = camera.render(world)
canvas.toPPMFile(File("output/ch11_world.ppm"))
}
println("Time elapsed: ${elapsed / 1000.0} s")
}
9 changes: 7 additions & 2 deletions src/main/kotlin/apps/ch12_world.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import scene.Camera
import scene.World
import shapes.Cube
import java.io.File
import kotlin.system.measureTimeMillis

fun main() {
val floorCeiling = run {
Expand Down Expand Up @@ -152,6 +153,10 @@ fun main() {
Camera(2400, 1200, 0.7805, t)
}

val canvas = camera.render(world)
canvas.toPPMFile(File("output/ch12_world.ppm"))
val elapsed = measureTimeMillis {
val canvas = camera.render(world)
canvas.toPPMFile(File("output/ch12_world.ppm"))
}
println("Time elapsed: ${elapsed / 1000.0} s")
}

8 changes: 6 additions & 2 deletions src/main/kotlin/apps/ch13_world.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import shapes.Cylinder
import shapes.Plane
import java.io.File
import kotlin.math.PI
import kotlin.system.measureTimeMillis

fun main() {
val flooring = run {
Expand Down Expand Up @@ -108,6 +109,9 @@ fun main() {
Camera(2400, 1200, PI / 10, t)
}

val canvas = camera.render(world)
canvas.toPPMFile(File("output/ch13_world.ppm"))
val elapsed = measureTimeMillis {
val canvas = camera.render(world)
canvas.toPPMFile(File("output/ch13_world.ppm"))
}
println("Time elapsed: ${elapsed / 1000.0} s")
}
40 changes: 40 additions & 0 deletions src/main/kotlin/apps/ch14_hexagon.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package apps

// By Sebastian Raaphorst, 2023.

import light.PointLight
import math.Matrix
import math.Tuple
import scene.Camera
import scene.World
import shapes.Group
import java.io.File
import kotlin.math.PI
import kotlin.system.measureTimeMillis

fun main() {
val hexagon = run {
val sides = (0 until 6).map { Matrix.rotateY(it * PI / 3) }.map {
side.withTransformation(it)
}
Group(Matrix.rotateX(-0.4363) * Matrix.rotateY(-PI / 18), children = sides)
}

val world = run {
val light = PointLight(Tuple.point(0, 10, -5))
World(listOf(hexagon), light)
}

val camera = run {
val from = Tuple.point(0, 0, -5)
val to = Tuple.PZERO
val t = from.viewTransformationFrom(to, Tuple.VY)
Camera(1200, 600, 1, t)
}

val elapsed = measureTimeMillis {
val canvas = camera.render(world)
canvas.toPPMFile(File("output/ch14_hexagon.ppm"))
}
println("Time elapsed: ${elapsed / 1000.0} s")
}
20 changes: 20 additions & 0 deletions src/main/kotlin/apps/ch14_shared.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package apps

// By Sebastian Raaphorst, 2023.

import math.Matrix
import shapes.Cylinder
import shapes.Group
import shapes.Sphere
import kotlin.math.PI

// The shared side of a hexagon.
val side = run {
val corner = Sphere(Matrix.translate(0, 0, -1) * Matrix.scale(0.25, 0.25, 0.25))
val edge = Cylinder(
0, 1, false,
Matrix.translate(0, 0, -1) * Matrix.rotateY(-PI / 6)
* Matrix.rotateZ(-PI / 2) * Matrix.scale(0.25, 1, 0.25)
)
Group(children = listOf(corner, edge))
}
83 changes: 83 additions & 0 deletions src/main/kotlin/apps/ch14_world.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package apps

// By Sebastian Raaphorst, 2023.
// From https://forum.raytracerchallenge.com/thread/13/groups-scene-description

import light.PointLight
import material.Material
import math.Color
import math.Matrix
import math.Tuple
import scene.Camera
import scene.World
import shapes.*
import java.io.File
import kotlin.math.PI
import kotlin.system.measureTimeMillis

fun main() {
val transforms = listOf(0, PI / 3, 2 * PI / 3, PI, 4 * PI / 3, 5 * PI / 3)
.map(Matrix::rotateY)

val cap = run {
val trans = Matrix.rotateX(-PI / 4 ) * Matrix.scale(0.24606, 1.37002, 0.24606)
val cones = transforms.map {
Cone(-1, 0, false, it * trans)
}
Group(children = cones)
}

val wacky = run {
val legs = transforms.map(side::withTransformation)
val cap1 = cap.withTransformation(Matrix.translate(0, 1, 0))
val cap2 = cap.withTransformation(Matrix.rotateX(PI) * Matrix.translate(0, 1, 0))
Group(children = legs + listOf(cap1, cap2))
}

val backdrop = run {
val t = Matrix.translate(0, 0, 100) * Matrix.rotateX(PI / 2)
val m = Material(Color.WHITE, ambient = 1, diffuse = 0, specular = 0)
Plane(t, m)
}

val wacky1 = run {
val t = Matrix.translate(-2.8, 0, 0) * Matrix.rotateX(0.4363) * Matrix.rotateY(PI / 18)
val m = Material(Color(0.9, 0.2, 0.4), ambient = 0.2, diffuse = 0.8, specular = 0.7, shininess = 20)
wacky.withTransformation(t).withMaterial(m)
}

val wacky2 = run {
val t = Matrix.rotateY(PI / 18)
val m = Material(Color(0.2, 0.9, 0.6), ambient = 0.2, diffuse = 0.8, specular = 0.7, shininess = 20)
wacky.withTransformation(t).withMaterial(m)
}

val wacky3 = run {
val t = Matrix.translate(2.8, 0, 0) * Matrix.rotateX(-0.4363) * Matrix.rotateY(-PI / 18)
val m = Material(Color(0.2, 0.3, 1), ambient = 0.2, diffuse = 0.8, specular = 0.7, shininess = 20)
wacky.withTransformation(t).withMaterial(m)
}

val world = run {
val color = Color(0.25, 0.25, 0.25)
val light1 = PointLight(Tuple.point(10_000, 10_000, -10_000), color)
val light2 = PointLight(Tuple.point(-10_000, 10_000, -10_000), color)
val light3 = PointLight(Tuple.point(10_000, -10_000, -10_000), color)
val light4 = PointLight(Tuple.point(-10_000, -10_000, -10_000), color)
World(listOf(backdrop, wacky1, wacky2, wacky3), listOf(light1, light2, light3, light4))
}

val camera = run {
val from = Tuple.point(0, 0, -9)
val to = Tuple.PZERO
val t = from.viewTransformationFrom(to, Tuple.VY)
Camera(1200, 400, 0.9, t)
}

val elapsed = measureTimeMillis {
val canvas = camera.render(world)
canvas.toPPMFile(File("output/ch14_world.ppm"))
}
println("Time elapsed: ${elapsed / 1000.0} s")
}

Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,39 @@ package material
import light.Light
import math.Color
import math.Tuple
import math.almostEquals
import pattern.Pattern
import pattern.SolidPattern
import shapes.Shape
import kotlin.math.pow

data class Material(val pattern: Pattern = SolidPattern(Color.WHITE),
val ambient: Double = DEFAULT_AMBIENT,
val diffuse: Double = DEFAULT_DIFFUSE,
val specular: Double = DEFAULT_SPECULAR,
val shininess: Double = DEFAULT_SHININESS,
val reflectivity: Double = DEFAULT_REFLECTIVITY,
val transparency: Double = DEFAULT_TRANSPARENCY,
val refractiveIndex: Double = DEFAULT_REFRACTIVE_INDEX) {
class Material(val pattern: Pattern = SolidPattern(Color.WHITE),
ambient: Number = DEFAULT_AMBIENT,
diffuse: Number = DEFAULT_DIFFUSE,
specular: Number = DEFAULT_SPECULAR,
shininess: Number = DEFAULT_SHININESS,
reflectivity: Number = DEFAULT_REFLECTIVITY,
transparency: Number = DEFAULT_TRANSPARENCY,
refractiveIndex: Number = DEFAULT_REFRACTIVE_INDEX) {

val ambient = ambient.toDouble()
val diffuse = diffuse.toDouble()
val specular = specular.toDouble()
val shininess = shininess.toDouble()
val reflectivity = reflectivity.toDouble()
val transparency = transparency.toDouble()
val refractiveIndex = refractiveIndex.toDouble()

// Convenience constructor to create a material with a solid pattern.
constructor(color: Color,
ambient: Double = DEFAULT_AMBIENT,
diffuse: Double = DEFAULT_DIFFUSE,
specular: Double = DEFAULT_SPECULAR,
shininess: Double = DEFAULT_SHININESS,
reflectivity: Double = DEFAULT_REFLECTIVITY,
transparency: Double = DEFAULT_TRANSPARENCY,
refractiveIndex: Double = DEFAULT_REFRACTIVE_INDEX):
this(SolidPattern(color), ambient, diffuse, specular, shininess, reflectivity, transparency, refractiveIndex)
ambient: Number = DEFAULT_AMBIENT,
diffuse: Number = DEFAULT_DIFFUSE,
specular: Number = DEFAULT_SPECULAR,
shininess: Number = DEFAULT_SHININESS,
reflectivity: Number = DEFAULT_REFLECTIVITY,
transparency: Number = DEFAULT_TRANSPARENCY,
refractiveIndex: Number = DEFAULT_REFRACTIVE_INDEX):
this(SolidPattern(color),
ambient, diffuse, specular, shininess, reflectivity, transparency, refractiveIndex)

internal fun lighting(shape: Shape,
light: Light,
Expand Down Expand Up @@ -62,23 +70,6 @@ data class Material(val pattern: Pattern = SolidPattern(Color.WHITE),
return ambient + diffuse + specular
}

override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is Material) return false

if (pattern != other.pattern) return false
if (!almostEquals(ambient, other.ambient)) return false
if (!almostEquals(diffuse, other.diffuse)) return false
if (!almostEquals(specular, other.specular)) return false
if (!almostEquals(shininess, other.shininess)) return false

return true
}

override fun hashCode(): Int =
31 * (31 * (31 * (31 * pattern.hashCode() + ambient.hashCode()) +
diffuse.hashCode()) + specular.hashCode()) + shininess.hashCode()

companion object {
const val DEFAULT_AMBIENT = 0.1
const val DEFAULT_DIFFUSE = 0.9
Expand Down
Loading

0 comments on commit b9bf227

Please sign in to comment.