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

[SUGGESTION] Point Sprites #394

Open
ghost opened this issue Mar 17, 2022 · 4 comments
Open

[SUGGESTION] Point Sprites #394

ghost opened this issue Mar 17, 2022 · 4 comments

Comments

@ghost
Copy link

ghost commented Mar 17, 2022

Maybe you should make a short article about point sprites- drawing GL_POINTS, setting gl_PointSize and interpolating your texture with gl_PointCoord

@greggman
Copy link
Member

This is not a bad idea. Unfortunately POINTS in OpenGL/OpenGL ES/WebGL have incompatibilities across GPUs.

See the bottom of this article

If you're curious, here's an example of how to emulate POINTS using instancing. This emulation doesn't have the issues that POINTS has.

There's no article for WebGL1 on instanced drawing but there is for WebGL2 since it's not an extension there.

@ghost
Copy link
Author

ghost commented Mar 18, 2022

TL;DR: You can also use a transformfeedback with gl.INTERLEAVED_ATTRIBS to emit multiple vertices at a time, which is good for drawing small particles like quads.

Hi Greg, Thanks for the reply.

These past few weeks I've taken a look at the various methods of drawing quads to the screen with WebGL2/OpenGLES3. I've also run into those same issues with point sprites- namely the size limit.

I've also become quite familiar with the instanced rendering method (thanks in large part to webgl2fundamentals.org). However, there are a couple problems with instanced rendering I'd like to discuss in further detail, but I'll skip to my main point:

Yesterday I came up with a (new?) method of drawing small groups of primatives like quads and whatnot faster using transform feedbacks with gl.INTERLEAVED_ATTRIBS. Well, I have not benchmarked it yet, but it should be faster and more versatile than an equivalent instanced render, assuming you are already using transform feedbacks to write to per-instance (per-quad) data like to interpolate the quad's position or texture-coordinates.

I can send you some of the code I've been working on if you are interested, but it goes something like this:

  1. You have a particle shader program that uses a vertex shader with transform feedbacks, it takes in stuff like the position and outputs one vertex for each vertex the shape needs (for example a quad drawn with GL_TRIANGLES would need 6). you use transformfeedbackvaryings with gl.INTERLEAVED_ATTRIBS so they're packed back-to-back in the same output buffer after you do the transform feedback on a draw call like gl.drawArrays(gl.POINTS,0,<num_quads>);

  2. You configure the transformFeedbackVaryings of the particle shader program and the vertexAttribPointers (using stride and offset) of the quad-drawing shader program such that the inputs line up with the outputs properly.

  3. What you end up with is a single buffer with all 6*<num_quads> verticies lined up nicely, so you can draw them with another shader program in one fell gl.drawArrays(gl.TRIANGLES,0, 6*<num_quads>)

It gets more complicated than that if you want the particle shader to output multiple per-vertex attributes, like position and color at the same time, because you need to interleave them, but that is the basic gist of it.

It might be a bit better than instanced rendering. I've heard that instanced rendering adds an additional CPU overhead in terms of draw calls or something, and that it's really for larger models as opposed to quads. The other problem with instanced rendering is that you have to do a little more per-vertex processing (to draw a 2D quad at least). This was my motivation for using this method in the first place (for example, instead of transforming texture co-ordinates by a per-instance matrix or something, you can just put those in a static draw buffer). You can just generally make it faster by optimizing stuff that should only be calculated once per quad.

I guess the only downside is that you can't use GL_TRIANGLE_STRIP to draw a single quad like you could with instanced rendering.

Point Sprites have their advantages too, because you can run the vertex shader once per-quad without doing all the transform feedbacks calls. But they really don't outweigh the limitiations. Also, I'm told that many OpenGL implementations just process GL_POINTS as two triangles in their backend.

Sorry if this is was too wordy.

@ghost
Copy link
Author

ghost commented Mar 18, 2022

Oh, I should mention that the GL_TRIANGLE_STRIP with instanced rendering trick is just to prevent you from running the vertex shader an extra two more times per quad instance.
But using this method you still only run those same per-vertex calculatons four times in the particle shader, and then copy two of them to get a total of 6 output verts.

@greggman
Copy link
Member

Yea, you can do particles without instancing. An example is covered in this article though it may or may not be faster than instancing.

TRIANGLE_STRIP is generally slower than TRIANGLES because on several implementations it has to be emulated.

Also, you can easily do the particle calculations in the shaders for more speed though of course likely without any collisions and other limitations. That's often fine depending on your needs. The same article above shows a simple example of moving particles based on time. This article shows moving particles in the GPU.

This example shows a single shader that does time based particles where every particle has these inputs

  'attribute vec4 uvLifeTimeFrameStart; // uv, lifeTime, frameStart\n' +
  'attribute vec4 positionStartTime;    // position.xyz, startTime\n' +
  'attribute vec4 velocityStartSize;    // velocity.xyz, startSize\n' +
  'attribute vec4 accelerationEndSize;  // acceleration.xyz, endSize\n' +
  'attribute vec4 spinStartSpinSpeed;   // spinStart.x, spinSpeed.y\n' +
  'attribute vec4 orientation;          // orientation quaternion\n' +
  'attribute vec4 colorMult;            // multiplies color and ramp textures\n' +

copied from here

And things are computed based on time as in

// pp = per particle
localTime = currentTime - pp.startTime;   
lerp = localTime / pp.lifeTime;
position = pp.startPosition + (pp.velocity + pp.acceleration * localTime) * localTime;
color = mix(pp.startColor, pp.endColor, lerp)

etc.....

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

No branches or pull requests

1 participant