processing / p5.js

p5.js is a client-side JS platform that empowers artists, designers, students, and anyone to learn to code and express themselves creatively on the web. It is based on the core principles of Processing. http://twitter.com/p5xjs —
http://p5js.org/
GNU Lesser General Public License v2.1
21.7k stars 3.33k forks source link

webgl: incorrect billboarding of thick lines #2488

Closed Spongman closed 6 years ago

Spongman commented 6 years ago

When the view is rotated, the segments of thick lines are not being billboarded correctly.

(also, the joins are not pretty, but that's a different bug)

image

function setup() {
  createCanvas(400, 400, WEBGL);
}

function draw() {
  background(0);
  rotateX(millis()/1000);
  noFill();
  stroke(255);
  strokeWeight(100);

  beginShape();
  vertex(-100, -100);
  vertex( 100, -100);
  vertex( 100,  100);
  vertex(-100,  100);
  endShape(CLOSE);
}

https://codepen.io/Spongman/pen/ppwQEj?editors=0010

stalgiag commented 6 years ago

Right so the line.vert shader is based on an older version of a processing line shader. It is less than ideal. It would be great to update to the newer shader that processing uses now. I don't know that this will automatically fix any billboarding issues but it would make the code more readable and easier to modify for problems like this.

As far as joins, I think that in order to make lines well with different kinds of joins we would need to make a stroke segment class of some kind. Doing nice clean joins that can be adjusted would be awesome but feels like a non-trivial endeavor (at least for my skill level) and unfortunately I am swamped with my last few months of school. If anybody has any interest in doing either of these things that would be super welcome.

Spongman commented 6 years ago

ok, i plugged in that shader from processing and it looks much better. although the scaling seems to be off - not sure how to solve that one easily.

image

stalgiag commented 6 years ago

Hm yeah that is preferable. As far as the scaling, if I remember correctly the line vertices were supposed to extend half the width of strokeWeight in their direction but I might be missing where that is still happening. It is possible that Processing does it on the cpu side and so we should be doing it somewhere like _edgesToVertices. I guess this just isn't that noticeable until strokeWeight is huge like this, so it is possible that adding in that part was missed somehow.

Spongman commented 6 years ago

yeah, i had to change to strokeWeight to 25 to render the 2nd image above.

btw, my branch with this change is here: https://github.com/Spongman/p5.js/tree/processing-line-vert

Spongman commented 6 years ago

i think it might be work separating the methods used to to polygon wireframes from the 3d line drawing code. that way the wireframe stuff can be done in a single render pass with a fragment shader that lerps between face fragments and barycentric-cutoff edge fragments. the line drawing stuff can continue to use the more traditional tesselated line/join/cap algorithm that doesn't work well for vertices with more than 2 edges.

the cube's removed edges are a bit of a pain, though.

stalgiag commented 6 years ago

Hm yeah I went down that road a bit a while back. I made a barycentric coord generator and used a bary frag shader. I eventually abandoned that route to bring things more in line with Processing. The problem with the barycentric approach is that if someone wants noFill() then they won't really be able to change the strokeWeight() in the way that they would expect. The barycentric cutoff can be extended to a certain point but not really that far with any semi-dense model because then vert proximity will start forcing strangeness. But that said, my approach to barycentric was probably not the absolute best so maybe it would be a good idea to retry this. Though my instinct is to say that in order to get Processing-like behavior (which feels like a good standard for this part of the library) we will just need to improve the tesselated line situation that is already in place.

Spongman commented 6 years ago

Yeah I agree. I played around with the barycentric stuff, and while I think the noFill() thing could be solved reasonably easily, the visuals don't really match up to the processing tesselated wireframes even after compensating for face normals and drawing model outlines with stencil buffers. Plus bevels, ugh ;)

stalgiag commented 6 years ago

Hm so I was playing around with your adaptation of the Processing shader and I agree that some of the billboarding becomes nicer. It does introduce new unwelcome behavior, a snapping action with rotating strokes that is unexpected. A quick way to see this is

function draw() {
  background(0);
  rotateX(frameCount * 0.015);
  rotateY(frameCount *0.015);
  fill(100);
  stroke(255);
  sphere(150);
}

But we should still try to get it working because I noticed that switching to this shader fixes #2324

Spongman commented 6 years ago

ok, so the snapping is due to one half of the edge disappearing suddenly behind the face as it rotates.

Processing shows the same artifact.

i think it might be possible to fix by moving the edge toward the camera (post projection) and ensuring edges are not rendered unless at least one of their faces are oriented toward the camera.