p5.js 3D graphics-cube

Introduction to this article

Bringing the awkward monkey, I’m the director of the Moral Education Department

I have written several p5.js articles before, but they have not yet touched upon 3D graphics, but in fact p5.js provides basic 3D graphics.

This article will start with the simplest cube, and make a few small demos to help fellow workers master the usage of the cube.

Basic usage of cube

Use the box() method in p5.js to create a cube.

Basic Grammar Instructions

According to the instructions on the official website, the syntax of box() is as follows:

box([width], [height], [depth], [detailX], [detailY])

I think the parameter explanation given on the official website is a bit confusing. The following is my understanding.

  • width: The width of the cube (optional), the default value is 50.
  • height: The height of the cube (optional).
  • depth: The depth of the cube (optional).
  • detailX: A number used to specify the subdivision level of the cube in the x-axis direction. The larger the value, the smoother the surface of the cube. (optional)
  • detailY: A number used to specify the subdivision level of the cube in the y-axis direction. The larger the value, the smoother the surface of the cube. (optional)

First, you need to understand the three parameters width, height and depth. They are all optional parameters. The following situations will occur when passing parameters. :

  1. When none of the three parameters are passed: their values default to 50.
  2. When only width is passed: height and depth will use the value of width.
  3. When width and height are passed: depth will use the value of height.
  4. All three parameters are passed: each uses its own value.

Try it yourself

First try creating a base cube.

file

<script>
  function setup() {
    createCanvas(200, 200, WEBGL) // Create a 200 * 200 canvas
    background(200) //Set the canvas background color (gray)
    box(100) //Create a cube
  }
</script>

In this example, the cube created using box() does not look like a cube, but a plane. The main reason is that we are facing it directly, so we can only see one side of it.

Rotate the angle and you will see that it is a cube.

file

<script>
  function setup() {
    createCanvas(200, 200, WEBGL) // Create a 200 * 200 canvas
    background(200) //Set the canvas background color (gray)
    // Rotation angle
    rotateX(10)
    rotateY(10)
    box(100) //Create a cube
  }
</script>

Set style

To set the style for the cube, write the style function before box()! ! !

fill color fill

Use the fill() method to set the fill color.

file

<script>
  function setup() {
    createCanvas(200, 200, WEBGL) // Create a 200 * 200 canvas
    background(200) //Set the canvas background color (gray)

    // Rotation angle
    rotateX(10)
    rotateY(10)

    // fill color
    fill(255, 0, 0)

    box(100) //Create a cube
  }
</script>

Do not use fill color

The default fill color of box() is white. If you don’t need the fill color, you can use the noFill() method to modify it.

file

<script>
  function setup() {
    createCanvas(200, 200, WEBGL) // Create a 200 * 200 canvas
    background(200) //Set the canvas background color (gray)

    // Rotation angle
    rotateX(10)
    rotateY(10)

    // Do not use fill color
    noFill()

    box(100) //Create a cube
  }
</script>

stroke color stroke

Use the stroke() method to set the cube’s stroke color.

file

<script>
  function setup() {
    createCanvas(200, 200, WEBGL)
    background(200)

    noFill()
    stroke(255, 0, 0) //Set the red border color

    rotateX(10)
    rotateY(10)

    box(100)
  }
</script>

Set border width

Use the strokeWeight() method to set the cube border width, and you need to pass in a numeric data.

file

<script>
  function setup() {
    createCanvas(200, 200, WEBGL)
    background(200)

    noFill()
    stroke(255, 0, 0) //Set the red border color
    strokeWeight(4) //Set the border width

    rotateX(10)
    rotateY(10)

    box(100)
  }
</script>

Texture

In addition to basic fill and stroke, the cube can also be textured.

Textures can be pictures or videos. Let me first use picture resources as an example.

Loading resources needs to be processed in the preload() life cycle. I mentioned it in “Introduction to p5.js Lightspeed”. Workers who have forgotten this knowledge point can take a look.

To attach the texture to the cube, there are the following steps:

  1. Load texture resources (images or videos)
  2. Set texture
  3. Create cube

file

<script>
  let myTexture = null

  function preload() {
    myTexture = loadImage('texture.gif') // Load texture
  }

  function setup() {
    createCanvas(200, 200, WEBGL)
    background(200)

    rotateX(0.5)
    rotateY(0.5)

    texture(myTexture) //Set texture

    box(100) //Create a cube
  }
</script>

In this example, I loaded a gif texture, but this texture will not move when attached to the cube, because the cube is created in setup(), and it will move if needed. We The texture needs to be set and the cube created in the draw() statement cycle. I will discuss this part later in the “Animation” chapter.

Lighting Effect

You read that right, p5.js also provides lighting effects. I have not talked about lighting effects in previous articles, and I will not talk about this part in this article (I will leave it to the next article) Talk), but workers can also understand this part first.

I used ambientLight() and directional light directionalLight() to hit the cube.

file

<script>
  function setup() {
    createCanvas(200, 200, WEBGL)
    background(200)

    rotateX(10)
    rotateY(10)

    ambientLight(255) //Set ambient lighting
    directionalLight(255, 255, 255, 0, 0, -1) // Set directional lighting

    box(100)
  }
</script>

It can be seen that the colors on different sides are slightly different.

animation

Animation is very simple, just change the properties of the cube in the draw() life cycle.

In addition, we also need to understand frameCount, which is a global system variable provided by p5.js, which records p5.js How many frames have been run. During setup(), the value of frameCount is 0, and then frameCount will be given every time draw() is executed. plus 1.

Workers who are still unclear about the life cycle of draw() must read the “drap” chapter of “Introduction to p5.js Lightspeed”.

Rotation animation

For example, if you want to do a rotation animation, just keep changing rotateX, rotateY or rotateX in draw() Produce a good effect.

file

<script>
  function setup() {
    createCanvas(200, 200, WEBGL)
  }

  function draw() {
    // The canvas color must be refilled every time it is refreshed, otherwise the last drawn cube will be left behind.
    background(200)

    // rotate
    rotateX(frameCount * 0.01)
    rotateY(frameCount * 0.01)
    rotateZ(frameCount * 0.01)

    // Draw the cube
    box(100)
  }
</script>

gif sticker

In the previous texture example, we already know how to paste the image. If you paste a gif animation and want the image to really move at runtime, you need to set it in draw() Texture mapped.

file

<script>
  let myTexture = null

  function preload() {
    myTexture = loadImage('texture.gif') // Load gif
  }

  function setup() {
    createCanvas(200, 200, WEBGL)
  }

  function draw() {
    background(200)

    rotateX(frameCount * 0.01)
    rotateY(frameCount * 0.01)
    rotateZ(frameCount * 0.01)

    //Set texture map
    texture(myTexture)
    box(100)
  }
</script>

Video texture

Setting video texture is actually similar to setting image texture, except that the type of resource loaded is different.

Use the createVideo() method to load video resources, and then hide the video, otherwise it will take up space on the page.

file

<script>
let video = null

function preload() {
  video = createVideo('video.mp4') // Load mp4
  video.hide() // Hide video element
}

function setup() {
  createCanvas(640, 480, WEBGL)
  video.loop() // loop playback
  video.volume(0) //Set the volume
}

function draw() {
  background(200)

  rotateX(frameCount * 0.01)
  rotateY(frameCount * 0.01)

  //Set video texture
  texture(video)
  box(200)
}
</script>

The above code still uses the video.loop() and video.volume() methods.

  • video.loop(): loop playback.
  • video.volume(): Set the video volume, the value range is 0 ~ 1.

Small Case

p5.js is an art-oriented canvas library. We have mastered the basic usage of box() to create a cube. Let’s understand it next. With a few small cases, you should be able to implement some special effects yourself. Very suitable for living in the Nuggets~

Case 1: Rotate Push Pop

The first case is called “Rotate Push Pop”, which is an example of processing. I converted his code to be written using p5.js.

Let me first mention the relationship between processing and p5.js: processing is written in Java, and p5.js is the JS version of processing.

If you want to learn about processing, you can find “Brother from the South”, who has written articles related to Processing.

Let’s take a look at the effect and code of this example first.

file

<script>
  let a = 0
  let offset = Math.PI / 24
  let num = 12

  function setup() {
    createCanvas(440, 460, WEBGL)
    noStroke()
  }

  function draw() {
    lights()
    background(200, 200, 200)

    for(let i = 0; i < num; i + + ) {
      // map mapping
      let gray = map(i, 0, num - 1, 0, 255)
      push()
      fill(gray)
      rotateY(a + offset * i)
      rotateX(a / 2 + offset * i)
      box(200)
      pop()
    }

    a + = 0.01
  }
</script>

This example uses the knowledge of “p5.js State Management” and “p5.js Map Mapping”. Workers can understand it by themselves first. If you don’t understand, I will leave annotations for this example in the comment area.

Case 2: Moving Cubes

file

<script>
  function setup() {
    createCanvas(400, 400, WEBGL)
  }

  let letter = [
    [0, 0, 0],
    [0, 0, 50],
    [25, 0, 25],
    [0, 50, 0],
    [0, 50, 50],
    [25, 25, 25],
    [50, 0, 0],
    [50, 0, 50],
    [50, 50, 0],
    [50, 50, 50]
  ]

  function draw() {
    background(200)
    rotateX(frameCount * 0.01)
    rotateY(frameCount * 0.01)
    for (let i = 0; i < letter.length; i + + ) {
      push()
      translate(letter[i][0], letter[i][1], letter[i][2])
      box(10)
      pop()
    }
  }
</script>

In this example, I combined the knowledge mentioned in “p5.js Transformation Operation” and “p5.js State Management”.

letter creates a bunch of coordinate points, which record the positions of the cubes. Constantly change their positions in draw().

In order to prevent the cubes from reflecting each other when translate(), you need to use push() and pop() to “isolate them from each other” “.

Case 3: Arranging Cubes

file

<script>
  function setup() {
    createCanvas(400, 400, WEBGL)
  }

  function draw() {
    background(200);
    rotateX(frameCount * 0.01);
    rotateY(frameCount * 0.01);
    for (let x = -50; x <= 50; x + = 10) {
      for (let y = -50; y <= 50; y + = 10) {
        for (let z = -50; z <= 50; z + = 10) {
          push();
          translate(x, y, z);
          box(5);
          pop();
        }
      }
    }
  }
</script>

If you understand “Case 2”, then the example “Case 3” will be relatively simple. It is a bit like the “Nine-Nine Multiplication Table” exercise we did when we first learned JS.

Case 4: Still a cube, I don’t know how to name it

Let’s compile another cube case, I’ll try my best. . .

file

<script>
  function setup() {
    createCanvas(400, 400, WEBGL)
  }

  function fractalBox(size, level) {
    box(size)
    if (level > 1) {
      level--
      push()
      translate(-size/2, -size/2, -size/2)
      fractalBox(size/2, level)
      pop()

      push()
      translate(-size/2, -size/2, size/2)
      fractalBox(size/2, level)
      pop()

      push()
      translate(-size/2, size/2, -size/2)
      fractalBox(size/2, level)
      pop()

      push()
      translate(-size/2, size/2, size/2)
      fractalBox(size/2, level)
      pop()

      push()
      translate(size/2, -size/2, -size/2)
      fractalBox(size/2, level)
      pop()

      push()
      translate(size/2, -size/2, size/2)
      fractalBox(size/2, level)
      pop()

      push()
      translate(size/2, size/2, -size/2)
      fractalBox(size/2, level)
      pop()

      push()
      translate(size/2, size/2, size/2)
      fractalBox(size/2, level)
      pop()
    }
  }

  function draw() {
    background(200)
    rotateX(frameCount * 0.01)
    rotateY(frameCount * 0.01)
    fractalBox(200, 3)
  }
</script>

I still used the method in the previous example and modified it a bit.

I really have no sense of art

“p5.js Lightspeed Introduction”

“p5.js State Management”

《p5.js How to use p5.js after installing it with npm? 》

“p5.js map mapping”

Like + Follow + Collection = Learned Code Warehouse