When I try to run the code below the following 2 errors pop up:
WebGL: INVALID_VALUE: vertexAttribPointer: index out of range
WebGL: INVALID_VALUE: enableVertexAttribArray: index out of range
What is the problem with the code and what should I do to fix it?
let cols, rows;
let h = 600;
let w = 600;
let scl = 20;
let terrain = [];
function setup() {
createCanvas(600,600,WEBGL);
background(0);
cols = w / scl;
rows = h / scl;
for (i = 0; i <= rows*cols-1; i++){
terrain[i] = random(-10,10);
}
}
function draw() {
background(0);
stroke(255);
noFill();
translate(width/2, height/2);
rotateX(PI/3);
translate(-w/2, -h/2);
for( let y = 0; y < rows; y++){
beginShape(TRIANGLE_STRIP);
for( let x = 0; x < cols; x++){
vertex(x*scl,y*scl,terrain[y+x*rows]);
vertex(x*scl,(y+1)*scl,terrain[y+1+x*rows]);
}
endShape();
}
}
You should get into the habit of googling your errors. Googling WebGL: INVALID_VALUE: enableVertexAttribArray: index out of range returns a ton of results, including this one: beginShape should not require call to fill() in order to render
Basically, this is telling you that you don't have a fill color, so you won't be able to see the stuff you're drawing. Change the noFill() line to something like fill(255, 0, 0) to see the stuff you're drawing.
Btw, in the future please try to narrow your problem down to a MCVE. You'll often figure out the problem yourself in the process of coming up with a small example, like in this case narrowing the problem down to the noFill() call. Good luck.
Related
I made a project called "pixel paint" by javascript with p5js library, but when I run it, that project ran too slow. I don't know why and how to make it run faster. And here is my code:
let h = 40, w = 64;
let checkbox;
let scl = 10;
let painting = new Array(h);
let brush = [0, 0, 0];
for(let i = 0; i < h; i++) {
painting[i] = new Array(w);
for(let j = 0; j < w; j++) {
painting[i][j] = [255, 255, 255];
}
}
function setup() {
createCanvas(w * scl, h * scl);
checkbox = createCheckbox('Show gird line', true);
checkbox.changed(onChange);
}
function draw() {
background(220);
for(let y = 0; y < h; y++) {
for(let x = 0; x < w; x++) {
fill(painting[y][x]);
rect(x * scl, y * scl, scl, scl);
}
}
if(mouseIsPressed) {
paint();
}
}
function onChange() {
if (checkbox.checked()) {
stroke(0);
} else {
noStroke();
}
}
function paint() {
if(mouseX < w * scl && mouseY < h * scl) {
let x = floor(mouseX / scl);
let y = floor(mouseY / scl);
painting[y][x] = brush;
}
}
<!--Include-->
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.min.js"></script>
Is there a solution to make my project run faster?
The code you have is easy to read.
It might not be worth optimising at this stage as it would make the code potentially needlessly more complex/harder to read and change in the future.
If you want to learn about different ways you could achieve the same thing I can provide a few ideas, though, for your particular use case int terms of performance might won't make a huge difference:
Instead of using the painting as a nested [w][h] array you could use a flat [w * h] array and use a single for loop instead of a nested for loop. This would be somewhat similar to using pixels[]. (You can convert x,y to an index (index = x + (y * width)) and the other way around(x = index % width, y = floor(index / width))
You could in theory use a p5.Image, access pixels[] to draw into it and render using image() (ideally you'd get lower level access to the WebGL renderer to enable antialiasing if it's supported by the browser). The grid itself could be a texture() for a quad where you'd use vertex() to specify not only x,y geometry positions, but also u, v texture coordinates in tandem with textureWrap(REPEAT). (I posted an older repeat Processing example: the logic is the same and syntax is almost identical)
Similar to the p5.Image idea, you can cache the drawing using createGraphics(): e.g. only update the p5.Graphics instance when the mouse is dragged, otherwise render the cached drawing. Additionally you can make use of noLoop()/loop() to control when p5's canvas gets updated (e.g. loop() on mousePressed(), updated graphics on mouseMoved(), noLoop() on mouseReleased())
There are probably other methods too.
While it's good to be aware of techniques to optimise your code,
I strongly recommend not optimising until you need to; and when you do
use a profiler (DevTools has that) to focus only the bits are the slowest
and not waste time and code readability on part of code where optimisation
wouldn't really make an impact.
I'm trying to generate a grid of points on the canvas described by vectors in order to make a flow field. I generate the vectors in a nested loop, then push them to a list, and finally attempt to draw them. However, when I attempt to draw them the .x and .y attributes aren't recognised. I think this is because the list of vectors is empty/only has one entry in it and I can't work out why. Apologies if this is a simple problem - this is my first time using javascript and p5.js. My code is shown below, it should generate a uniform grid of points.
let width = 600;
let height = 600;
let points = [];
function setup() {
createCanvas(width, height);
background(30);
let density = 50;
let spacing = width / density;
for (var x = 0; x < width; x += spacing); {
for (var y = 0; y < height; y += spacing); {
var p = createVector(x, y)
points.push(p)
}
}
}
function draw() {
noStroke();
fill(255);
for (var i = 0; i < points.length; i++); {
circle(points[i].x, points[i].y, 1);
}
}
EDIT: My code is definitely generating one vector, but only one for some reason. So I believe the issue is the for loops not executing correctly.
Your for loop syntax is incorrect. There should not be a semicolon after the closing parenthesis and the opening curly brace:
// !
for (var i = 0; i < points.length; i++); {
circle(points[i].x, points[i].y, 1);
}
You will need to fix each of your for loops.
I am using javascript with p5.js framework. I created an offscreen graphics buffer. Now I want to clear part of that buffer (so it becomes invisible again). What is the best way to do it?
Right now I can only achieve that by direct changing of the alpha value for every pixel I need.
Here is a MCVE:
// sketch.js, requires p5.js
function setup() {
createCanvas(100,100);
background(0);
let mymap = createGraphics(100,100);
mymap.fill(255);
mymap.rect(20,20,40,40);
mymap.loadPixels();
for (let i = 23; i < 37; i++) {
for (let j = 23; j < 37; j++) {
mymap.pixels[400*i+4*j+3] = 0;
}
}
mymap.updatePixels();
image(mymap,0,0);
}
Some side-notes:
1) there is a tiledmap.js library to p5.js, but I am still reading into it's source code and right now it doesn't look like they got 1 buffer for the entire tilemap.
2) there is a clear function to clear the canvas graphics, it does clear things, but it clears everything on the given buffer.
You should be able to use the set() function. More info can be found in the reference.
I would expect something like this to work:
const transparency = createGraphics(20, 20);
mymap.set(30, 30, transparency);
mymap.updatePixels();
Unfortunately, this seems to only set a single pixel. This smells like a bug to me, so I've filed this bug on GitHub.
Like you've discovered, you can also set the pixel values directly. This might be simpler than what you currently have:
let mymap;
function setup() {
createCanvas(100,100);
mymap = createGraphics(100,100);
mymap.fill(255, 0, 0);
mymap.rect(20,20,40,40);
for(let y = 30; y < 50; y++){
for(let x = 30; x < 50; x++){
mymap.set(x, y, color(0, 0, 0, 0));
}
}
mymap.updatePixels();
}
function draw() {
background(0, 255, 0);
image(mymap,0,0);
}
Note that there's a tradeoff: this code is simpler, but it's also slower.
Here's the code, below is a link to the jsfiddle.
The first row of the block isn't drawn for some reason, all these 2 dimension for loops are hard for me to wrap my head around, and can't find the reason why the block is only being drawn from the second row and not complete.
The add_block function is supposed to read array data from any block and put it on the grid where my x and y coordinates are.
If there's anyone who knows how to rotate the block, that would be cool too, i know in order to turn something +90 degrees i need to transpose and then revese each row, but it hasn't really worked in earlier tries.
I know i'm not good at explaining but i'll be sure to answer any of your questions.
Thanks in advance! I really want to have a complete picture of how arrays and double for loops interact with eachother, in my head.
var canvas = document.getElementById('c');
var ctx = canvas.getContext('2d');
canvas.width = 300;
canvas.height = 500;
var grid_columns = 10;
var grid_rows = 15;
var grid_cell_size = 10;
var grid = [];
function create_empty_grid(){
for(var i=0;i<grid_columns;i++){
grid[i] = [];
for(var j=0;j<grid_rows;j++){
grid[i][j] = 0;
}
}
// END DOUBLE FOR LOOPS
}
function clear_grid(){
for(var i=0;i<grid_columns;i++){
for(var j=0;j<grid_rows;j++){
grid[i][j] = 0;
}
}
// END DOUBLE FOR LOOPS
}
var x = 0;
var y = 0;
var w = 2;
var h = 3;
var block = [];
block[0] = [
[1,0,0,0],
[1,1,0,0],
[0,1,0,0],
[0,0,0,0]
];
function add_block(num){
var b = block[num];
for(var i=0;i<w;i++){
for(var j=0;j<h;j++){
if(i >= x && j >= y && i <= w && j <= h){
grid[i][j] = b[i][j];
}
}
}
// END DOUBLE FOR LOOPS
}
function draw(){
for(var i=0;i<grid_columns;i++){
for(var j=0;j<grid_rows;j++){
ctx.fillStyle = "black";
if(grid[i][j] === 1){
ctx.fillStyle = "red";
}else if(grid[i][j] === 0){
ctx.fillStyle = "green";
}
ctx.fillRect(i*grid_cell_size,j*grid_cell_size,grid_cell_size-1,grid_cell_size-1);
}
}
// END DOUBLE FOR LOOP
}
function update(){
}
function tick(){
clearRect(0,0,canvas.width,canvas.height);
clear_grid();
draw();
update();
}
create_empty_grid();
add_block(0);
draw();
View in jsfiddle
It seems like you're using i and j to represent "block" coordinates, and
x and y to represent "grid" coordinates. So I think this condition is wrong:
if(i >= x && j >= y && i <= w && j <= h){
grid[i][j] = b[i][j];
}
I think all you really need here is to replace the if statement with something like:
grid[i+x][j+y] = b[i][j];
But as #Arnauld pointed out, there is also a bit of confusion about whether i,j denotes "row, column" or "column, row", and it looks like your usage is opposite from the way the arrays are initialized. In other words, you're using:
grid[row][column] and b[row][column]
but the way you laid out the arrays, it needs to be
grid[column][row] and b[column][row]
So you'll need to make a few adjustments here and there to make the code do what you want.
The "b" arrays were upside-down for some reason so i changed grid[j+x][i+y] = b[j][i] to grid[j+x][i+y] = b[i][j] this displays the full block, but i still can't visualise the arrays or be able to prevent these problems in the future.
Arrays are so confusing to me.
I'll figure it out eventually i guess, but a day has to go by.
New to coding and working in p5.js here...the struggle is real. I've created a digital clock, and used an array and for loops to output the time values of the clock around it's periphery (1-12). I've also made the clock tick. I've also set it so that when the ticker passes a certain value, text next to the ticker changes from "human!" to "wear wolf!" (weird project, I know.). Each time the ticker passes the pre-set value, I've also set the code to change the time values on the clock from 1-12 to say "HOWL" instead. However, I can still see the 1-12 time value numbers behind the word "HOWL" and can't figure out how to remove them.
How do I do this? Thanks for any advice! Code is below:
//var line = line(0,0,x,y);
var radius = 110.0;
var angle = 0.0;
var x=0, y=0;
function setup() {
createCanvas(windowWidth,windowHeight);
}
function draw() {
background(255);
translate(width/2, height/2);
stroke(205,205,55);
fill(255,0,255);
ellipse(0,0,10,10);
noFill();
ellipse(0,0,250,250);
stroke(25);
var angleOffset = -1*PI/2; //create variable//
for (var i=1; i<=12; i++) { //generate 12 numbers//
angle = 2*PI*i/12 + angleOffset; //what is this? another variable? //
text(i, radius*cos(angle), radius*sin(angle));
}
//text outputs the i generated in for loop, then x and y coordinates times the angle set//
if (x < TWO_PI/2) {
//fill(10,10,124);
fill(10,105,55);
text("WEARWOLF", x+10, y+15);
var angleOffset = -1*PI/2; //create variable//
for (var i=1; i<=12; i++) { //generate 12 numbers//
angle = 2*PI*i/12 + angleOffset; //what is this? another variable? //
text("HOWL", radius*cos(angle), radius*sin(angle))
noFill();
text(i, radius*cos(angle), radius*sin(angle))
}
} else {
noFill();
text("HUMAN!", x+10, y+5);
//ellipse(0,0,250,250)
}
angle = (second() / 59.0) * TWO_PI;
// memorize this formula, it's helpful
x = cos(angle)* radius;
y = sin(angle)* radius;
stroke(255,0,255);
//draw a line from the center of our screen and as long as our radius
line(0,0,x,y);
ellipse(x,y,10,10);
}
Use proper indenting to make it more obvious what your code is doing:
var radius = 110.0;
var angle = 0.0;
var x=0, y=0;
function setup() {
createCanvas(windowWidth,windowHeight);
}
function draw() {
background(255);
translate(width/2, height/2);
stroke(205,205,55);
fill(255,0,255);
ellipse(0,0,10,10);
noFill();
ellipse(0,0,250,250);
stroke(25);
var angleOffset = -1*PI/2; //create variable//
for (var i=1; i<=12; i++) { //generate 12 numbers//
angle = 2*PI*i/12 + angleOffset; //what is this? another variable? //
text(i, radius*cos(angle), radius*sin(angle));
}
//text outputs the i generated in for loop, then x and y coordinates times the angle set//
if (x < TWO_PI/2) {
fill(10,105,55);
text("WEARWOLF", x+10, y+15);
var angleOffset = -1*PI/2; //create variable//
for (var i=1; i<=12; i++) { //generate 12 numbers//
angle = 2*PI*i/12 + angleOffset; //what is this? another variable? //
text("HOWL", radius*cos(angle), radius*sin(angle))
noFill();
text(i, radius*cos(angle), radius*sin(angle))
}
} else {
noFill();
text("HUMAN!", x+10, y+5);
//ellipse(0,0,250,250)
}
angle = (second() / 59.0) * TWO_PI;
// memorize this formula, it's helpful
x = cos(angle)* radius;
y = sin(angle)* radius;
stroke(255,0,255);
//draw a line from the center of our screen and as long as our radius
line(0,0,x,y);
ellipse(x,y,10,10);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.4.23/p5.min.js"></script>
Now check out the code in your draw() function. Notice the first for loop: you're always drawing the numbers 1-12!
Then you have an if statement that checks whether x < TWO_PI/2 (note: why not just use pi?). When that if statement evaluates to true, then you draw "HOWL" around the clock. But wait, you already drew the 1-12!
You're going to have to modify your code so that you only draw the 1-12 or "HOWL".
There are two ways to do that:
Option 1: Move the first for loop into the else{} section of your code. That way you only draw the 1-12 when you draw the `"HUMAN!" label.
Option 2: Only use a single for loop, but move your if statement inside that for loop. Here's a skeleton:
for (var i=1; i<=12; i++) {
if (x < TWO_PI/2) {
//draw HOWL
}
else{
//draw the number
}
}