WebGL - sizes of arrays of variables for vertex shader calls

Context

I'm trying to draw bezier curves in canvas. I achieved drawing quadratic and cubic curves from inside the shader, but so far I have had a single variable for each control point.

So, I click on my canvas, add points and when I have enough (3 and 4, respectively), I draw my curve.

Now I am trying to generalize Bezier curves. Although I achieved this in order to do this on the JavaScript side, it seems to me that it would be better to do this on the shader side, as the rendering speed will be significantly increased.

So, I would like to draw my curve as soon as I have at least two points. But I could continue to add points and draw my curve, using each point as control points.

Explanation

So, I know that it is not possible to set a dynamic array in GLSL, but is it possible to dynamically declare a GLSL array based on a JS variable?

If my question is not clear (I know that I have problems formulating things right now), let me explain with an example.

uniform vec2 uMyPoints[<length>]; 

So this is what I would like to achieve, but, of course, the size of the array should be constant according to the glsl specifications.

However, from my point of view, it seems to me that I can configure length from a JS variable. My intuition is that the size of the array in GLSL would be constant during the execution time of different shaders during rendering, but could change from one rendering to another.

Question

So, my question is for you: based on these, do you know any good way or tricks to be able to set a constant in GLSL from a javascript variable?

If this is possible, it will help me a lot. Thank you for your attention.

By way of comparison: How can we set numLights "in this example from JS?

Answer

String substitution is suitable for my needs. Although this is a bit complicated, everything will be fine. Further research led me to conclude that the implicit array size is available. Open GL ES version 3, which should be used by future versions of WebGL, but not now.

On the other hand, the second sentence does not meet my needs, because I definitely wanted to avoid the presence of N points in the shader, since this number of points can change.

Thanks for the answer;)

+5
source share
1 answer

String replacement is performed

 <script id="vs" type="notjs"> uniform vec2 uMyPoints[<length>]; ... </script> 

Js

 var numPoints = 10; var vSrc = document.getElementById("vs").text; vSrc = vSrc.replace(/<length>/g, numPoints); 

These are the most complex programs for shaders. They generate shaders with string manipulations.

Of course, you can use a more convenient function for string substitution. For example, it might be something like

  /** * Replace %(id)s in strings with values in objects(s) * * Given a string like `"Hello %(name)s from $(user.country)s"` * and an object like `{name:"Joe",user:{country:"USA"}}` would * return `"Hello Joe from USA"`. * * @function * @param {string} str string to do replacements in * @param {Object|Object[]} params one or more objects. * @returns {string} string with replaced parts * @memberOf module:Strings */ var replaceParams = (function() { var replaceParamsRE = /%\(([^\)]+)\)s/g; return function(str, params) { if (!params.length) { params = [params]; } return str.replace(replaceParamsRE, function(match, key) { var keys = key.split('.'); for (var ii = 0; ii < params.length; ++ii) { var obj = params[ii]; for (var jj = 0; jj < keys.length; ++jj) { var part = keys[jj]; obj = obj[part]; if (obj === undefined) { break; } } if (obj !== undefined) { return obj; } } console.error("unknown key: " + key); return "%(" + key + ")s"; }); }; }()); 

now if you look like a shader

 uniform Lights u_lights[%(numLights)s]; uniform vec2 u_points[%(numPoints)s]; 

you can replace

 vSrc = replaceParams(vsrc, { numLights: 4, numPoints: 10, }); 

You can also use `#define in the shader

 #define NUM_LIGHTS %(numLights)s #define NUM_POINTS %(numPoints)s uniform Lights u_lights[NUM_LIGHTS]; uniform vec2 u_points[NUM_POINTS]; void main() { for (int i = 0; i < NUM_LIGHTS; ++i) { ... } } 

etc..

But honestly, most people did not pass the bezier breakpoints as uniforms, because there is a serious limitation on the number of uniforms. Most people passed bezier breakpoints in attributes. You can probably even set the pitch and offset when calling gl.vertexAttribPointer so that if your points go

  [pt0, pt1, pt2, pt3, pt4, pt5, pt6, pt7, pt8, ..] 

You can make 4 attributes

 attribute vec2 p0; attribute vec2 p1; attribute vec2 p2; attribute vec2 p3; 

And specify all of them with offset and step to set 4 attributes so that the points are pulled

 p0 = pt0, p1 = pt1, p2 = pt2, p3 = pt3, p0 = pt1, p1 = pt2, p2 = pt3, p3 = pt4, p0 = pt2, p1 = pt3, p2 = pt4, p3 = pt5, p0 = pt3, p1 = pt4, p2 = pt5, p3 = pt6, 

etc.

+2
source

Source: https://habr.com/ru/post/1234357/


All Articles