File size: 5,281 Bytes
8271a05 3d65984 8271a05 9fb4ad4 3d65984 8271a05 9fb4ad4 a749ad2 901eeb8 3d65984 8271a05 a749ad2 8271a05 a749ad2 9fb4ad4 8271a05 a749ad2 8271a05 3d65984 d8ab0d3 a749ad2 d8ab0d3 3d65984 901eeb8 7f5ff84 901eeb8 a749ad2 d8ab0d3 901eeb8 7f5ff84 901eeb8 d8ab0d3 901eeb8 7f5ff84 3d65984 bc73aa3 7f5ff84 a749ad2 7f5ff84 a749ad2 7f5ff84 d8ab0d3 7f5ff84 d8ab0d3 3d65984 d8ab0d3 2f66b0f 8271a05 d8ab0d3 a749ad2 8271a05 d8ab0d3 8271a05 a749ad2 8271a05 | 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 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 | import gradio as gr
import io
import sys
import pandas as pd
from PIL import Image
from rayt_rust._core import Camera, Point3, Vec3
from rayt.gpu_utils import is_cuda_available
from rayt.scene import random_scene
from rayt.cuda_renderer import render_with_cuda
from rayt.numba_renderer import render_with_numba
from rayt.rust_renderer import render_with_rust
from numba import cuda
IS_CUDA_AVAILABE, _ = is_cuda_available()
def render_image(image_width, samples_per_pixel, aspect_ratio_str, max_depth, engine):
"""
Renders an image using the rayt library and returns it.
"""
# --- Capture stdout ---
old_stdout = sys.stdout
sys.stdout = captured_output = io.StringIO()
# --- Run the rendering logic ---
aspect_ratio = eval(aspect_ratio_str)
image_height = int(image_width / aspect_ratio)
world = random_scene()
camera = Camera(
lookfrom=Point3(13, 2, 3),
lookat=Point3(0, 0, 0),
vup=Vec3(0, 1, 0),
vfov=20.0,
aspect_ratio=aspect_ratio,
aperture=0.1,
focus_dist=10.0,
)
render_funcs = {
"rust": render_with_rust,
"numba": render_with_numba,
"cuda": render_with_cuda,
}
if engine == "cuda" and not IS_CUDA_AVAILABE:
gr.Warning(
"CUDA renderer is not available in this server. Falling back to numba."
)
engine = "numba"
render_func = render_funcs[engine]
render_func(world, camera, image_width, image_height, samples_per_pixel, max_depth)
# --- Restore stdout and get the PPM data ---
sys.stdout = old_stdout
ppm_data = captured_output.getvalue()
# --- Convert PPM to Image ---
# The PPM data is in a string, so we need to wrap it in a file-like object
ppm_file = io.BytesIO(ppm_data.encode())
image = Image.open(ppm_file)
return image
# --- Create the Gradio Interface ---
with gr.Blocks() as iface:
gr.Markdown("# Ray Tracing in One Weekend")
with gr.Row():
with gr.Column():
gr.Markdown(
"A Python implementation of the book by Peter Shirley, rendered with Numba."
)
if IS_CUDA_AVAILABE:
device = cuda.get_current_device()
cc = device.compute_capability
cuda_info_dataframe = pd.DataFrame(
{
"CUDA support": ["AVAILABLE"],
"Found GPU": [device.name.decode("utf-8")],
"Compute Capability": [f"{cc[0]}.{cc[1]}"],
}
)
else:
cuda_info_dataframe = pd.DataFrame(
{
"CUDA support": ["NOT AVAILABLE"],
"Found GPU": ["-"],
"Compute Capability": ["-"],
}
)
gr.DataFrame(cuda_info_dataframe)
gr.Markdown(
"If you have an NVIDIA GPU on your local machine, you can clone the project and "
"run it locally. You can also deploy it to a server with CUDA support for faster "
"rendering."
)
gr.Image(
label="Sample Output",
value="image.webp",
show_download_button=False,
show_fullscreen_button=False,
show_label=True,
)
with gr.Column():
gr.Markdown("## PERFORMANCE")
gr.Markdown("RAYT tested on this machine:")
test_info_dataframe = pd.DataFrame(
{
"CPU": ["AMD Radeon 9 9900X"],
"GPU": ["NVIDIA RTX 5080"],
"Image Width": ["600"],
"Samples Per Pixel": ["100"],
}
)
gr.DataFrame(test_info_dataframe)
gr.Image(
label="Performance",
value="performance.png",
show_download_button=False,
show_fullscreen_button=False,
show_label=True,
)
with gr.Row():
with gr.Column():
gr.Markdown("## INPUT")
image_width = gr.Slider(
label="Image Width", minimum=100, maximum=1200, value=300, step=10
)
samples_per_pixel = gr.Slider(
label="Samples Per Pixel", minimum=1, maximum=200, value=20, step=1
)
max_depth = gr.Slider(
label="Max Ray Depth", minimum=1, maximum=100, value=50, step=1
)
aspect_ratio = gr.Dropdown(
label="Aspect Ratio", choices=["16/9", "3/2", "1/1"], value="16/9"
)
engine = gr.Dropdown(
label="Engine", choices=["rust", "numba", "cuda"], value="rust"
)
render_button = gr.Button("Render Image")
with gr.Column():
gr.Markdown("## OUTPUT")
output_image = gr.Image(label="Rendered Image", type="pil")
render_button.click(
fn=render_image,
inputs=[image_width, samples_per_pixel, aspect_ratio, max_depth, engine],
outputs=output_image,
)
if __name__ == "__main__":
iface.launch()
|