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()