Skip to content

Latest commit

 

History

History
533 lines (416 loc) · 19 KB

File metadata and controls

533 lines (416 loc) · 19 KB

3D Ray Tracing Engine with Anti-Aliasing

Java JUnit OOP

Ray Tracer Engine - Realistic Piano Scene

Complex algorithms, clean architecture, photorealistic results

View Project JourneyTechnical ArchitectureAnti-Aliasing


🎯 The Story Behind the Code

The Question: How do computers generate realistic 3D images? How does a sphere "know" it should look round? How does light "know" how to bounce?

This project started as an academic assignment in Introduction to Software Engineering. But somewhere between calculating ray-sphere intersections and debugging reflection vectors, it became something more: a deep dive into algorithmic thinking and software architecture.

The Challenge

"Build a system where math, physics, and code come together to create something beautiful."

When you're rendering photorealistic images, precision is everything:

  • Mathematical precision – one misplaced sign and your shadows point the wrong way
  • Architectural clarity – 10+ geometry types, 4 light sources, recursive reflections... all working together
  • Rigorous testing – every feature needs tests, because visual bugs are expensive to debug

✨ What It Does

A full-featured 3D rendering engine that traces light rays through a virtual scene to generate photorealistic images:

Scene scene = new Scene("My First Render");

// Build the world
scene.geometries.add(
    new Sphere(50, new Point(0, 0, -200))
        .setEmission(new Color(100, 50, 50))
        .setMaterial(new Material()
            .setKD(0.5).setKS(0.5).setShininess(100)
            .setKR(0.3))  // 30% reflective
);

// Add lights
scene.lights.add(
    new SpotLight(new Color(400, 240, 0), new Point(60, 50, 0),
                  new Vector(-1, -1, -2))
        .setKL(0.00001).setKQ(0.000005)
);

// Render with anti-aliasing
Camera camera = Camera.getBuilder()
    .setLocation(new Point(0, 0, 1000))
    .setDirection(new Vector(0, 0, -1), new Vector(0, 1, 0))
    .setVPDistance(1000).setVPSize(200, 200)
    .setImageWriter(new ImageWriter("myRender", 800, 800))
    .setRayTracer(new SimpleRayTracer(scene))
    .setSamplingConfig(new SamplingConfig()
        .enableAntiAliasing(81, 1.0, SamplingPattern.JITTERED))
    .build();

camera.generateRenderedImage().writeToImage();

Result: A photorealistic image with smooth edges, realistic lighting, and reflections.

Complete Scene Render

A complete scene showcasing the engine's capabilities: spheres with reflections, transparent triangles, realistic shadows, and multiple light sources working together.


🏆 Key Achievement: Super-Sampling Anti-Aliasing

Visual Comparison

No Anti-Aliasing

No Anti-Aliasing
❌ Jagged edges
❌ Pixelated curves
1 ray per pixel

Standard AA - 81 samples

Standard AA (81 samples)
✅ Smooth edges
✅ Natural curves
~40× slower, excellent quality

High Quality AA - 324 samples

High Quality (324 samples)
✅ Professional quality
✅ Production-ready
~160× slower, outstanding

Notice how the edges become progressively smoother as we increase the number of sample rays per pixel. This is the power of super-sampling anti-aliasing.

The Implementation

File: src/renderer/superSampling/

The anti-aliasing system uses Jittered Sampling (a bonus feature):

// Generate 81 rays around the intersection point
List<Ray> rays = antiAliasingSampler.generateSampleRays(
    intersection.point, primaryRay);

// Trace each ray and average the colors
List<Color> colors = new ArrayList<>();
for (Ray ray : rays) {
    GeoPoint intersection = findClosestIntersection(ray);
    colors.add(intersection == null ? scene.background :
               traceSimpleRay(ray, intersection));
}

return calculateAverageColor(colors);

Why Jittered? Combines the benefits of grid sampling (even coverage) with random sampling (avoids repetitive artifacts).

Performance Comparison

Configuration Samples/Pixel Render Time Quality Use Case
No AA 1 Baseline Standard Debugging
Standard AA 81 (9×9) ~40× slower Excellent Demo images
High Quality 324 (18×18) ~160× slower Outstanding Final renders

🛠️ Technical Architecture

Design Patterns Implemented

The codebase showcases 7 design patterns, demonstrating architectural maturity:

Pattern Location Purpose
Builder Camera.Builder Fluent API for developer empathy and robust validation
Strategy SamplingPattern enum Pluggable sampling algorithms (JITTERED, RANDOM)
Composite Geometries class Treat single/multiple geometries uniformly
Template Method RayTracerBase Define ray tracing skeleton
Null Object AmbientLight.NONE Eliminate null checks
Factory Method SamplingPattern.generate() Create sample points by pattern
Flyweight Point.ZERO, Color.BLACK Share immutable objects

Package Structure

src/
├── primitives/         # Math foundation
│   ├── Point.java
│   ├── Vector.java     ← createPerpendicular() for sampling
│   ├── Ray.java
│   ├── Color.java
│   └── Material.java
├── geometries/         # 3D shapes
│   ├── Sphere.java
│   ├── Triangle.java
│   ├── Plane.java
│   ├── Cylinder.java
│   └── Geometries.java (Composite)
├── lighting/           # Light sources
│   ├── AmbientLight.java
│   ├── DirectionalLight.java
│   ├── PointLight.java
│   └── SpotLight.java
├── renderer/           # Core rendering
│   ├── Camera.java     ← Builder Pattern
│   ├── SimpleRayTracer.java
│   └── superSampling/  ← ★ Anti-Aliasing System ★
│       ├── SuperSampling.java
│       ├── SamplingPattern.java (Strategy)
│       ├── SamplingConfig.java
│       └── TargetArea.java
└── scene/
    └── Scene.java      (Plain Data Structure)

🧪 Quality Assurance

Test-Driven Development

100+ unit tests written using JUnit 5:

unitTests/
├── primitives/          # Point, Vector, Ray tests
├── geometries/          # Intersection accuracy tests
├── renderer/
│   ├── CameraTests.java
│   ├── ShadowTests.java
│   ├── ReflectionRefractionTests.java
│   └── superSampling/
│       ├── AntiAliasingTest.java          ← 3-way comparison
│       ├── SuperSamplingTests.java
│       └── SamplingPatternTests.java
└── bigScenes/
    ├── RealisticPianoFinal.java           ← 30 samples
    ├── CoffeeCupScene.java
    ├── HouseScene.java
    └── SunflowerScene.java

Example: Anti-Aliasing Test (AntiAliasingTest.java)

@Test
public void runCompleteAntiAliasingTest() {
    // Render 3 versions of the same scene
    long noAATime = renderWithoutAntiAliasing();
    long standardAATime = renderWithStandardAntiAliasing();  // 81 samples
    long highAATime = renderWithHighQualityAntiAliasing();    // 324 samples

    // Output performance comparison
    printPerformanceSummary(noAATime, standardAATime, highAATime);
}

Output:

====================================
        PERFORMANCE SUMMARY
====================================
No Anti-Aliasing:       1,203 ms
Standard AA (81):      48,560 ms (40.4x slower)
High Quality AA (324): 194,832 ms (162.0x slower)

MP1 Requirements fulfilled:
✓ 10+ geometric bodies
✓ 3 different light sources
✓ Anti-aliasing ON/OFF capability
✓ 50+ sample rays
✓ Performance timing measurements
====================================

🎨 Features

Rendering Capabilities

  • Ray-Geometry Intersections – Sphere, Plane, Triangle, Polygon, Cylinder, Tube
  • Phong Lighting Model – Ambient + Diffuse + Specular components
  • Multiple Light Sources – Ambient, Directional, Point, Spot lights with attenuation
  • Shadows – Shadow ray casting with transparency support
  • Recursive Ray Tracing – Reflections (kR) and refractions (kT) up to 10 levels
  • Material Properties – kD (diffuse), kS (specular), nShininess, kR (reflection), kT (transparency)
  • Super-Sampling Anti-Aliasing – JITTERED and RANDOM patterns
  • Camera Builder – Fluent API with validation
  • Scene Composition – Multiple geometries and lights with Composite pattern
  • Image Export – PNG output with customizable resolution

Code Quality

  • 7 Design Patterns – Builder, Strategy, Composite, Template Method, Null Object, Factory, Flyweight
  • SOLID Principles – Single Responsibility, Open/Closed, Liskov Substitution, Dependency Inversion
  • Defensive Programming – Input validation on every public method
  • Comprehensive JavaDoc – All classes and methods documented
  • 100+ Unit Tests – JUnit 5 with EP/BVA testing strategies
  • Immutable Primitives – Thread-safe Point, Vector, Ray, Color. No side effects, easier testing

📸 Demo Images

Lighting Techniques

Rainbow Lighting Effect

Multiple colored lights create a rainbow effect on a reflective sphere

Complex Multi-Light Scene

Three light sources (blue, yellow, white) illuminate geometric shapes

Shadow Progression

Shadow Initial

Base configuration

Shadow Move 1

Light position adjusted

Shadow Move 2

Dynamic shadow casting

Demonstrating how shadows change realistically as light sources move through the scene.


🚀 Getting Started

Prerequisites

  • Java 17 or higher
  • JUnit 5 (included in project)

Running the Examples

  1. Clone the repository

    git clone https://github.com/Noammandelbaum/ISE5784_1674.git
    cd ISE5784_1674
  2. Compile the project

    javac -d out src/**/*.java
  3. Run a demo scene

    java -cp out bigScenes.RealisticPianoFinal
  4. Run tests

    # From your IDE (IntelliJ IDEA / Eclipse):
    # Right-click on unitTests folder → Run All Tests
  5. View generated images

    # Images are saved to the images/ directory
    open images/antiAliasing/with_antialiasing_81_samples.png

📚 What I Learned

Technical Depth

  • Linear Algebra in Practice – Dot products, cross products, vector normalization aren't just theory
  • Floating-Point Precision – Why alignZero() is critical for numerical stability
  • Recursive Algorithms – Balancing depth vs. performance (MAX_LEVEL = 10, MIN_K = 0.001)
  • Performance Optimization – Understanding the cost of each ray (81× slowdown = need for optimization)

Software Engineering

  • TDD Discipline – Writing tests first prevents expensive visual debugging
  • Builder Pattern Value – Compare new Camera(8 params) vs. fluent API
  • Strategy Pattern Flexibility – Added JITTERED pattern without changing existing code
  • Defensive Programming – Input validation prevents silent failures

Quality Mindset

"The best code isn't the cleverest – it's the code you can trust in 6 months."

This principle guided every decision:

  • Named variables clearly (pixelColumnIndex not j)
  • Validated inputs explicitly (throw IllegalArgumentException with clear messages)
  • Tested edge cases obsessively (colinear points, zero vectors, parallel rays)
  • Documented intent with JavaDoc (not just "what" but "why")

Build quality in from the start – don't patch it later.


🎬 Project Journey: From Primitives to Photorealism

Exercise 1-2: Foundation - Geometries & Ray Intersections

Basic Geometries

First successful render: Basic sphere, plane, and triangle with simple color emission. This proved the core ray-intersection math was correct.

Exercise 5-6: Adding Realism - Lighting Models

Directional Light

Directional Light

Point Light

Point Light with Attenuation

Spot Light

Spot Light with Direction

Implemented the Phong reflection model with three types of light sources. Each light type required different attenuation calculations.

Exercise 7: Shadows - Adding Depth

Hard Shadows

Hard shadows with opaque objects

Soft Shadows

Soft shadows through transparent objects

Shadow rays revealed which objects block light. Transparent materials (kT > 0) create soft, realistic shadows.

Exercise 7: Reflections & Refractions - Recursive Ray Tracing

Reflections

Mirror-like reflections (kR = 0.9)

Refractions

Glass-like transparency (kT = 0.8)

Recursive ray tracing up to 10 levels. Each reflection/refraction spawns a new ray, creating stunning realism but requiring careful performance management.

Exercise 8 (Mini-Project 1): Anti-Aliasing - The Finishing Touch

See the Anti-Aliasing section above for the full before/after comparison.

Final Achievement: Complex Scenes

Realistic Piano

Realistic Upright Piano
50+ primitives, reflection materials, realistic lighting and shadows

House Scene

Architectural Scene
Multiple geometries, complex lighting, shadows

From a single sphere to photorealistic scenes - 8 exercises, countless tests, one powerful engine.


🔗 Clean Code Principles in Practice

Developer-Friendly API Design

Builder Pattern for Complex Objects:

// Bad: Constructor with 8 parameters - easy to mix up
Camera camera = new Camera(p0, vTo, vUp, width, height, distance, imageWriter, rayTracer);

// Good: Builder with validation - clear and safe
Camera camera = Camera.getBuilder()
    .setLocation(new Point(0, 0, 0))
    .setDirection(new Vector(0, 0, -1), new Vector(0, 1, 0))
    .setVPSize(200, 200)
    .build();  // Validates all required fields

Defensive Programming

Clear Error Messages:

if (numSamples < 1) {
    throw new IllegalArgumentException("Number of samples must be at least 1");
}

Performance Awareness

Transparent Benchmarking:

System.out.println("Standard AA (81): 48,560 ms (40.4x slower)");

Real performance data helps make informed tradeoffs between quality and speed.

Code Quality Highlights

  • 100+ unit tests – Comprehensive coverage
  • Immutable primitives – No side effects, thread-safe
  • Defensive validation – Fail fast with clear messages
  • Continuous optimization – First render: 2 min → After optimization: 45 sec

📖 Documentation

  • JavaDoc: All classes and public methods fully documented
  • Code Comments: Explain "why" not "what" (algorithms, edge cases, optimizations)
  • Test Documentation: Each test describes expected behavior
  • README: You're reading it! 😊

🙏 Acknowledgments

  • Course: Introduction to Software Engineering, Jerusalem College of Technology
  • Concepts Inspired By: Peter Shirley's "Ray Tracing in One Weekend"
  • Pattern Reference: Gang of Four Design Patterns

📬 Contact

I'm always excited to discuss this project, ray tracing techniques, or software development opportunities.

LinkedIn Email GitHub


Built with precision, tested with discipline, designed for clarity.

A journey through algorithms, architecture, and the art of turning math into visuals