# Coding jam: Pixel-perfect spheres without high-res geometry

**URL:** <https://discourse.threejs.org/t/coding-jam-pixel-perfect-spheres-without-high-res-geometry/10154>\
**Category:** Resources\
**Created:** [October 11, 2019, 10:17am UTC](https://discourse.threejs.org/t/coding-jam-pixel-perfect-spheres-without-high-res-geometry/10154 "2019-10-11T10:17:05Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![EliasHasle](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/eliashasle/32/3010_2.png) [@EliasHasle](https://discourse.threejs.org/u/EliasHasle)\
**Post date:** [October 11, 2019, 10:17am UTC](https://discourse.threejs.org/t/coding-jam-pixel-perfect-spheres-without-high-res-geometry/10154/1 "2019-10-11T10:17:06Z")

</div>

Coding jam context: [Interest for collaborative coding "jams"?](https://discourse.threejs.org/t/interest-for-collaborative-coding-jams/9880)  
Discussion context: [PointsMaterial + light support · Issue #17618 · mrdoob/three.js · GitHub](https://github.com/mrdoob/three.js/issues/17618#issuecomment-536753086)

**Idea: Pixel-perfect spheres rendered by raycasting on view-space quads in the fragment shader.**

~~Demo: [https://eliashasle.github.io/perfect\_spheres.html~~](https://eliashasle.github.io/perfect_spheres.html~~) Does not work anymore, due to lazy maintenance. Try this version with instanced quads instead: [Perfect spheres](https://eliashasle.github.io/perfect_spheres_instanced.html)

JSFiddle version: [Perfect spheres (WIP) - JSFiddle - Code Playground](https://jsfiddle.net/EliasHasle/yrh4k72d/) (Try increasing the number of spheres while decreasing the radius. On my computer, but in the low-resolution corner viewport of JSFiddle, 1 million spheres render at interactive/movie FPS, and 10 million spheres render at a few FPS. In full screen, 100 k spheres work, 1 M spheres lag.)

Problems:

1. ~~Fragment depth calculation is ad hoc and wrong. The light source mesh consistently lands behind all sphere quads.~~ Fixed!
2. I don’t understand the need for padding in the perspective branch. 😕 The point of radius-scaled view-space quads was to be sure that the quads would be guaranteed to encompass the spheres.
3. Branching shaders may harm performance.
4. Quads have a large geometry overhead compared to points.
5. Only a custom Blinn-Phong shading is implemented. Depending on custom code for all shading is not sustainable, but should be avoidable.
6. Custom fragment depth disables early depth testing, but in principle many early tests would be correct. This may be possible to benefit from by using WebGL2 and `layout (depth_greater)` (or something like that) on the `gl_FragDepth` output declaration.
7. Aliasing on the edges. [https://www.desultoryquest.com/blog/drawing-anti-aliased-circular-points-using-opengl-slash-webgl/](https://www.desultoryquest.com/blog/drawing-anti-aliased-circular-points-using-opengl-slash-webgl/) seems to provide a viable solution, but all attempts so far have resulted in black squares around the particles.
8. The demo reminds me of m&m’s… 🤤🤪🙄

Possible extensions:

1. Texturing from inverse rectangular projection. This can be achieved by converting back from view space to object space by multiplying with a uniform inverse modelViewMatrix, and finding spherical coordinates which can be converted to UV.
2. Back and front renders.
3. “Volumetric” rendering dependent on distance passed within sphere.
4. Connect to as much as possible of the functionality of three.js ShaderChunks for mesh materials, using the derived view-space positions and normals. Multiple lights, shadowing, and envmaap reflection/refraction should be achieveable.
5. A simplified API for reuse.

**Is it possible to achieve the same result with points instead of quads?**  
Advantages:

1. Smaller geometry.
2. Only have to change one attribute entry to move a particle.

Disadvantages:

1. Cannot reduce the number of discarded fragments further, while the quad approach can, if desired, be extended to regular polygons with more edges, tightening around the circle.

Obstacles:

1. Maximal point size is device-specific.
2. Setting the right point size in the vertex shader.
3. Converting clip-space(?) point coordinates into view-space rays?

Useful context: [WebGL2 Drawing Without Data](https://webgl2fundamentals.org/webgl/lessons/webgl-drawing-without-data.html)

Another approach would be to use instancing on the quads, as suggested by gman/greggman [here](https://stackoverflow.com/a/15418797/4071801). Update: Implemented in [Perfect spheres](https://eliashasle.github.io/perfect_spheres_instanced.html) . The code is much simpler, memory may be saved, updating positions is simpler, along with possible benefits that I do not know yet. But I think there is much more depth flickering on the level of whole spheres, indicating that fragment depth does not work perfectly.

**Improvements are more than welcome! The coding jam may eventually result in a collaborative GitHub repo.**

---

<div class="post-metadata">

**Author:** ![EliasHasle](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/eliashasle/32/3010_2.png) [@EliasHasle](https://discourse.threejs.org/u/EliasHasle)\
**Post date:** [October 12, 2019, 7:34am UTC](https://discourse.threejs.org/t/coding-jam-pixel-perfect-spheres-without-high-res-geometry/10154/2 "2019-10-12T07:34:08Z")

</div>

Note: This is my first comment in this thread, but I have made many changes to the original post and demo.

I opened an [issue](https://github.com/mrdoob/three.js/issues/17715) on the flickering observed with instancing, but closed it again because of doubt. Does anyone here have an idea what may possibly cause differences in fragment depth calculations for instancing versus repeated quads?

---

<div class="post-metadata">

**Author:** ![EliasHasle](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/eliashasle/32/3010_2.png) [@EliasHasle](https://discourse.threejs.org/u/EliasHasle)\
**Post date:** [October 12, 2019, 10:33pm UTC](https://discourse.threejs.org/t/coding-jam-pixel-perfect-spheres-without-high-res-geometry/10154/3 "2019-10-12T22:33:12Z")

</div>

I perceive a small improvement on the “flickering” after increasing the camera’s near plane distance. However, the problem still persists. I no longer see a difference between the instanced and the non-instanced versions though. So I am leaning toward going instanced.

I have a hypothesis that the inconsistent depth may be a consequence of early depth tests run with interpolated depth against a depth buffer that is populated with custom depth. That explains the half-good results. I do not know of a workaround yet, except maybe upgrading to WebGL2 and seeing if layout specifiers for `gl_FragDepth` can do the trick. Then I can also test WebGL2 instancing with UBO.

Update: My source for the `layout` trick is [LearnOpenGL - Advanced GLSL](https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL) . It does not sound very promising:

> Setting the depth value by our self has a major disadvantage however, because OpenGL **disables all early depth testing** (as discussed in the [depth testing](https://learnopengl.com/#!Advanced-OpenGL/Depth-testing) tutorial) as soon as we write to gl\_FragDepth in the fragment shader. It is disabled, because OpenGL cannot know what depth value the fragment will have _before_ we run the fragment shader, since the fragment shader might completely change this depth value.
> 
> By writing to gl\_FragDepth you should take this performance penalty into consideration. **From OpenGL 4.2** however, we can still sort of mediate between both sides by redeclaring the gl\_FragDepth variable at the top of the fragment shader with a depth condition:
> 
> ```javascript
> layout (depth_<condition>) out float gl_FragDepth;
> 
> ```

So on one hand, my hypothesis cannot possibly be right if the implementation is specification-compliant, and on the other hand, if it is right, WebGL2 cannot fix it because the relevant `layout` specifier is only available in OpenGL 4.2 and above.

---

<div class="post-metadata">

**Author:** ![munrocket](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/munrocket/32/20903_2.png) [@munrocket](https://discourse.threejs.org/u/munrocket)\
**Post date:** [October 13, 2019, 10:29am UTC](https://discourse.threejs.org/t/coding-jam-pixel-perfect-spheres-without-high-res-geometry/10154/4 "2019-10-13T10:29:44Z")

</div>

It can be some generative volume filling pattern with spheres.

---

<div class="post-metadata">

**Author:** ![EliasHasle](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/eliashasle/32/3010_2.png) [@EliasHasle](https://discourse.threejs.org/u/EliasHasle)\
**Post date:** [October 13, 2019, 1:08pm UTC](https://discourse.threejs.org/t/coding-jam-pixel-perfect-spheres-without-high-res-geometry/10154/5 "2019-10-13T13:08:14Z")

</div>

What do you mean? Are you talking about an application of the quad shader?

---

<div class="post-metadata">

**Author:** ![munrocket](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/munrocket/32/20903_2.png) [@munrocket](https://discourse.threejs.org/u/munrocket)\
**Post date:** [October 13, 2019, 1:32pm UTC](https://discourse.threejs.org/t/coding-jam-pixel-perfect-spheres-without-high-res-geometry/10154/6 "2019-10-13T13:32:19Z")

</div>

![c5095b3b22e5cef0bacadf2924c8a056](https://canada1.discourse-cdn.com/flex035/uploads/threejs/original/2X/8/86a747d2057fc142bfbd41cac5d1f0242993bace.jpeg) just an idea

---

<div class="post-metadata">

**Author:** ![EliasHasle](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/eliashasle/32/3010_2.png) [@EliasHasle](https://discourse.threejs.org/u/EliasHasle)\
**Post date:** [October 13, 2019, 2:08pm UTC](https://discourse.threejs.org/t/coding-jam-pixel-perfect-spheres-without-high-res-geometry/10154/7 "2019-10-13T14:08:31Z")

</div>

Ideas are welcome! 🙂

And, as always, bugs too! 😉

That idea may be realized by many iterations of a fake physical simulation with “gravity”, damping and (soft) intersection constraints between particles, as well as coloring depending on position (or particle density and color correlated). Typically accelerated with a spatial index.

I also have my own application with [bouncing balls](https://eliashasle.github.io/bouncing_points_spatial_hashing.html), which will benefit from nicer shading. In that one I am also planning to make the collisions exact, and GPU-accelerate collision-free transitions.

I don’t feel that the spheres are quite ready for applications yet, though. First I want them to be bug-free and bound tighter by the quads (requiring understanding of problem 2 above). Also, I will need to pack them into a reusable module with a nice API (easy).

---

<div class="post-metadata">

**Author:** ![Fyrestar](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/fyrestar/32/52793_2.png) [@Fyrestar](https://discourse.threejs.org/u/Fyrestar)\
**Post date:** [October 13, 2019, 5:25pm UTC](https://discourse.threejs.org/t/coding-jam-pixel-perfect-spheres-without-high-res-geometry/10154/8 "2019-10-13T17:25:02Z")

</div>

Wasn’t the context this [topic](https://discourse.threejs.org/t/how-to-animate-thousands-of-spheres-with-the-best-performance/9441)? 😄

The technique itself is a little case depended and without more expensive raymarching on distance field based data restricted to basic shapes. Another issue comes with depth like in your demo with very dense distributed shapes, without altering depth they won’t naturally intersect with each other in the depth buffer.

For the API i would recommend just transforming the inputs to work like actual geometries like i mentioned there:

> [@How to animate thousands of spheres with the best performance?](https://discourse.threejs.org/t/how-to-animate-thousands-of-spheres-with-the-best-performance/9441/6):
>
> Billboards can respect light, i use billboards with g-buffer and volumetric impostors with the same features as regular geometry. Spheres are especially easy to make as billboards instead actual geometry, shadows only require a depth buffer rendered by the light camera, receiving the shadow requires to translate the sphere position instead the billboard quad.

I’d give more feedback but im sick and my cough makes typing be like typing in a launching space rocket. 🤒

---

<div class="post-metadata">

**Author:** ![EliasHasle](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/eliashasle/32/3010_2.png) [@EliasHasle](https://discourse.threejs.org/u/EliasHasle)\
**Post date:** [October 13, 2019, 9:27pm UTC](https://discourse.threejs.org/t/coding-jam-pixel-perfect-spheres-without-high-res-geometry/10154/9 "2019-10-13T21:27:49Z")

</div>

> [@Fyrestar](#):
>
> Wasn’t the context this [topic](https://discourse.threejs.org/t/how-to-animate-thousands-of-spheres-with-the-best-performance/9441)? 😄

I had forgotten about that topic. Certainly one of the more interesting threads. 😄

> [@Fyrestar](#):
>
> without altering depth they won’t naturally intersect with each other in the depth buffer.

I do use the fragment depth extension, but it doesn’t seem to be 100% reliable.

> [@Fyrestar](#):
>
> For the API i would recommend just transforming the inputs to work like actual geometries like i mentioned there:

Ehm, “working like actual geometries” sounds very ambitious. I focus only on making spheres as good as possible. 😉 I am thinking of something like:

```javascript
class Quads extends Mesh {}

class Spheres extends class Quads {
    constructor(count, position, radius, color) {
        //All optional except count and maybe position.
        //radius and color can be single or array
    }
    raycast //custom, based on mathematical spheres
    etc., etc.
}

```

> [@Fyrestar](#):
>
> I’d give more feedback but im sick and my cough makes typing be like typing in a launching space rocket. 🤒

I must admit I am always a little _fyrestarstruck_ by your comments. Hope you will get well soon!

---

<div class="post-metadata">

**Author:** ![TiagoCavalcanteTrindade](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/tiagocavalcantetrindade/32/19347_2.png) [@TiagoCavalcanteTrindade](https://discourse.threejs.org/u/TiagoCavalcanteTrindade)\
**Post date:** [January 3, 2022, 1:00pm UTC](https://discourse.threejs.org/t/coding-jam-pixel-perfect-spheres-without-high-res-geometry/10154/10 "2022-01-03T13:00:02Z")

</div>

@EliasHasle you do believe this is faster than IntancedBufferGeometry + SphereGeometry?

---

<div class="post-metadata">

**Author:** ![EliasHasle](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/eliashasle/32/3010_2.png) [@EliasHasle](https://discourse.threejs.org/u/EliasHasle)\
**Post date:** [January 3, 2022, 11:03pm UTC](https://discourse.threejs.org/t/coding-jam-pixel-perfect-spheres-without-high-res-geometry/10154/11 "2022-01-03T23:03:19Z")

</div>

Update: This comment was written too long after I last worked on the project/experiment, and it looks like I did not remember everything correctly. Sorry.

I haven’t properly simplified the shader to optimize for speed (Update: or have I? I will have to check), but it can be roughly compared to a bunch of sphere geometries with Phong material. A fair comparison is difficult, since depth ordering (per pixel, i.e. depth testing, intersections) will more often fail with “point spheres” (Update: Marked as “fixed” in last version of the original post), whereas pixel-perfect shape and normals will fail for mesh spheres (or require very high resolution meshes). I have not made any tests to compare performance, and the results could depend on hardware, drivers etc… (Update: And possibly usage, if any extra effort is put into ordering by depth, to resolve any remaining depth issues, e.g. when trying to introduce anti-aliasing using the alpha channel.)
