Skip to content

Hybrid Lens Design

Design and simulation of hybrid refractive-diffractive optical systems.

Overview

Hybrid lenses combine:

  • Refractive elements: Traditional glass lenses
  • Diffractive elements: DOEs, metasurfaces, diffractive optics

Benefits:

  • Chromatic correction: Diffractive elements have opposite dispersion
  • Compact form factor: Thin diffractive elements replace thick glass
  • Novel functionalities: Wavefront shaping, multi-focal, etc.

Congli Wang, Ni Chen, and Wolfgang Heidrich, "dflens: A Differentiable Pipeline for Hybrid Refractive-Diffractive Lens Design," SIGGRAPH Asia 2024.

Basic Hybrid Lens

Creating a Hybrid System

import torch
from deeplens.optics import HybridLens

# HybridLens consists of a GeoLens (refractive) and a DOE (diffractive)
# The easiest way is to load from a JSON file that contains both:
lens = HybridLens(
    filename='./datasets/lenses/hybrid/hybridlens_example.json',
    device='cuda'
)

# The HybridLens has two main components:
# - lens.geolens: The refractive lens (GeoLens)
# - lens.doe: The diffractive optical element (DOE)

# For accurate wave optics simulation, use double precision
torch.set_default_dtype(torch.float64)
lens.double()

# Access refractive lens properties (via embedded GeoLens)
print(f"Focal length: {lens.geolens.foclen}")
print(f"Sensor size: {lens.geolens.sensor_size}")

# The DOE can be: Binary2, Pixel2D, Fresnel, or Zernike type
print(f"DOE type: {type(lens.doe)}")

Ray-Wave Hybrid Tracing

import torch
from deeplens.optics.config import PSF_KS, DEFAULT_WAVE, SPP_COHERENT

# Ensure double precision for accurate phase computation
torch.set_default_dtype(torch.float64)

# HybridLens uses a ray-wave model:
# 1. Coherent ray tracing through refractive elements to DOE plane
# 2. DOE phase modulation
# 3. Wave propagation (Angular Spectrum Method) to sensor

# Compute PSF using the psf() method
point = [0.0, 0.0, -10000.0]  # Point source position [x, y, z]
psf = lens.psf(
    points=point,        # Point source location
    ks=PSF_KS,           # Kernel size (128 default)
    wvln=DEFAULT_WAVE,   # Wavelength (0.58756180 um)
    spp=SPP_COHERENT     # Rays for coherent tracing (~16.7M)
)

# PSF is normalized intensity distribution, shape [ks, ks]
print(f"PSF shape: {psf.shape}")
print(f"PSF sum: {psf.sum():.4f}")  # Should be ~1.0

Metasurface Design

Pixelated Metasurface

from deeplens.optics.diffractive_surface import Pixel2D

# Pixel2D DOE allows arbitrary phase patterns
# Parameters are typically loaded from JSON or optimized during training

# Example: create a Pixel2D DOE with focusing phase
import torch
import math

res = 512  # DOE resolution
ps = 0.008  # Pixel size in mm (8 um)
wvln = 0.550  # Design wavelength in um
foclen = 50.0  # Focal length in mm

# Create focusing phase profile
x = torch.linspace(-res//2, res//2-1, res) * ps
y = torch.linspace(-res//2, res//2-1, res) * ps
xx, yy = torch.meshgrid(x, y, indexing='xy')
r2 = xx**2 + yy**2

# Focusing phase: φ = -π * r^2 / (λ * f)
phase = -math.pi * r2 / (wvln * 1e-3 * foclen)
phase = phase % (2 * math.pi)  # Wrap to [0, 2π]

# Create DOE (see deeplens/optics/diffractive_surface/ for details)
# In practice, DOEs are defined in JSON files and loaded with HybridLens

Zernike-Based DOE

from deeplens.optics.diffractive_surface import Zernike

# Zernike DOE uses Zernike polynomial coefficients to define phase
# Useful for correcting specific aberrations

# DOE parameters are typically defined in JSON:
# {
#     "param_model": "zernike",
#     "d": 45.0,           # DOE position (mm)
#     "h": 4.0,            # DOE height (mm)
#     "w": 4.0,            # DOE width (mm)
#     "res": [512, 512],   # DOE resolution
#     "coef": [0, 0, 0, -1.0, 0, 0, 0, 0, 0, 0.5]  # Zernike coefficients
# }

# In HybridLens, the DOE is accessed via lens.doe
# Zernike coefficients can be optimized during training

Achromatic Hybrid Lens

Combining Refractive and Diffractive

# HybridLens combines a GeoLens (refractive) with a DOE (diffractive)
# The DOE is placed at the end of the optical system

# Benefits of hybrid design:
# - DOE has opposite chromatic dispersion to glass
# - Can correct chromatic aberration with a single thin element
# - Enables compact achromatic designs

# Load a hybrid lens design
from deeplens.optics import HybridLens

lens = HybridLens(
    filename='./datasets/lenses/hybrid/achromatic_hybrid.json',
    device='cuda'
)

# Access components
geolens = lens.geolens  # Refractive part (GeoLens)
doe = lens.doe          # Diffractive part (Binary2, Pixel2D, Fresnel, or Zernike)

# The DOE compensates for chromatic aberration from refractive elements

Multi-Wavelength Optimization

import torch
import torch.optim as optim
from deeplens.optics.config import WAVE_RGB, SPP_COHERENT, PSF_KS

# Set double precision for coherent ray tracing
torch.set_default_dtype(torch.float64)

# Get optimizer for both GeoLens and DOE
optimizer = lens.get_optimizer(
    doe_lr=1e-4,                      # DOE learning rate
    lens_lr=[1e-4, 1e-4, 1e-2, 1e-5], # GeoLens [d, c, k, a]
    lr_decay=0.01
)

wavelengths = WAVE_RGB  # [0.656, 0.588, 0.486] um
point = [0.0, 0.0, -10000.0]  # On-axis point source

for epoch in range(1000):
    optimizer.zero_grad()
    total_loss = 0.0

    # Optimize for all wavelengths
    for wvln in wavelengths:
        # Compute PSF using ray-wave model
        psf = lens.psf(
            points=point,
            ks=PSF_KS,
            wvln=wvln,
            spp=SPP_COHERENT
        )

        # Loss: minimize PSF spread (maximize peak)
        loss = -psf.max()  # Simple peak sharpening loss
        total_loss += loss

    total_loss.backward()
    optimizer.step()

    if epoch % 100 == 0:
        print(f"Epoch {epoch}, Loss: {total_loss.item():.6f}")

Multi-Focal Lens

Design lens with multiple focal points:

from deeplens.optics.diffractive_surface import Binary2

# Create binary phase pattern for two focal lengths
# f1 = 50mm, f2 = 100mm

H, W = 512, 512
phase_pattern = torch.zeros(H, W, dtype=torch.bool)

for i in range(H):
    for j in range(W):
        x = (i - H/2) * 0.01  # mm
        y = (j - W/2) * 0.01
        r2 = x**2 + y**2

        # Combined phase for two focal lengths
        phase1 = -torch.pi * r2 / (0.550e-3 * 50.0)
        phase2 = -torch.pi * r2 / (0.550e-3 * 100.0)

        # Binary encoding (simplified)
        total_phase = phase1 + phase2
        phase_pattern[i, j] = (total_phase % (2*torch.pi)) > torch.pi

# Add to lens
multi_focal_doe = Binary2(
    phase_pattern=phase_pattern,
    d=0.001,
    wavelength=0.550
)

lens.surfaces.append(multi_focal_doe)

Extended Depth of Field

Using Cubic Phase Plate

# For extended depth of field (EDoF), use a GeoLens with Cubic surface
# Cubic phase creates a depth-invariant PSF (wavefront coding)

from deeplens import GeoLens
from deeplens.optics.config import DEPTH, PSF_KS, SPP_PSF

# Load lens with cubic phase element
# Cubic surfaces are defined in JSON with type "Cubic"
lens = GeoLens(
    filename='./datasets/lenses/camera/edof_cubic.json',
    device='cuda'
)

# Test PSF at multiple depths
import torch
depths = [-500.0, -1000.0, -2000.0, -5000.0]
psfs = []

for depth in depths:
    point = torch.tensor([0.0, 0.0, depth])
    psf = lens.psf(points=point, ks=PSF_KS, spp=SPP_PSF)
    psfs.append(psf)

# Cubic phase creates similar PSF shapes across depth
# Post-processing with deconvolution recovers sharp images

Optimization for EDoF

import torch
from deeplens.optics.config import PSF_KS, SPP_PSF

# Optimize DOE for consistent PSF across depth range
depths = torch.linspace(-500, -5000, 5)

for epoch in range(500):
    optimizer.zero_grad()
    loss = 0.0

    # Calculate PSFs at all depths
    psfs = []
    for depth in depths:
        point = torch.tensor([0.0, 0.0, depth.item()])
        psf = lens.psf(points=point, ks=PSF_KS, spp=SPP_PSF)
        psfs.append(psf)

    # Loss: Encourage similar PSFs across depths
    psf_stack = torch.stack(psfs, dim=0)
    psf_mean = psf_stack.mean(dim=0)

    # Variance loss (minimize PSF variation)
    variance_loss = ((psf_stack - psf_mean) ** 2).mean()

    # Sharpness loss (maximize PSF peak)
    sharpness_loss = -psf_mean.max()

    loss = variance_loss + 0.1 * sharpness_loss

    loss.backward()
    optimizer.step()

Fabrication Considerations

Manufacturable DOE Design

# For manufacturability, consider:
# 1. Minimum feature size (typically > 0.5 μm for photolithography)
# 2. Maximum aspect ratio (height/width < 5 for standard processes)
# 3. Quantization levels (8 or 16 levels for multi-level DOE)

# Apply phase quantization for multi-level DOE
import torch

def quantize_phase(phase, num_levels=8):
    """Quantize continuous phase to discrete levels."""
    phase_norm = (phase % (2 * torch.pi)) / (2 * torch.pi)
    phase_quantized = torch.round(phase_norm * num_levels) / num_levels
    return phase_quantized * 2 * torch.pi

# Access DOE phase map
wvln = 0.550
phase_map = lens.doe.get_phase_map(wvln)

# Quantize for fabrication
phase_quantized = quantize_phase(phase_map, num_levels=8)

Multi-Level DOE

import torch

# Quantize continuous phase to discrete levels for fabrication
num_levels = 8  # Common: 2 (binary), 4, 8, 16 levels

def quantize_phase(phase, num_levels):
    """Quantize phase to discrete levels."""
    phase_norm = (phase % (2 * torch.pi)) / (2 * torch.pi)
    phase_quantized = torch.round(phase_norm * num_levels) / num_levels
    return phase_quantized * 2 * torch.pi

# Get DOE phase map and quantize
wvln = 0.550  # Design wavelength
phase_continuous = lens.doe.get_phase_map(wvln)
phase_multilevel = quantize_phase(phase_continuous, num_levels)

# Note: For training with quantization, use straight-through estimator
# to allow gradients to flow through the quantization operation

Running Example

Complete script available as 6_hybridlens_design.py:

python 6_hybridlens_design.py

Performance Comparison

Metric Pure Refractive Hybrid Design
Chromatic Aberration Requires multiple elements Single DOE can correct
Weight Heavy (multiple glass elements) Light (thin DOEs)
Thickness Thick lens stack Compact design
Cost High (precision glass) Lower (planar fabrication)
Efficiency High (>95%) Moderate (70-90%)
Diffraction Orders N/A Manage higher orders

Advantages and Limitations

Advantages:

  • Compact and lightweight
  • Excellent chromatic correction
  • Novel functionalities (multi-focal, EDoF)
  • Potential for lower cost

Limitations:

  • Lower diffraction efficiency
  • Wavelength-dependent performance
  • More complex fabrication
  • Stray light from higher orders

Tips and Best Practices

  1. Start with Refractive: Design refractive system first, add DOE for correction
  2. Wavelength Range: DOEs are wavelength-sensitive, design for target range
  3. Efficiency: Balance performance with diffraction efficiency
  4. Sampling: Use high SPP (>2048) for accurate diffraction simulation
  5. Fabrication: Consider manufacturing constraints early in design
  6. Validation: Prototype and test critical designs

See Also