{"id":1890,"date":"2024-08-26T15:17:43","date_gmt":"2024-08-26T15:17:43","guid":{"rendered":"https:\/\/summergeometry.org\/sgi2024\/?p=1890"},"modified":"2024-08-26T15:17:43","modified_gmt":"2024-08-26T15:17:43","slug":"implementing-spectral-conformal-parameterization-using-rxmesh","status":"publish","type":"post","link":"https:\/\/summergeometry.org\/sgi2024\/implementing-spectral-conformal-parameterization-using-rxmesh\/","title":{"rendered":"Implementing Spectral Conformal Parameterization using RXMesh"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Fellows: Sachin Kishan and Diany Pressato<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Teaching Assistant: Supriya Gadi Patil<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Mentor: Ahmed Mahmoud<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Introduction<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/github.com\/owensgroup\/RXMesh\" data-type=\"link\" data-id=\"https:\/\/github.com\/owensgroup\/RXMesh\">RXMesh <\/a>is a framework for processing triangle mesh on the GPU. It allows developers to easily use the GPU\u2019s massive parallelism to perform common geometry processing tasks on triangle mesh. For a preliminary introduction to RXMesh, check out this tutorial blog <a href=\"https:\/\/summergeometry.org\/sgi2024\/an-introduction-to-rxmesh\/\">here<\/a>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Our task was to use RXMesh to implement some classic algorithms in geometry processing. This blog details one of the algorithms we implemented: Spectral Conformal Parameterization (SCP).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This blog will discuss the algorithm in terms of its basic concepts, the steps of the algorithm, the implementation using RXMesh, and our results.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Our entire implementation of SCP application in RXMesh can be found <a href=\"https:\/\/github.com\/owensgroup\/RXMesh\/tree\/main\/apps\/SCP\" data-type=\"link\" data-id=\"https:\/\/github.com\/owensgroup\/RXMesh\/blob\/main\/apps\/SCP\/scp.cu\">here<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Spectral Conformal Parameterization<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><em>Parameterization<\/em> refers to the mapping of a triangle surface mesh in \\(R^{3}\\) space to a planar \\(R^{2}\\) space. This mapping can then be subsequently used in different application such as texture mapping, where we map the values from some other \\(R^{2}\\) space (e.g., image) to the surface of the mesh. In this case, we map color values to the mesh surface.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Often when we perform mesh parameterization, there is a possibility to change some properties of the geometric elements of the mesh, such as the size, shape, or angle between edges. It is desired to preserve these properties since we usually want to create a direct mapping of our intended values (e.g., texture color) to be in the &#8220;correct&#8221; positions on the mesh. What is correct is usually up to artistic opinion, and thus a mapping that preserves these properties is desirable. A mapping that conserves angles (and thus shape) is called a <em>conformal<\/em> mapping.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Mullen et al. (2008) provides a method to perform conformal parameterization of an open surface mesh by modelling the problem as a generalized Eigenvalue problem. Using an initial guess, we can perform multiple iterations of a general eigen vector solving method (i.e., the power method [2] ) to determine the largest eigen vector. This eigen vector, in our case, is the parameterized coordinates of the given mesh, giving us our desired output.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The algorithm for the power method is as follows:<\/p>\n\n\n\n<div class=\"wp-block-group is-vertical is-layout-flex wp-container-core-group-is-layout-4fc3f8e1 wp-block-group-is-layout-flex\">\n<p class=\"wp-block-paragraph\">Given \\((A-\\lambda B)x=0 \\)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">We want to find \\(x\\).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Given an initial guess of eigen-vector \\(x_{0}\\)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For some number of iterations, on iteration k<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\\(B y_{k+1}  = A x_{k}\\)<\/li>\n\n\n\n<li>\\(x_{k+1}= \\frac{y_{k+1}}{\\left| y_{k+1} \\right|} \\)<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">where \\(x\\) is the desired eigenvector solution.<\/p>\n<\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Implementation<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">We applied the power method to our application in the following way, based on the formulation given by Mullen et al. (2008) in equation 7:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\\([B-\\frac{1}{V_{b}}e_{b}e_{b}^{t}]u=\\mu L_{c}u \\)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">To find the eigenvector \\(u\\), we can the power method since the problem is in the form \\(B y_{k+1} = A x_{k}\\).  This can be done in the following way:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\\(T2=dot(eb,u) \\)<br>\\(T1=Bu &#8211; T2 * eb \\)<br>\\(L_{c}u=T1 \\)<br>\\(u=u\/normalise (u) \\)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Where \\(u\\) is the parameterized coordinates. \\(T1\\) and \\(T2\\) are temporary variables to store certain scalar or vector values. Other variables are explained by Mullen et al. (2008). This loop can be implemented like this using RXMesh:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>for (int i = 0; i &lt; iterations; i++) \n{\n    cuComplex T2 = eb.dot(uv_mat);\n    rx.for_each_vertex(rxmesh::DEVICE,\n                       &#091;eb, T2, T1, uv_mat, B] __device__(\n                       const rxmesh::VertexHandle vh) mutable\n                       {\n                           T1(vh, 0) =\n                       cuCsubf(cuCmulf(B(vh, vh), uv_mat(vh, 0)),\n                       cuCmulf(eb(vh, 0), T2));\n                       });\n\n    Lc.solve(T1, uv_mat);\n    float norm = uv_mat.norm2();\n    uv_mat.multiply(1.0f \/ norm);\n    if (std::abs(prv_norm - norm) &lt; 0.0001)\/\/convergence check\n    {\n        break; \n    }\n    prv_norm = norm;\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">\\(T1\\) is a vector whose values are per-vertex, which is why we perform a per vertex computation. \\(T2\\) is a scalar in which we store the value of the dot product <code>dot(eb,uv)<\/code>. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Since our system matrix \\(L_{c}\\) has dimensions <code>2V x 2V<\/code>, we use complex representations of value entries in the vectors and matrices we use. So, for example, our \\(L_{c}\\)  matrix now has dimensions <code>V x V<\/code> where each entry is a complex number (with a real and complex part). <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This means subsequent computations must use CUDA functions that operate on complex numbers (e.g., <code>cuCsubf<\/code> which subtracts complex numbers).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">All initial pre-computations only allocate to the real part of the entry, except for the calculation of the area term \\(A\\) where we calculate \\(L_{c}=L_{D}-A\\) . For the area term, we assign values to the complex part of the area term. \\(L_{D}\\) represents the standard Laplace-Beltrami operator, which we calculate using the cotangent edge weights and assign to the real part.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">After we obtain the eigenvector \\(u\\) in complex form, we can convert it to a standard <code>V x 2<\/code> matrix form and use that for further processing (such as rendering).<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Results<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Using the SCP implementation, we can now apply texturing to our open meshes as shown in the examples below. <\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"938\" height=\"772\" src=\"https:\/\/summergeometry.org\/sgi2024\/wp-content\/uploads\/2024\/08\/image-41.png\" alt=\"\" class=\"wp-image-2346\" srcset=\"https:\/\/summergeometry.org\/sgi2024\/wp-content\/uploads\/2024\/08\/image-41.png 938w, https:\/\/summergeometry.org\/sgi2024\/wp-content\/uploads\/2024\/08\/image-41-300x247.png 300w, https:\/\/summergeometry.org\/sgi2024\/wp-content\/uploads\/2024\/08\/image-41-768x632.png 768w\" sizes=\"auto, (max-width: 938px) 100vw, 938px\" \/><figcaption class=\"wp-element-caption\"><em>The bunny with a checker texture. <\/em><\/figcaption><\/figure>\n<\/div>\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"828\" height=\"606\" src=\"https:\/\/summergeometry.org\/sgi2024\/wp-content\/uploads\/2024\/08\/image-42.png\" alt=\"\" class=\"wp-image-2347\" srcset=\"https:\/\/summergeometry.org\/sgi2024\/wp-content\/uploads\/2024\/08\/image-42.png 828w, https:\/\/summergeometry.org\/sgi2024\/wp-content\/uploads\/2024\/08\/image-42-300x220.png 300w, https:\/\/summergeometry.org\/sgi2024\/wp-content\/uploads\/2024\/08\/image-42-768x562.png 768w\" sizes=\"auto, (max-width: 828px) 100vw, 828px\" \/><figcaption class=\"wp-element-caption\"><em>The beetle mesh with a checker texture.<\/em><\/figcaption><\/figure>\n<\/div>\n\n\n<h2 class=\"wp-block-heading\">References<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">[1] Mullen, P., Tong, Y., Alliez, P., &amp; Desbrun, M. (2008). Spectral Conformal Parameterization. <em>Computer Graphics Forum<\/em>, <em>27<\/em>(5), 1487\u20131494. https:\/\/doi.org\/10.1111\/j.1467-8659.2008.01289.x<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">[2] Koz\u0142owski, W. (1992). The power method for the generalized eigenvalue problem. <em>Roczniki Polskiego Towarzystwa Matematycznego. Seria 3, Matematyka Stosowana\/Matematyka Stosowana\/Mathematica Applicanda<\/em>, <em>21<\/em>(35). https:\/\/doi.org\/10.14708\/ma.v21i35.1790<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This blog was written by Sachin Kishan and Diany Pressato during the SGI 2024 Fellowship as one of the outcomes of a two-week project under the mentorship of Ahmed Mahmoud and the support of Supriya Gadi Patil as a teaching assistant.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Fellows: Sachin Kishan and Diany Pressato Teaching Assistant: Supriya Gadi Patil Mentor: Ahmed Mahmoud Introduction RXMesh is a framework for processing triangle mesh on the GPU. It allows developers to easily use the GPU\u2019s massive parallelism to perform common geometry processing tasks on triangle mesh. For a preliminary introduction to RXMesh, check out this tutorial [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":2346,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[37],"tags":[54,97,53,6],"ppma_author":[26,10],"class_list":["post-1890","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-research","tag-gpu","tag-paramterization","tag-rxmesh","tag-sgi2024"],"authors":[{"term_id":26,"user_id":0,"is_guest":1,"slug":"cap-sachinkishan02","display_name":"sachinkishan02","avatar_url":"https:\/\/secure.gravatar.com\/avatar\/?s=96&d=mm&r=g","author_category":"","first_name":"","last_name":"","user_url":"","job_title":"","description":""},{"term_id":10,"user_id":0,"is_guest":1,"slug":"cap-pressatodiany","display_name":"pressatodiany","avatar_url":"https:\/\/secure.gravatar.com\/avatar\/?s=96&d=mm&r=g","author_category":"","first_name":"","last_name":"","user_url":"","job_title":"","description":""}],"_links":{"self":[{"href":"https:\/\/summergeometry.org\/sgi2024\/wp-json\/wp\/v2\/posts\/1890","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/summergeometry.org\/sgi2024\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/summergeometry.org\/sgi2024\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/summergeometry.org\/sgi2024\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/summergeometry.org\/sgi2024\/wp-json\/wp\/v2\/comments?post=1890"}],"version-history":[{"count":10,"href":"https:\/\/summergeometry.org\/sgi2024\/wp-json\/wp\/v2\/posts\/1890\/revisions"}],"predecessor-version":[{"id":2558,"href":"https:\/\/summergeometry.org\/sgi2024\/wp-json\/wp\/v2\/posts\/1890\/revisions\/2558"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/summergeometry.org\/sgi2024\/wp-json\/wp\/v2\/media\/2346"}],"wp:attachment":[{"href":"https:\/\/summergeometry.org\/sgi2024\/wp-json\/wp\/v2\/media?parent=1890"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/summergeometry.org\/sgi2024\/wp-json\/wp\/v2\/categories?post=1890"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/summergeometry.org\/sgi2024\/wp-json\/wp\/v2\/tags?post=1890"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/summergeometry.org\/sgi2024\/wp-json\/wp\/v2\/ppma_author?post=1890"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}