├── .gitignore ├── README.md ├── augen ├── App.py ├── Camera.py ├── __init__.py ├── mesh.py └── utils.py ├── doc └── screenshot.png ├── main.py ├── optional-requirements.txt ├── requirements.txt ├── sample-data └── dragon.obj └── shaders ├── mesh.frag.glsl └── mesh.vert.glsl /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ 2 | imgui.ini 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Python 3D Viewer 2 | ================ 3 | 4 | A simple starter Python code for experimenting 3D graphics using [glfw](https://github.com/FlorianRhiem/pyGLFW), [moderngl](https://moderngl.readthedocs.io) and [imgui](https://pyimgui.readthedocs.io). 5 | 6 | ![Screenshot](doc/screenshot.png) 7 | 8 | The squeleton of an application using this template looks like: 9 | 10 | ```python 11 | class MyApp(App): 12 | def init(self): 13 | """Load shaders, meshes, etc.""" 14 | # ... 15 | 16 | def update(self, time, delta_time): 17 | """Update scene""" 18 | # ... 19 | 20 | def render(self): 21 | """Use modergl to draw 3d here""" 22 | # ... 23 | 24 | def ui(self): 25 | """Use the imgui module here to draw the UI""" 26 | # ... 27 | 28 | def on_key(self, key, scancode, action, mods): 29 | """React to user input""" 30 | # ... 31 | 32 | def on_mouse_move(self, x, y): 33 | # ... 34 | 35 | def on_mouse_button(self, button, action, mods): 36 | # ... 37 | 38 | def on_scroll(self, x, y): 39 | # ... 40 | 41 | def on_resize(self, width, height): 42 | # ... 43 | 44 | app = MyApp(1280, 720, "My 3D application") 45 | app.main_loop() 46 | ``` 47 | 48 | License 49 | ------- 50 | 51 | ``` 52 | Copyright (c) 2020 -- Élie Michel 53 | 54 | Permission is hereby granted, free of charge, to any person obtaining a copy 55 | of this software and associated documentation files (the "Software"), to 56 | deal in the Software without restriction, including without limitation the 57 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 58 | sell copies of the Software, and to permit persons to whom the Software is 59 | furnished to do so, subject to the following conditions: 60 | 61 | The above copyright notice and this permission notice shall be included in 62 | all copies or substantial portions of the Software. 63 | 64 | The Software is provided "as is", without warranty of any kind, express or 65 | implied, including but not limited to the warranties of merchantability, 66 | fitness for a particular purpose and non-infringement. In no event shall the 67 | authors or copyright holders be liable for any claim, damages or other 68 | liability, whether in an action of contract, tort or otherwise, arising 69 | from, out of or in connection with the software or the use or other dealings 70 | in the Software. 71 | ``` 72 | -------------------------------------------------------------------------------- /augen/App.py: -------------------------------------------------------------------------------- 1 | # This file is part of PyAugen 2 | # 3 | # Copyright (c) 2020 -- Élie Michel 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to 7 | # deal in the Software without restriction, including without limitation the 8 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9 | # sell copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # The Software is provided "as is", without warranty of any kind, express or 16 | # implied, including but not limited to the warranties of merchantability, 17 | # fitness for a particular purpose and non-infringement. In no event shall the 18 | # authors or copyright holders be liable for any claim, damages or other 19 | # liability, whether in an action of contract, tort or otherwise, arising 20 | # from, out of or in connection with the software or the use or other dealings 21 | # in the Software. 22 | 23 | import glfw 24 | import moderngl 25 | import imgui 26 | from imgui.integrations.glfw import GlfwRenderer as ImguiRenderer 27 | 28 | class App: 29 | def __init__(self, width = 640, height = 480, title = "Hello world"): 30 | imgui.create_context() 31 | 32 | if not glfw.init(): 33 | return 34 | 35 | self.window = glfw.create_window(width, height, title, None, None) 36 | if not self.window: 37 | glfw.terminate() 38 | return 39 | 40 | glfw.make_context_current(self.window) 41 | self.ctx = moderngl.create_context(require=460) 42 | 43 | self.impl = ImguiRenderer(self.window, attach_callbacks=False) 44 | 45 | glfw.set_key_callback(self.window, self._on_key) 46 | glfw.set_cursor_pos_callback(self.window, self._on_mouse_move) 47 | glfw.set_mouse_button_callback(self.window, self._on_mouse_button) 48 | glfw.set_window_size_callback(self.window, self._on_resize) 49 | glfw.set_char_callback(self.window, self._on_char) 50 | glfw.set_scroll_callback(self.window, self._on_scroll) 51 | 52 | self.init() 53 | 54 | def main_loop(self): 55 | previous_time = glfw.get_time() 56 | 57 | # Loop until the user closes the window 58 | while not glfw.window_should_close(self.window): 59 | glfw.poll_events() 60 | self.impl.process_inputs() 61 | 62 | current_time = glfw.get_time() 63 | delta_time = current_time - previous_time 64 | previous_time = current_time 65 | self.update(current_time, delta_time) 66 | self.render() 67 | 68 | imgui.new_frame() 69 | self.ui() 70 | imgui.render() 71 | self.impl.render(imgui.get_draw_data()) 72 | 73 | glfw.swap_buffers(self.window) 74 | 75 | self.impl.shutdown() 76 | glfw.terminate() 77 | 78 | def should_close(self): 79 | glfw.set_window_should_close(self.window, True) 80 | 81 | def mouse_pos(self): 82 | return glfw.get_cursor_pos(self.window) 83 | 84 | def size(self): 85 | return glfw.get_window_size(self.window) 86 | 87 | def init(self): 88 | pass 89 | 90 | def update(self, time): 91 | pass 92 | 93 | def render(self): 94 | pass 95 | 96 | def ui(self): 97 | pass 98 | 99 | def _on_key(self, window, key, scancode, action, mods): 100 | self.impl.keyboard_callback(window, key, scancode, action, mods) 101 | self.on_key(key, scancode, action, mods) 102 | 103 | def on_key(self, key, scancode, action, mods): 104 | pass 105 | 106 | def _on_char(self, window, codepoint): 107 | self.impl.char_callback(window, codepoint) 108 | self.on_char(codepoint) 109 | 110 | def on_char(self, codepoint): 111 | pass 112 | 113 | def _on_mouse_move(self, window, x, y): 114 | self.impl.mouse_callback(window, x, y) 115 | self.on_mouse_move(x, y) 116 | 117 | def on_mouse_move(self, x, y): 118 | pass 119 | 120 | def _on_mouse_button(self, window, button, action, mods): 121 | if not imgui.get_io().want_capture_mouse: 122 | self.on_mouse_button(button, action, mods) 123 | 124 | def on_mouse_button(self, button, action, mods): 125 | pass 126 | 127 | def _on_scroll(self, window, xoffset, yoffset): 128 | self.impl.scroll_callback(window, xoffset, yoffset) 129 | self.on_scroll(xoffset, yoffset) 130 | 131 | def on_scroll(self, xoffset, yoffset): 132 | pass 133 | 134 | def _on_resize(self, window, width, height): 135 | self.impl.resize_callback(window, width, height) 136 | self.on_resize(width, height) 137 | 138 | def on_resize(self, width, height): 139 | pass 140 | -------------------------------------------------------------------------------- /augen/Camera.py: -------------------------------------------------------------------------------- 1 | # This file is part of PyAugen 2 | # 3 | # Copyright (c) 2020 -- Élie Michel 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to 7 | # deal in the Software without restriction, including without limitation the 8 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9 | # sell copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # The Software is provided "as is", without warranty of any kind, express or 16 | # implied, including but not limited to the warranties of merchantability, 17 | # fitness for a particular purpose and non-infringement. In no event shall the 18 | # authors or copyright holders be liable for any claim, damages or other 19 | # liability, whether in an action of contract, tort or otherwise, arising 20 | # from, out of or in connection with the software or the use or other dealings 21 | # in the Software. 22 | 23 | import numpy as np 24 | from scipy.spatial.transform import Rotation 25 | 26 | from .utils import perspective 27 | 28 | class Camera: 29 | def __init__(self, width, height): 30 | self.sensitivity = 0.01 31 | self.zoom_sensitivity = 0.1 32 | self.momentum = 0.93 33 | 34 | self._zoom = 2 35 | self.rot = Rotation.identity() 36 | self.previous_mouse_pos = None 37 | self.angular_velocity = None 38 | self.rot_around_vertical = 0 39 | self.rot_around_horizontal = 0 40 | self.resize(width, height) 41 | 42 | def resize(self, width, height): 43 | self.perspectiveMatrix = perspective(np.radians(80), width/height, 0.01, 100.0) 44 | 45 | def zoom(self, steps): 46 | self._zoom *= pow(1 - self.zoom_sensitivity, steps) 47 | 48 | def update(self, time, delta_time): 49 | if self.previous_mouse_pos is None and self.angular_velocity is not None: 50 | self._damping() 51 | 52 | self.rot = Rotation.identity() 53 | self.rot *= Rotation.from_rotvec(self.rot_around_horizontal * np.array([1,0,0])) 54 | self.rot *= Rotation.from_rotvec(self.rot_around_vertical * np.array([0,1,0])) 55 | 56 | viewMatrix = np.eye(4) 57 | viewMatrix[:3,:3] = self.rot.as_matrix() 58 | viewMatrix[0:3,3] = 0, 0, -self._zoom 59 | self.viewMatrix = viewMatrix 60 | 61 | def set_uniforms(self, program): 62 | if "uPerspectiveMatrix" in program: 63 | program["uPerspectiveMatrix"].write(self.perspectiveMatrix.T.astype('f4').tobytes()) 64 | if "uViewMatrix" in program: 65 | program["uViewMatrix"].write(self.viewMatrix.T.astype('f4').tobytes()) 66 | 67 | def start_rotation(self, x, y): 68 | self.previous_mouse_pos = x, y 69 | 70 | def update_rotation(self, x, y): 71 | if self.previous_mouse_pos is None: 72 | return 73 | sx, sy = self.previous_mouse_pos 74 | dx = x - sx 75 | dy = y - sy 76 | self._rotate(dx, dy) 77 | self.previous_mouse_pos = x, y 78 | 79 | def stop_rotation(self): 80 | self.previous_mouse_pos = None 81 | 82 | def _rotate(self, dx, dy): 83 | self.rot_around_vertical += dx * self.sensitivity 84 | self.rot_around_horizontal += dy * self.sensitivity 85 | self.rot_around_horizontal = np.clip(self.rot_around_horizontal, -np.pi / 2, np.pi / 2) 86 | self.angular_velocity = dx, dy 87 | 88 | def _damping(self): 89 | dx, dy = self.angular_velocity 90 | if dx * dx + dy * dy < 1e-6: 91 | self.angular_velocity = None 92 | else: 93 | self._rotate(dx * self.momentum, dy * self.momentum) 94 | -------------------------------------------------------------------------------- /augen/__init__.py: -------------------------------------------------------------------------------- 1 | # This file is part of PyAugen 2 | # 3 | # Copyright (c) 2020 -- Élie Michel 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to 7 | # deal in the Software without restriction, including without limitation the 8 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9 | # sell copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # The Software is provided "as is", without warranty of any kind, express or 16 | # implied, including but not limited to the warranties of merchantability, 17 | # fitness for a particular purpose and non-infringement. In no event shall the 18 | # authors or copyright holders be liable for any claim, damages or other 19 | # liability, whether in an action of contract, tort or otherwise, arising 20 | # from, out of or in connection with the software or the use or other dealings 21 | # in the Software. 22 | 23 | from .Camera import Camera 24 | from .App import App 25 | -------------------------------------------------------------------------------- /augen/mesh.py: -------------------------------------------------------------------------------- 1 | # This file is part of PyAugen 2 | # 3 | # Copyright (c) 2020 -- Élie Michel 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to 7 | # deal in the Software without restriction, including without limitation the 8 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9 | # sell copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # The Software is provided "as is", without warranty of any kind, express or 16 | # implied, including but not limited to the warranties of merchantability, 17 | # fitness for a particular purpose and non-infringement. In no event shall the 18 | # authors or copyright holders be liable for any claim, damages or other 19 | # liability, whether in an action of contract, tort or otherwise, arising 20 | # from, out of or in connection with the software or the use or other dealings 21 | # in the Software. 22 | 23 | import numpy as np 24 | from moderngl import TRIANGLES 25 | 26 | class Mesh: 27 | """Simply contains an array of triangles and an array of normals. 28 | Could be enhanced, for instance with an element buffer""" 29 | def __init__(self, P, N): 30 | self.P = P 31 | self.N = N 32 | 33 | 34 | class ObjMesh(Mesh): 35 | """An example of mesh loader, using the pywavefront module. 36 | Only load the first mesh of the file if there are more than one.""" 37 | def __init__(self, filepath): 38 | import pywavefront 39 | print(f"Loading mesh from {filepath}...") 40 | scene = pywavefront.Wavefront(filepath) 41 | for name, material in scene.materials.items(): 42 | assert(material.vertex_format == "N3F_V3F") # T2F, C3F, N3F and V3F may appear in this string 43 | data = np.array(material.vertices).reshape(-1, 6) 44 | self.P = data[:,3:] 45 | self.N = data[:,:3] 46 | break 47 | print(f"(Object has {len(self.P)//3} points)") 48 | 49 | 50 | class RenderedMesh: 51 | """The equivalent of a Mesh, but stored in OpenGL buffers (on the GPU) 52 | ready to be rendered.""" 53 | def __init__(self, ctx, mesh, program): 54 | self.mesh = mesh 55 | self.vboP = ctx.buffer(mesh.P.astype('f4').tobytes()) 56 | self.vboN = ctx.buffer(mesh.N.astype('f4').tobytes()) 57 | self.vao = ctx.vertex_array( 58 | program, 59 | [ 60 | (self.vboP, "3f", "in_vert"), 61 | (self.vboN, "3f", "in_normal"), 62 | ] 63 | ) 64 | 65 | def release(self): 66 | self.vboP.release() 67 | self.vboN.release() 68 | self.vao.release() 69 | 70 | def render(self, ctx): 71 | self.vao.render(TRIANGLES) 72 | -------------------------------------------------------------------------------- /augen/utils.py: -------------------------------------------------------------------------------- 1 | # This file is part of PyAugen 2 | # 3 | # Copyright (c) 2020 -- Élie Michel 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to 7 | # deal in the Software without restriction, including without limitation the 8 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9 | # sell copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # The Software is provided "as is", without warranty of any kind, express or 16 | # implied, including but not limited to the warranties of merchantability, 17 | # fitness for a particular purpose and non-infringement. In no event shall the 18 | # authors or copyright holders be liable for any claim, damages or other 19 | # liability, whether in an action of contract, tort or otherwise, arising 20 | # from, out of or in connection with the software or the use or other dealings 21 | # in the Software. 22 | 23 | import numpy as np 24 | 25 | def _perspective(n, f, t, b, l, r): 26 | return np.array([ 27 | [ 2*n/(r-l), 0 , (r+l)/(r-l) , 0 ], 28 | [ 0 , 2*n/(t-b), (t+b)/(t-b) , 0 ], 29 | [ 0 , 0 , -((f+n)/(f-n)), -(2*n*f/(f-n)) ], 30 | [ 0 , 0 , -1 , 0 ], 31 | ]) 32 | 33 | def perspective(fovy, aspect, near, far): 34 | top = near * np.tan(fovy / 2) 35 | right = top * aspect 36 | return _perspective(near, far, top, -top, -right, right) 37 | -------------------------------------------------------------------------------- /doc/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eliemichel/Python3dViewer/170568f93a099987d2dbd6d22cf72b210c18edaf/doc/screenshot.png -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | # This file is part of Python 3D Viewer 2 | # 3 | # Copyright (c) 2020 -- Élie Michel 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to 7 | # deal in the Software without restriction, including without limitation the 8 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9 | # sell copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # The Software is provided "as is", without warranty of any kind, express or 16 | # implied, including but not limited to the warranties of merchantability, 17 | # fitness for a particular purpose and non-infringement. In no event shall the 18 | # authors or copyright holders be liable for any claim, damages or other 19 | # liability, whether in an action of contract, tort or otherwise, arising 20 | # from, out of or in connection with the software or the use or other dealings 21 | # in the Software. 22 | 23 | import moderngl 24 | import struct 25 | import glfw 26 | import imgui 27 | import numpy as np 28 | 29 | from augen import App, Camera 30 | from augen.mesh import ObjMesh, RenderedMesh 31 | 32 | class MyApp(App): 33 | def init(self): 34 | ctx = self.ctx 35 | # Load a mesh 36 | self.mesh = ObjMesh("sample-data/dragon.obj") 37 | 38 | # Load the glsl program 39 | self.program = ctx.program( 40 | vertex_shader=open("shaders/mesh.vert.glsl").read(), 41 | fragment_shader=open("shaders/mesh.frag.glsl").read(), 42 | ) 43 | 44 | # Create the rendered mesh from the mesh and the program 45 | self.rendered_mesh = RenderedMesh(ctx, self.mesh, self.program) 46 | 47 | # Setup camera 48 | w, h = self.size() 49 | self.camera = Camera(w, h) 50 | 51 | # Initialize some value used in the UI 52 | self.some_slider = 0.42 53 | 54 | def update(self, time, delta_time): 55 | # Update damping effect (and internal matrices) 56 | self.camera.update(time, delta_time) 57 | 58 | def render(self): 59 | ctx = self.ctx 60 | self.camera.set_uniforms(self.program) 61 | 62 | ctx.screen.clear(1.0, 1.0, 1.0, -1.0) 63 | 64 | ctx.enable_only(moderngl.DEPTH_TEST | moderngl.CULL_FACE) 65 | self.rendered_mesh.render(ctx) 66 | 67 | def on_key(self, key, scancode, action, mods): 68 | if key == glfw.KEY_ESCAPE: 69 | self.should_close() 70 | 71 | def on_mouse_move(self, x, y): 72 | self.camera.update_rotation(x, y) 73 | 74 | def on_mouse_button(self, button, action, mods): 75 | if action == glfw.PRESS and button == glfw.MOUSE_BUTTON_LEFT: 76 | x, y = self.mouse_pos() 77 | self.camera.start_rotation(x, y) 78 | if action == glfw.RELEASE and button == glfw.MOUSE_BUTTON_LEFT: 79 | self.camera.stop_rotation() 80 | 81 | def on_resize(self, width, height): 82 | self.camera.resize(width, height) 83 | self.ctx.viewport = (0, 0, width, height) 84 | 85 | def on_scroll(self, x, y): 86 | self.camera.zoom(y) 87 | 88 | def ui(self): 89 | """Use the imgui module here to draw the UI""" 90 | if imgui.begin_main_menu_bar(): 91 | if imgui.begin_menu("File", True): 92 | 93 | clicked_quit, selected_quit = imgui.menu_item( 94 | "Quit", 'Esc', False, True 95 | ) 96 | 97 | if clicked_quit: 98 | self.should_close() 99 | 100 | imgui.end_menu() 101 | imgui.end_main_menu_bar() 102 | 103 | imgui.begin("Hello, world!", True) 104 | self.shape_need_update = False 105 | changed, self.some_slider = imgui.slider_float( 106 | "Some Slider", self.some_slider, 107 | min_value=0.0, max_value=1.0, 108 | format="%.02f" 109 | ) 110 | imgui.end() 111 | 112 | def main(): 113 | app = MyApp(1280, 720, "Python 3d Viewer - Elie Michel") 114 | app.main_loop() 115 | 116 | if __name__ == "__main__": 117 | main() 118 | 119 | -------------------------------------------------------------------------------- /optional-requirements.txt: -------------------------------------------------------------------------------- 1 | PyWavefront 2 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | numpy 2 | glfw 3 | moderngl 4 | scipy 5 | imgui[glfw] -------------------------------------------------------------------------------- /shaders/mesh.frag.glsl: -------------------------------------------------------------------------------- 1 | #version 330 2 | 3 | /** 4 | * This file is part of Python 3D Viewer 5 | * 6 | * Copyright (c) 2020 -- Élie Michel 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to 10 | * deal in the Software without restriction, including without limitation the 11 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 12 | * sell copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * The Software is provided "as is", without warranty of any kind, express or 19 | * implied, including but not limited to the warranties of merchantability, 20 | * fitness for a particular purpose and non-infringement. In no event shall the 21 | * authors or copyright holders be liable for any claim, damages or other 22 | * liability, whether in an action of contract, tort or otherwise, arising 23 | * from, out of or in connection with the software or the use or other dealings 24 | * in the Software. 25 | */ 26 | 27 | in vec3 v_normal; 28 | in vec3 v_position; 29 | 30 | out vec4 f_color; 31 | 32 | uniform vec4 uColor = vec4(1.0, 0.5, 0.1, 1.0); 33 | uniform mat4 uViewMatrix; 34 | uniform float uHardness = 16.0; 35 | 36 | const vec3 lightpos0 = vec3(22.0, 16.0, 50.0); 37 | const vec3 lightcolor0 = vec3(1.0, 0.95, 0.9); 38 | const vec3 lightpos1 = vec3(-22.0, -8.0, -50.0); 39 | const vec3 lightcolor1 = vec3(0.9, 0.95, 1.0); 40 | const vec3 ambient = vec3(1.0); 41 | 42 | void main() { 43 | vec3 viewpos = inverse(uViewMatrix)[3].xyz; 44 | 45 | // This is a very basic lighting, for visualization only // 46 | 47 | vec3 n = normalize(v_normal); 48 | vec3 c = uColor.rgb * ambient; 49 | vec3 v = normalize(viewpos - v_position); 50 | vec3 l, r; 51 | float s, spec; 52 | 53 | l = normalize(lightpos0 - v_position); 54 | s = max(0.0, dot(n, l)); 55 | c += uColor.rgb * s * lightcolor0; 56 | if (s > 0) { 57 | r = reflect(-l, n); 58 | spec = pow(max(0.0, dot(v, r)), uHardness); 59 | c += spec * lightcolor0; 60 | } 61 | 62 | l = normalize(lightpos1 - v_position); 63 | s = max(0.0, dot(n, l)); 64 | c += uColor.rgb * s * lightcolor1; 65 | if (s > 0) { 66 | r = reflect(-l, n); 67 | spec = pow(max(0.0, dot(v, r)), uHardness); 68 | c += spec * lightcolor1; 69 | } 70 | 71 | f_color = vec4(c * 0.5, uColor.a); 72 | } 73 | -------------------------------------------------------------------------------- /shaders/mesh.vert.glsl: -------------------------------------------------------------------------------- 1 | #version 330 2 | 3 | /** 4 | * This file is part of Python 3D Viewer 5 | * 6 | * Copyright (c) 2020 -- Élie Michel 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to 10 | * deal in the Software without restriction, including without limitation the 11 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 12 | * sell copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * The Software is provided "as is", without warranty of any kind, express or 19 | * implied, including but not limited to the warranties of merchantability, 20 | * fitness for a particular purpose and non-infringement. In no event shall the 21 | * authors or copyright holders be liable for any claim, damages or other 22 | * liability, whether in an action of contract, tort or otherwise, arising 23 | * from, out of or in connection with the software or the use or other dealings 24 | * in the Software. 25 | */ 26 | 27 | in vec3 in_vert; 28 | in vec3 in_normal; 29 | 30 | out vec3 v_normal; 31 | out vec3 v_position; 32 | 33 | uniform mat4 uPerspectiveMatrix; 34 | uniform mat4 uViewMatrix; 35 | 36 | void main() { 37 | v_normal = in_normal; 38 | v_position = in_vert; 39 | gl_Position = uPerspectiveMatrix * uViewMatrix * vec4(v_position, 1.0); 40 | } 41 | --------------------------------------------------------------------------------