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: Enable rendering without vertex data. #24179

Merged
merged 15 commits into from
Oct 29, 2022
Merged

Conversation

filonik
Copy link
Contributor

@filonik filonik commented Jun 2, 2022

Fixed #19430

Description

Attempt at cleaning up pull request #19451 to make the logic/control flow easier to follow. The goal is to enable rendering procedural geometries without vertex data (using raw shader materials).

Implements the following logic:

  1. Initialize drawStart and drawEnd to 0 and Infinity.
  2. If a drawRange is specified, clamp drawStart and drawEnd to drawRange.start and drawRange.end.
  3. If a group is specified, clamp drawStart and drawEnd to group.start and group.end.
  4. If the geometry is indexed, clamp drawStart and drawEnd to 0 and index.count.
  5. If the geometry is non-indexed with a position attribute, clamp drawStart and drawEnd to 0 and position.count .

@filonik
Copy link
Contributor Author

filonik commented Jun 2, 2022

I created an example with a simple usage example of a geometry without attributes. Is there any documentation on how to add a description/screenshot? (CI fails because of it...)

@LeviPesin
Copy link
Contributor

Is there any documentation on how to add a description/screenshot?

Via the npm run make-screenshot examplename.

@filonik
Copy link
Contributor Author

filonik commented Jun 2, 2022

Via the npm run make-screenshot examplename.

I tried this, and I am getting:

ERROR! Diff wrong in 0.095 of pixels in file: webgl2_buffergeometry_no_attributes

Maybe this is platform dependent? I use some pseudo random noise in the example. It should be deterministic, but maybe the results differ between platforms.

I can move the example to a separate pull request if that would be preferable.

@filonik
Copy link
Contributor Author

filonik commented Jun 2, 2022

Via the npm run make-screenshot examplename.

I tried this, and I am getting:

ERROR! Diff wrong in 0.095 of pixels in file: webgl2_buffergeometry_no_attributes

Maybe this is platform dependent? I use some pseudo random noise in the example. It should be deterministic, but maybe the results differ between platforms.

I can move the example to a separate pull request if that would be preferable.

Ok, I swapped out the noise function in the example for a stable (yet slightly more complicated) one and CI finally seems happy.

@Mugen87
Copy link
Collaborator

Mugen87 commented Jun 3, 2022

This is a real nice refactoring! 👍

Mugen87
Mugen87 previously approved these changes Jun 3, 2022
Copy link
Collaborator

@Mugen87 Mugen87 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, let's try this!

@Mugen87
Copy link
Collaborator

Mugen87 commented Jun 3, 2022

I have noticed one thing after the approval. Is the use case mentioned in #19430 actually fixed by this PR?

It works now if you define a draw range on BufferGeometry level but not when using InstancedBufferGeometry. In this context, you only define instanceCount and not draw range. I guess we have to remove the drawCount === Infinity check otherwise the following piece of code is never reached.

const instanceCount = Math.min( geometry.instanceCount, geometry._maxInstanceCount );
renderer.renderInstances( drawStart, drawCount, instanceCount );

@filonik
Copy link
Contributor Author

filonik commented Jun 3, 2022

Hm, I would have thought that drawCount essentially works the same way for instanced geometries. I am still not sure what the expected result of passing Infinity actually is. I would have thought that both drawCount and instanceCount need to be some positive integer in order for something to be rendered. Let me investigate further...

@filonik
Copy link
Contributor Author

filonik commented Jun 3, 2022

Yeah, I think the problem in #19430 still exists, but for a reason unrelated to drawCount. It comes down to _maxInstanceCount being undefined/NaN, which results in instanceCount being NaN on this line:

const instanceCount = Math.min( geometry.instanceCount, geometry._maxInstanceCount );

Otherwise the following code from #19430 should now work fine:

https://jsfiddle.net/sjpt/Lrhjkstz/latest

My proposed solution would be to initialize _maxInstanceCount to Infinity in InstancedBufferGeometry, or if that is not an option add a check for undefined.

Edit: Yeah, I think a check for undefined is perhaps more appropriate given that _maxInstanceCount is usually computed here:

if ( object.isInstancedMesh !== true && geometry._maxInstanceCount === undefined ) {
geometry._maxInstanceCount = data.meshPerAttribute * data.count;
}

@filonik
Copy link
Contributor Author

filonik commented Jun 3, 2022

I have pushed a minimal fix which should now also fully address #19430.

@filonik
Copy link
Contributor Author

filonik commented Jun 3, 2022

Here is my updated working version of the instanced example, if desired I can also add it to the pull request:

https://gist.github.com/filonik/9ff5d59945a16785bbd524534112e3d3

@Mugen87 Mugen87 dismissed their stale review June 3, 2022 17:57

Needs more investigation.

@@ -828,7 +835,8 @@ function WebGLRenderer( parameters = {} ) {

} else if ( geometry.isInstancedBufferGeometry ) {

const instanceCount = Math.min( geometry.instanceCount, geometry._maxInstanceCount );
const maxInstanceCount = geometry._maxInstanceCount !== undefined ? geometry._maxInstanceCount : Infinity;
const instanceCount = Math.min( geometry.instanceCount, maxInstanceCount );
Copy link
Collaborator

@Mugen87 Mugen87 Jun 3, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should touch this code block in this PR.

Your example assumes you use setDrawRange() with InstancedBufferGeometry which should not be necessary. I think we should keep the assumption that Infinity means "render everything". Hence, the drawCount === Infinity definitely needs to be removed.

Copy link
Contributor Author

@filonik filonik Jun 3, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can remove the check for Infinity, but it won't fix the problem in #19430, since _maxInstanceCount will still be undefined. The problem does not come from the drawCount === Infinity check, but from this:

isNaN( Math.min( geometry.instanceCount, undefined ) )

Copy link
Contributor Author

@filonik filonik Jun 3, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure I understand the use case for an InstancedBufferGeometry where the final drawCount ends up being Infinity. (Note that I am specifically talking about the drawCount that will be passed to drawArraysInstanced.)

https://developer.mozilla.org/en-US/docs/Web/API/WebGL2RenderingContext/drawArraysInstanced

Even when you are rendering multiple instances, you need to somehow specify what range of vertices you want to render multiple instances of. The correct way of handling an InstancedBufferGeometry without attributes like in #19430 is to specify the number of vertices (drawRange.count) and the number of instances (instanceCount) as illustrated in the example. Only specifying either one of both is not enough.

Maybe there's some other use case that I am not seeing...

Copy link
Collaborator

@Mugen87 Mugen87 Jun 4, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had to debug this issue more closely to understand what's going on 😅 .

_maxInstanceCount is undefined in your example since setupVertexAttributes() in WebGLBindingStates never sets it. That makes sense since the geometry has no attributes. So you are right, the additional check is actually required.

I've also verified that Infinity is not a valid input for all WebGL drawing methods. So it's true that we can't use it as an input.

I'm feeling more confident with the PR now. The only thing that can be changed is the beginning of the drawStart/drawEnd code block:

const drawRange = geometry.drawRange;
const position = geometry.attributes.position;

let drawStart = drawRange.start * rangeFactor ;
let drawEnd = ( drawRange.start + drawRange.count ) * rangeFactor;

We can use this simplification since drawRange is always defined when using BufferGeometry. It is not a nullable property. So the respective if clause can be removed.

Copy link
Contributor Author

@filonik filonik Jun 4, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this reflects my understanding of things. Initializing drawStart/drawEnd as you suggest makes sense. I suppose in this instance I was being overly defensive, trying to account for the possibility of drawRange being set to null. If this can never happen your approach makes more sense, I will adopt it.

Otherwise, I have high confidence that the current code is correct, and that it is a simplification/improvement over the existing code. The only possible alternative that I see for checking _maxInstanceCount against undefined would be to always initialize it to Infinity in setupVertexAttributes(). However, I am not sure if that would really be an improvement...

@Mugen87 Mugen87 added this to the r142 milestone Jun 8, 2022
@mrdoob mrdoob modified the milestones: r142, r143 Jun 29, 2022
@mrdoob mrdoob modified the milestones: r143, r144 Jul 28, 2022
@mrdoob mrdoob modified the milestones: r144, r145 Aug 31, 2022
@mrdoob mrdoob modified the milestones: r145, r146 Sep 29, 2022
@Mugen87 Mugen87 modified the milestones: r146, r147 Oct 22, 2022
@Mugen87 Mugen87 changed the title WebGLRenderer: Fix issue #19430 and enable rendering without vertex data. WebGLRenderer: Enable rendering without vertex data. Oct 28, 2022
@Mugen87 Mugen87 merged commit 00c4ff6 into mrdoob:dev Oct 29, 2022
@Mugen87 Mugen87 mentioned this pull request Oct 29, 2022
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.

InstancedBufferGeometry with no used attributes not rendered
4 participants