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

WebGLRenderer: Add WEBGL_clip_cull_distance support. #27371

Merged
merged 14 commits into from
Dec 15, 2023

Conversation

RenaudRohlinger
Copy link
Collaborator

@RenaudRohlinger RenaudRohlinger commented Dec 13, 2023

WEBGL_clip_cull_distance is now supported by default on the latest version of Chrome and Safari.

This PR adds support for the new WEBGL_clip_cull_distance extension. Made an example to demonstrate how to use the extension. Added the build for testing purpose. Will remove it once it's approved.

Here the live demo. Also in this example I'm tricking the renderer by enabling isPoints = true on the mesh and not specifying the gl_PointSize in the shader which seems to produce a cool bug, I think it looks cool so I would like to let it like that:
https://raw.githack.com/mrdoob/three.js/dev/examples/webgl2_clipculldistance.html
image

Copy link

github-actions bot commented Dec 13, 2023

📦 Bundle size

Full ESM build, minified and gzipped.

Filesize dev Filesize PR Diff
668.6 kB (166 kB) 668.9 kB (166.1 kB) +338 B

🌳 Bundle size after tree-shaking

Minimal build including a renderer, camera, empty scene, and dependencies.

Filesize dev Filesize PR Diff
449.5 kB (108.9 kB) 449.8 kB (109 kB) +338 B
@RenaudRohlinger RenaudRohlinger changed the title Examples: Add EXT_clip_cull_distance support Dec 13, 2023
@Mugen87
Copy link
Collaborator

Mugen87 commented Dec 13, 2023

I'm new to WEBGL_clip_cull_distance^^.

When I understand the extension correctly, it allows clipping in the vertex shader which means it is an alternative to three.js's clipping planes, right?

Do you mind explaining why gl_ClipDistance is an array? What does the distance values represent?

Also in this example I'm tricking the renderer by enabling isPoints = true on the mesh and not specifying the gl_PointSize in the shader which seems to produce a cool bug,

Even if this works would you mind not doing this 😇 ? Setting the read-only is* flags is not something that we want to promote in the examples.

@RenaudRohlinger
Copy link
Collaborator Author

Do you mind explaining why gl_ClipDistance is an array? What does the distance values represent?

It's an array to handle more complex culling I believe, for example multiple culling shapes or handling a view frustum culling directly in the vertex shader. For example:

// Define frustum planes in clip space
// Near and far planes
gl_ClipDistance[0] = gl_Position.z + 1.0; // near plane z = -1
gl_ClipDistance[1] = -gl_Position.z + 10.0; // far plane z = 10

// Left and right planes
gl_ClipDistance[2] = gl_Position.x + 5.0; // left plane x = -5
gl_ClipDistance[3] = -gl_Position.x + 5.0; // right plane x = 5

// Bottom and top planes
gl_ClipDistance[4] = gl_Position.y + 5.0; // bottom plane y = -5
gl_ClipDistance[5] = -gl_Position.y + 5.0; // top plane y = 5

Also there is gl_CullDistance available if we want to cull completely the primitive instead of just clipping the vertices of it. The documentation available indeed doesn't make things so it's mostly my understanding of the current implementation here 😄

@lexaknyazev
Copy link
Contributor

Do you mind explaining why gl_ClipDistance is an array? What does the distance values represent?

Each vertex may have up to 8 clip distance values; they are interpolated across primitives as regular varyings. During primitive rasterization, pixels that have negative clip distance values are discarded. This should be more efficient than using discard in fragment shaders.

For example, here's how you can turn a unit quad into an octagon using only a vertex shader:

void main() {
    gl_Position = vec4(a_position, 0.0, 1.0);

    gl_ClipDistance[0] = dot(gl_Position, vec4( 1,  0, 0, 0.5));
    gl_ClipDistance[1] = dot(gl_Position, vec4(-1,  0, 0, 0.5));
    gl_ClipDistance[2] = dot(gl_Position, vec4( 0,  1, 0, 0.5));
    gl_ClipDistance[3] = dot(gl_Position, vec4( 0, -1, 0, 0.5));
    gl_ClipDistance[4] = dot(gl_Position, vec4( 1,  1, 0, 0.70710678));
    gl_ClipDistance[5] = dot(gl_Position, vec4( 1, -1, 0, 0.70710678));
    gl_ClipDistance[6] = dot(gl_Position, vec4(-1,  1, 0, 0.70710678));
    gl_ClipDistance[7] = dot(gl_Position, vec4(-1, -1, 0, 0.70710678));
}
@lexaknyazev
Copy link
Contributor

Drawing points without specifying gl_PointSize is undefined and may produce inconsistent results on different platforms.

@RenaudRohlinger
Copy link
Collaborator Author

RenaudRohlinger commented Dec 14, 2023

Updated with the feedbacks.

FYI since I'm using a builtin material I attached a custom object extensions in order to make the extension work since it's only available on ShaderMaterial:

material = new THREE.MeshNormalMaterial();
material.extensions = {
	clipCullDistance: true,
};

Are you ok with this logic or do you have anything better in mind @Mugen87? For example we could move the extensions property from ShaderMaterial to Material?

@Mugen87
Copy link
Collaborator

Mugen87 commented Dec 14, 2023

Are you ok with this logic or do you have anything better in mind @Mugen87? For example we could move the extensions property from ShaderMaterial to Material?

For now, let's make an example based on ShaderMaterial. We can think of moving extensions to Material in a different PR/issue. Maybe there is also the possibility to integrate WEBGL_clip_cull_distance in the shader chunks and configure the distances with a new material property.

@Mugen87
Copy link
Collaborator

Mugen87 commented Dec 14, 2023

@RenaudRohlinger I've updated the example to ensure formatting matches all other examples 👍 .

@gkjohnson
Copy link
Collaborator

gkjohnson commented Dec 14, 2023

Beyond potential performance benefits of performing plane clipping with this extension vs using discard in the fragment shader is that it seems to enable a hardware anti-aliased edge on the clipped edges:

image

#22172 implements AA on edges using alpha to coverage but this solution would be better, I think. I'd say this should be used to implement Material.clipPlanes but I don't know if supporting Material.clipIntersection === true (ie unioned clipping planes) is possible with this technique.

@RenaudRohlinger
Copy link
Collaborator Author

For now, let's make an example based on ShaderMaterial

Updated the material with ShaderMaterial /cc @Mugen87

@RenaudRohlinger
Copy link
Collaborator Author

RenaudRohlinger commented Dec 14, 2023

@gkjohnson FYI WEBGL_clip_cull_distance is not yet supported on firefox, here to follow the current development:
https://bugzilla.mozilla.org/show_bug.cgi?id=1854001

@lexaknyazev
Copy link
Contributor

Since clipping distances are set per-vertex, each primitive gets its own clipping half-space. For instance, this could be used to make it look like clipping happens against a "sphere".

@Mugen87
Copy link
Collaborator

Mugen87 commented Dec 14, 2023

@lexaknyazev I wonder how we could integrate the extension in the material system so it can be used without writing custom shader code.

Do you think it is sufficient if an engine provides the possibility to set clipping distances on material level like with clipping planes? How common is it to define additional vertex data attributes (next to vertex normals, colors etc.) to configure clipping distances per vertex?

@lexaknyazev
Copy link
Contributor

how we could integrate the extension in the material system

It could be used for HW acceleration of regular clipping planes, not explicitly exposed to users at all. Anything beyond that would likely require a custom shader.

How common is it to define additional vertex data attributes (next to vertex normals, colors etc.) to configure clipping distances per vertex?

Never saw such usage. Clip distances are supposed to be dynamically computed akin to gl_Position.

@Mugen87
Copy link
Collaborator

Mugen87 commented Dec 14, 2023

It could be used for HW acceleration of regular clipping planes, not explicitly exposed to users at all.

That is something we could try to tackle after this PR is merged.

@Mugen87 Mugen87 added this to the r160 milestone Dec 15, 2023
@Mugen87
Copy link
Collaborator

Mugen87 commented Dec 15, 2023

@RenaudRohlinger I've simplified the example so the focus lies more on the extension handling. It is not that visually appealing like your original draft but maintenance aspect and simplicity is more important right now.

@RenaudRohlinger
Copy link
Collaborator Author

Awesome! LGTM, thanks @Mugen87

@Mugen87 Mugen87 changed the title Examples: Add WEBGL_clip_cull_distance support Dec 15, 2023
@Mugen87 Mugen87 changed the title WebGLRenderer: Add WEBGL_clip_cull_distance support Dec 15, 2023
@Mugen87 Mugen87 merged commit f765d87 into mrdoob:dev Dec 15, 2023
12 checks passed
AdaRoseCannon pushed a commit to AdaRoseCannon/three.js that referenced this pull request Jan 15, 2024
* init WEBGL_clip_cull_distance support

* remove unecessary conditions webgl2

* try fix directx

* feedbacks

* remove unecessary code

* Update webgl_clipculldistance.html

Style clean up.

* Update WebGLExtensions.js

* Update webgl_clipculldistance.html

* replace material with shader

* remove unused time const

* Update webgl_clipculldistance.html

Simplify example.

* Update clipping example.

* Revert builds.

* ShaderMaterial: Update `extensions` and docs.

---------

Co-authored-by: Michael Herzog <michael.herzog@human-interactive.org>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
4 participants