wgl-shader.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471
  1. /* *
  2. *
  3. * Copyright (c) 2019-2019 Highsoft AS
  4. *
  5. * Boost module: stripped-down renderer for higher performance
  6. *
  7. * License: highcharts.com/license
  8. *
  9. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  10. *
  11. * */
  12. 'use strict';
  13. import H from '../../parts/Globals.js';
  14. import U from '../../parts/Utilities.js';
  15. var clamp = U.clamp;
  16. var pick = H.pick;
  17. /* eslint-disable valid-jsdoc */
  18. /**
  19. * A static shader mimicing axis translation functions found in parts/Axis
  20. *
  21. * @private
  22. * @function GLShader
  23. *
  24. * @param {WebGLContext} gl
  25. * the context in which the shader is active
  26. *
  27. * @return {*}
  28. */
  29. function GLShader(gl) {
  30. var vertShade = [
  31. /* eslint-disable max-len, @typescript-eslint/indent */
  32. '#version 100',
  33. '#define LN10 2.302585092994046',
  34. 'precision highp float;',
  35. 'attribute vec4 aVertexPosition;',
  36. 'attribute vec4 aColor;',
  37. 'varying highp vec2 position;',
  38. 'varying highp vec4 vColor;',
  39. 'uniform mat4 uPMatrix;',
  40. 'uniform float pSize;',
  41. 'uniform float translatedThreshold;',
  42. 'uniform bool hasThreshold;',
  43. 'uniform bool skipTranslation;',
  44. 'uniform float xAxisTrans;',
  45. 'uniform float xAxisMin;',
  46. 'uniform float xAxisMinPad;',
  47. 'uniform float xAxisPointRange;',
  48. 'uniform float xAxisLen;',
  49. 'uniform bool xAxisPostTranslate;',
  50. 'uniform float xAxisOrdinalSlope;',
  51. 'uniform float xAxisOrdinalOffset;',
  52. 'uniform float xAxisPos;',
  53. 'uniform bool xAxisCVSCoord;',
  54. 'uniform bool xAxisIsLog;',
  55. 'uniform bool xAxisReversed;',
  56. 'uniform float yAxisTrans;',
  57. 'uniform float yAxisMin;',
  58. 'uniform float yAxisMinPad;',
  59. 'uniform float yAxisPointRange;',
  60. 'uniform float yAxisLen;',
  61. 'uniform bool yAxisPostTranslate;',
  62. 'uniform float yAxisOrdinalSlope;',
  63. 'uniform float yAxisOrdinalOffset;',
  64. 'uniform float yAxisPos;',
  65. 'uniform bool yAxisCVSCoord;',
  66. 'uniform bool yAxisIsLog;',
  67. 'uniform bool yAxisReversed;',
  68. 'uniform bool isBubble;',
  69. 'uniform bool bubbleSizeByArea;',
  70. 'uniform float bubbleZMin;',
  71. 'uniform float bubbleZMax;',
  72. 'uniform float bubbleZThreshold;',
  73. 'uniform float bubbleMinSize;',
  74. 'uniform float bubbleMaxSize;',
  75. 'uniform bool bubbleSizeAbs;',
  76. 'uniform bool isInverted;',
  77. 'float bubbleRadius(){',
  78. 'float value = aVertexPosition.w;',
  79. 'float zMax = bubbleZMax;',
  80. 'float zMin = bubbleZMin;',
  81. 'float radius = 0.0;',
  82. 'float pos = 0.0;',
  83. 'float zRange = zMax - zMin;',
  84. 'if (bubbleSizeAbs){',
  85. 'value = value - bubbleZThreshold;',
  86. 'zMax = max(zMax - bubbleZThreshold, zMin - bubbleZThreshold);',
  87. 'zMin = 0.0;',
  88. '}',
  89. 'if (value < zMin){',
  90. 'radius = bubbleZMin / 2.0 - 1.0;',
  91. '} else {',
  92. 'pos = zRange > 0.0 ? (value - zMin) / zRange : 0.5;',
  93. 'if (bubbleSizeByArea && pos > 0.0){',
  94. 'pos = sqrt(pos);',
  95. '}',
  96. 'radius = ceil(bubbleMinSize + pos * (bubbleMaxSize - bubbleMinSize)) / 2.0;',
  97. '}',
  98. 'return radius * 2.0;',
  99. '}',
  100. 'float translate(float val,',
  101. 'float pointPlacement,',
  102. 'float localA,',
  103. 'float localMin,',
  104. 'float minPixelPadding,',
  105. 'float pointRange,',
  106. 'float len,',
  107. 'bool cvsCoord,',
  108. 'bool isLog,',
  109. 'bool reversed',
  110. '){',
  111. 'float sign = 1.0;',
  112. 'float cvsOffset = 0.0;',
  113. 'if (cvsCoord) {',
  114. 'sign *= -1.0;',
  115. 'cvsOffset = len;',
  116. '}',
  117. 'if (isLog) {',
  118. 'val = log(val) / LN10;',
  119. '}',
  120. 'if (reversed) {',
  121. 'sign *= -1.0;',
  122. 'cvsOffset -= sign * len;',
  123. '}',
  124. 'return sign * (val - localMin) * localA + cvsOffset + ',
  125. '(sign * minPixelPadding);',
  126. '}',
  127. 'float xToPixels(float value) {',
  128. 'if (skipTranslation){',
  129. 'return value;// + xAxisPos;',
  130. '}',
  131. 'return translate(value, 0.0, xAxisTrans, xAxisMin, xAxisMinPad, xAxisPointRange, xAxisLen, xAxisCVSCoord, xAxisIsLog, xAxisReversed);// + xAxisPos;',
  132. '}',
  133. 'float yToPixels(float value, float checkTreshold) {',
  134. 'float v;',
  135. 'if (skipTranslation){',
  136. 'v = value;// + yAxisPos;',
  137. '} else {',
  138. 'v = translate(value, 0.0, yAxisTrans, yAxisMin, yAxisMinPad, yAxisPointRange, yAxisLen, yAxisCVSCoord, yAxisIsLog, yAxisReversed);// + yAxisPos;',
  139. 'if (v > yAxisLen) {',
  140. 'v = yAxisLen;',
  141. '}',
  142. '}',
  143. 'if (checkTreshold > 0.0 && hasThreshold) {',
  144. 'v = min(v, translatedThreshold);',
  145. '}',
  146. 'return v;',
  147. '}',
  148. 'void main(void) {',
  149. 'if (isBubble){',
  150. 'gl_PointSize = bubbleRadius();',
  151. '} else {',
  152. 'gl_PointSize = pSize;',
  153. '}',
  154. // 'gl_PointSize = 10.0;',
  155. 'vColor = aColor;',
  156. 'if (skipTranslation && isInverted) {',
  157. // If we get translated values from JS, just swap them (x, y)
  158. 'gl_Position = uPMatrix * vec4(aVertexPosition.y + yAxisPos, aVertexPosition.x + xAxisPos, 0.0, 1.0);',
  159. '} else if (isInverted) {',
  160. // But when calculating pixel positions directly,
  161. // swap axes and values (x, y)
  162. 'gl_Position = uPMatrix * vec4(yToPixels(aVertexPosition.y, aVertexPosition.z) + yAxisPos, xToPixels(aVertexPosition.x) + xAxisPos, 0.0, 1.0);',
  163. '} else {',
  164. 'gl_Position = uPMatrix * vec4(xToPixels(aVertexPosition.x) + xAxisPos, yToPixels(aVertexPosition.y, aVertexPosition.z) + yAxisPos, 0.0, 1.0);',
  165. '}',
  166. // 'gl_Position = uPMatrix * vec4(aVertexPosition.x, aVertexPosition.y, 0.0, 1.0);',
  167. '}'
  168. /* eslint-enable max-len, @typescript-eslint/indent */
  169. ].join('\n'),
  170. // Fragment shader source
  171. fragShade = [
  172. /* eslint-disable max-len, @typescript-eslint/indent */
  173. 'precision highp float;',
  174. 'uniform vec4 fillColor;',
  175. 'varying highp vec2 position;',
  176. 'varying highp vec4 vColor;',
  177. 'uniform sampler2D uSampler;',
  178. 'uniform bool isCircle;',
  179. 'uniform bool hasColor;',
  180. // 'vec4 toColor(float value, vec2 point) {',
  181. // 'return vec4(0.0, 0.0, 0.0, 0.0);',
  182. // '}',
  183. 'void main(void) {',
  184. 'vec4 col = fillColor;',
  185. 'vec4 tcol;',
  186. 'if (hasColor) {',
  187. 'col = vColor;',
  188. '}',
  189. 'if (isCircle) {',
  190. 'tcol = texture2D(uSampler, gl_PointCoord.st);',
  191. 'col *= tcol;',
  192. 'if (tcol.r < 0.0) {',
  193. 'discard;',
  194. '} else {',
  195. 'gl_FragColor = col;',
  196. '}',
  197. '} else {',
  198. 'gl_FragColor = col;',
  199. '}',
  200. '}'
  201. /* eslint-enable max-len, @typescript-eslint/indent */
  202. ].join('\n'), uLocations = {},
  203. // The shader program
  204. shaderProgram,
  205. // Uniform handle to the perspective matrix
  206. pUniform,
  207. // Uniform for point size
  208. psUniform,
  209. // Uniform for fill color
  210. fillColorUniform,
  211. // Uniform for isBubble
  212. isBubbleUniform,
  213. // Uniform for bubble abs sizing
  214. bubbleSizeAbsUniform, bubbleSizeAreaUniform,
  215. // Skip translation uniform
  216. skipTranslationUniform,
  217. // Set to 1 if circle
  218. isCircleUniform,
  219. // Uniform for invertion
  220. isInverted,
  221. // Error stack
  222. errors = [],
  223. // Texture uniform
  224. uSamplerUniform;
  225. /**
  226. * Handle errors accumulated in errors stack
  227. * @private
  228. */
  229. function handleErrors() {
  230. if (errors.length) {
  231. H.error('[highcharts boost] shader error - ' + errors.join('\n'));
  232. }
  233. }
  234. /**
  235. * String to shader program
  236. * @private
  237. * @param {string} str - the program source
  238. * @param {string} type - the program type: either `vertex` or `fragment`
  239. * @returns {bool|shader}
  240. */
  241. function stringToProgram(str, type) {
  242. var t = type === 'vertex' ? gl.VERTEX_SHADER : gl.FRAGMENT_SHADER, shader = gl.createShader(t);
  243. gl.shaderSource(shader, str);
  244. gl.compileShader(shader);
  245. if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
  246. errors.push('when compiling ' +
  247. type +
  248. ' shader:\n' +
  249. gl.getShaderInfoLog(shader));
  250. return false;
  251. }
  252. return shader;
  253. }
  254. /**
  255. * Create the shader.
  256. * Loads the shader program statically defined above
  257. * @private
  258. */
  259. function createShader() {
  260. var v = stringToProgram(vertShade, 'vertex'), f = stringToProgram(fragShade, 'fragment');
  261. if (!v || !f) {
  262. shaderProgram = false;
  263. handleErrors();
  264. return false;
  265. }
  266. /**
  267. * @private
  268. */
  269. function uloc(n) {
  270. return gl.getUniformLocation(shaderProgram, n);
  271. }
  272. shaderProgram = gl.createProgram();
  273. gl.attachShader(shaderProgram, v);
  274. gl.attachShader(shaderProgram, f);
  275. gl.linkProgram(shaderProgram);
  276. if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
  277. errors.push(gl.getProgramInfoLog(shaderProgram));
  278. handleErrors();
  279. shaderProgram = false;
  280. return false;
  281. }
  282. gl.useProgram(shaderProgram);
  283. gl.bindAttribLocation(shaderProgram, 0, 'aVertexPosition');
  284. pUniform = uloc('uPMatrix');
  285. psUniform = uloc('pSize');
  286. fillColorUniform = uloc('fillColor');
  287. isBubbleUniform = uloc('isBubble');
  288. bubbleSizeAbsUniform = uloc('bubbleSizeAbs');
  289. bubbleSizeAreaUniform = uloc('bubbleSizeByArea');
  290. uSamplerUniform = uloc('uSampler');
  291. skipTranslationUniform = uloc('skipTranslation');
  292. isCircleUniform = uloc('isCircle');
  293. isInverted = uloc('isInverted');
  294. return true;
  295. }
  296. /**
  297. * Destroy the shader
  298. * @private
  299. */
  300. function destroy() {
  301. if (gl && shaderProgram) {
  302. gl.deleteProgram(shaderProgram);
  303. shaderProgram = false;
  304. }
  305. }
  306. /**
  307. * Bind the shader.
  308. * This makes the shader the active one until another one is bound,
  309. * or until 0 is bound.
  310. * @private
  311. */
  312. function bind() {
  313. if (gl && shaderProgram) {
  314. gl.useProgram(shaderProgram);
  315. }
  316. }
  317. /**
  318. * Set a uniform value.
  319. * This uses a hash map to cache uniform locations.
  320. * @private
  321. * @param name {string} - the name of the uniform to set
  322. * @param val {float} - the value to set
  323. */
  324. function setUniform(name, val) {
  325. if (gl && shaderProgram) {
  326. var u = uLocations[name] = (uLocations[name] ||
  327. gl.getUniformLocation(shaderProgram, name));
  328. gl.uniform1f(u, val);
  329. }
  330. }
  331. /**
  332. * Set the active texture
  333. * @private
  334. * @param texture - the texture
  335. */
  336. function setTexture(texture) {
  337. if (gl && shaderProgram) {
  338. gl.uniform1i(uSamplerUniform, texture);
  339. }
  340. }
  341. /**
  342. * Set if inversion state
  343. * @private
  344. * @flag is the state
  345. */
  346. function setInverted(flag) {
  347. if (gl && shaderProgram) {
  348. gl.uniform1i(isInverted, flag);
  349. }
  350. }
  351. /**
  352. * Enable/disable circle drawing
  353. * @private
  354. */
  355. function setDrawAsCircle(flag) {
  356. if (gl && shaderProgram) {
  357. gl.uniform1i(isCircleUniform, flag ? 1 : 0);
  358. }
  359. }
  360. /**
  361. * Flush
  362. * @private
  363. */
  364. function reset() {
  365. if (gl && shaderProgram) {
  366. gl.uniform1i(isBubbleUniform, 0);
  367. gl.uniform1i(isCircleUniform, 0);
  368. }
  369. }
  370. /**
  371. * Set bubble uniforms
  372. * @private
  373. * @param series {Highcharts.Series} - the series to use
  374. */
  375. function setBubbleUniforms(series, zCalcMin, zCalcMax) {
  376. var seriesOptions = series.options, zMin = Number.MAX_VALUE, zMax = -Number.MAX_VALUE;
  377. if (gl && shaderProgram && series.type === 'bubble') {
  378. zMin = pick(seriesOptions.zMin, clamp(zCalcMin, seriesOptions.displayNegative === false ?
  379. seriesOptions.zThreshold : -Number.MAX_VALUE, zMin));
  380. zMax = pick(seriesOptions.zMax, Math.max(zMax, zCalcMax));
  381. gl.uniform1i(isBubbleUniform, 1);
  382. gl.uniform1i(isCircleUniform, 1);
  383. gl.uniform1i(bubbleSizeAreaUniform, (series.options.sizeBy !== 'width'));
  384. gl.uniform1i(bubbleSizeAbsUniform, series.options
  385. .sizeByAbsoluteValue);
  386. setUniform('bubbleZMin', zMin);
  387. setUniform('bubbleZMax', zMax);
  388. setUniform('bubbleZThreshold', series.options.zThreshold);
  389. setUniform('bubbleMinSize', series.minPxSize);
  390. setUniform('bubbleMaxSize', series.maxPxSize);
  391. }
  392. }
  393. /**
  394. * Set the Color uniform.
  395. * @private
  396. * @param color {Array<float>} - an array with RGBA values
  397. */
  398. function setColor(color) {
  399. if (gl && shaderProgram) {
  400. gl.uniform4f(fillColorUniform, color[0] / 255.0, color[1] / 255.0, color[2] / 255.0, color[3]);
  401. }
  402. }
  403. /**
  404. * Set skip translation
  405. * @private
  406. */
  407. function setSkipTranslation(flag) {
  408. if (gl && shaderProgram) {
  409. gl.uniform1i(skipTranslationUniform, flag === true ? 1 : 0);
  410. }
  411. }
  412. /**
  413. * Set the perspective matrix
  414. * @private
  415. * @param m {Matrix4x4} - the matrix
  416. */
  417. function setPMatrix(m) {
  418. if (gl && shaderProgram) {
  419. gl.uniformMatrix4fv(pUniform, false, m);
  420. }
  421. }
  422. /**
  423. * Set the point size.
  424. * @private
  425. * @param p {float} - point size
  426. */
  427. function setPointSize(p) {
  428. if (gl && shaderProgram) {
  429. gl.uniform1f(psUniform, p);
  430. }
  431. }
  432. /**
  433. * Get the shader program handle
  434. * @private
  435. * @return {GLInt} - the handle for the program
  436. */
  437. function getProgram() {
  438. return shaderProgram;
  439. }
  440. if (gl) {
  441. if (!createShader()) {
  442. return false;
  443. }
  444. }
  445. return {
  446. psUniform: function () {
  447. return psUniform;
  448. },
  449. pUniform: function () {
  450. return pUniform;
  451. },
  452. fillColorUniform: function () {
  453. return fillColorUniform;
  454. },
  455. setBubbleUniforms: setBubbleUniforms,
  456. bind: bind,
  457. program: getProgram,
  458. create: createShader,
  459. setUniform: setUniform,
  460. setPMatrix: setPMatrix,
  461. setColor: setColor,
  462. setPointSize: setPointSize,
  463. setSkipTranslation: setSkipTranslation,
  464. setTexture: setTexture,
  465. setDrawAsCircle: setDrawAsCircle,
  466. reset: reset,
  467. setInverted: setInverted,
  468. destroy: destroy
  469. };
  470. }
  471. export default GLShader;