Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extruded Polygon Side Face Normals and Other Issues #17

Merged
merged 25 commits into from
May 9, 2020

Conversation

brodieG
Copy link
Contributor

@brodieG brodieG commented May 3, 2020

Thanks!

First, thanks a bunch for implementing this feature. It really adds a new
dimension (!) to rayrender. It came in quite handy as I had it on my backburner
to implement something similar manually.

More generally, thanks for getting path tracing into R. I might never have
played this closely with it otherwise, and it's been a great value-add to my life.

Now, in using it I discovered a few possible issues. All these issues
ended up being closely related and as I tried understanding what was going on I
fixed them and they all kind of rely on each other so it is more work than I
think it's worth to split them up into separate issues (though some could be).

Also, I realize that it's bad form of me to submit a patch without first
submitting the bug report, particularly one as extensive as this one. I'm
loathe to submit bug reports without at least doing some work to figure what's
going on and one thing led to another and here we are. I realize that there is
a good chance you're already fixed much of this in your internal versions, and
that you might balk at the magnitude of this patch, but I guess that's the risk
I took by getting hooked on wanting to fix this.

I've tried to be reasonably careful about what I'm doing, and tried to test as
much as possible, but I'll admit finding flipped face normals is tricky, and
this is not my domain of expertise.

All the code I used to generate the below is in this gist.

Base Polygons are Assumed to be Closed

AFAICT this is what SF does, but many polygon applications in R will self close
polygons, including decido, graphics::polygon, and others. It might be
worth either documenting and checking that inputs comply, or alternatively as in
this patch auto-close any open polygons.

Here we compare an extruded cube (left) with a cube (right). Notice how the
back is open:

01_cube-open-cw

Auto-closing polygons as in the patch resolves this:

01_cube-open-cw

Another option is to document that polygons are required to be closed, but it's
easy enough to do this automatically.

Polygon Path Direction Affect Side Triangle Normals

With the current logic depending on which direction the polygon input in the
side polygons will end up with flipped normals. The colors indicate that the side
face normals are flipped.

Note we are now viewing the scene from the opposite direction as the previous one,
so this time the cube cube is on the left, and the extruded one on the right.

02_cube-cw

This may only be an issue with manually specified polygons, but it's easy and
cheap to check the winding with a signed area calculation so we can specify the
correct side as this patch does:

02_cube-cw

The effect is particularly noticeable in holes, showing on the left a version
hacked together with cubes, and on the right an extrusion with a hole:

03_cube-hole

After patch:

03_cube-hole

And the same thing now only extruded versions rotated so we can better see them:

04_cube-hole-angles

After patch:

04_cube-hole-angles

Decido Earcut Hole Index Is First Vertex of Hole

Currently, examples use the last index of the outside polygon as the starting
vertex for the holes. This happens to work when polygons are explicitly closed,
but I believe it adds an extra vertex to the hole, you end up with:

intended?        actual?
+-----+          +-----+
|     |          |\    |   <<- diagonal face is part of the hole
| +-+ |          | +-+ |
| | | |          | | | |
| +-+ |          | +-+ |
|     |          |     |
+-----+          +-----+

With closed polygons you can't see this, but if that edge ends up
crossing another hole or a convexity in the outer polygon it will become
visible. Additionally, it seems worth changing if only to match what decido
does.

I'm going off the decido vignette which I read to indicate we want the
first index in the hole polygon..

Only one Hole is Supported

Logic assumes that only one hole is present, but decido documents (and
supports) multiple holes, as does SF. The patch adds support for multiple
holes. Attempting two holes with manually specified polygons fails:

Error in data.frame(x = x, y = y, hole = holes) : 
  arguments imply differing number of rows: 15, 2

With the patch applied it works:

04_cube-holes-two

This will appear to work with sf polygons with holes in some cases, but it is
because the hole is rendered like so:

+----------+
|          |
| +-+--+-+ |  <<- One hole masquerading as two
| | |  | | |
| +-+  +-+ |
|          |
+----------+

Here is an image comparing after the patch (left) and before the patch (right) with
the code modified to omit the top/bottom faces so you can see inside. Notice
how the pre-patch (right) has a face connecting the two holes. This is the
same polygons used for the sf tests near the end of this PR.

inside-connection

This is probably fine in many cases, but could cause problems with non-convex
polygons or joining edges that show up inside holes.

Material Ids Were Implicitly Required to Be Ordered

The C++ logic assumes that material ids will show up in incrementing order. If
this is not the case as happens if material ids are passed to render_scene in
non-increasing order, materials are incorrectly re-used. Here you can see the
same color re-used:

02_material-id

The patch changes the logic for generating material ids to ensure they are
always ordered:

02_material-id

Harmonizing SF and Base Logic

The logic for flipping vertical/horizontal, earcutting, and centering is
essentially the same for either SF or base polygons. I normally wouldn't have
bothered harmonizing them because it works fine, but in this case the flipping
part was only applied to the SF derived polygons. You can see that as we
attempt to render one polygon with all the flip permutations, and all
four polygons are rendered right on top of each other:

06_arrows-flip

Post-patch you see all four correctly flipped horizontally/vertically:

06_arrows-flip

Related, the following was inside the poly loop when generating the faces.
Since decido always returns CCW faces this should only be needed for the
side polygons, but because this was inside the loop it will affect the face
polygons after the first loop iteration.

if(!(flip_horizontal && flip_vertical) && ((flip_horizontal || flip_vertical))) {
  reversed = !reversed
}

Because the patch adds logic to auto-detect the desired winding for the side
polygons this reversing is no longer needed so I removed it.

Multi-polygons in SF

sf objects may contain multi-polygons, where a single feature defines many
polygons. The internal logic did not seem to explicitly handle this. I didn't
dig too much into as I had to change so much to handle this as well as handle
multiple holes that it was easier to re-write. With the multi-polygon polygons
in North Carolina this is what it looks like:

ex-nc-multi

With patch applied.

ex-nc-multi

All of NC:

ex-nc-a

With patch:

ex-nc-a

I also built a bunch of variations of multipolygon/polygon/base to compare them.
With these the main issues seemed to be alignment (in addition to side face normals).

Various examples follow. Two simple polygons viewed from two angles, original:

09a_sf-multi-holes-angle-1

09a_sf-multi-holes-angle-2

With patch:

09a_sf-multi-holes-angle-2

09a_sf-multi-holes-angle-1

Same polygons, but this time as a single MULTIPOLYGON with two polygons:

09a_sf-multi-holes-mp

With patch (look at the shadows in the holes):

09a_sf-multi-holes-mp

The two POLYGONS, as well as the MULTIPOLYGON together all in one sf object:

09a_sf-multi-holes-p-and-mp

With patch:

09a_sf-multi-holes-p-and-mp

Other than the alignment, you can see how the face normals of the hole insides is wrong prior to patch.

Examples

I ran some of the examples to make sure those still look good with the patch
applied:

ex-hollow-star

ex-texas

I could not run all the states together as I got a C stack error (will report
separately).

@tylermorganwall
Copy link
Owner

Hi Brodie, this looks great. It's going to take me a while to go through it due to some personal issues ongoing now, but thank you for the great work and the detailed pull request.

@brodieG
Copy link
Contributor Author

brodieG commented May 4, 2020

No rush on my account. I have my fork working for what I need it to for now. Sorry you've got stuff going on and hope it resolves quickly and well.

@tylermorganwall tylermorganwall merged commit 2d8b676 into tylermorganwall:master May 9, 2020
@mdsumner
Copy link
Contributor

mdsumner commented May 16, 2020

I didn't realize you wrote this code @brodieG amazing analysis here so much to explore 👍 and @tylermorganwall I appreciate better now how you are going about this. I'm putting a note here in the hope that we can standardize approaches somewhat, hope it's of interest but no probs.

With @dcooley we've been discussing the best ways to apply this triangulation (the best way is to follow Dave's lead for having C++ converters for these things IMO), in my context it's coming from silicate where I support more formats than sp/sf but here's a barebones sf-to-triangles approach.

dcooley/geometries#4 (comment)

You can ignore the idxTRI0 class construction at the end - the list of triangles is straightforward and in feature_triangles with row indexes into coords (here coords is not de-duplicated here in the usual silicate way, not that it makes a difference given the row index).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants