I am trying to create a three.js ParticleSystem with the below Code.
On my Chrome installed on a Linux host this works perfectly fine, but as soon as I am trying run run it on a Windows Chrome portable i get the following WegGL error on the first rendering call:
WebGL: INVALID_OPERATION: vertexAttribPointer: no bound ARRAY_BUFFER
WebGL: INVALID_OPERATION: drawArrays: attribs not setup correctly
... snippet from the shader configuration: ...
'particle' : {
uniforms: {
now: {
type: 'f',
value: 0
},
hitstart: {
type: 'f',
value: RADIUS
},
hitend: {
type: 'f',
value: RADIUS / 10
},
texture: {
type: "t",
value: 0,
texture: THREE.ImageUtils.loadTexture( "img/particle.png" )
},
color: {
type: 'v4',
value: particleColorToVector(this.particleColor,this.particleIntensity)
}
},
attributes: {
longitude: {
type: 'f',
value: []
},
latitude: {
type: 'f',
value: []
},
longitude_d: {
type: 'f',
value: []
},
latitude_d: {
type: 'f',
value: []
},
created: {
type: 'f',
value: []
},
lifetime: {
type: 'f',
value: []
},
size: {
type: 'f',
value: []
}
}, vertexShader: [
'uniform float now;',
'uniform float hitstart;',
'uniform float hitend;',
'attribute float created;',
'attribute float lifetime;',
'attribute float longitude;',
'attribute float latitude;',
'attribute float longitude_d;',
'attribute float latitude_d;',
'attribute float size;',
'attribute float height;',
'varying vec3 vNormal;',
'varying float age;',
... Creation of the Particle System ....
function buildParticles() {
var shader = shaders['particle'];
var longs = shader.attributes.longitude.value;
var lats = shader.attributes.latitude.value;
var destx = shader.attributes.longitude_d.value;
var desty = shader.attributes.latitude_d.value;
var lifetimes = shader.attributes.lifetime.value;
var created = shader.attributes.created.value;
var sizes = shader.attributes.size.value;
particles = new THREE.Geometry();
for (var i = 0; i < particle_count;i++) {
particles.vertices.push( new THREE.Vector3(0,0,0));
longs.push(degree_to_radius_longitude(0));
lats.push(degree_to_radius_latitude(0));
created.push(tic);
destx.push(degree_to_radius_longitude(0));
desty.push(degree_to_radius_latitude(0));
lifetimes.push(0);
sizes.push(0);
}
var material = new THREE.ShaderMaterial ( {
uniforms: shader.uniforms,
attributes: shader.attributes,
vertexShader: shader.vertexShader,
fragmentShader: shader.fragmentShader,
transparent: true,
blending: THREE.AdditiveBlending,
depthTest: true
});
particleSystem = new THREE.ParticleSystem(
particles,
material
);
scene.add(particleSystem);
}
I have no idea why the VBO is not bound or cannot be bound.
The issues i found so far that were related to this topic:
https://github.com/mrdoob/three.js/issues/1985
https://github.com/mrdoob/three.js/issues/2073
But both seem not to be applicable in my case.
Any help would be highly appreciated ;)
Just for the record:
Removed the "varying vec3 vNormal" from both vertex and fragment shader as it was not used which fixed the problem.
However I am curious what makes the behaviour so different on various platforms.
Related
I am using horizontal bar chart with continuous update of series data. This is achieved successfully, but now i want these series to be sorted on data (continuously with every update of series) in desc order with animation. I mean when a bar get max value then move it to the top with animation.
how can i achieve this?
That type of functionality is not supported by default in Highcharts. Below you can find an example that shows how you can achieve the wanted result by custom code:
var options = {
chart: {
type: 'bar'
},
xAxis: {
categories: ['Cat1', 'Cat2', 'Cat3'],
},
series: [{
data: [1000, 900, 800]
}]
};
var chart = Highcharts.chart('container', options);
// Add custom data labels
chart.series[0].points.forEach(function(point, i) {
var x = chart.plotWidth - point.plotY + chart.plotLeft,
y = chart.xAxis[0].ticks[i].label.xy.y;
point.customDataLabel = chart.renderer.text(
point.y,
x,
y
)
.css({
color: '#000000',
fontSize: '14px'
})
.attr({
zIndex: 3
})
.add();
setAlign(point.customDataLabel);
});
function setAlign(label, xPos) {
var align = 'left',
bbox = label.getBBox();
if (chart.chartWidth < (xPos ? xPos : bbox.x + bbox.width) + 50) {
align = 'right';
}
label.attr({
align: align
})
}
var update = function() {
var points = chart.series[0].points;
chart.series[0].setData([Math.round(Math.random() * 1000), Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)]);
};
var rotate = function() {
var points = chart.series[0].points,
labelX,
ticks = chart.xAxis[0].ticks;
var sortedPoints = points.slice();
sortedPoints.sort(function(a, b) {
return b.y - a.y;
});
points.forEach(function(point, i) {
sortedPoints.forEach(function(sPoint, j) {
if (point === sPoint) {
labelX = chart.plotWidth - points[i].plotY + chart.plotLeft;
// Animate the column
points[i].graphic.animate({
x: points[j].shapeArgs.x
});
// Animate the label
points[i].customDataLabel.attr({
text: points[i].y
}).animate({
y: ticks[j].label.xy.y,
x: labelX
});
setAlign(points[i].customDataLabel, labelX);
// Animate the axis label
ticks[i].label.animate({
y: ticks[j].label.xy.y
});
}
});
});
};
document.getElementById("button").addEventListener("click", function() {
update();
rotate();
}, false);
Live demo: https://jsfiddle.net/BlackLabel/mg5bv3s8/
API Reference: https://api.highcharts.com/class-reference/Highcharts.SVGElement#animate
I am starting with regl and I am trying to make a small demo for plotting points in all three axis. I have used this link to start with.
In the example above the points were initialized as
const points = d3.range(numPoints).map(i => ({
x: //Code,
y: //Code,
color: [1, 0, 0],
}));
I modified it to the one below to get a spiral that goes into infinity
const points = d3.range(numPoints).map(i => ({
x: 200+radius*Math.cos(i*Math.PI/180),
y: 200+radius*Math.sin(i*Math.PI/180),
z: i,
color: [1, 0, 0],
}));
I modified the vertex shader to account for the additional axis. The following is the code which draws the points
const drawPoints = regl({
frag:`
precision highp float;
varying vec3 fragColor;
void main()
{
gl_FragColor = vec4(fragColor, 1);
}
`,
vert:`
attribute vec3 position;
attribute vec3 color;
varying vec3 fragColor;
uniform float pointWidth;
uniform float stageWidth;
uniform float stageHeight;
uniform float stageDepth;
vec3 normalizeCoords(vec3 position)
{
float x = position[0];
float y = position[1];
float z = position[2];
return vec3(2.0 * ((x / stageWidth) - 0.5),-(2.0 * ((y / stageHeight) - 0.5)),1.0 * ((z / stageDepth) - 0.0));
}
void main()
{
gl_PointSize = pointWidth;
fragColor = color;
gl_Position = vec4(normalizeCoords(position), 1.0);
}
`,
attributes:
{
position: points.map(d => [d.x, d.y, d.z]),
color: points.map(d => d.color),
},
uniforms:
{
pointWidth: regl.prop('pointWidth'),
stageWidth: regl.prop('stageWidth'),
stageHeight: regl.prop('stageHeight'),
stageDepth: regl.prop('stageDepth'),
},
count: points.length,
depth:
{
enable: true,
mask: true,
func: 'less',
range: [0, 1]
},
primitive: 'points',
});
frameLoop = regl.frame(() => {
// clear the buffer
regl.clear({
// background color (black)
color: [0, 0, 0, 1],
depth: 1,
});
drawPoints({
pointWidth,
stageWidth: width,
stageHeight: height,
});
if (frameLoop) {
frameLoop.cancel();
}
});
But the result of this is a circle which is plotted on the same plane. The third input to the position doesn't seem to have any effect. I tried interchanging the y and z values in the position and I obtained a sine curve. So the value of z is getting assigned properly. Another thing I noted is that if the value of z is zero, nothing is plotted. Any other value of z doesn't seem to produce any effect.
The reason the added z coordinate has no effect is because you currently have no concept of "depth projection" in your rendering pipeline.
Typically, you'll need to add a "projection matrix" to your rendering pipeline which will account for the z coordinate in vertices, when mapping those 3D vertex positions to your 2D screen.
You should be able to add this projection fairly simply by using something like the canvas-orbit-camera module. Once you'd added that module to your project, consider making the following adjustments to your code (see comments tagged with [Add]):
// Your init code ..
// [Add] Register camera middleware with canvas
const camera = require('canvas-orbit-camera')(canvas)
// Your init code ..
const drawPoints = regl({
frag:`
precision highp float;
varying vec3 fragColor;
void main()
{
gl_FragColor = vec4(fragColor, 1);
}
`,
vert:`
attribute vec3 position;
attribute vec3 color;
varying vec3 fragColor;
uniform float pointWidth;
uniform float stageWidth;
uniform float stageHeight;
uniform float stageDepth;
uniform mat4 proj; // [Add] Projection matrix uniform
vec3 normalizeCoords(vec3 position)
{
float x = position[0];
float y = position[1];
float z = position[2];
return vec3(2.0 * ((x / stageWidth) - 0.5),-(2.0 * ((y / stageHeight) - 0.5)),1.0 * ((z / stageDepth) - 0.0));
}
void main()
{
gl_PointSize = pointWidth;
fragColor = color;
gl_Position = proj * vec4(normalizeCoords(position), 1.0); // [Add] Multiply vertex by projection matrix
}
`,
attributes:
{
position: points.map(d => [d.x, d.y, d.z]),
color: points.map(d => d.color),
},
uniforms:
{
pointWidth: regl.prop('pointWidth'),
stageWidth: regl.prop('stageWidth'),
stageHeight: regl.prop('stageHeight'),
stageDepth: regl.prop('stageDepth'),
// [Add] Projection matrix calculation
proj: ({viewportWidth, viewportHeight}) =>
mat4.perspective([],
Math.PI / 2,
viewportWidth / viewportHeight,
0.01,
1000),
},
count: points.length,
depth:
{
enable: true,
mask: true,
func: 'less',
range: [0, 1]
},
primitive: 'points',
});
frameLoop = regl.frame(() => {
// clear the buffer
regl.clear({
// background color (black)
color: [0, 0, 0, 1],
depth: 1,
});
// [Add] Camera re computation
camera.tick()
drawPoints({
pointWidth,
stageWidth: width,
stageHeight: height,
});
if (frameLoop) {
frameLoop.cancel();
}
});
Hope this helps!
I am trying to generate a triangle which will be subdivided recursively. I am pushing the points to an array after each recursion. After getting out of the recursion I am rendering the triangle. But I am getting the following warning
[.WebGLRenderingContext-008E98C8]GL ERROR :GL_INVALID_OPERATION : glDrawArrays: attempt to access out of range vertices in attribute 0
Here is my code in javascript
var canvas=document.getElementById("painter");
var gl;
var points = [];
var NumTimesToSubdivide = 5;
try
{
gl=canvas.getContext("experimental-webgl");
}catch(e)
{
alert("WebGL not supported");
}
// Create Shaders
var shader_vertex_source="\n\
attribute vec4 vPosition;\n\
void main(void)\n\
{\n\
gl_Position=vPosition;\n\
}\n\
";
var shader_fragment_source="\n\
precision mediump float;\n\
void main(void)\n\
{\n\
gl_FragColor=vec4(1.0,0,0.0,1);\n\
}\n\
";
var vertices = [
[-1, -1],
[0, 1],
[1, -1]
];
divideTriangle( vertices[0], vertices[1], vertices[2],
NumTimesToSubdivide);
gl.viewport( 0, 0, canvas.width, canvas.height );
gl.clearColor( 0.0, .5, .5, 1.0 );
var get_shader=function(shadersource,shadertype)
{
var shader=gl.createShader(shadertype);
gl.shaderSource(shader,shadersource);
gl.compileShader(shader);
return shader;
}
var vertex_shader=get_shader(shader_vertex_source,gl.VERTEX_SHADER);
var fragment_shader=get_shader(shader_fragment_source,gl.FRAGMENT_SHADER);
var program=gl.createProgram();
gl.attachShader(program,vertex_shader);
gl.attachShader(program,fragment_shader);
gl.linkProgram(program);
gl.useProgram(program);
var buffer=gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER,buffer);
gl.bufferData(gl.ARRAY_BUFFER,new Float32Array(points),gl.STATIC_DRAW);
var position=gl.getAttribLocation(program,"vPosition");
gl.vertexAttribPointer(position,2,gl.FLOAT,false,0,0);
gl.enableVertexAttribArray(position);
console.log(points);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.POINTS,0,points.length);
function triangle( a, b, c )
{
points.push( a, b, c );
}
function divideTriangle( a, b, c, count )
{
// check for end of recursion
if ( count === 0 ) {
// console.log(a[0]+" \t"+b[0]+"\n");
triangle( a[0], a[1], b[0] );
triangle( b[1], c[0], c[1] );
}
else {
//bisect the sides
var ab = [(a[0]+b[0])/2,(a[1]+b[1])/2];
var ac = [(a[0]+c[0])/2,(a[1]+c[1])/2];
var bc = [(b[0]+c[0])/2,(b[1]+c[1])/2];
// console.log(ac[0]+" "+ac[1]+" "+"\n" )
--count;
// three new triangles
divideTriangle( a, ab, ac, count );
divideTriangle( c, ac, bc, count );
divideTriangle( b, bc, ab, count );
}
}
Here is the jsfiddle I am working with. Can anyone help me to find the problem?
I changed it to gl.drawArrays(gl.TRIANGLES,0,points.length/2); and it renders: http://jsfiddle.net/4Lbb78vs/1. The /2 is because it takes the number of unique vertices to draw and you specified that each vertex need 2 data points in gl.vertexAttribPointer(position,2,gl.FLOAT,false,0,0);
Is it possible to omit the vertical lines from the "square wave" line? I think you could call that a level line, here's an illustration:
The easiest way to achieve this look is to use a scatter chart with a custom "line" symbol:
// define a custom line symbol
Highcharts.SVGRenderer.prototype.symbols.line = function (x, y, w, h) {
return ['M', x, y, 'L', x - w * 2, y, 'M', x, y, 'L', x + w * 2, y, 'z'];
};
if (Highcharts.VMLRenderer) {
Highcharts.VMLRenderer.prototype.symbols.cross = Highcharts.SVGRenderer.prototype.symbols.cross;
}
$('#container').highcharts({
chart: {
type: 'scatter'
},
title: {
text: 'Look At Lines!!'
},
series: [{
name: 'Line Symbol',
data: [54.4, 29.9, {y: 129.2, radius: 8}, 144.0, 176.0, 135.6],
marker: {
symbol: 'line',
lineColor: null,
lineWidth: 2
}
}]
});
Note that you can adjust the length of an individual point by upping the radius.
Fiddle here.
Produces:
While above answer works fine, the issue I faced was that width of the line symbol is not the same as column width when displayed in combination with a column chart.
example image
Another option is to extend column type chart in a new chart type like so:
$(function () {
(function (H) {
var each = H.each,
pick = H.pick,
Series = H.Series,
seriesType = H.seriesType;
seriesType('floatingStep', 'column', {
fixedPointLength: 2
}, /** #lends seriesTypes.floatingStep.prototype */ {
/**
* Translate each point to the plot area coordinate system and find shape positions
*/
translate: function () {
var series = this,
chart = series.chart,
options = series.options,
dense = series.dense = series.closestPointRange * series.xAxis.transA < 2,
borderWidth = series.borderWidth = pick(
options.borderWidth,
dense ? 0 : 1 // #3635
),
yAxis = series.yAxis,
threshold = options.threshold,
translatedThreshold = series.translatedThreshold = yAxis.getThreshold(threshold),
minPointLength = pick(options.minPointLength, 5),
fixedPointLength = options.fixedPointLength,
metrics = series.getColumnMetrics(),
pointWidth = metrics.width,
seriesBarW = series.barW = Math.max(pointWidth, 1 + 2 * borderWidth), // postprocessed for border width
pointXOffset = series.pointXOffset = metrics.offset;
if (chart.inverted) {
translatedThreshold -= 0.5; // #3355
}
// When the pointPadding is 0, we want the columns to be packed tightly, so we allow individual
// columns to have individual sizes. When pointPadding is greater, we strive for equal-width
// columns (#2694).
if (options.pointPadding) {
seriesBarW = Math.ceil(seriesBarW);
}
Series.prototype.translate.apply(series);
// Record the new values
each(series.points, function (point) {
var yBottom = pick(point.yBottom, translatedThreshold),
safeDistance = 999 + Math.abs(yBottom),
plotY = Math.min(Math.max(-safeDistance, point.plotY), yAxis.len + safeDistance), // Don't draw too far outside plot area (#1303, #2241, #4264)
barX = point.plotX + pointXOffset,
barW = seriesBarW,
barY = Math.min(plotY, yBottom),
up,
barH = Math.max(plotY, yBottom) - barY;
// Handle options.minPointLength
if (Math.abs(barH) < minPointLength) {
if (minPointLength) {
barH = minPointLength;
up = (!yAxis.reversed && !point.negative) || (yAxis.reversed && point.negative);
barY = Math.abs(barY - translatedThreshold) > minPointLength ? // stacked
yBottom - minPointLength : // keep position
translatedThreshold - (up ? minPointLength : 0); // #1485, #4051
}
}
if (fixedPointLength) {
barH = fixedPointLength;
}
// Cache for access in polar
point.barX = barX;
point.pointWidth = pointWidth;
// Fix the tooltip on center of grouped columns (#1216, #424, #3648)
point.tooltipPos = chart.inverted ? [yAxis.len + yAxis.pos - chart.plotLeft - plotY, series.xAxis.len - barX - barW / 2, barH] : [barX + barW / 2, plotY + yAxis.pos - chart.plotTop, barH];
// Register shape type and arguments to be used in drawPoints
point.shapeType = 'rect';
point.shapeArgs = series.crispCol.apply(
series,
point.isNull ?
// #3169, drilldown from null must have a position to work from
// #6585, dataLabel should be placed on xAxis, not floating in the middle of the chart
[barX, translatedThreshold, barW, 0] : [barX, barY, barW, barH]
);
});
},
});
})(Highcharts);
});
View in JS Fiddle
I have the following issue:
I initialize the chart with null values because I don't have meaningful initial values.
I add points with addPoint after getting the data through ajax
The problem is that the chart shifts continuously and not just on the end of the time range.
I use the code:
generateInitialData = function() {
var initialData = [];
var time = (new Date()).getTime();
var i;
for (i = -99; i <= 0; i++) {
initialData.push([time + i * 3000,null]);
}
return initialData;
}
addPoint = function(){
var max = 1000;
var min = 300;
var value = Math.floor(Math.random() * (max - min + 1)) + min;
chart.series[0].addPoint([(new Date()).getTime(), value], true, true);
}
$('#container').highcharts({
chart: {
type: 'line',
},
xAxis: {
type: 'datetime',
minRange:1000 * 60 * 5,
maxRange:1000 * 60 * 5
}
});
var chart = $('#container').highcharts();
chart.addSeries({"name": "example","data":generateInitialData()});
setInterval(addPoint,3000);
Please see jsFiddle demo of the issue :
http://jsfiddle.net/wXmLQ/7/
Please help
Tali
You don't need to set null data from the beginning, instead of that you can just addd one empty point and just check if you want to shift or not, see: http://jsfiddle.net/Fusher/wXmLQ/8/
addPoint = function(){
var max = 1000;
var min = 300;
var value = Math.floor(Math.random() * (max - min + 1)) + min;
var len = chart.series[0].data.length;
chart.series[0].addPoint([(new Date()).getTime(), value], len < 99);
}
$('#container').highcharts({
chart: {
type: 'line',
},
xAxis: {
type: 'datetime',
minTickInterval: 1000 * 60, //one minute
minRange:1000 * 60 * 5,
maxRange:1000 * 60 * 5
}
});
var chart = $('#container').highcharts();
chart.addSeries({"name": "example","data":[[+new Date(), null]]});
setInterval(addPoint,3000);
The addPoint and addSeries methods have a re-draw parameter that defaults to true. Make this parameter false whenever you call an updating method
chart.series[0].addPoint([(new Date()).getTime(), value], false);
chart.addSeries({"name": "example","data":generateInitialData()}, false);
Then once you've made all the updates, explicitly redraw the chart
chart.redraw();