-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
- Loading branch information
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
import glm | ||
import pygame as pg | ||
|
||
FOV = 50 # deg | ||
Near = 0.1 | ||
Far = 100 | ||
SPEED = 0.01 | ||
SENSITIVITY = 0.05 | ||
|
||
|
||
class Camera: | ||
def __init__(self, app, position=(0, 0, 4), yaw=-90, pitch=0): | ||
self.app = app | ||
self.aspect_ratio = app.WIN_SIZE[0] / app.WIN_SIZE[1] | ||
self.position = glm.vec3(position) | ||
self.up = glm.vec3(0, 1, 0) | ||
self.right = glm.vec3(1, 0, 0) | ||
self.forward = glm.vec3(0, 0, -1) | ||
self.yaw = yaw | ||
self.pitch = pitch | ||
# view matrix | ||
self.m_view = self.get_view_matrix() | ||
# projection matrix | ||
self.m_proj = self.get_projection_matrix() | ||
|
||
def rotate(self): | ||
rel_x, rel_y = pg.mouse.get_rel() | ||
self.yaw += rel_x * SENSITIVITY | ||
self.pitch -= rel_y * SENSITIVITY | ||
self.pitch = max(-89, min(89, self.pitch)) | ||
|
||
def update_camera_vectors(self): | ||
yaw, pitch = glm.radians(self.yaw), glm.radians(self.pitch) | ||
|
||
self.forward.x = glm.cos(yaw) * glm.cos(pitch) | ||
self.forward.y = glm.sin(pitch) | ||
self.forward.z = glm.sin(yaw) * glm.cos(pitch) | ||
|
||
self.forward = glm.normalize(self.forward) | ||
self.right = glm.normalize(glm.cross(self.forward, glm.vec3(0, 1, 0))) | ||
self.up = glm.normalize(glm.cross(self.right, self.forward)) | ||
|
||
def update(self): | ||
self.move() | ||
self.rotate() | ||
self.update_camera_vectors() | ||
self.m_view = self.get_view_matrix() | ||
|
||
def move(self): | ||
velocity = SPEED * self.app.delta_time | ||
keys = pg.key.get_pressed() | ||
if keys[pg.K_w]: | ||
self.position += self.forward * velocity | ||
if keys[pg.K_s]: | ||
self.position -= self.forward * velocity | ||
if keys[pg.K_d]: | ||
self.position += self.right * velocity | ||
if keys[pg.K_a]: | ||
self.position -= self.right * velocity | ||
if keys[pg.K_q]: | ||
self.position += self.up * velocity | ||
if keys[pg.K_e]: | ||
self.position -= self.up * velocity | ||
|
||
def get_view_matrix(self): | ||
return glm.lookAt(self.position, self.position + self.forward, self.up) | ||
|
||
def get_projection_matrix(self): | ||
return glm.perspective(glm.radians(FOV), self.aspect_ratio, Near, Far) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import glm | ||
|
||
|
||
class Light: | ||
def __init__(self, position=(3, 3, -3), color=(1, 1, 1), intensity=1): | ||
self.position = glm.vec3(position) | ||
self.color = glm.vec3(color) | ||
|
||
self.intensity = intensity * self.color |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
import pygame as pg | ||
import moderngl as mgl | ||
import sys | ||
import model | ||
from camera import Camera | ||
from light import Light | ||
from mesh import Mesh | ||
|
||
|
||
class GraphicsEngin: | ||
def __init__(self, win_size=(1600, 900)): | ||
# init pygame modules | ||
pg.init() | ||
# window size | ||
self.WIN_SIZE = win_size | ||
# set opengl attr | ||
pg.display.gl_set_attribute(pg.GL_CONTEXT_MAJOR_VERSION, 4) | ||
pg.display.gl_set_attribute(pg.GL_CONTEXT_MINOR_VERSION, 3) | ||
pg.display.gl_set_attribute(pg.GL_CONTEXT_PROFILE_MASK, pg.GL_CONTEXT_PROFILE_CORE) | ||
# create opengl context | ||
pg.display.set_mode(self.WIN_SIZE, flags=pg.OPENGL | pg.DOUBLEBUF) | ||
# mouse settings | ||
pg.event.set_grab(True) | ||
pg.mouse.set_visible(False) | ||
# detect and use existing opengl context | ||
self.ctx = mgl.create_context() | ||
self.ctx.enable(flags=mgl.DEPTH_TEST | mgl.CULL_FACE) | ||
# create an object to help track time | ||
self.clock = pg.time.Clock() | ||
self.time = 0 | ||
self.delta_time = 0 | ||
# light | ||
self.light = Light() | ||
# camera | ||
self.camera = Camera(self) | ||
# mesh | ||
self.mesh = Mesh(self) | ||
# scene | ||
self.scene = model.Cube(self) | ||
|
||
def check_events(self): | ||
for event in pg.event.get(): | ||
if event.type == pg.QUIT or event.type == pg.KEYDOWN and event.key == pg.K_ESCAPE: | ||
self.mesh.destroy() | ||
pg.quit() | ||
sys.exit() | ||
|
||
def render(self): | ||
self.ctx.clear(color=(0.08, 0.16, 0.18, 1.0)) | ||
|
||
self.scene.render() | ||
|
||
pg.display.flip() | ||
|
||
@staticmethod | ||
def get_time(): | ||
return pg.time.get_ticks()*0.001 | ||
|
||
def run(self): | ||
while True: | ||
self.time = self.get_time() | ||
self.check_events() | ||
self.camera.update() | ||
self.render() | ||
self.delta_time = self.clock.tick(60) | ||
|
||
|
||
if __name__ == "__main__": | ||
app = GraphicsEngin() | ||
app.run() |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
from vao import VAO | ||
from texture import Texture | ||
|
||
|
||
class Mesh: | ||
def __init__(self, app): | ||
self.app = app | ||
self.vao = VAO(app.ctx) | ||
self.texture = Texture(app.ctx) | ||
|
||
def destroy(self): | ||
self.vao.destroy() | ||
self.texture.destroy() |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import glm | ||
|
||
|
||
class BaseModel: | ||
def __init__(self, app, vao_name, tex_id): | ||
self.app = app | ||
self.m_model = self.get_model_matrix() | ||
self.tex_id = tex_id | ||
self.vao = app.mesh.vao.vaos[vao_name] | ||
self.program = self.vao.program | ||
self.camera = self.app.camera | ||
|
||
def update(self): ... | ||
|
||
@staticmethod | ||
def get_model_matrix(): | ||
m_model = glm.mat4() | ||
return m_model | ||
|
||
def render(self): | ||
self.update() | ||
self.vao.render() | ||
|
||
|
||
class Cube(BaseModel): | ||
def __init__(self, app, vao_name='cube', tex_id=0): | ||
super().__init__(app, vao_name, tex_id) | ||
self.texture = None | ||
self.on_init() | ||
|
||
def update(self): | ||
m_model = glm.rotate(self.m_model, self.app.time * 0.5, glm.vec3(0, 1, 0)) | ||
self.program['m_model'].write(m_model) | ||
self.program['m_view'].write(self.app.camera.m_view) | ||
self.program['camPos'].write(self.app.camera.position) | ||
|
||
def on_init(self): | ||
# texture | ||
self.texture = self.app.mesh.texture.textures[self.tex_id] | ||
self.program['u_texture_0'] = 0 | ||
self.texture.use() | ||
# mvp | ||
self.program['m_proj'].write(self.app.camera.m_proj) | ||
self.program['m_view'].write(self.app.camera.m_view) | ||
self.program['m_model'].write(self.m_model) | ||
# light | ||
self.program['light.position'].write(self.app.light.position) | ||
self.program['light.intensity'].write(self.app.light.intensity) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
class Scene: | ||
def __init__(self, app): | ||
self.app = app |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
|
||
|
||
class ShaderProgram: | ||
def __init__(self, ctx): | ||
self.ctx = ctx | ||
self.programs = {'default': self.get_program('default')} | ||
|
||
def get_program(self, shader_program_name): | ||
with open(f'shaders/{shader_program_name}.vert') as f: | ||
vertex_shader = f.read() | ||
|
||
with open(f'shaders/{shader_program_name}.frag') as f: | ||
fragment_shader = f.read() | ||
|
||
program = self.ctx.program(vertex_shader=vertex_shader, fragment_shader=fragment_shader) | ||
return program | ||
|
||
def destroy(self): | ||
[program.release() for program in self.programs.values()] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
#version 430 core | ||
#define PI 3.1415926535897932384626433832795 | ||
|
||
layout (location=0) out vec4 fragColor; | ||
|
||
in vec2 uv_0; | ||
in vec3 normal; | ||
in vec3 fragPos; | ||
|
||
struct Light { | ||
vec3 position; | ||
vec3 intensity; | ||
}; | ||
|
||
uniform Light light; | ||
uniform sampler2D u_texture_0; | ||
uniform vec3 camPos; | ||
|
||
vec3 getLight(vec3 albedo) { | ||
vec3 N = normalize(normal); | ||
vec3 V = normalize(camPos - fragPos); | ||
vec3 L = normalize(light.position - fragPos); | ||
|
||
// Calculate the half vector (microfacet normal) | ||
vec3 H = normalize(V + L); | ||
|
||
// Calculate the roughness (example value) | ||
float roughness = 1.0; | ||
|
||
// Calculate the NdotL and NdotV terms | ||
float NdotL = max(dot(N, L), 0.0); | ||
float NdotV = max(dot(N, V), 0.0); | ||
|
||
// Calculate the distribution term using GGX | ||
float alpha = roughness * roughness; | ||
float alpha2 = alpha * alpha; | ||
float NdotH = max(dot(N, H), 0.0); | ||
float D = alpha2 / (PI * pow(NdotH * NdotH * (alpha2 - 1.0) + 1.0, 2.0)); | ||
|
||
// Calculate the geometric attenuation term | ||
float Vis = min(1.0, min(2.0 * NdotH * NdotV / dot(V, H), 2.0 * NdotH * NdotL / dot(V, H))); | ||
|
||
// Apply the intensity for specular | ||
vec3 intensity = light.intensity; | ||
|
||
// Calculate the specular term | ||
vec3 specular = (D * Vis * intensity) / (4.0 * NdotL * NdotV) * albedo; | ||
|
||
// Diffuse | ||
vec3 diffuse = (NdotL * intensity) * albedo; | ||
|
||
vec3 ambient = 0.1 * albedo; | ||
|
||
// Combine specular and diffuse reflections | ||
vec3 lighting = specular + diffuse + ambient; | ||
|
||
return lighting; | ||
|
||
} | ||
|
||
void main() { | ||
vec3 color = texture(u_texture_0, uv_0).rgb; | ||
fragColor = vec4(getLight(color), 1.0); | ||
fragColor = fragColor - vec4(getLight(color), 1.0); | ||
fragColor = fragColor + vec4(uv_0, 0.0, 1.0); | ||
} |