create
This commit is contained in:
commit
541c8cb42e
13 changed files with 1410 additions and 0 deletions
BIN
mandelbulb/__pycache__/camera.cpython-310.pyc
Normal file
BIN
mandelbulb/__pycache__/camera.cpython-310.pyc
Normal file
Binary file not shown.
BIN
mandelbulb/__pycache__/tools.cpython-310.pyc
Normal file
BIN
mandelbulb/__pycache__/tools.cpython-310.pyc
Normal file
Binary file not shown.
60
mandelbulb/camera.py
Normal file
60
mandelbulb/camera.py
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from tools import *
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
class Camera:
|
||||||
|
def __init__(self, r1 : np.array, phi : float, theta : float):
|
||||||
|
assert r1.shape == (3,)
|
||||||
|
|
||||||
|
self.r1 = r1
|
||||||
|
self.r1_temp = 0, 0, 0
|
||||||
|
|
||||||
|
self.camThetaBase = 0
|
||||||
|
self.camPhiBase = 0
|
||||||
|
|
||||||
|
self.camTheta = theta
|
||||||
|
self.camPhi = phi
|
||||||
|
|
||||||
|
r1_temp = self.rotateCam()
|
||||||
|
self.camMat = self.getCam(r1_temp)
|
||||||
|
|
||||||
|
|
||||||
|
def getCam(self, r1):
|
||||||
|
camF = normalize(r1)
|
||||||
|
camR = normalize(cross(vec3(0, 1, 0), camF))
|
||||||
|
camU = cross(camF, camR)
|
||||||
|
|
||||||
|
mat_ = mat3(camR, camU, camF)
|
||||||
|
return mat_
|
||||||
|
|
||||||
|
def rotateCam(self):
|
||||||
|
yz = vec2(self.r1[1], self.r1[2])
|
||||||
|
new_yz = pR(yz, self.camTheta + self.camThetaBase)
|
||||||
|
xz = vec2(self.r1[0], new_yz[1])
|
||||||
|
new_xz = pR(xz, self.camPhi + self.camPhiBase)
|
||||||
|
|
||||||
|
vec = vec3(new_xz[0], new_yz[0], new_xz[1])
|
||||||
|
|
||||||
|
|
||||||
|
return vec
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def mouseToAngle(u_mouse, u_resolution):
|
||||||
|
m = (u_mouse - u_resolution / 2) / u_resolution
|
||||||
|
return m
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
self.r1_temp = self.rotateCam()
|
||||||
|
self.camMat = self.getCam(self.r1_temp)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
1
mandelbulb/controls.py
Normal file
1
mandelbulb/controls.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
|
109
mandelbulb/main.py
Normal file
109
mandelbulb/main.py
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
import moderngl_window as mglw
|
||||||
|
from tools import *
|
||||||
|
from camera import Camera
|
||||||
|
from time import sleep
|
||||||
|
|
||||||
|
print(__name__)
|
||||||
|
|
||||||
|
class App(mglw.WindowConfig):
|
||||||
|
window_size = 1600, 900
|
||||||
|
ro = 0, -0.5, -1.5
|
||||||
|
ro = mult_ext_tuples(1, ro)
|
||||||
|
rc = 0, -1, 2
|
||||||
|
u_mouse = 0, 0
|
||||||
|
forward = False
|
||||||
|
backward = False
|
||||||
|
resource_dir = 'resources/programs'
|
||||||
|
cam = Camera(vec3(*rc), 0, 0)
|
||||||
|
|
||||||
|
mouse_pressed = False
|
||||||
|
|
||||||
|
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
self.quad = mglw.geometry.quad_fs()
|
||||||
|
self.program = self.load_program(vertex_shader='vertex.glsl', fragment_shader='fragment.glsl')
|
||||||
|
# uniforms
|
||||||
|
self.set_uniform('u_resolution', self.window_size)
|
||||||
|
self.set_uniform('ro', self.ro)
|
||||||
|
self.set_uniform('rc', self.rc)
|
||||||
|
#self.set_uniform('u_mouse', self.u_mouse)
|
||||||
|
self.set_uniform('cam', mat3ToTuple(np.eye(3)))
|
||||||
|
|
||||||
|
def set_uniform(self, u_name, u_value):
|
||||||
|
try:
|
||||||
|
self.program[u_name] = u_value
|
||||||
|
except KeyError:
|
||||||
|
None
|
||||||
|
print(f'{u_name} not used in shader')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def moove(self):
|
||||||
|
x, y, z = tuple(self.cam.r1_temp)
|
||||||
|
x = -x
|
||||||
|
y = -y
|
||||||
|
if self.forward:
|
||||||
|
self.ro = add_tuples(self.ro, mult_ext_tuples(0.1, (x, y, z)))
|
||||||
|
|
||||||
|
if self.backward:
|
||||||
|
self.ro = add_tuples(self.ro, mult_ext_tuples(-0.1, (x, y, z)))
|
||||||
|
|
||||||
|
|
||||||
|
def cam_render(self):
|
||||||
|
angles = self.cam.mouseToAngle(vec2(*self.u_mouse), vec2(*self.window_size))
|
||||||
|
self.cam.camPhi, self.cam.camTheta = -angles[0]*4, angles[1]*2
|
||||||
|
self.cam.update()
|
||||||
|
|
||||||
|
def render(self, time, frame_time):
|
||||||
|
self.ctx.clear()
|
||||||
|
#self.program['u_time'] = time
|
||||||
|
self.quad.render(self.program)
|
||||||
|
self.set_uniform('cam', mat3ToTuple(self.cam.camMat))
|
||||||
|
|
||||||
|
if self.mouse_pressed:
|
||||||
|
self.cam_render()
|
||||||
|
|
||||||
|
if self.forward or self.backward:
|
||||||
|
self.moove()
|
||||||
|
|
||||||
|
self.set_uniform('ro', self.ro)
|
||||||
|
|
||||||
|
|
||||||
|
sleep(0)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def mouse_drag_event(self, x: int, y: int, dx: int, dy: int):
|
||||||
|
self.u_mouse = x, y
|
||||||
|
self.mouse_pressed = True
|
||||||
|
|
||||||
|
def mouse_release_event(self, x: int, y: int, button: int):
|
||||||
|
self.mouse_pressed = False
|
||||||
|
self.cam.camThetaBase += self.cam.camTheta
|
||||||
|
self.cam.camPhiBase += self.cam.camPhi
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def key_event(self, key, action, modifiers):
|
||||||
|
|
||||||
|
match key:
|
||||||
|
case self.wnd.keys.UP:
|
||||||
|
if action == self.wnd.keys.ACTION_PRESS:
|
||||||
|
self.forward = True
|
||||||
|
elif action == self.wnd.keys.ACTION_RELEASE:
|
||||||
|
self.forward = False
|
||||||
|
|
||||||
|
case self.wnd.keys.DOWN:
|
||||||
|
if action == self.wnd.keys.ACTION_PRESS:
|
||||||
|
self.backward = True
|
||||||
|
elif action == self.wnd.keys.ACTION_RELEASE:
|
||||||
|
self.backward = False
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
mglw.run_window_config(App)
|
175
mandelbulb/resources/programs/fragment.glsl
Normal file
175
mandelbulb/resources/programs/fragment.glsl
Normal file
|
@ -0,0 +1,175 @@
|
||||||
|
#version 330 core
|
||||||
|
#include hg_sdf.glsl
|
||||||
|
layout (location = 0) out vec4 fragColor;
|
||||||
|
|
||||||
|
uniform vec2 u_resolution;
|
||||||
|
uniform vec2 u_mouse;
|
||||||
|
uniform float u_time;
|
||||||
|
uniform vec3 ro;
|
||||||
|
uniform mat3 cam;
|
||||||
|
|
||||||
|
const float FOV = 2;
|
||||||
|
const int MAX_STEPS = 256;
|
||||||
|
const float MAX_DIST = 500;
|
||||||
|
const float EPSILON = 0.0005;
|
||||||
|
const float DIST_CAM = 2;
|
||||||
|
|
||||||
|
float packColor(vec3 color) {
|
||||||
|
return color.r + color.g * 256.0 + color.b * 256.0 * 256.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 unpackColor(float f) {
|
||||||
|
vec3 color;
|
||||||
|
color.b = floor(f / 256.0 / 256.0);
|
||||||
|
color.g = floor((f - color.b * 256.0 * 256.0) / 256.0);
|
||||||
|
color.r = floor(f - color.b * 256.0 * 256.0 - color.g * 256.0);
|
||||||
|
// now we have a vec3 with the 3 components in range [0..255]. Let's normalize it!
|
||||||
|
return color / 255.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 fOpUnionChamferId(vec3 res1, vec3 res2, float r) {
|
||||||
|
float a = res1.x;
|
||||||
|
float b = res2.x;
|
||||||
|
|
||||||
|
return vec3(min(min(a, b), (a - r + b)*sqrt(0.5)), res1.yz);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 fOpDifferenceId(vec3 res1, vec3 res2) {
|
||||||
|
return (res1.x > -res2.x) ? res1 : vec3(-res2.x, res2.yz);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 fOpUnionId(in vec3 res1, in vec3 res2){
|
||||||
|
return (res1.x < res2.x) ? res1 : res2;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
vec3 map(in vec3 p){
|
||||||
|
//pMod3(p, vec3(10.0));
|
||||||
|
|
||||||
|
float planeId = 1;
|
||||||
|
float planeColor = packColor(vec3(50, 50, 50));
|
||||||
|
float planeDist = fPlane(p, vec3(0, 1, 0), 1.0);
|
||||||
|
vec3 plane = vec3(planeDist, planeColor, planeId);
|
||||||
|
|
||||||
|
float sphereId = 2;
|
||||||
|
float sphereColor = packColor(vec3(200, 0, 0));
|
||||||
|
vec3 spherePos = vec3(2.0, 0.0, 0.0);
|
||||||
|
float sphereDist = fSphere(p-spherePos, 1);
|
||||||
|
vec3 sphere = vec3(sphereDist, sphereColor, sphereId);
|
||||||
|
|
||||||
|
float playerId = 3;
|
||||||
|
float playerColor = packColor(vec3(0, 200, 0));;
|
||||||
|
vec3 playerPos = ro+vec3(0, -1, DIST_CAM);
|
||||||
|
float playerDist = fCapsule(p-playerPos, 0.2, 0.2);
|
||||||
|
vec3 player = vec3(playerDist, playerColor, playerId);
|
||||||
|
|
||||||
|
float mandelBulbId = 3;
|
||||||
|
float mandelBulbColor = packColor(vec3(50, 50, 50));;
|
||||||
|
float mandelBulbDist = fmandelBulbe(p);;
|
||||||
|
vec3 mandelBulb = vec3(mandelBulbDist, mandelBulbColor, mandelBulbId);;
|
||||||
|
|
||||||
|
//vec3 res = fOpUnionId(plane, sphere);
|
||||||
|
vec3 res = mandelBulb;
|
||||||
|
//res = fOpUnionId(res, mandelBulb);
|
||||||
|
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 rayMarch(vec3 ro, vec3 rd) {
|
||||||
|
vec3 hit = vec3(0.0);
|
||||||
|
vec3 object = vec3(0.0);
|
||||||
|
vec3 p = vec3(0.0);
|
||||||
|
float compt = 0;
|
||||||
|
for (float i = 0; i < MAX_STEPS; i++) {
|
||||||
|
p = ro + object.x * rd;
|
||||||
|
hit = map(p);
|
||||||
|
object.x += hit.x;
|
||||||
|
object.yz = hit.yz;
|
||||||
|
if (hit.z == 2 || hit.z == 3) compt += 1;
|
||||||
|
|
||||||
|
if (abs(hit.x) < EPSILON || object.x > MAX_DIST){
|
||||||
|
if (hit.z != 2) return vec4(object, compt);
|
||||||
|
return vec4(object, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return vec4(object, compt);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 getNormal(vec3 p) {
|
||||||
|
vec2 e = vec2(EPSILON, 0.0);
|
||||||
|
vec3 n = vec3(map(p).x) - vec3(map(p - e.xyy).x, map(p - e.yxy).x, map(p - e.yyx).x);
|
||||||
|
return normalize(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 getLight(vec3 p, vec3 rd, vec3 color) {
|
||||||
|
vec3 lightPos = vec3(10.0, 55.0, -20.0);
|
||||||
|
vec3 L = normalize(lightPos - p);
|
||||||
|
vec3 N = getNormal(p);
|
||||||
|
vec3 V = -rd;
|
||||||
|
vec3 R = reflect(-L, N);
|
||||||
|
|
||||||
|
vec3 specColor = vec3(0.5);
|
||||||
|
vec3 specular = specColor * pow(clamp(dot(R, V), 0.0, 1.0), 10.0);
|
||||||
|
vec3 diffuse = color * clamp(dot(L, N), 0.0, 1.0);
|
||||||
|
vec3 ambient = color * 0.05;
|
||||||
|
vec3 fresnel = 0.25 * color * pow(1.0 + dot(rd, N), 3.0);
|
||||||
|
|
||||||
|
// shadows
|
||||||
|
float d = rayMarch(p + N * 0.02, normalize(lightPos)).x;
|
||||||
|
if (d < length(lightPos - p)) return ambient + fresnel;
|
||||||
|
|
||||||
|
return diffuse + ambient + specular + fresnel;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 getMaterial(vec3 p, float color_pack, float id) {
|
||||||
|
vec3 m = vec3(0.0);
|
||||||
|
vec3 color = unpackColor(color_pack);
|
||||||
|
switch (int(id)) {
|
||||||
|
case 1:
|
||||||
|
m = vec3(0.2 + 0.4 * mod(floor(p.x) + floor(p.z), 2.0)); break;
|
||||||
|
}
|
||||||
|
return color+m;
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void render(inout vec3 col, in vec3 ro, in mat3 cam, in vec2 uv) {
|
||||||
|
vec3 r1 = vec3(0, 0, 1);
|
||||||
|
vec3 lookAt = ro + r1;
|
||||||
|
vec3 rd = cam*normalize(vec3(uv, FOV));
|
||||||
|
vec4 result = rayMarch(ro, rd);
|
||||||
|
vec3 object = result.xyz;
|
||||||
|
vec3 glowing_color = vec3(0.5, 0, 0);
|
||||||
|
float nb_itter = result.w;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
vec3 background = vec3(0, 0, 0);
|
||||||
|
if (object.x < MAX_DIST) {
|
||||||
|
vec3 p = ro + object.x * rd;
|
||||||
|
vec3 material = getMaterial(p, object.y, object.z);
|
||||||
|
col += getLight(p, rd, material);
|
||||||
|
// fog
|
||||||
|
col = mix(col, background, 1.0 - exp(-0.00002 * object.x * object.x));
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// col += background - max(0.9 * rd.y, 0.0);
|
||||||
|
col = col;
|
||||||
|
}
|
||||||
|
float glow_fact = smoothstep(0, MAX_STEPS, nb_itter);
|
||||||
|
col += glowing_color*glow_fact;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec2 uv = (2.0 * gl_FragCoord.xy - u_resolution.xy) / u_resolution.y;
|
||||||
|
|
||||||
|
vec3 col;
|
||||||
|
render(col, ro, cam, uv);
|
||||||
|
|
||||||
|
// gamma correction
|
||||||
|
col = pow(col, vec3(0.4545));
|
||||||
|
fragColor = vec4(col, 1.0);
|
||||||
|
}
|
1004
mandelbulb/resources/programs/hg_sdf.glsl
Normal file
1004
mandelbulb/resources/programs/hg_sdf.glsl
Normal file
File diff suppressed because it is too large
Load diff
8
mandelbulb/resources/programs/vertex.glsl
Normal file
8
mandelbulb/resources/programs/vertex.glsl
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
#version 440 core
|
||||||
|
|
||||||
|
|
||||||
|
in vec3 in_position;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
gl_Position = vec4(in_position, 1);
|
||||||
|
}
|
0
mandelbulb/resources/textures/fur.jpg
Normal file
0
mandelbulb/resources/textures/fur.jpg
Normal file
0
mandelbulb/resources/textures/noise.png
Normal file
0
mandelbulb/resources/textures/noise.png
Normal file
0
mandelbulb/resources/textures/wall.jpg
Normal file
0
mandelbulb/resources/textures/wall.jpg
Normal file
5
mandelbulb/test.py
Normal file
5
mandelbulb/test.py
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
import numpy as np
|
||||||
|
import tools
|
||||||
|
|
||||||
|
m = np.array(((1, 2, 3), (4, 5, 6), (7, 8, 9)))
|
||||||
|
print(tools.mat3ToTuple(m))
|
48
mandelbulb/tools.py
Normal file
48
mandelbulb/tools.py
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
import numpy as np
|
||||||
|
from numpy import cos, sin, tan, exp
|
||||||
|
|
||||||
|
from numpy.linalg import norm, det
|
||||||
|
from numpy import cross
|
||||||
|
import numba
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
PI = np.pi
|
||||||
|
TAU = 2*np.pi
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def vec2(x, y, dtype=float):
|
||||||
|
return np.array((x, y), dtype=dtype)
|
||||||
|
|
||||||
|
def vec3(x, y, z, dtype=float):
|
||||||
|
return np.array((x, y, z), dtype=dtype)
|
||||||
|
|
||||||
|
def mat3(a : np.array, b : np.array, c : np.array):
|
||||||
|
return np.array((a, b, c)).T
|
||||||
|
|
||||||
|
def mat3ToTuple(m):
|
||||||
|
return m[0, 0], m[0, 1], m[0, 2], m[1, 0], m[1, 1], m[1, 2], m[2, 0], m[2, 1], m[2, 2]
|
||||||
|
#return m[0, 0], m[1, 0], m[2, 0], m[0, 1], m[1, 1], m[2, 1], m[0, 2], m[1, 2], m[2, 2]
|
||||||
|
|
||||||
|
def getRot2D(theta):
|
||||||
|
return np.array( [( cos(theta), -sin(theta) ), (sin(theta), cos(theta))] )
|
||||||
|
|
||||||
|
def pR(p, a):
|
||||||
|
return cos(a)*p + sin(a)*vec2(p[1], -p[0])
|
||||||
|
|
||||||
|
|
||||||
|
def add_tuples(t1, t2):
|
||||||
|
assert len(t1) == len(t2)
|
||||||
|
temp = [0]*len(t1)
|
||||||
|
for i in range(len(t1)):
|
||||||
|
temp[i] += t1[i] + t2[i]
|
||||||
|
return tuple(temp)
|
||||||
|
|
||||||
|
def mult_ext_tuples(a, t1):
|
||||||
|
return tuple([a*t for t in t1])
|
||||||
|
|
||||||
|
def normalize(vec):
|
||||||
|
return vec/norm(vec)
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue