| 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. |
| """ |
| |
| old_stdout = sys.stdout |
| sys.stdout = captured_output = io.StringIO() |
|
|
| |
| 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) |
|
|
| |
| sys.stdout = old_stdout |
| ppm_data = captured_output.getvalue() |
|
|
| |
| |
| ppm_file = io.BytesIO(ppm_data.encode()) |
| image = Image.open(ppm_file) |
|
|
| return image |
|
|
|
|
| |
| 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() |
|
|