So it looks like 1) QUADS and QUAD_STRIP don’t work for some reason (use TRIANGLES and TRIANGLE_STRIP instead), and 2) the performance of numerous long lines with stroke is terrible, you can simulate this with a thin triangle strip though.

Thanks, @KumuPaul for your interest,

I must say however that I’m frustrated with P5.js’s drawing power.

Below is a very small sketch where P5.js struggles to get 8 frames per second, while P5.java easily goes to 60 f/s. When you draw lines with the line() function it even drops to just 1 frame per second, while P5.java stays within 60 f/s. Why I would need to use a TRIANGLE_STRIP?

P5.java

```
void setup() {
size(600, 600, P3D);
}
void draw() {
background(0);
translate(width/2, height/2, 0);
for (int i = 0; i < 100; i++) {
stroke(random(80, 255));
rotateX(random(PI * 2));
rotateY(random(PI * 2));
lines();
}
print(frameRate);
}
void lines() {
int y = 0;
beginShape();
for (int x = 0; x < 100; x++) {
// line(x, y, width - x, y);
vertex(x, y);
y++;
}
endShape();
}
```

P5.js

```
function setup() {
createCanvas(600, 600, WEBGL);
background(255);
}
function draw() {
background(0);
for (let i = 0; i < 100; i++) {
stroke(random(80, 255));
rotateX(random(PI * 2));
rotateY(random(PI * 2));
lines();
}
print(frameRate());
}
function lines() {
let y = 0;
beginShape();
for (let x = 0; x < 100; x++) {
// line(x, y, width - x, y);
vertex(x, y);
y++;
}
endShape();
}
```

@KumuPaul

So it’s true that QUADS and QUAD_STRIP don’t work with the WEBGL renderer, and already on 22 Mar 2020 the issue was reported, but not solved until today.

I am trying to translate another sketch now with a more defined closed shape. A nut. And it’s actually not that difficult to transform a QUAD_STRIP shape into a simple vertex() shape by repeating the ‘end’ vectors and inverting them. But still the sketch is far from running as smooth as in P5.java. Also the pointLight()'s work very differently. What can I do to improve this?

P5.js

```
const NUTS = 25;
const MIN_HEIGHT = 2;
const MAX_HEIGHT = 6;
const MIN_RADIUS = 15;
const MAX_RADIUS = 20;
const MAX_ROT = 0.01;
var nuts = [];
let l0, l1, l2;
let rx = 0,
ry = 0;
let dx = 0,
dy = 0;
let rotX = 0,
rotY = 0;
function setup() {
createCanvas(800, 700, WEBGL);
for (let i = 0; i < NUTS; i++) {
nuts[i] = new Nut();
}
l0 = createVector(-100, -100, -100);
l1 = createVector(-100, -100, -100);
l2 = createVector(-100, -100, -100);
rx = random(0, TWO_PI);
dx = random(-MAX_ROT, MAX_ROT);
ry = random(0, TWO_PI);
dy = random(-MAX_ROT, MAX_ROT);
}
function draw() {
background(0);
scale(5);
rotateX(rotX);
rotateY(-rotY);
rotateX(1.5);
lights();
pointLight(64, 64, 64, l0.x, l0.y, l0.z);
pointLight(64, 64, 64, l1.x, l1.y, l1.z);
pointLight(64, 64, 64, l2.x, l2.y, l2.z);
rx += dx;
ry += dy;
rotateX(rx);
rotateY(ry);
for (let i = 0; i < NUTS; i++) {
nuts[i].draw();
}
}
class Nut {
constructor() {
this.x = random(0);
this.y = random(0);
this.z = random(-20, 20);
this.h = random(MIN_HEIGHT, MAX_HEIGHT);
this.r0 = random(0, MIN_RADIUS);
this.r1 = this.r0 + random(MIN_RADIUS);
this.rx = 0;
this.ry = 0;
this.rz = random(0, TWO_PI);
this.dx = 0;
this.dy = 0;
this.dz = random(-MAX_ROT, MAX_ROT);
this.r = int(random(64, 256));
this.g = int(random(64, 256));
this.b = int(random(64, 256));
}
draw() {
fill(this.r, this.g, this.b);
noStroke();
this.rx += this.dx;
this.ry += this.dy;
this.rz += this.dz;
this.z = 30 * cos(this.rz);
rotateX(this.rx);
rotateY(this.ry);
rotateZ(this.rz);
this.tube(this.r0);
this.tube(this.r1);
this.side(this.z + this.h);
this.side(this.z - this.h);
}
tube(radius) {
let a = 0;
let angle = TWO_PI / 6;
for (let i = 0; i < 6; i++) {
beginShape();
vertex(radius * cos(a), radius * sin(a), this.z - this.h);
vertex(radius * cos(a), radius * sin(a), this.z + this.h);
a += angle;
vertex(radius * cos(a), radius * sin(a), this.z + this.h);
vertex(radius * cos(a), radius * sin(a), this.z - this.h);
endShape();
}
}
side(ht) {
let a = 0;
let angle = TWO_PI / 6;
for (let i = 0; i < 6; i++) {
beginShape();
vertex(this.r0 * cos(a), this.r0 * sin(a), ht);
vertex(this.r1 * cos(a), this.r1 * sin(a), ht);
a += angle;
vertex(this.r1 * cos(a), this.r1 * sin(a), ht);
vertex(this.r0 * cos(a), this.r0 * sin(a), ht);
endShape();
}
}
}
function mouseDragged() {
if (mouseY < height - 20) {
rotY -= (mouseX - pmouseX) * 0.01;
rotX -= (mouseY - pmouseY) * 0.01;
}
}
```

P5.java

```
float rotX, rotY;
int NUTS = 25;
float MIN_HEIGHT = 2;
float MAX_HEIGHT = 6;
float MIN_RADIUS = 15;
float MAX_RADIUS = 20;
float MAX_ROT = .01;
TheNut[] nuts = new TheNut[NUTS];
PVector l0, l1, l2;
float rx, ry;
float dx, dy;
void setup() {
size(1000, 750, P3D);
for (int i = 0; i < NUTS; i++) {
nuts[i] = new TheNut();
}
l0 = new PVector(random(-200, 200), random(-200, 200), random(-200, 200));
l1 = new PVector(random(-200, 200), random(-200, 200), random(-200, 200));
l2 = new PVector(random(-200, 200), random(-200, 200), random(-200, 200));
rx = random(0, TWO_PI);
dx = random(-MAX_ROT, MAX_ROT);
ry = random(0, TWO_PI);
dy = random(-MAX_ROT, MAX_ROT);
}
void draw() {
background(0);
translate(width/2, height/2);
scale(5);
rotateX(rotX);
rotateY(-rotY);
lights();
pointLight(64, 64, 64, l0.x, l0.y, l0.z);
pointLight(64, 64, 64, l1.x, l1.y, l1.z);
pointLight(64, 64, 64, l2.x, l2.y, l2.z);
rx += dx;
ry += dy;
rotateX(rx);
rotateY(ry);
for (int i = 0; i < NUTS; i++) {
nuts[i].draw();
}
}
class TheNut {
float x, y, z, h;
float r0, r1; // radii
float rx, ry, rz; // rotations
float dx, dy, dz; // deltas
int r, g, b;
TheNut() {
x = random(0);
y = random(0);
z = random(-20, 20);
h = random(MIN_HEIGHT, MAX_HEIGHT);
r0 = random(0, MIN_RADIUS);
r1 = r0 + random(MIN_RADIUS);
rx = 0;
ry = 0;
rz = random(0, TWO_PI);
dx = 0;
dy = 0;
dz = random(-MAX_ROT, MAX_ROT);
r = int(random(64, 256));
g = int(random(64, 256));
b = int(random(64, 256));
}
void draw() {
fill(r, g, b);
noStroke();
rx += dx;
ry += dy;
rz += dz;
z = 30 * cos(rz);
rotateX(rx);
rotateY(ry);
rotateZ(rz);
float angle = TWO_PI / 5;
// inner
tube(r0);
// outer
tube(r1);
// top
side(z + h);
// bottom
side(z - h);
}
void tube(float radius) {
float angle = TWO_PI / 6;
beginShape(QUAD_STRIP);
float a = 0;
for (int i = 0; i < 8; i++) {
vertex(radius * cos(a), radius * sin(a), z - h);
vertex(radius * cos(a), radius * sin(a), z + h);
a += angle;
}
endShape();
}
void side(float ht) {
float angle = TWO_PI / 6;
beginShape(QUAD_STRIP);
float a = 0;
for (int i = 0; i < 8; i++) {
vertex(r0 * cos(a), r0 * sin(a), ht);
vertex(r1 * cos(a), r1 * sin(a), ht);
a += angle;
}
endShape();
}
}
void mouseDragged() {
if (mouseY < height - 20) {
rotY -= (mouseX - pmouseX) * 0.01;
rotX -= (mouseY - pmouseY) * 0.01;
}
}
```

@KumuPaul I know I’m being insistent, but it seems that you are one of few that can help me here.

I was searching for other options to improve the sketch above, and I stumbled - on your post - commenting about p5.Geometry objects being much more efficient than drawing multiple shapes but like you said there is almost no documentation about this, so one needs to study the source code to understand it. My question is, do you think that using geometry objects will eventually run the code above similar to the speed in P5.java? Because before spending more time I’m really about to give up on WEBGL, and starting to learn GLSL to write shaders.

I’ve got it running online but on Pjs (Processing Java Mode syntax):

Still slow and not as vibrant colors as running it on Processing’s PDE itself, but much faster than your current p5js version.

As a bonus I’ve also got a “Rotating Arcs” sketch:

studio.ProcessingTogether.com/sp/pad/export/ro.9AqEIV-5Q9sCx

@GoToLoop

Thank you so much for your effort and interest. The sketch runs very well in openProcessing, and I learned a few more things from your code. It’s a pity that openProcessing’s Pjs mode only lets the function pointLight() work on basic shapes like sphere() etc. My final goal however is coding in P5.js.

As I see you are a long-time collaborator, I would like to ask you the same question as I made to @KumuPaul , because only for his contention that P5.js is superior to P5.java I didn’t give up already.

“Do you think that using geometry objects will eventually run the code above similar to the speed in P5.java?” or would you advise learning GLSL to write shaders

Actually I dunno whether p5.Geometry can pull that out. Dunno much about it.

I’d also love to learn shaders. It’s a way to directly code for the GPU.

It’s as fast as we can get for 3D stuff for sure.

Thanks. Yes, I made up my mind and I am going to dive into this. The shadertoy website has incredible examples. But at first, I also found it quite complicated, a totally different concept. But then I found this tutorial which gave me the first grip. It gives a bit of work because for every tutorial you have to change a number, but it’s easy to understand. Now I’m reading this tutorial and this Book of Shaders. Still none of these touch 3D shapes, but at least I am confident that after reading these tutorials I will get a grip on that as well and when I finish my first simple rotating nut, I will post it here.

Sorry, I haven’t been online much lately. I’ve been taking a vacation and dealing with a few other things. I have three quick thoughts, although they may not be entirely satisfactory:

- Regarding
`pointLight()`

working with geometry drawn with`beginShape()`

/`vertex()`

/`endShape()`

: In order for this to work you need to use the`normal()`

function which I am quite proud to say I contributed to p5.js (although it was a very minor change). If you don’t use this function before you call`vertex()`

then each vertex will get the default normal which is`(0, 0, 1)`

or something. - Regarding
`p5.Geometry`

being potentially as fast as Java processing’s rendering of similar models I think it should be comparable. - Regarding GLSL shaders, I think that learning shaders is a great thing to learn, and it can definitely unlock some massive performance gains in certain circumstances. However, having a solid grasp of 3d geometry and rendering pipelines is a prerequisite. For many scenarios you’ll want to transition to using custom
`p5.Geometry`

regardless, even if you then use shaders as well.

Unfortunately learning how to utilize `p5.Geometry`

is still a bit of a daunting task. I’m actively working on an in depth guide with examples which I will link to when I finish it, but I cannot promise how soon that will be. If you are interested in being able to use high performance 3d graphics with p5.js I would also suggest you support this github feature request that I supported via comment or reaction. This feature request covers making it possible to create `p5.Geometry`

using the existing p5.js 3d primitive and drawShape/endShape functions such that the existing functions can be used to create geometry that can be rendered with high performance.

Other notable shape features p5js lacks compared to Processing’s Java Mode is **loadShape()** (SVG & OBJ files) & **createShape()** (reusable geometry):

For easily transitioning Processing flavors to p5js those 2 functions above are crucial methinks.

Thanks, @KumuPaul. I am going to experiment with the normal() function although I don’t clearly understand how this would affect the pointLight() function which is I believe a camera function that shines light over the conjunction of all objects while as I understand the normal() will reflect light perpendicularly to the surface of each shape object. I clearly need to study the light functions for better understanding because they really seem to differ from java.

The normal for a vertex determines how brightly that vertex is illuminated. If the vector from the point light to the vertex is exactly opposite the normal it will get full illumination. If the normal points away from the light it will get no illumination and be black. Different types of lights have different formulas for how the vector of the light rays is calculated. You also need to consider distance falloff for point lights.

Here’s an example which uses pointLight:

Thanks for pointing those out. P5.js does have `loadModel()`

for loading 3D OBJ files. However the createShape capability does not exist and is what I would like to see added. I didn’t realize it supported 3D geometry in Java processing. I’ll have to take a look at that, because using that same API in p5.js might be a good way to go.

@KumuPaul — @GoToLoop

So I spent quite some time learning to write shaders, and I made progress, but at the same time, I realize that writing the ‘Nut sketch’ as a shader will be really difficult. So I came back to try the p5.Geometry and managed to design the ‘Nut’, and it seems really fast, but I can’t get any light function work on it decently. Without these functions working first I can’t proceed. Any hint?

Drag with mouse.

You need to define vertexNormals to get directional lighting to work. Here’s an example with some modifications to clearly demonstrate the effect:

https://editor.p5js.org/Kumu-Paul/sketches/hFpJ_8MBi

One thing I had to tweak was the order in which the vertices were listed for each face. The `computeNormals()`

function expects vertices to be listed in clockwise order (as you are facing the front of the triangle). I added some debugging display for the vertices and their normals which you can reveal by pressing the shift key.

(btw can you tell me how you get the embedded preview?)

If you want flat lighting instead of smooth/interpolated lighting (which is really better for smooth/rounded shapes where you want the triangle edges to be invisible), then you can use an alternate lighting shader like this.

Addendum: I should have also said that it is also possible to have flat-shaded faces if faces with different normals don’t share vertices. This would mean that your vertices list would contain the same vertex multiple times, but the corresponding vertexNormals would be different.

Thank you @KumuPaul So nice, exactly what I needed!

Here’s the code I used to embed this sketch:

You have to include some code though that lets the sketch only run when the mouse is over or something; otherwise, the whole page will have a lag.

Place mouse over sketch to run.

```
<iframe src="https://editor.p5js.org/J_Silva/full/u4Bpjjf4L"
width="500" height="550" allowfullscreen frameborder="0"
marginwidth="0" marginheight="0"></iframe>
```

Actually, I was working on a sketch that uses *.obj files because light functions work right out of the box on these objects. I got a link to a very nice sketch of @GreenCell which changes facial expression by blending/morphing several obj files. See here. The sketch I am translating uses at least 25 different ‘nut’ forms and sizes, so making that many different obj files is too much work. Though by using this concept, blending two files, you can draw all the different forms necessary.

However, using P5. Geometry is even better, so I started to make a ‘Nut’ class, but I’m having difficulties changing the ‘nut’ sizes for both the ‘blend obj’s’ and ‘the P5.Geometry’ sketches. The color parameter changes but the ‘nut’ forms are all the same. I guess it has to do because I’m using

canvas.createBuffers(gid, this.blendMesh);

this.canvas.drawBuffers(gid);

If you have some time please take a look. Here’s the code that runs so far.

Here’s a working example of “blending” two meshes using the formulas you had:

I thought this might interest you: Here’s an example I made for the article I’m writing. This example is designed to support variable `detailX`

/`detailY`

and leverage the built in capabilities of `computeFaces()`

and `computeNormals()`

:

Thanks, Paul. I knew I had to assign to each gid, but I tried

this.gid = ‘nut’+str(id++);

instead of

this.blendMesh.gid = ‘nut’+str(id++);

so that didn’t work. I have been ill for quite some days, but since yesterday I feel better and I decided to design “the enemy”, to look straight in its face. I used .obj files made by drawing vertexes in P5.java exporting them with a really good Processing library called OBJExport. Its not included in Processing’s “Contribution Manager”, but you can download it here. I used .obj because I thought that constructing the faces individually as I did with the ‘nut’ sketch would give tremendous work. But now I’ve read your last post, I see there is a method for it in the p5.Geometry Class to compute them. I should have taken your advice to study this Class sooner. But this will be my next step. Writing the objects of this sketch in p5.Geometry.