float4 position [[position]]; }; fragment float4 fragment_main(VertexOut vert [[stage_in]], const constant float4 &color) { return color; } ''' texture = Texture(pixel_format=Texture.PixelFormat.RGBA, component_format=Texture.ComponentFormat.UInt8, size=[512, 512], flags=Texture.TextureFlags.ShaderRead | Texture.TextureFlags.RenderTarget) render_pass = RenderPass([texture]) render_pass.set_viewport([10, 10], [512 - 10, 512 - 20]) shader = Shader(render_pass, "test_shader", vertex_program, fragment_program) p = np.array([[0.0, 0.5, 0], [-0.5, -0.5, 0], [0.5, -0.5, 0]], dtype=np.float32) shader.set_buffer("position", p) shader.set_buffer("color", np.array([1, 0, 0, 1], dtype=np.float32)) shader.set_buffer("indices", np.array([0, 1, 2], dtype=np.uint32)) with render_pass: with shader: if False: shader.draw_array(Shader.PrimitiveType.Triangle, 0, 3)
def __init__(self): super(TestApp, self).__init__((1024, 768), "NanoGUI Test") self.shader = None window = Window(self, "Button demo") window.set_position((15, 15)) window.set_layout(GroupLayout()) Label(window, "Push buttons", "sans-bold") b = Button(window, "Plain button") def cb(): print("pushed!") b.set_callback(cb) b = Button(window, "Styled", icons.FA_ROCKET) b.set_background_color(Color(0, 0, 1.0, 0.1)) b.set_callback(cb) Label(window, "Toggle buttons", "sans-bold") b = Button(window, "Toggle me") b.set_flags(Button.Flags.ToggleButton) def change_cb(state): print("Toggle button state: %s" % str(state)) b.set_change_callback(change_cb) Label(window, "Radio buttons", "sans-bold") b = Button(window, "Radio button 1") b.set_flags(Button.Flags.RadioButton) b = Button(window, "Radio button 2") b.set_flags(Button.Flags.RadioButton) Label(window, "A tool palette", "sans-bold") tools = Widget(window) tools.set_layout( BoxLayout(Orientation.Horizontal, Alignment.Middle, 0, 6)) ToolButton(tools, icons.FA_CLOUD) ToolButton(tools, icons.FA_FAST_FORWARD) ToolButton(tools, icons.FA_COMPASS) ToolButton(tools, icons.FA_UTENSILS) Label(window, "Popup buttons", "sans-bold") popup_btn = PopupButton(window, "Popup", icons.FA_FLASK) popup = popup_btn.popup() popup.set_layout(GroupLayout()) Label(popup, "Arbitrary widgets can be placed here") CheckBox(popup, "A check box") # popup right popup_btn = PopupButton(popup, "Recursive popup", icons.FA_CHART_PIE) popup_right = popup_btn.popup() popup_right.set_layout(GroupLayout()) CheckBox(popup_right, "Another check box") # popup left popup_btn = PopupButton(popup, "Recursive popup", icons.FA_DNA) popup_btn.set_side(Popup.Side.Left) popup_left = popup_btn.popup() popup_left.set_layout(GroupLayout()) CheckBox(popup_left, "Another check box") window = Window(self, "Basic widgets") window.set_position((200, 15)) window.set_layout(GroupLayout()) Label(window, "Message dialog", "sans-bold") tools = Widget(window) tools.set_layout( BoxLayout(Orientation.Horizontal, Alignment.Middle, 0, 6)) def cb2(result): print("Dialog result: %i" % result) b = Button(tools, "Info") def cb(): dlg = MessageDialog(self, MessageDialog.Type.Information, "Title", "This is an information message") dlg.set_callback(cb2) b.set_callback(cb) b = Button(tools, "Warn") def cb(): dlg = MessageDialog(self, MessageDialog.Type.Warning, "Title", "This is a warning message") dlg.set_callback(cb2) b.set_callback(cb) b = Button(tools, "Ask") def cb(): dlg = MessageDialog(self, MessageDialog.Type.Warning, "Title", "This is a question message", "Yes", "No", True) dlg.set_callback(cb2) b.set_callback(cb) import os import sys os.chdir(sys.path[0]) try: icons_data = nanogui.load_image_directory(self.nvg_context(), "icons") except: try: icons_data = nanogui.load_image_directory( self.nvg_context(), "../icons") except: icons_data = nanogui.load_image_directory( self.nvg_context(), "../resources/icons") Label(window, "Image panel & scroll panel", "sans-bold") image_panel_btn = PopupButton(window, "Image Panel") image_panel_btn.set_icon(icons.FA_IMAGES) popup = image_panel_btn.popup() vscroll = VScrollPanel(popup) img_panel = ImagePanel(vscroll) img_panel.set_images(icons_data) popup.set_fixed_size((245, 150)) img_window = Window(self, "Selected image") img_window.set_position((710, 15)) img_window.set_layout(GroupLayout()) img_view = ImageView(img_window) img_view.set_image( Texture(icons_data[0][1] + ".png", Texture.InterpolationMode.Trilinear, Texture.InterpolationMode.Nearest)) img_view.center() def cb(i): print("Selected item %i" % i) img_view.set_image( Texture(icons_data[i][1] + ".png", Texture.InterpolationMode.Trilinear, Texture.InterpolationMode.Nearest)) img_panel.set_callback(cb) Label(window, "File dialog", "sans-bold") tools = Widget(window) tools.set_layout( BoxLayout(Orientation.Horizontal, Alignment.Middle, 0, 6)) b = Button(tools, "Open") valid = [("png", "Portable Network Graphics"), ("txt", "Text file")] def cb(): result = nanogui.file_dialog(valid, False) print("File dialog result = %s" % result) b.set_callback(cb) b = Button(tools, "Save") def cb(): result = nanogui.file_dialog(valid, True) print("File dialog result = %s" % result) b.set_callback(cb) Label(window, "Combo box", "sans-bold") ComboBox(window, ["Combo box item 1", "Combo box item 2", "Combo box item 3"]) Label(window, "Check box", "sans-bold") def cb(state): print("Check box 1 state: %s" % state) chb = CheckBox(window, "Flag 1", cb) chb.set_checked(True) def cb(state): print("Check box 2 state: %s" % state) CheckBox(window, "Flag 2", cb) Label(window, "Progress bar", "sans-bold") self.progress = ProgressBar(window) Label(window, "Slider and text box", "sans-bold") panel = Widget(window) panel.set_layout( BoxLayout(Orientation.Horizontal, Alignment.Middle, 0, 20)) slider = Slider(panel) slider.set_value(0.5) slider.set_fixed_width(80) text_box = TextBox(panel) text_box.set_fixed_size((60, 25)) text_box.set_value("50") text_box.set_units("%") text_box.set_font_size(20) text_box.set_alignment(TextBox.Alignment.Right) def cb(value): text_box.set_value("%i" % int(value * 100)) slider.set_callback(cb) def cb(value): print("Final slider value: %i" % int(value * 100)) slider.set_final_callback(cb) window = Window(self, "Misc. widgets") window.set_position((425, 15)) window.set_layout(GroupLayout()) tab_widget = TabWidget(window) layer = Widget(tab_widget) layer.set_layout(GroupLayout()) tab_widget.append_tab("Color Wheel", layer) Label(layer, "Color wheel widget", "sans-bold") ColorWheel(layer) layer = Widget(tab_widget) layer.set_layout(GroupLayout()) tab_widget.append_tab("Function Graph", layer) Label(layer, "Function graph widget", "sans-bold") graph = Graph(layer, "Some function") graph.set_header("E = 2.35e-3") graph.set_footer("Iteration 89") values = [ 0.5 * (0.5 * math.sin(i / 10.0) + 0.5 * math.cos(i / 23.0) + 1) for i in range(100) ] graph.set_values(values) # Dummy tab used to represent the last tab button. plus_id = tab_widget.append_tab("+", Widget(tab_widget)) def tab_cb(index): if index == plus_id: global counter # When the "+" tab has been clicked, simply add a new tab. tab_name = "Dynamic {0}".format(counter) layer_dyn = Widget(tab_widget) layer_dyn.set_layout(GroupLayout()) new_id = tab_widget.insert_tab(tab_widget.tab_count() - 1, tab_name, layer_dyn) Label(layer_dyn, "Function graph widget", "sans-bold") graph_dyn = Graph(layer_dyn, "Dynamic function") graph_dyn.set_header("E = 2.35e-3") graph_dyn.set_footer("Iteration {0}".format(index * counter)) values_dyn = [ 0.5 * abs((0.5 * math.sin(i / 10.0 + counter)) + (0.5 * math.cos(i / 23.0 + 1 + counter))) for i in range(100) ] graph_dyn.set_values(values_dyn) counter += 1 # We must invoke the layout manager after adding tabs dynamically self.perform_layout() tab_widget.set_selected_id(new_id) tab_widget.set_callback(tab_cb) window = Window(self, "Grid of small widgets") window.set_position((425, 300)) layout = GridLayout(Orientation.Horizontal, 2, Alignment.Middle, 15, 5) layout.set_col_alignment([Alignment.Maximum, Alignment.Fill]) layout.set_spacing(0, 10) window.set_layout(layout) Label(window, "Floating point :", "sans-bold") float_box = TextBox(window) float_box.set_editable(True) float_box.set_fixed_size((100, 20)) float_box.set_value("50") float_box.set_units("GiB") float_box.set_default_value("0.0") float_box.set_font_size(16) float_box.set_format("[-]?[0-9]*\\.?[0-9]+") Label(window, "Positive integer :", "sans-bold") int_box = IntBox(window) int_box.set_editable(True) int_box.set_fixed_size((100, 20)) int_box.set_value(50) int_box.set_units("Mhz") int_box.set_default_value("0") int_box.set_font_size(16) int_box.set_format("[1-9][0-9]*") int_box.set_spinnable(True) int_box.set_min_value(1) int_box.set_value_increment(2) Label(window, "Checkbox :", "sans-bold") cb = CheckBox(window, "Check me") cb.set_font_size(16) cb.set_checked(True) Label(window, "Combo box :", "sans-bold") cobo = ComboBox(window, ["Item 1", "Item 2", "Item 3"]) cobo.set_font_size(16) cobo.set_fixed_size((100, 20)) Label(window, "Color picker :", "sans-bold") cp = ColorPicker(window, Color(255, 120, 0, 255)) cp.set_fixed_size((100, 20)) def cp_final_cb(color): print("ColorPicker Final Callback: [{0}, {1}, {2}, {3}]".format( color.r, color.g, color.b, color.w)) cp.set_final_callback(cp_final_cb) # setup a fast callback for the color picker widget on a new window # for demonstrative purposes window = Window(self, "Color Picker Fast Callback") window.set_position((425, 300)) layout = GridLayout(Orientation.Horizontal, 2, Alignment.Middle, 15, 5) layout.set_col_alignment([Alignment.Maximum, Alignment.Fill]) layout.set_spacing(0, 10) window.set_layout(layout) window.set_position((425, 500)) Label(window, "Combined: ") b = Button(window, "ColorWheel", icons.FA_INFINITY) Label(window, "Red: ") red_int_box = IntBox(window) red_int_box.set_editable(False) Label(window, "Green: ") green_int_box = IntBox(window) green_int_box.set_editable(False) Label(window, "Blue: ") blue_int_box = IntBox(window) blue_int_box.set_editable(False) Label(window, "Alpha: ") alpha_int_box = IntBox(window) def cp_fast_cb(color): b.set_background_color(color) b.set_text_color(color.contrasting_color()) red = int(color.r * 255.0) red_int_box.set_value(red) green = int(color.g * 255.0) green_int_box.set_value(green) blue = int(color.b * 255.0) blue_int_box.set_value(blue) alpha = int(color.w * 255.0) alpha_int_box.set_value(alpha) cp.set_callback(cp_fast_cb) self.perform_layout() self.render_pass = RenderPass([self]) self.render_pass.set_clear_color(0, Color(0.3, 0.3, 0.32, 1.0)) if nanogui.api == 'opengl': vertex_shader = """ #version 330 uniform mat4 mvp; in vec3 position; void main() { gl_Position = mvp * vec4(position, 1.0); }""" fragment_shader = """ #version 330 out vec4 color; uniform float intensity; void main() { color = vec4(vec3(intensity), 1.0); }""" elif nanogui.api == 'gles2' or nanogui.api == 'gles3': vertex_shader = """ precision highp float; uniform mat4 mvp; attribute vec3 position; void main() { gl_Position = mvp * vec4(position, 1.0); }""" fragment_shader = """ precision highp float; uniform float intensity; void main() { gl_FragColor = vec4(vec3(intensity), 1.0); }""" elif nanogui.api == 'metal': vertex_shader = """ using namespace metal; struct VertexOut { float4 position [[position]]; }; vertex VertexOut vertex_main(const device packed_float3 *position, constant float4x4 &mvp, uint id [[vertex_id]]) { VertexOut vert; vert.position = mvp * float4(position[id], 1.f); return vert; }""" fragment_shader = """ using namespace metal; fragment float4 fragment_main(const constant float &intensity) { return float4(intensity); }""" self.shader = Shader( self.render_pass, # An identifying name "A simple shader", vertex_shader, fragment_shader) self.shader.set_buffer("indices", np.array([0, 1, 2, 2, 3, 0], dtype=np.uint32)) self.shader.set_buffer( "position", np.array([[-1, -1, 0], [1, -1, 0], [1, 1, 0], [-1, 1, 0]], dtype=np.float32)) self.shader.set_buffer("intensity", np.array(0.5, dtype=np.float32))
def __init__(self): Screen.__init__(self, size=[512, 512], caption="Unnamed") if nanogui.api == 'opengl': vertex_program = ''' #version 330 in vec3 position; in vec2 uv; out vec2 uv_frag; uniform mat4 mvp; void main() { gl_Position = mvp * vec4(position, 1.0); uv_frag = uv; } ''' fragment_program = ''' #version 330 in vec2 uv_frag; out vec4 fragColor; uniform sampler2D albedo_texture; void main() { fragColor = texture(albedo_texture, uv_frag); } ''' elif nanogui.api == 'metal': vertex_program = ''' using namespace metal; struct VertexOut { float4 position [[position]]; float2 uv; }; vertex VertexOut vertex_main(const device packed_float3 *position, const device float2 *uv, constant float4x4 &mvp, uint id [[vertex_id]]) { VertexOut vert; vert.position = mvp * float4(position[id], 1.f); vert.uv = uv[id]; return vert; } ''' fragment_program = ''' using namespace metal; struct VertexOut { float4 position [[position]]; float2 uv; }; fragment float4 fragment_main(VertexOut vert [[stage_in]], texture2d<float, access::sample> albedo_texture, sampler albedo_sampler) { return albedo_texture.sample(albedo_sampler, vert.uv); } ''' base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) image_fname = os.path.join(base_dir, "resources/icons/icon1.png") image = np.array(Image.open(image_fname)) self.albedo_texture = Texture( pixel_format=Texture.PixelFormat.RGBA, component_format=Texture.ComponentFormat.UInt8, size=image.shape[:2]) self.albedo_texture.upload(image) self.render_pass = RenderPass(color_targets=[self]) self.shader = Shader(self.render_pass, "test_shader", vertex_program, fragment_program) p = np.array([[-1, -1, 0], [1, -1, 0], [1, 1, 0], [-1, 1, 0]], dtype=np.float32) uv = np.array([[1, 1], [0, 1], [0, 0], [1, 0]], dtype=np.float32) indices = np.array([0, 2, 1, 3, 2, 0], dtype=np.uint32) self.shader.set_buffer("position", p) self.shader.set_buffer("uv", uv) self.shader.set_buffer("indices", indices) self.shader.set_texture("albedo_texture", self.albedo_texture)
class MyScreen(Screen): def __init__(self): Screen.__init__(self, size=[512, 512], caption="Unnamed") if nanogui.api == 'opengl': vertex_program = ''' #version 330 in vec3 position; in vec2 uv; out vec2 uv_frag; uniform mat4 mvp; void main() { gl_Position = mvp * vec4(position, 1.0); uv_frag = uv; } ''' fragment_program = ''' #version 330 in vec2 uv_frag; out vec4 fragColor; uniform sampler2D albedo_texture; void main() { fragColor = texture(albedo_texture, uv_frag); } ''' elif nanogui.api == 'metal': vertex_program = ''' using namespace metal; struct VertexOut { float4 position [[position]]; float2 uv; }; vertex VertexOut vertex_main(const device packed_float3 *position, const device float2 *uv, constant float4x4 &mvp, uint id [[vertex_id]]) { VertexOut vert; vert.position = mvp * float4(position[id], 1.f); vert.uv = uv[id]; return vert; } ''' fragment_program = ''' using namespace metal; struct VertexOut { float4 position [[position]]; float2 uv; }; fragment float4 fragment_main(VertexOut vert [[stage_in]], texture2d<float, access::sample> albedo_texture, sampler albedo_sampler) { return albedo_texture.sample(albedo_sampler, vert.uv); } ''' base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) image_fname = os.path.join(base_dir, "resources/icons/icon1.png") image = np.array(Image.open(image_fname)) self.albedo_texture = Texture( pixel_format=Texture.PixelFormat.RGBA, component_format=Texture.ComponentFormat.UInt8, size=image.shape[:2]) self.albedo_texture.upload(image) self.render_pass = RenderPass(color_targets=[self]) self.shader = Shader(self.render_pass, "test_shader", vertex_program, fragment_program) p = np.array([[-1, -1, 0], [1, -1, 0], [1, 1, 0], [-1, 1, 0]], dtype=np.float32) uv = np.array([[1, 1], [0, 1], [0, 0], [1, 0]], dtype=np.float32) indices = np.array([0, 2, 1, 3, 2, 0], dtype=np.uint32) self.shader.set_buffer("position", p) self.shader.set_buffer("uv", uv) self.shader.set_buffer("indices", indices) self.shader.set_texture("albedo_texture", self.albedo_texture) def draw_contents(self): with self.render_pass: view = nanogui.look_at(origin=[0, -2, -10], target=[0, 0, 0], up=[0, 1, 0]) model = nanogui.rotate([0, 1, 0], glfw.getTime() * 0.01) fbsize = self.framebuffer_size() proj = nanogui.perspective(fov=25 * np.pi / 180, near=0.1, far=20, aspect=fbsize[0] / float(fbsize[1])) mvp = proj @ view @ model self.shader.set_buffer("mvp", np.float32(mvp.T)) with self.shader: self.shader.draw_array(Shader.PrimitiveType.Triangle, 0, 6, indexed=True) def keyboard_event(self, key, scancode, action, modifiers): if super(MyScreen, self).keyboard_event(key, scancode, action, modifiers): return True if key == glfw.KEY_ESCAPE and action == glfw.PRESS: self.set_visible(False) return True return False def resize_event(self, size): self.render_pass.resize(self.framebuffer_size()) super(MyScreen, self).resize_event(size) return True
def __init__(self): Screen.__init__(self, size=[512, 512], caption="Unnamed", depth_buffer=True ) if nanogui.api == 'opengl': vertex_program = ''' #version 330 in vec3 position; in vec4 color; out vec4 color_frag; uniform mat4 mvp; void main() { gl_Position = mvp * vec4(position, 1.0); color_frag = color; } ''' fragment_program = ''' #version 330 in vec4 color_frag; out vec4 fragColor; void main() { fragColor = color_frag; } ''' elif nanogui.api == 'metal': vertex_program = ''' using namespace metal; struct VertexOut { float4 position [[position]]; float4 color; }; vertex VertexOut vertex_main(const device packed_float3 *position, const device float4 *color, constant float4x4 &mvp, uint id [[vertex_id]]) { VertexOut vert; vert.position = mvp * float4(position[id], 1.f); vert.color = color[id]; return vert; } ''' fragment_program = ''' using namespace metal; struct VertexOut { float4 position [[position]]; float4 color; }; fragment float4 fragment_main(VertexOut vert [[stage_in]]) { return vert.color; } ''' self.render_pass = RenderPass( color_targets=[self], depth_target=self ) self.shader = Shader( self.render_pass, "test_shader", vertex_program, fragment_program ) p = np.array([ [-1, 1, 1], [-1, -1, 1], [1, -1, 1], [1, 1, 1], [-1, 1, -1], [-1, -1, -1], [1, -1, -1], [1, 1, -1]], dtype=np.float32 ) color = np.array([ [0, 1, 1, 1], [0, 0, 1, 1], [1, 0, 1, 1], [1, 1, 1, 1], [0, 1, 0, 1], [0, 0, 0, 1], [1, 0, 0, 1], [1, 1, 0, 1]], dtype=np.float32 ) indices = np.array([ 3, 2, 6, 6, 7, 3, 4, 5, 1, 1, 0, 4, 4, 0, 3, 3, 7, 4, 1, 5, 6, 6, 2, 1, 0, 1, 2, 2, 3, 0, 7, 6, 5, 5, 4, 7], dtype=np.uint32 ) self.shader.set_buffer("position", p) self.shader.set_buffer("color", color) self.shader.set_buffer("indices", indices)
class MyScreen(Screen): def __init__(self): Screen.__init__(self, size=[512, 512], caption="Unnamed", depth_buffer=True ) if nanogui.api == 'opengl': vertex_program = ''' #version 330 in vec3 position; in vec4 color; out vec4 color_frag; uniform mat4 mvp; void main() { gl_Position = mvp * vec4(position, 1.0); color_frag = color; } ''' fragment_program = ''' #version 330 in vec4 color_frag; out vec4 fragColor; void main() { fragColor = color_frag; } ''' elif nanogui.api == 'metal': vertex_program = ''' using namespace metal; struct VertexOut { float4 position [[position]]; float4 color; }; vertex VertexOut vertex_main(const device packed_float3 *position, const device float4 *color, constant float4x4 &mvp, uint id [[vertex_id]]) { VertexOut vert; vert.position = mvp * float4(position[id], 1.f); vert.color = color[id]; return vert; } ''' fragment_program = ''' using namespace metal; struct VertexOut { float4 position [[position]]; float4 color; }; fragment float4 fragment_main(VertexOut vert [[stage_in]]) { return vert.color; } ''' self.render_pass = RenderPass( color_targets=[self], depth_target=self ) self.shader = Shader( self.render_pass, "test_shader", vertex_program, fragment_program ) p = np.array([ [-1, 1, 1], [-1, -1, 1], [1, -1, 1], [1, 1, 1], [-1, 1, -1], [-1, -1, -1], [1, -1, -1], [1, 1, -1]], dtype=np.float32 ) color = np.array([ [0, 1, 1, 1], [0, 0, 1, 1], [1, 0, 1, 1], [1, 1, 1, 1], [0, 1, 0, 1], [0, 0, 0, 1], [1, 0, 0, 1], [1, 1, 0, 1]], dtype=np.float32 ) indices = np.array([ 3, 2, 6, 6, 7, 3, 4, 5, 1, 1, 0, 4, 4, 0, 3, 3, 7, 4, 1, 5, 6, 6, 2, 1, 0, 1, 2, 2, 3, 0, 7, 6, 5, 5, 4, 7], dtype=np.uint32 ) self.shader.set_buffer("position", p) self.shader.set_buffer("color", color) self.shader.set_buffer("indices", indices) def draw_contents(self): with self.render_pass: view = Matrix4f.look_at( origin=[0, -2, -10], target=[0, 0, 0], up=[0, 1, 0] ) model = Matrix4f.rotate( [0, 1, 0], glfw.getTime() ) fbsize = self.framebuffer_size() proj = Matrix4f.perspective( fov=25 * np.pi / 180, near=0.1, far=20, aspect=fbsize[0] / float(fbsize[1]) ) mvp = proj @ view @ model self.shader.set_buffer("mvp", np.float32(mvp).T) with self.shader: self.shader.draw_array(Shader.PrimitiveType.Triangle, 0, 36, indexed=True) def keyboard_event(self, key, scancode, action, modifiers): if super(MyScreen, self).keyboard_event(key, scancode, action, modifiers): return True if key == glfw.KEY_ESCAPE and action == glfw.PRESS: self.set_visible(False) return True return False def resize_event(self, size): self.render_pass.resize(self.framebuffer_size()) super(MyScreen, self).resize_event(size) return True
def __init__(self): Screen.__init__(self, [512, 512], "Unnamed") if nanogui.api == 'opengl': vertex_program = ''' #version 330 in vec3 position; void main() { gl_Position = vec4(position, 1); } ''' fragment_program = ''' #version 330 uniform vec4 color; out vec4 fragColor; void main() { fragColor = color; } ''' elif nanogui.api == 'metal': vertex_program = ''' using namespace metal; struct VertexOut { float4 position [[position]]; }; vertex VertexOut vertex_main(const device packed_float3 *position, uint id [[vertex_id]]) { VertexOut vert; vert.position = float4(position[id], 1.f); return vert; } ''' fragment_program = ''' using namespace metal; struct VertexOut { float4 position [[position]]; }; fragment float4 fragment_main(VertexOut vert [[stage_in]], const constant float4 &color) { return color; } ''' self.render_pass = RenderPass([self]) self.render_pass.set_viewport([10, 10], self.framebuffer_size() - [10, 20]) self.shader = Shader(self.render_pass, "test_shader", vertex_program, fragment_program) p = np.array([[0.0, 0.5, 0], [-0.5, -0.5, 0], [0.5, -0.5, 0]], dtype=np.float32) self.shader.set_buffer("position", p) self.shader.set_buffer("color", np.array([0, 1, 0, 1], dtype=np.float32)) self.shader.set_buffer("indices", np.array([0, 1, 2], dtype=np.uint32))