From f18a67323cd9e279d33f97cd63a994c9582c9cb2 Mon Sep 17 00:00:00 2001 From: Nobuyuki Date: Mon, 1 Jul 2024 09:57:38 +0900 Subject: [PATCH] task09 --- .gitignore | 3 +- README.md | 15 +-- task09/README.md | 110 +++++++++++++++++++ task09/main.py | 210 +++++++++++++++++++++++++++++++++++++ task09/out_bilaplacian.png | Bin 0 -> 9577 bytes task09/out_default.png | Bin 0 -> 6640 bytes task09/out_laplacian.png | Bin 0 -> 12051 bytes task09/preview.png | Bin 0 -> 11328 bytes task09/requirements.txt | 9 ++ task09/util_for_task09.py | 76 ++++++++++++++ 10 files changed, 415 insertions(+), 8 deletions(-) create mode 100644 task09/README.md create mode 100644 task09/main.py create mode 100644 task09/out_bilaplacian.png create mode 100644 task09/out_default.png create mode 100644 task09/out_laplacian.png create mode 100644 task09/preview.png create mode 100644 task09/requirements.txt create mode 100644 task09/util_for_task09.py diff --git a/.gitignore b/.gitignore index 3001f4b..1e3ed9c 100644 --- a/.gitignore +++ b/.gitignore @@ -71,4 +71,5 @@ build !asset/armadillo.obj -venv \ No newline at end of file +venv +task09/output.png \ No newline at end of file diff --git a/README.md b/README.md index 61ce5db..d02a69e 100644 --- a/README.md +++ b/README.md @@ -55,8 +55,8 @@ Topics: |(8)
June 3| **Character animation**
Linear blend skinning | [task07](task07) | [[21]](http://nobuyuki-umetani.com/acg2024s/jacobian.pdf) | |(9)
June 10| Guest lecture by Dr. Rex West | | | |(10)
June 17| **Character animation2**
Inverse kinematic | [task08](task08) | [[20]](http://nobuyuki-umetani.com/acg2024s/character_deformation.pdf) | -|(11)
June 24| **Optimization** | | | -|(12)
July 12| **Laplacian mesh deformation**
| task09 | | +|(11)
June 24| **Optimization**
Newton-Raphson method, gradient descent | | [[22]](http://nobuyuki-umetani.com/acg2024s/optimization.pdf) | +|(12)
July 1| **Laplacian mesh deformation**
Sparse linear system | [task09](task09) | [[23]](http://nobuyuki-umetani.com/acg2024s/mesh_laplacian.pdf) [[24]](http://nobuyuki-umetani.com/acg2024s/sparse_linear_system.pdf) | |(13)
July 8| **Grid-based Fluid**
Poisson equation, Stam fluid | | | @@ -85,8 +85,8 @@ Look at the following document. | [task05](task05) | **Fragment shader practice**
Ray marching method, CSG modeling, implicit modeling | | | [task06](task06) | **Monte Carlo integration1**
Ambient occlusion, importance sampling, BVH | | | [task07](task07) | **Monte Carlo integration2**
Multiple importance sampling | | -| task08 | **Skeletal Character Animation**
Linear blend skinning, articulated rigid body | | -| task09 | TBD | | +| [task08](task08) | **Skeletal Character Animation**
Linear blend skinning, articulated rigid body | | +| [task09](task09) | **Laplacian Mesh Deformation**
Quadratic programming, sparse linear system | | ### Policy @@ -136,9 +136,10 @@ Look at the following document. - [[19]Ray Triangle Collision](http://nobuyuki-umetani.com/acg2024s/ray_triangle_collision.pdf) - [[20]Character deformation](http://nobuyuki-umetani.com/acg2024s/character_deformation.pdf) -- [[21]Jacobian&Hessian](http://nobuyuki-umetani.com/acg2024s/jacobian.pdf) - - +- [[21] Jacobian&Hessian](http://nobuyuki-umetani.com/acg2024s/jacobian.pdf) +- [[22] Optimization](http://nobuyuki-umetani.com/acg2024s/optimization.pdf) +- [[23] Mesh laplacian](http://nobuyuki-umetani.com/acg2024s/mesh_laplacian.pdf) +- [[24] Sparse linear system](http://nobuyuki-umetani.com/acg2024s/sparse_linear_system.pdf) diff --git a/task09/README.md b/task09/README.md new file mode 100644 index 0000000..b45bcd9 --- /dev/null +++ b/task09/README.md @@ -0,0 +1,110 @@ +# Task09: Laplacian Mesh Deformation (Quadratic Programming, Sparse Matrix) + +![preview](preview.png) + +**Deadline: July 4th (Thu) at 15:00pm** + +---- + +## Before Doing Assignment + +### Install Python (if necessary) +We use Python for this assignment. +This assigment only supports Python ver. 3. + +To check if Python 3.x is installed, launch a command prompt and type `python3 --version` and see the version. + +For MacOS and Ubuntu you have Python installed by default. +For Windows, you may need to install the Python by yourself. +[This document](https://docs.python.org/3/using/windows.html) show how to install Python 3.x on Windows. + + +### Virtual environment + +We want to install dependency ***locally*** for this assignment. + +```bash +cd acg- +python3 -m venv venv # make a virtual environment named "venv" +``` + +Then, start the virtual environment. +For Mac or Linux, type + +```bash +source venv/bin/activate # start virtual environment +``` + +For Windows, type + +```bash +venv\Scripts\activate.bat # start virtual environment +``` + +In the command prompt, you will see `(venv)` at the beginning of each line. +There will be `venv` folder under `acg-`. + +### Install dependency + +In this assignment we use many external library. We use `pip` to install these. + +```bash +pip3 install numpy +pip3 install moderngl +pip3 install moderngl_window +pip3 install scipy +``` + +Alternatively, you can install above dependency at once by + +```bash +cd acg-/task09 +pip3 install -r requirements.txt +``` + +type `pip3 list` and then confirm you have libraries such as `moderngl`, `numpy`, `pillow`, `pyglet`, `pyrr`, `scipy` etc. + +### Make branch + +Follow [this document](../doc/submit.md) to submit the assignment, In a nutshell, before doing the assignment, +- make sure you synchronized the `main ` branch of your local repository to that of remote repository. +- make sure you created branch `task09` from `main` branch. +- make sure you are currently in the `task09` branch (use `git branch -a` command). + +Now you are ready to go! + +--- + +## Problem 1 (Python execution practice) + +Run the code with `python3 main.py` + +The program will output `out.png`. Rename it to `out_default.png` then it replaces the image below. + +![problem1](out_default.png) + + +## Problem 2 (smooth deformation with Laplacian) + +Write a few lines of code around line #134 to implement smooth mesh deformation using Laplacian. + +The program will output `output.png`. Rename it to `out_laplacian.png` then it replaces the image below. + +![problem1](out_laplacian.png) + + +## Problem 3 (even smoother deformation with Bi-Laplacian) + +Write a few lines of code around line #134 to implement smooth mesh deformation using BiLaplacian. + +The program will output `out.png`. Rename it to `out_bilaplacian.png` then it replaces the image below. + +![problem1](out_bilaplacian.png) + + +## After Doing the Assignment + +After modify the code, push the code and submit a pull request. Make sure your pull request only contains the files you edited. Good luck! + +BTW, You can exit the virtual environment by typing `deactivate` in the command prompt. + diff --git a/task09/main.py b/task09/main.py new file mode 100644 index 0000000..63702b8 --- /dev/null +++ b/task09/main.py @@ -0,0 +1,210 @@ +import os +import math + +import numpy +# +import pyrr +import numpy as np +import moderngl +import moderngl_window as mglw +from PIL import Image, ImageOps +from scipy import sparse +from scipy.sparse.linalg import spsolve +# +import util_for_task09 + + +def matrix_graph_laplacian(tri2vtx, num_vtx): + ''' + function to make graph laplacian matrix + :param tri2vtx: index of vertex for triangles + :param num_vtx: number of vertex + :return: sparse matrix using scipy.sparse + ''' + tri2ones = np.ones((tri2vtx.shape[0],)) + W = np.empty(0, np.float32) + I = np.empty(0, np.uint32) + J = np.empty(0, np.uint32) + # + for (i, j) in ((0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)): + tri2vi = tri2vtx[:, i] + tri2vj = tri2vtx[:, j] + if i == j: + coeff = 1. + else: + coeff = -0.5 + W = np.append(W, tri2ones * coeff) + I = np.append(I, tri2vi) + J = np.append(J, tri2vj) + return sparse.csr_matrix((W, (I, J)), shape=(num_vtx, num_vtx)) + + +class HelloWorld(mglw.WindowConfig): + ''' + Window to show the mesh deformation + ''' + gl_version = (3, 3) + title = "task09: laplacian mesh deformation" + window_size = (500, 500) + aspect_ratio = float(window_size[0]) / float(window_size[1]) + resizable = False + + def __init__(self, **kwargs): + super().__init__(**kwargs) + self.is_screenshot_taken = False + + # initialize mesh + asset_path = os.path.normpath(os.path.join(__file__, '../../asset/bunny.obj')) + self.tri2vtx, self.vtx2xyz_ini = util_for_task09.load_triangle_mesh_from_wavefront_obj_file(asset_path) + util_for_task09.normalize_vtx2xyz(self.vtx2xyz_ini) + self.vtx2xyz_def = self.vtx2xyz_ini.copy() + + # make a list for fixed vertices + self.fixbase2vtx = (self.vtx2xyz_ini[:, 2] < -0.48).nonzero()[0].tolist() + self.fixear2vtx = (self.vtx2xyz_ini[:, 2] > +0.48).nonzero()[0].tolist() + self.fixback2vtx = (np.logical_and( + self.vtx2xyz_ini[:, 0] > +0.15, + self.vtx2xyz_ini[:, 2] > +0.115)).nonzero()[0].tolist() + + # make laplacian, bi-laplacian matrices + num_vtx = self.vtx2xyz_ini.shape[0] + self.matrix_laplace = matrix_graph_laplacian(self.tri2vtx, num_vtx) + self.matrix_bilaplace = self.matrix_laplace * self.matrix_laplace + + # make matrix for fixed points + coeff_penalty = 100.0 + fix2vtx = numpy.concatenate([self.fixback2vtx, self.fixear2vtx, self.fixbase2vtx]) + self.matrix_fix = sparse.csr_matrix(( + np.ones(fix2vtx.shape[0], np.float32) * coeff_penalty, (fix2vtx, fix2vtx)), + shape=(num_vtx, num_vtx)) + + # ----------------------------- + # below: visualization related + + self.prog = self.ctx.program( + vertex_shader=''' + #version 330 + uniform mat4 matrix; + in vec3 in_vert; + in vec3 in_nrm; + out vec3 out_nrm; + void main() { + out_nrm = normalize((matrix * vec4(in_nrm, 0.0)).xyz); + gl_Position = matrix * vec4(in_vert, 1.0); + } + ''', + fragment_shader=''' + #version 330 + uniform bool is_shading; // variable of the program + uniform vec3 color; + out vec4 f_color; + in vec3 out_nrm; + void main() { + float ratio = abs(out_nrm.z); + if( !is_shading ){ ratio = 1.0; } + f_color = vec4(color*ratio, 1.0); + } + ''', + ) + + # initialize visualization for mesh + ebo = self.ctx.buffer(self.tri2vtx) # send triangle index data to GPU (element buffer object) + self.vbo_vtx2xyz_def = self.ctx.buffer(self.vtx2xyz_def) # send initial vertex coordinates data to GPU + vtx2nrm = util_for_task09.vtx2nrm(self.tri2vtx, self.vtx2xyz_def) + self.vbo_vtx2nrm_def = self.ctx.buffer(vtx2nrm) # send initial vertex coordinates data to GPU + self.vao_ini = self.ctx.vertex_array( + self.prog, [ + (self.vbo_vtx2xyz_def, '3f', 'in_vert'), + (self.vbo_vtx2nrm_def, '3f', 'in_nrm')], + ebo, 4, mode=moderngl.TRIANGLES) # tell gpu about the mesh information and how to draw it + + # initialize visualization for points + tri2vtx_octa, vtx2xyz_octa = util_for_task09.octahedron() + self.vao_octa = self.ctx.vertex_array( + self.prog, [ + (self.ctx.buffer(vtx2xyz_octa), '3f', 'in_vert')], + self.ctx.buffer(tri2vtx_octa), 4, mode=moderngl.TRIANGLES) + + def render(self, time, frame_time): + self.vtx2xyz_def[:] = self.vtx2xyz_ini[:] + # set fixed deformation + self.vtx2xyz_def[self.fixear2vtx, 0] += -0.5 * math.sin(time) + self.vtx2xyz_def[self.fixback2vtx, 2] += -0.3 * math.sin(2. * time) + + # write a few line code below to implement laplacian mesh deformation + # Problem 2: Laplacian deformation, which minimizes (x-x_def)D(x-x_def) + (x-x_ini)L(x-x_ini) w.r.t x, + # Problem 3: Bi-Laplacian deformation, which minimizes (x-x_def)D(x-x_def) + (x-x_ini)L^2(x-x_ini) w.r.t x, + # where D is the diagonal matrix with entry 1 if the vertex is fixed, 0 if the vertex is free, a.k.a `self.matrix_fix` + # L is the graph Laplacian matrix a.k.a `self.matrix_laplace` + # you may use `spsolve` to solve the liner system + # spsolve: https://docs.scipy.org/doc/scipy/reference/generated/scipy.sparse.linalg.spsolve.html#scipy.sparse.linalg.spsolve + + + # do not edit beyond here + # above: deformation + # --------------------- + # below: visualization + + self.ctx.clear(1.0, 1.0, 1.0) # initizlize frame buffer with white + self.ctx.enable(moderngl.DEPTH_TEST) + + self.vbo_vtx2xyz_def.write(self.vtx2xyz_def) # send vertex information to GPU + vtx2nrm = util_for_task09.vtx2nrm(self.tri2vtx, self.vtx2xyz_def) + self.vbo_vtx2nrm_def.write(vtx2nrm) # send normal information to GPU + + # make view transformation + view_rot_z = pyrr.Matrix44.from_z_rotation(-np.pi * 0.2) + view_rot_x = pyrr.Matrix44.from_x_rotation(np.pi * 0.4) + view_z_flip = pyrr.Matrix44.from_scale((1.3, 1.3, -1.3, 1.)) + view_transf = view_z_flip * view_rot_x * view_rot_z + + # render triangle mesh + self.prog['matrix'].value = tuple(view_transf.flatten()) + self.prog['color'].value = (0.8, 0.8, 0.8) + self.prog['is_shading'].value = 1 + self.vao_ini.render() + + r = 0.02 + self.prog['is_shading'].value = 0 + + # render fixed bases + for i_vtx in self.fixbase2vtx: + pos = self.vtx2xyz_def[i_vtx].copy() + model_transf = pyrr.Matrix44.from_translation(pos) * pyrr.Matrix44.from_scale((r, r, r, 1.)) + self.prog['matrix'].value = tuple((view_transf * model_transf).flatten()) + self.prog['color'].value = (0.0, 0.0, 1.0) + self.vao_octa.render() + + # render fixed ear + for i_vtx in self.fixear2vtx: + pos = self.vtx2xyz_def[i_vtx].copy() + model_transf = pyrr.Matrix44.from_translation(pos) * pyrr.Matrix44.from_scale((r, r, r, 1.)) + self.prog['matrix'].value = tuple((view_transf * model_transf).flatten()) + self.prog['color'].value = (0.0, 1.0, 0.0) + self.vao_octa.render() + + # render fixed back + for i_vtx in self.fixback2vtx: + pos = self.vtx2xyz_def[i_vtx].copy() + model_transf = pyrr.Matrix44.from_translation(pos) * pyrr.Matrix44.from_scale((r, r, r, 1.)) + self.prog['matrix'].value = tuple((view_transf * model_transf).flatten()) + self.prog['color'].value = (1.0, 0.0, 0.0) + self.vao_octa.render() + + # take a screenshot + if not self.is_screenshot_taken and time > 2.2: + self.is_screenshot_taken = True + rgb = np.frombuffer(self.ctx.fbo.read(), dtype=np.uint8) + rgb = rgb.reshape(self.ctx.fbo.size[0], self.ctx.fbo.size[1], 3) + if rgb.shape[0] == 1000: + rgb = rgb[::2, ::2, :].copy('C') + rgb = Image.fromarray(rgb) + ImageOps.flip(rgb).save("output.png") + + +def main(): + HelloWorld.run() + + +if __name__ == "__main__": + main() diff --git a/task09/out_bilaplacian.png b/task09/out_bilaplacian.png new file mode 100644 index 0000000000000000000000000000000000000000..668093442d545d25e10327b9d9560a1c19d95de4 GIT binary patch literal 9577 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 TduxEG00000NkvXXu0mjfGys%7 literal 0 HcmV?d00001 diff --git a/task09/out_default.png b/task09/out_default.png new file mode 100644 index 0000000000000000000000000000000000000000..2bbe6479c9ba77dd46d0112e765047df26058ce0 GIT binary patch literal 6640 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 literal 0 HcmV?d00001 diff --git a/task09/preview.png b/task09/preview.png new file mode 100644 index 0000000000000000000000000000000000000000..81fa83a42cbcb078c395a33f7d8c73215edc5558 GIT binary patch literal 11328 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 hue$kvu0q_a9=0ZxsLl literal 0 HcmV?d00001 diff --git a/task09/requirements.txt b/task09/requirements.txt new file mode 100644 index 0000000..aaa64f3 --- /dev/null +++ b/task09/requirements.txt @@ -0,0 +1,9 @@ +glcontext==2.5.0 +moderngl==5.10.0 +moderngl-window==2.4.6 +multipledispatch==1.0.0 +numpy==1.26.4 +pillow==10.3.0 +pyglet==2.0.15 +pyrr==0.10.3 +scipy==1.14.0 diff --git a/task09/util_for_task09.py b/task09/util_for_task09.py new file mode 100644 index 0000000..866b01e --- /dev/null +++ b/task09/util_for_task09.py @@ -0,0 +1,76 @@ +import numpy +import numpy.typing +import math + + +def load_triangle_mesh_from_wavefront_obj_file(file_path: str): + try: + vtx2xyz = [] + tri2vtx = [] + with open(file_path) as f: + for line in f: + if line[0] == "v": + xyz = list(map(float, line[2:].strip().split())) + vtx2xyz.append(xyz) + elif line[0] == "f": + vtx = list(map(int, line[2:].strip().split())) + tri2vtx.append(vtx) + tri2vtx = numpy.array(tri2vtx, dtype=numpy.uint32) - 1 + vtx2xyz = numpy.array(vtx2xyz, dtype=numpy.float32) + return (tri2vtx, vtx2xyz) + + except FileNotFoundError: + print(f"{file_path} not found.") + except: + print("An error occurred while loading the shape.") + + +def normalize_vtx2xyz(vtx2xyz): + """ + fit the points inside unit cube [0,1]^3 + :param tri2center: + :return: + """ + vmin = numpy.min(vtx2xyz, axis=0) + vmax = numpy.max(vtx2xyz, axis=0) + vtx2xyz -= (vmin+vmax)*0.5 + vtx2xyz *= 1.0/(vmax - vmin).max() + + +def vtx2nrm(tri2vtx, vtx2xyz) -> numpy.typing.NDArray: + v0 = tri2vtx[:, 0] + v1 = tri2vtx[:, 1] + v2 = tri2vtx[:, 2] + u = vtx2xyz[v1] - vtx2xyz[v0] + v = vtx2xyz[v2] - vtx2xyz[v0] + c = numpy.cross(u, v) + c = c / numpy.linalg.norm(c, axis=1)[:, numpy.newaxis] + vtx2nrm = numpy.zeros_like(vtx2xyz) + vtx2nrm[v0] += c + vtx2nrm[v1] += c + vtx2nrm[v2] += c + vtx2nrm = vtx2nrm / numpy.linalg.norm(vtx2nrm, axis=1)[:, numpy.newaxis] + return vtx2nrm + + +def octahedron() -> (numpy.typing.NDArray, numpy.typing.NDArray): + tri2vtx = [ + [0, 1, 2], + [0, 2, 3], + [0, 3, 4], + [0, 4, 1], + [5, 4, 3], + [5, 3, 2], + [5, 2, 1], + [5, 1, 4]] + vtx2xyz = [ + [0., 0., -1.], + [1., 0., 0.], + [0., 1., 0.], + [-1., 0., 0.], + [0., -1., 0.], + [0., 0., 1.]] + tri2vtx = numpy.array(tri2vtx, dtype=numpy.uint32) + vtx2xyz = numpy.array(vtx2xyz, dtype=numpy.float32) + + return (tri2vtx, vtx2xyz)