diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml
index bb44937..c224ad5 100644
--- a/.idea/kotlinc.xml
+++ b/.idea/kotlinc.xml
@@ -1,6 +1,6 @@
-
+
\ No newline at end of file
diff --git a/build.gradle.kts b/build.gradle.kts
index 3c449f6..0aea81a 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -1,5 +1,5 @@
plugins {
- kotlin("jvm") version "2.1.0"
+ kotlin("jvm") version "2.0.21"
}
group = "org.example"
diff --git a/src/main/kotlin/Ray.kt b/src/main/kotlin/Ray.kt
index 7094e81..8e2a2ea 100644
--- a/src/main/kotlin/Ray.kt
+++ b/src/main/kotlin/Ray.kt
@@ -1,6 +1,5 @@
import lights.PointLight
import materials.MaterialColor
-import materials.PathTracedMaterial
import materials.ReflectiveMaterial
import materials.WhateverMaterial
import materials.times
@@ -16,46 +15,19 @@ fun main() {
val bmp = Bitmap(1000, 1000)
val origin = Point(500f, 500f, -500f)
- val material1 = PathTracedMaterial(
- MaterialColor(1f, 0f, 0f),
- specularProbability = 0.1f,
- specularColour = MaterialColor.White,
- smoothness = 1.0f,
- )
+ val sphere = Sphere(Point(500f, 500f, 500f), radius = 500f, material = ReflectiveMaterial(reflectivity = 0.2f, MaterialColor(0f, 1.0f, 0f)))
+ val sphere2 = Sphere(Point(0f, 500f, 50f), radius = 50f, material = ReflectiveMaterial(reflectivity = 0.2f, MaterialColor(1.0f, 0.0f, 0.1f)))
- val materialFloor = PathTracedMaterial(
- MaterialColor(1f, 0f, 1f),
- specularProbability = 0.1f,
- specularColour = MaterialColor.White,
- smoothness = 1.0f,
- )
-
- val lightMaterial = PathTracedMaterial(
- color = MaterialColor.Black,
- emissionColor = MaterialColor.White,
- emissionStrength = 0.2f,
- specularColour = MaterialColor.White
- )
-
- val material2 = ReflectiveMaterial(reflectivity = 0.2f, MaterialColor(0f, 1.0f, 0f))
- val material3 = ReflectiveMaterial(reflectivity = 0.2f, MaterialColor(1.0f, 0.0f, 0.1f))
-
- val sphere = Sphere(Point(500f, 500f, 500f), radius = 500f, material = material1)
- val sphere2 = Sphere(Point(0f, 500f, 50f), radius = 50f, material = material1)
- val sphere3 = Sphere(Point(1000f, 1000f, -500f), radius = 100f, material = lightMaterial)
-
- val material4 = WhateverMaterial(MaterialColor(1f, 0.0f, 0.0f))
val plane = Plane(
Point(0f, -1000f, 0f),
normal = Vector(0f, 1f, 0f),
- material = materialFloor
+ material = WhateverMaterial(MaterialColor(1f, 0.0f, 0.0f))
)
- val material5 = WhateverMaterial(MaterialColor(0.0f, 1f, 0.0f))
val leftPlane = Plane(
Point(-1000f, 0f, 0f),
Vector(1f, 0f, 0f),
- material = materialFloor
+ material = WhateverMaterial(MaterialColor(0.0f, 1f, 0.0f))
)
val light = PointLight(Point(1000f, 1000f, -500f), color = 0.2f * MaterialColor(0.9f, 0.9f, 0.9f))
@@ -64,30 +36,20 @@ fun main() {
sphere,
sphere2,
plane,
- leftPlane,
- sphere3
- ), light = light, ambientLight = MaterialColor.White * 0.1f)
+ leftPlane
+ ), light = light)
for (y in 0 until bmp.height) {
for (x in 0 until bmp.width) {
- var color = MaterialColor.Black
+ val point = Point(x.toFloat(), y.toFloat(), 0f)
+ val ray = origin.rayTo(point)
- repeat(samplesPerPixel) {
- val point = Point(x.toFloat(), y.toFloat(), 0f) + Vector.randomInCircleXY() * blurriness
- val ray = origin.rayTo(point)
-
- color += scene.colorForRay(ray)
+ val color = scene.colorForRay(ray)
+ if (color != null) {
+ bmp.setPixel(x, y, color.toColor())
}
-
- bmp.setPixel(
- x, y,
- (color * (1.0f / samplesPerPixel)).toColor()
- )
}
}
File("test.tga").writeBitmap(bmp)
}
-
-const val samplesPerPixel = 64
-const val blurriness = 1f
\ No newline at end of file
diff --git a/src/main/kotlin/materials/MaterialColor.kt b/src/main/kotlin/materials/MaterialColor.kt
index ccc4063..0e192b3 100644
--- a/src/main/kotlin/materials/MaterialColor.kt
+++ b/src/main/kotlin/materials/MaterialColor.kt
@@ -11,11 +11,9 @@ data class MaterialColor(val r: Float, val g: Float, val b: Float) {
operator fun times(rhs: MaterialColor) = MaterialColor(r * rhs.r, g * rhs.g, b * rhs.b)
operator fun plus(rhs: MaterialColor) = MaterialColor(r + rhs.r, g + rhs.g, b + rhs.b)
- operator fun times(rhs: Float) = MaterialColor(rhs * r, rhs * g, rhs * b)
companion object {
val Black: MaterialColor = MaterialColor(0f, 0f, 0f)
- val White = MaterialColor(1f, 1f, 1f)
}
}
diff --git a/src/main/kotlin/materials/PathTracedMaterial.kt b/src/main/kotlin/materials/PathTracedMaterial.kt
deleted file mode 100644
index 17df071..0000000
--- a/src/main/kotlin/materials/PathTracedMaterial.kt
+++ /dev/null
@@ -1,85 +0,0 @@
-package materials
-
-import Hit
-import math.Ray
-import math.Vector
-import things.Scene
-import kotlin.math.cos
-import kotlin.math.ln
-import kotlin.math.log
-import kotlin.math.sqrt
-import kotlin.random.Random
-
-data class PathTracedMaterial(
- val color: MaterialColor,
- val emissionColor: MaterialColor = MaterialColor(0f, 0f, 0f),
- val specularProbability: Float = 0f,
- val smoothness: Float = 0.5f,
- val emissionStrength: Float = 0f,
- val specularColour: MaterialColor,
-) : Material {
- override fun shade(ray: Ray, hit: Hit, scene: Scene): MaterialColor {
- // source: https://github.com/SebLague/Ray-Tracing/blob/main/Assets/Scripts/Shaders/RayTracer.shader#L328
-
- var incomingLight = MaterialColor.Black
- var rayColor = MaterialColor.White
-
- var hit: Hit? = hit
- var ray = ray
-
- repeat(maxBounces) {
- if (hit != null && hit.material is PathTracedMaterial) {
- val isSpecular = hit.material.specularProbability >= Random.Default.nextFloat()
-
- val diffuseDir = (hit.normal + Random.Default.direction()).normalized()
- val specularDir = reflect(ray.direction, hit.normal)
-
- val rayDir = if (isSpecular) {
- lerp(diffuseDir, specularDir, hit.material.smoothness).normalized()
- } else {
- diffuseDir
- }
-
- val emittedLight = hit.material.emissionColor * hit.material.emissionStrength;
- incomingLight += emittedLight * rayColor;
- rayColor *= if (isSpecular) hit.material.specularColour else hit.material.color
-
- val p = maxOf(rayColor.r, rayColor.g, rayColor.b)
- if (Random.Default.nextFloat() >= p) {
- return@repeat
- }
-
- rayColor *= 1.0f / p;
-
- ray = Ray(hit.point, rayDir)
-
- } else if (hit != null) {
- incomingLight += hit.material.shade(ray, hit, scene) * rayColor
- return@repeat
- } else {
- incomingLight += scene.ambientLight * rayColor
- return@repeat
- }
-
- hit = scene.intersects(ray)
- }
-
- return incomingLight
- }
-
-}
-
-const val maxBounces = 4
-
-fun Random.normalDistributedFloat(): Float {
- val theta = nextFloat() * 2 * Math.PI.toFloat()
- val rho = sqrt(-2 * ln(nextFloat()))
- return rho * cos(theta)
-}
-
-fun Random.direction(): Vector =
- Vector(normalDistributedFloat(), normalDistributedFloat(), normalDistributedFloat()).normalized()
-
-fun lerp(a: Float, b: Float, c: Float): Float = a * (1 - c) + b * c
-fun lerp(a: MaterialColor, b: MaterialColor, c: Float): MaterialColor = a * (1 - c) + b * c
-fun lerp(a: Vector, b: Vector, c: Float): Vector = a * (1 - c) + b * c
diff --git a/src/main/kotlin/materials/ReflectiveMaterial.kt b/src/main/kotlin/materials/ReflectiveMaterial.kt
index afa8b92..068d818 100644
--- a/src/main/kotlin/materials/ReflectiveMaterial.kt
+++ b/src/main/kotlin/materials/ReflectiveMaterial.kt
@@ -2,7 +2,6 @@ package materials
import Hit
import math.Ray
-import math.Vector
import things.Scene
import math.times
import kotlin.math.pow
@@ -30,14 +29,9 @@ data class ReflectiveMaterial(
color += intensity * MaterialColor(0.3f, 0f, 0f)
}
- val reflectionDirection = reflect(ray.direction, hit.normal)
- val reflectedColor = scene.colorForRay(Ray(offsetPoint, reflectionDirection))
+ val reflectionDirection = ray.direction - 2 * (ray.direction dot hit.normal) * hit.normal
+ val reflectedColor = scene.colorForRay(Ray(offsetPoint, reflectionDirection)) ?: MaterialColor.Black
return (1.0f - reflectivity) * color + reflectivity * reflectedColor
}
-}
-
-fun reflect(
- rayDirection: Vector,
- normal: Vector
-): Vector = rayDirection - 2 * (rayDirection dot normal) * normal
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/src/main/kotlin/math/Vector.kt b/src/main/kotlin/math/Vector.kt
index d4d769d..dccb03a 100644
--- a/src/main/kotlin/math/Vector.kt
+++ b/src/main/kotlin/math/Vector.kt
@@ -1,9 +1,6 @@
package math
-import kotlin.math.cos
-import kotlin.math.sin
import kotlin.math.sqrt
-import kotlin.random.Random
data class Vector(val x: Float, val y: Float, val z: Float) {
val length: Float get() = sqrt(squaredLength)
@@ -24,14 +21,6 @@ data class Vector(val x: Float, val y: Float, val z: Float) {
)
infix fun dot(rhs: Vector): Float = x * rhs.x + y * rhs.y + z * rhs.z
-
- companion object {
- fun randomInCircleXY(): Vector {
- val r = sqrt(Random.Default.nextFloat())
- val theta = Random.Default.nextFloat() * 2 * Math.PI.toFloat()
- return Vector(r * sin(theta), r * cos(theta), 0f)
- }
- }
}
operator fun Float.times(rhs: Vector): Vector = rhs * this
diff --git a/src/main/kotlin/things/Scene.kt b/src/main/kotlin/things/Scene.kt
index 3f5a0af..974ce7c 100644
--- a/src/main/kotlin/things/Scene.kt
+++ b/src/main/kotlin/things/Scene.kt
@@ -3,10 +3,9 @@ package things
import Hit
import materials.MaterialColor
import lights.PointLight
-import materials.Material
import math.Ray
-data class Scene(val things: List, val light: PointLight, val ambientLight: MaterialColor = MaterialColor.Black): Thing {
+data class Scene(val things: List, val light: PointLight): Thing {
override fun intersects(ray: Ray): Hit? {
var closest: Hit? = null
for (thing in things) {
@@ -19,11 +18,11 @@ data class Scene(val things: List, val light: PointLight, val ambientLigh
return closest
}
- fun colorForRay(ray: Ray): MaterialColor {
+ fun colorForRay(ray: Ray): MaterialColor? {
val hit = intersects(ray)
if (hit != null) {
return hit.material.shade(ray, hit, this)
}
- return ambientLight
+ return null
}
}
\ No newline at end of file