16

I have a vertex shader that accepts the following attributes:

  • a_posCoord: vertex position
  • a_texCoord: texture coordinate (passed to the fragment shader)
  • a_alpha: transparency factor (passed to the fragment shader)

The objects I'm rendering are all "billboards" (a pair of right triangles to make a rectangle).

I'm using a single call to glDrawArrays to render many billboards, each which may have a unique alpha value. A single billboard has 6 vertices. Here's some pseudocode to illustrate how I organize the vertex attribute buffer for a single billboard:

vertexAttributes = [

  px1,py1,pz1, // vertex 1: a_posCoord
  tx1,ty1,     // vertex 1: a_texCoord
  alpha,       // vertex 1: a_alpha

  px2,py2,pz2, // vertex 2: a_posCoord
  tx2,ty2,     // vertex 2: a_texCoord
  alpha,       // vertex 2: a_alpha

  px3,py3,pz3, // vertex 3: a_posCoord
  tx3,ty3,     // vertex 3: a_texCoord
  alpha,       // vertex 3: a_alpha

  px4,py4,pz4, // vertex 4: a_posCoord
  tx4,ty4,     // vertex 4: a_texCoord
  alpha,       // vertex 4: a_alpha

  px5,py5,pz5, // vertex 5: a_posCoord
  tx5,ty5,     // vertex 5: a_texCoord
  alpha,       // vertex 5: a_alpha

  px6,py6,pz6, // vertex 6: a_posCoord
  tx6,ty6,     // vertex 6: a_texCoord
  alpha        // vertex 6: a_alpha

  // ... Many more billboards not shown ...
];

Notice how the same alpha value is repeated 6 times, once for each vertex.

Is there a way I can specify an attribute for all 6 vertices without repeating it for each individual vertex?

Here's what I want my vertex attribute buffer to look like, in the interest of reducing the size of the buffer:

vertexAttributes = [
  px1,py1,pz1, // vertex 1: a_posCoord
  tx1,ty1,     // vertex 1: a_texCoord

  px2,py2,pz2, // vertex 2: a_posCoord
  tx2,ty2,     // vertex 2: a_texCoord

  px3,py3,pz3, // vertex 3: a_posCoord
  tx3,ty3,     // vertex 3: a_texCoord

  px4,py4,pz4, // vertex 4: a_posCoord
  tx4,ty4,     // vertex 4: a_texCoord

  px5,py5,pz5, // vertex 5: a_posCoord
  tx5,ty5,     // vertex 5: a_texCoord

  px6,py6,pz6, // vertex 6: a_posCoord
  tx6,ty6,     // vertex 6: a_texCoord

  alpha        // vertex 1-6: a_alpha

  // ... Many more billboards not shown ...
];

For what it's worth, my current solution works just fine, but makes me feel dirty. I've only just begun to get a handle on using glVertexAttribPointer and was wondering if it somehow supported something like this, or if there's a different method or technique I could use to achieve something less brute-force.


This is more specifically a WebGL question, but I'm curious about OpenGL in the general sense.

I'm aware that a geometry shader is really what I need for this particular example, but it's out of the question since they are currently unsupported in WebGL.

genpfault
  • 51,148
  • 11
  • 85
  • 139
namuol
  • 9,816
  • 6
  • 41
  • 54
  • possible duplicate of [Rendering meshes with multiple indices](http://stackoverflow.com/questions/11148567/rendering-meshes-with-multiple-indices) – Nicol Bolas Jan 05 '13 at 06:23
  • 1
    Very close to being a duplicate, indeed, but I'd like to keep it open for a few days to see if there's any other knowledge out there since my question is a little more specific and someone may have an entirely different approach I am unaware of. I'm guessing I'll have to stick with redundant data until WebGL utilizes ES3's geometry shader support. – namuol Jan 05 '13 at 08:03
  • 1
    I noticed glVertexBindingDivisor isn't part of WebGL 1.0 or ES 2.0 (which I'm using)... :( – Andy Dec 20 '13 at 17:54

5 Answers5

9

A vertex is not a position A vertex is a long vector consisting of multiplie attributes. Change one single attribute and you end up with a different vertex. So no, you can not use a single vertex attribute for multiple vertices, because that makes no sense semantically.

However what is possible with newer versions of OpenGL is setting the rate at which a certain vertex attribute's buffer offset advances. Effectively this means that the data for a given vertex array is virtually "replicated" into n vertex invocations before the buffer offset for a attribute advances. The function to set this divisor is glVertexAttribDivisor. In your case you'd set a Binding Divisor of 6 for the alpha array. But it's important that this does not use a single attribute for multiple vertices, but it makes OpenGL do that duplication you were doing for you.

datenwolf
  • 159,371
  • 13
  • 185
  • 298
  • 1
    I think it's also important to note that this duplication happens only during rendering, so alpha values stored with a binding divisor of 6 would use less memory. (Unless I gravely misunderstand this feature!) – Andy Dec 03 '13 at 17:57
  • What is the downside of OpenGL doing the duplication instead of using single attribute for multiple vertices? Does this mean that the vertex buffer on the GPU is somehow expanded and the memory is not saved? If not, then what's the observable difference? – Ruslan Oct 17 '17 at 15:01
  • 7
    "Effectively this means that the data for a given vertex array gets duplicated to n vertices before the buffer offset for a attribute advances." This is not true, the divisor only works on instance indices, not on vertex indices. One could solve the particular issue of the OP with instancing each billboard, but without explicitely mentioning instancing in the answer, it is [quite misleading](https://stackoverflow.com/q/50650457/2327517). – derhass Jun 01 '18 at 21:02
1

I haven't looked into this in detail but I think you could possibly also use glDrawElementsInstanced. With this you would put your alpha values into a big uniform array and the instance index would somehow be passed to the shader. Check this out:

http://sol.gfxile.net/instancing.html

Andy
  • 7,885
  • 5
  • 55
  • 61
0

If some vertex attribute is constant for each vertex, why don't you use an uniform variable? If it is constant, it is uniform! By this way you set the uniform value only once and your data set is reduced.

Luca
  • 11,646
  • 11
  • 70
  • 125
  • 1
    This wont work because I'm only calling `glDrawArrays` once for the entire buffer object which contains _many_ billboards, each of which has its own alpha value. – namuol Jan 05 '13 at 07:55
  • That was not clear to me. Indeed I think you're out of luck! Sorry. – Luca Jan 05 '13 at 08:03
  • Yup -- after rereading my question I realized I didn't emphasize the important parts. Thanks nonetheless! – namuol Jan 05 '13 at 08:04
0

I achieve this effect with instanced rendering. glVertexAttribDivisor(1); makes openGL read vertex attribute once per instance. If your bullboards have different geometry you may consider passing you model matrix as an attribute the same way as alpha.

Oleg Titov
  • 1,100
  • 1
  • 8
  • 13
0

A couple ideas:

1.) Bite the bullet and update the vertex buffer but only when alphas change.

2.) Break the list of quads into batches and only rebuild batches that change..

3.) Control the number of billboards and bind an array of alpha values to a uniform... In the alpha of the vertex data, or in some unused channel/additional channel, store the index of the alpha for that billboard, in your uniform array. at least that way you're potentially only uploading n alpha values.

4.) combine 2 and 3...

I am only taking a wild guess... I'd like to see if anyone else has good solutions!

manthrax
  • 4,918
  • 1
  • 17
  • 16