GithubHelp home page GithubHelp logo

computer-graphics-meshes's Introduction

Computer Graphics – Meshes

To get started: Clone this repository and all its submodule dependencies using:

git clone --recursive https://github.com/alecjacobson/computer-graphics-meshes.git

Do not fork: Clicking "Fork" will create a public repository. If you'd like to use GitHub while you work on your assignment, then mirror this repo as a new private repository: https://stackoverflow.com/questions/10065526/github-how-to-make-a-fork-of-public-repository-private

Background

Read Section 12.1 of Fundamentals of Computer Graphics (4th Edition).

Skim read Chapter 11 of Fundamentals of Computer Graphics (4th Edition).

There are many ways to store a triangle (or polygonal) mesh on the computer. The data-structures have very different complexities in terms of code, memory, and access performance. At the heart of these structures, is the problem of storing the two types of information defining a mesh: the geometry (where are points on the surface located in space) and the connectivity (which points are connected to each other). The connectivity is also sometimes referred to as the topology of the mesh.

The graphics pipeline works on a per-triangle and per-vertex basis. So the simplest way to store geometry is a 3D position for each -th vertex of the mesh. And to store triangle connectivity as an ordered triplet of indices referencing vertices: defines a triangle with corners at vertices , and . Thus, the geometry is stored as a list of 3D vectors: efficiently, we can put these vectors in the rows of a real-valued matrix . Likewise, the connectivity is stored as a list of triplets: efficiently, we can put these triplets in the rows of an integer-valued matrix .

Question: What if we want to store a (pure-)quad mesh?

Texture Mapping

Texture mapping is a process for mapping image information (e.g., colors) onto a surface (e.g., triangle mesh). The standard way to define a texture mapping is to augment the 3D geometric information of a mesh with additional 2D parametrization information: where do we find each point on the texture image plane? Typically, parameterization coordinates are bound to the unit square.

Mapping a 3D flat polygon to 2D is rather straightforward. The problem of finding a good mapping from a 3D surface to 2D becomes much harder if our surface is not flat (e.g., like a hemisphere), if the surface does not have exact one boundary (e.g., like a sphere) or if the surface has "holes" (e.g., like a torus/doughnut).

Curved surfaces must get distorted when flattened onto the plane. This is why Greenland looks bigger than Africa on a common map of the Earth.

The lack or presence of too many boundaries or the presence of "doughnut holes" in surfaces implies that we need to "cut" the surface to lay out it on the plane so all parts of the surface are "face up". Think about trying to flatten a deflated basketball on the ground.

Normals

For a smooth surface, knowing the surface geometry (i.e., position in space) near a point fully determines the normal vector at that point.

For a discrete mesh, the normal is only well-defined in the middle of planar faces (e.g., inside the triangles of a triangle mesh, but not along the edges or at vertices). Furthermore, if we use these normals for rendering, the surface will have a faceted appearance. This appearance is mathematically correct, but not necessarily desired if we wish to display a smooth looking surface.

Phong realized that linearly interpolating normals stored at the corners of each triangle leads to a smooth appearance.

This raises the question: what normals should we put at vertices or corners of our mesh?

For a faceted surface (e.g., a cube), all corners of a planar face should share the face's normal .

For a smooth surface (e.g., a sphere), corners of triangles located at the same vertex should share the same normal vector. This way the rendering is continuous across the vertex. A common way to define per-vertex normals is to take a weighted average of normals from incident faces. Different weighting schemes are possible: uniform average (easy, but sensitive to irregular triangulations), angle-weighted (geometrically well motivated, but not robust near zero-area triangles), area-weighted (geometrically reasonable, well behaved). In this assignment, we'll compute area-weighted per-vertex normals:

where is the set of faces neighboring the -th vertex.

[per-vertex-normal]: images/per-vertex-normal.png height=300px ![Unique triangle normals (orange) are well-defined. We can define a notion of a normal for each vertex (purple) by taking a (weighted) average of normals from incident triangles.][per-vertex-normal]

For surfaces with a mixture of smooth-looking parts and creases, it is useful to define normals independently for each triangle corner (as opposed to each mesh vertex). For each corner, we'll again compute an area-weighted average of normals triangles incident on the shared vertex at this corner, but we'll ignore triangle's whose normal is too different from the corner's face's normal:

where is the minimum dot product between two face normals before we declare there is a crease between them.

./normals should open a viewing window. Toggling 1,2,3 should switch between normal types. Notice that per-face has sharp corners, but a faceted appearance in the curved regions; the per-vertex has nice smooth regions but ugly corners where averaging acts up; and per-corner is the best of both worlds.

.obj File Format

The .obj file format is a face-based representation of a mesh. The connectivity/topological data is stored implicitly by a list of a faces whose corners can share geometric information.

There are three main types of geometric information stored at vertices:

  • 3D position information (let's call it V),
  • 3D normal vector information (let's call it NV), and
  • 2D parameterization information (let's call it UV).

Faces know where to find the position, normal and parameterization information for each corner by following a pointer/index. For a given corner of a given face, the index for position, normal or parameterization information may be different.

Warning: In C++ indices "start" at 0, but in a .obj file the first element in a list has index 1.

Running ./obj will first pop up a viewer with your generated cube textured with a Rubik's cube

After closing that window, another viewer will pop up with your generated sphere textured with the earth.

Cube example

In this way, the quadrilateral faces of a cube mesh may use 8 unique 3D vertex positions, giving the appearance of a connected surface when visualized in 3D. The same set of faces may use 14 unique 2D parameterization positions, giving the appearance of a cross with a boundary when visualized in 2D. Finally, that same set of faces may use only 6 unique 3D normal vectors.

Eigen Matrices

Every row of Eigen::MatrixXd can store a mesh vertex position using _doubles.

Every row of Eigen::MatrixXi can store a list of indices into rows of position matrix using integers as indices.

Use .resize(num_rows,num_cols) to resize a matrix to be num_rows by num_cols. Use X = Eigen::MatrixXd::Zero(num_rows,num_cols) to resize and initialize a matrix with zeros.

Subdivision Surfaces

A subdivision surface is a natural generalization of a spline curve. A smooth spline can be defined as the limit of a recursive process applied to a polygon: each edge of the polygon is split with a new vertex and the vertices are smoothed toward eachother. If you've drawn smooth curves using Adobe Illustrator, PowerPoint or Inkscape, then you've used splines.

At a high-level, subdivision surfaces work the same way. We start with a polyhedral mesh and subdivide each face. This adds new vertices on the faces and/or edges of the mesh. Then we smooth vertices toward each other.

The first and still (most) popular subdivision scheme was invented by Catmull (who went on to co-found Pixar) and Clark (founder of Silicon Graphics and Netscape). Catmull-Clark subdivision is defined for inputs meshes with arbitrary polygonal faces (triangles, quads, pentagons, etc.) but always produces a pure-quad mesh as output (i.e., all faces have 4 sides).

To keep things simple, in this assignment we'll assume the input is also a pure-quad mesh.

Running ./quad_subdivision and repeated pressing space will show this Bob converging toward a smooth surface.

Mesh Viewers

Mesh Lab is a free mesh-viewer used widely in computer graphics and computer vision research. Warning: Mesh Lab does not appear to respect user-provided normals in .obj files.

Autodesk Maya is a commericial 3D modeling and animation software. They often have free student versions.

Tasks

White list

You're encouraged to use #include <Eigen/Geometry> to compute the cross product of two 3D vectors .cross.

Black list

This assignment uses libigl for mesh viewing. libigl has many mesh processing functions implemented in C++, including some of the functions assigned here. Do not copy or look at the following implementations:

igl::per_vertex_normals
igl::per_face_normals
igl::per_corner_normals
igl::double_area
igl::vertex_triangle_adjacency
igl::writeOBJ

src/write_obj.cpp

Write a pure-triangle or pure-quad mesh with 3D vertex positions V and faces F, 2D parametrization positions UV and faces UF, 3D normal vectors NV and faces NF to a .obj file.

Note: These two function overloads represent only a small subset of meshes and mesh-data that can be written to a .obj file.

src/cube.cpp

Construct the quad mesh of a cube including parameterization and per-face normals.

Hint: Draw out on paper and label with indices the 3D cube, the 2D parameterized cube, and the normals.

src/sphere.cpp

Construct a quad mesh of a sphere with num_faces_u × num_faces_v faces.

src/triangle_area_normal.cpp

Compute the normal vector of a 3D triangle given its corner locations. The output vector should have length equal to the area of the triangle.

src/per_face_normals.cpp

Compute per-face normals for a triangle mesh.

src/per_vertex_normals.cpp

Compute per-vertex normals for a triangle mesh.

src/vertex_triangle_adjacency.cpp

Compute a vertex-triangle adjacency list. For each vertex store a list of all incident faces.

src/per_corner_normals.cpp

Compute per corner normals for a triangle mesh by computing the area-weighted average of normals at incident faces whose normals deviate less than the provided threshold.

src/catmull_clark.cpp

Conduct num_iters iterations of Catmull-Clark subdivision on a pure quad mesh (V,F).

computer-graphics-meshes's People

Contributors

abhimadan avatar alecjacobson avatar rarora7777 avatar texify[bot] avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

computer-graphics-meshes's Issues

Difficult to read math equations in README

The math equations in the README is very difficult to read, I think Github is not properly converting the syntax to a readable format...

How do we get the equations to be in a readable format?

Why is the vertices index ordering in F provided by catmull_clack inconsistent?

Hey, when dealing with catmull_clark I printed the given V F and found most vertex index of are ordered clockwise and start from top-left, here is the example:

for face 0, the four indices for vertices are 0, 1, 12, 11
for face 1, the four indices for vertices are 1, 2, 13, 12
for face 2, the four indices for vertices are 2, 3, 14, 13
for face 3, the four indices for vertices are 3, 4, 15, 14
for face 4, the four indices for vertices are 4, 5, 16, 15
for face 5, the four indices for vertices are 5, 6, 17, 16

so i guess the mesh would look like this:
1

however when debugging I found there are several exceptions:

for face 262, the four indices for vertices are 322, 0, 11, 315
...
for face 280, the four indices for vertices are 326, 121, 0, 322
...
for face 298, the four indices for vertices are 322, 315, 160, 149
...
for face 316, the four indices for vertices are 326, 322, 149, 270

by combing this four tiles i get image like this:
2

Here is my question, why the F does not always following the same ordering of vertex indices? I think it would be better if the first index always start from upper-left so we can finding the neighbor faces more quickly?

About indices in faces

Do we only increment each index for face vertices by 1 when writing to the obj file or should we keep the proper C++ index within the face vertices list as well? Also which do we assume for the triangle mesh questions?

obj.cpp has no return value

Question: Should the starter code for obj.cpp be changed?

Context:

I was trying to debug a runtime error. Since the error wasn't very informative, I was recommended to rename the function in obj.cpp from main to main1. I made the following changes:

Screen Shot 2022-02-14 at 9 06 14 PM

Screen Shot 2022-02-14 at 9 06 19 PM

Problem:

With the changes shown above, I get the following compile time warning:

Screen Shot 2022-02-14 at 9 08 38 PM

Where does data of cube comes from? what does`cube.cpp` want us to do?

This might be a dumb question but I dont see how we are expected to implement cube.cpp. By checking obj.cpp I see the order of functions being called is:

    Eigen::MatrixXd V,UV,NV;
    Eigen::MatrixXi F,UF,NF;
    cube(V,F,UV,UF,NV,NF);
    write_obj("cube.obj",V,F,UV,UF,NV,NF);

We initialize V, UV, NV, F, UF, NF and pass it to cube(), cube() should change and fill the six parameters in certain format to let write_obj read and parse further. So i am expecting to receive some raw data of the position of each vertices/ faces / normals and fill them in the six parameters, but where does the data of cube come from?

Help debugging write_obj.cpp & cube.cpp

Problem:

I'm having a runtime error from an assertion. I cannot find the assertion in our starter code, nor in libigl. Does anyone know where this assertion is and/or what I'm doing wrong?

My approach:

  1. First, I commented out the sphere code from obj.cpp and implementing cube.cpp. This built without warnings/errors.
  2. Next, I implemented just "v", "vt" and "vn" for write_obj.cpp. This built and ran as expected.
  3. Finally, when I added the implementation for "f" for write_obj.cpp, I got this runtime error:

Screen Shot 2022-02-14 at 9 20 31 PM

Assertion in igl::readOBJ

When I try to run .obj, I get the following assertion, which seems to come from igl::readOBJ:

obj: /nfs/ug/homes-1/n/nguy1109/CSC418-asst5/libigl/external/eigen/Eigen/src/Core/DenseCoeffsBase.h:114: Eigen::DenseCoeffsBase<Derived, 0>::CoeffReturnType Eigen::DenseCoeffsBase<Derived, 0>::operator()(Eigen::DenseCoeffsBase<Derived, 0>::Index, Eigen::DenseCoeffsBase<Derived, 0>::Index) const [with Derived = Eigen::Matrix<int, -1, -1>; Eigen::DenseCoeffsBase<Derived, 0>::CoeffReturnType = const int&; Eigen::DenseCoeffsBase<Derived, 0>::Index = long int]: Assertion `row >= 0 && row < rows() && col >= 0 && col < cols()' failed.
Abort

This appears to happen when the obj file called contains face data (ie. f v1 v2 v3 v4), as removing these lines from the obj file does not result in an assertion.

I have even tried calling igl::readOBJ on an standard obj file that I downloaded online, which had the same results as using the obj file I generated.

cmake failed on macOS10.15

When I try to make and build the program, it keeping failed, and showing an error message: "could not find openGL". I'm guessing this might caused by the new version of the macOS since I just updated it recently. I've tried a lots of ways to solve this issue, but still can not fix it. And here's the error message, how should I do to fix it?
Screen Shot 2019-10-19 at 9 53 06 PM

sphere top and bottom vertex

Hi,

I was wondering for the sphere, since the north and south pole vertices are at the same two locations for all quads around it, should we just have two vertex points to represent them? Or have many duplicates at the same location?

I.e. should/can a quad face have the same vertex index repeated?

How to check if the output solution is correct?

Is there any way to verify that our output is correct besides just visual checks? My outputs look the same as the ones posted on the github page, but I don't know how the output will be tested so i want to make sure.
Thanks.

dimmed libigl image

The image I see in libigl is dimmed. I am not able to figure out where I maybe going wrong. Any hints to point me in the right direction? I write out the face coordinates, vector normals, vector parameterization, face normals, face parameterization and the vector coordinates.

dull_output

How many times will Catmull-Clark subdivision be applied for grading?

How many times will Catmull-Clark subdivision be applied for grading? Currently, I can apply Catmull-Clark subdivision 5 times on bob.obj before segmentation fault. I guess it is because of stack overflow.
I wonder if it is good grading? or I need to optimize my code so that it can apply more subdivisions?

cube.h docs typo?

In cube.h line 12, the documentation states // NV 6 by 4 list of 3D unit normal vectors. Should this be 6 by 3 since there are 6 3D unit normal vectors?

cmake and ./obj failing on CDF computers

I've been working on the lab from UG computers and was having no issues, but today tried to remote into the CDF computers from home. Running "cmake .." from a created build folder gives the following:

nxsrv:~/computer-graphics-meshes/build$ cmake ..
-- CGAL not found. Set the CGAL_DIR cmake variable or environment variable to the directory containing CGALConfig.cmake. This is either the binary directory where CGAL was configured or PREFIX/lib/CGAL for an installation.
-- Boost version: 1.58.0
-- Found the following Boost libraries:
-- thread
-- system
-- chrono
-- date_time
-- atomic
-- Could NOT find MOSEK (missing: MOSEK_LIBRARIES MOSEK_INCLUDE_DIR)
-- Creating target: igl::core (igl)
-- Creating target: igl::opengl (igl_opengl)
-- Creating target: igl::opengl_glfw (igl_opengl_glfw)
-- Could NOT find Vulkan (missing: VULKAN_LIBRARY VULKAN_INCLUDE_DIR)
-- Using X11 for window creation
-- Creating target: igl::png (igl_png)
-- Configuring done
-- Generating done
-- Build files have been written to: /h/u4/c8/05/sissonde/computer-graphics-meshes/build

And running ./obj does not launch a window and gives the following:

nxsrv:~/computer-graphics-meshes/build$ ./obj
igl::opengl::glfw::Viewer usage:
[drag] Rotate scene
A,a Toggle animation (tight draw loop)
F,f Toggle face based
I,i Toggle invert normals
L,l Toggle wireframe
O,o Toggle orthographic/perspective projection
T,t Toggle filled faces
Z Snap to canonical view
[,] Toggle between rotation control types (trackball, two-axis
valuator with fixed up, 2D mode with no rotation))
<,> Toggle between models
; Toggle vertex labels
: Toggle face labels
QuadViewer usage:
F,f [disabled]
L,l Show/hide quad eges
U,u Show/hide texture

GLX: GLX version 1.3 is requiredobj: /h/u4/c8/05/sissonde/computer-graphics-meshes/libigl/external/glfw/src/window.c:472: glfwWindowShouldClose: Assertion `window != NULL' failed.
Aborted

All the resources online seem to bring up driver issues as a cause for the last error message, but I can't fix something like that on school computers. I'll be continuing to work from the UG labs, but wanted to note this error down.

Extension for A5

I know some of the students have tests for CSC373 and CSC411 on Fridays, and I know that we asked for an extension for a4 last week already, while an extension for A5 would be such a relief for us to spend enough time to work on the assignment and preparing for the test. Since the assignment this time requires caution and 100% concentration (maybe it's just for me not to mess up the index and mapping, which causes me a lot of troubles), I would really appropriate an extension, even for one day.

per-vertex-normals header instruction mistake?

Hello, I think there is a mistake regarding the instructions in the header per-vertex-normals.h. It says that the output should return a "#V by 3 list of per-face unit normal vectors" when, from my understanding, it should be returning something more similar to the per-corner-normals function, which is a "#F*3 by 3 list of mesh vertex 3D normals". Is this correct?

Warnings on build

Environment:
Device: MacBook Pro (Mid 2015)
OS: macOS Big Sur Version 11.5.2
Processor: 2.2 GHz Quad-Core Intel Core i7
Memory: 16 GB 1600 MHz DDR3
Graphics: Intel Iris PRo 1536 MB

Build script:

cmake -DCMAKE_BUILD_TYPE=Debug ..

OR

cmake ../ -DCMAKE_BUILD_TYPE=Release

Warnings:
Screen Shot 2022-02-14 at 3 16 20 PM

Question about per-corner normals

I have two questions about the use of per-corner normals in assignment 5:

  1. In the formula given for the corner normal, the sum considers only those vectors whose dot products with the face normal are less than epsilon. Shouldn't this be greater than epsilon, since the higher the dot product, the smaller angle between the vectors, and thus the less of a crease between the faces?
  2. In normals.cpp, a value of 20 is given for corner_threshold. This means that the normal vectors being considered must not be normalized first (or the dot product would always be <= 1), which seems counter-intuitive to me since then the angle cutoff before a normal vector is included in the average is dependent on the overall scale of the model (and thus the magnitude of the normal vectors), whereas it seems like we would want it to be scale-invariant. I also found that when I used the given value of 20 and didn't normalize my normal vectors, the model shows up as entirely dark for me, but if I use unit normal vectors and a corner_threshold value such as 0.8 I get the desired appearance.

Please let me know if there's something I'm misunderstanding.

src/vertex_triangle_adjacency.cpp used in both computing per vertex normals and per corner normals

From my understanding in computing both per vertex normals and per corner normals, we need to be able to get all the Faces which are incident on the vertex.

Is there any particular reason why the function vertex_triangle_adjacency() is only included in the per_corner_normals.cpp and not in per_vertex_normals.cpp ??

EDIT
I ended up just importing the function in my per_vertex_normals.cpp , just verifying that we are allowed to do this

Different threshold in images in README

I just wanted to confirm whether the fandisk images in the README used a different degree threshold or not.
DeepinScreenshot_select-area_20191021192750

The reason I ask is if you look at the image above (a per corner normal image), on the left side there is a smooth curve transition rather than sharp as in the readme images, and for the life of me I can't figure out why this corner case occurs

About cloning ligibl

Okay, so I know this is late, but when I was cloning my fork of the repository, something happened while it was cloning ligibl and now it shows up as empty and within the git repository, it shows up as up to date. When I looked at it in my account repository, it had everything. Git status shows it as modified, but I can't seem to get it onto my current branch. Is there a way to get ligibl onto my current branch without having to reclone it?

Inverse Normal

duck

How do I fix this issue of inverse normal? When I do the cross product of two vectors of a triangle, it gives me the inverse (only for the edges, as seen in the image). My algorithm works great for per-corner normal smoothing since it doesn't smooth out the hard edges.

readOBJ Warnings: UV and UF

write_obj.cpp requires us to write the UV and UF values into the .obj file, however, when running ./obj, libigl returns the warnings saying that it chose to ignore the lines representing the UV and UF values. My understanding was that UV and UF are the coords/indices to represent the 2D parameterization, which I have seen described as being the vp and curv2 values. I noticed that in obj.cpp, UV and UF are passed as the texture coordinates, so should we be treating them as that in our write_obj.cpp?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.