[圖形化] 池塘裏的祕密

基於p5.js繪製的頁面

柏林噪聲函數

用於程序模擬生成自然紋理。

柏林噪聲是一個非常強大算法,經常用於程序生成隨機內容,在遊戲和其他像電影等多媒體領域廣泛應用

柏林噪聲絕大部分應用在2維,3維層面上,但某種意義上也能拓展到4維。柏林噪聲在1維層面上可用於卷軸地形、模擬手繪線條等。
如果將柏林噪聲拓展到4維層面,以第4維,即w軸代表時間,就能利用柏林噪聲做動畫。例如,2D柏林噪聲可以通過插值生成地形,而3D柏林噪聲則可以模擬海平面上起伏的波浪。下面是柏林噪聲在不同維度的圖像以及在遊戲中的應用場景。

360度的範圍

函數繪製器

我們可以查看週期(0.8,0,0.4)這個範圍內起伏變化的

大概6.283的大致範圍

p5的噪點函數介紹

noiseDetail(x,y)

語法
noiseDetail(lod, falloff)
參數
lod 數字:噪音該使用的八度數
falloff 數字:每個八度的衰退因數

默認 noiseDetail(0.5, 0.75)

第一個八度的影響力爲 50% 。這衰退值能通過加多一個參數而改變。比如說如果衰退因數爲 0.75 那表示每個八度將會有其前者的 75% 的影響力(減少 25%),是不是摸不着頭腦

任何介於 0.0 與 1.0 的值都能被接受,不過注意高於 0.5 的值可能會造成 noise() 函數會返回大於 1.0 的值。

noise(x,y) 噪聲值

柏林噪聲與 random() 函數最主要的不同點在於前者是在一個無限的 n 維空間內定義的,這空間內每一對座標都相對於一個固定的半隨機值(noiseSedd()函數)

返回的值一定會在 0.0 至 1.0 之間

輸入座標的值並不重要,只有個別座標之間的距離需要被注意(如在循環內使用 noise() 時)。

一般來說座標之間的距離越小,生成噪聲隨機數列將會越平滑。介於 0.005-0.03 之間的距離應該適合大多數應用場合

      // 根據上面的可以知道,減少0.2
      noiseDetail(1, .8)
      // radians(deg)  角度轉成弧度
	// TWO_PI  2π
      for (let i = 0; i < TWO_PI; i += radians(1)) {
        let x = this.offset * cos(i) + this.offset
        let y = this.offset * sin(i) + this.offset
        // 由於偏移值 0.4
        // 起點
        // 0.8 0.4
        console.log(x,y,noise(x, y));

用到 p5關於api介紹

beginShape() endShape()

記錄創建形狀的頂點,停止記錄

beginShape();
vertex(20, 20);
vertex(40, 20);
vertex(40, 40);
vertex(60, 40);
vertex(60, 60);
vertex(20, 60);
endShape(CLOSE);

push(), pop()

    push(); // 啓動一個新的繪製狀態
    strokeWeight(10);
    fill(204, 153, 0);
    translate(50, 0);
    ellipse(0, 50, 33, 33); // Middle circle
    pop(); // 恢復原始狀態

radians(deg) 角度轉成弧度

map

map(值,開始,結束,開始,結束)
例如 我的鼠標x, 0到最大的寬度內,展示0-255的值
let num=map(mouseX,0,width,0,255)
看了源碼好像類似於線性插值,線性插值可以看之前的文章
(n, start1, stop1, start2, stop2)
 newval = (n - start1) / (stop1 - start1) * (stop2 - start2) + start2;

繪製具有一定隨機噪點的荷葉形狀

  const shadowColor = 'rgba(0,0,0,0.05)'
    class lotusLeaf {
    constructor(x, y, offset, scale) {
      this.x = x
      this.y = y
      this.offset = offset
      this.scale = scale
      this.color = color(71, 184, 151)
    }

    drawShape(vertices, offset, color) {
      fill(color)
      beginShape()
      vertices.map(v => vertex(v.x + offset, v.y + offset))
      endShape()
    }

    show() {
      push()
      translate(this.x, this.y)
      noiseDetail(1, .8)
      let vertices = []
      // radians(deg)  角度轉成弧度
      for (let i = 0; i < TWO_PI; i += radians(1)) {
        let x = this.offset * cos(i) + this.offset
        let y = this.offset * sin(i) + this.offset
        
        let r = 180 + map(noise(x, y), 0, 1, -this.scale, this.scale)
        console.log(r);
        let x1 = r * cos(i)
        let y1 = r * sin(i)

        vertices.push({x: x1, y: y1})
      }

      noStroke()
      //當前荷葉
      this.drawShape(vertices, 0, this.color)
      // 偏移值用於繪製,荷葉陰影
      this.drawShape(vertices, 50, shadowColor)
       // 繪製荷葉上的紋理
      vertices.map((v, index) => {
        //根莖有9個
        if ((index + 1) % 40 === 0) {
          strokeWeight(6)
          stroke(23,111,88,40)
          // x,y 是最外點, 就是圓點,當前點的距離
          line(v.x * .1, v.y * .19, v.x * .9, v.y * .86)
        }
      })
      pop()
    }

  }
function setup() {
   createCanvas(windowWidth, windowHeight)

    background(230)
   new lotusLeaf(100, 100, 0.4, 100).show()
}

function draw() {

}

我們發現陰影顯示了,其實我們應該先畫陰影再畫荷葉就不會出現這種情況,因爲陰影被荷葉覆蓋掉了

繪製漣漪

默認幀率1s 60幀

p5的知識

向量

pos=createVector(width/2,height/2)
pos.x  pos.y 這樣使用

代碼

  /*==================
  漣漪
  ===================*/
  class Ripple {
    constructor(x, y) {
      this.position = createVector(x, y)
      this.size = random(50, 100)
      // 透明度的值
      this.lifespan = 255
      this.color = color(255, 255, 255)
      this.sizeStep = random(2, 3)
      // 顯示的時間
      this.lifeStep = random(2, 10)
    }

    drawShape(x, y, offset, size, color) {
      stroke(color)
      strokeWeight(1)
      noFill()
      circle(x + offset, y + offset, size)
    }

    show() {
      // 設置顏色透明度
      this.color.setAlpha(this.lifespan)
      // 當前圓,顏色白色
      this.drawShape(this.position.x, this.position.y, 0, this.size, this.color)
      //偏移50的圓,顏色另一種
      this.drawShape(this.position.x, this.position.y, 50, this.size, color(shadowColor))
    }

    update() {
      // 圓變大
      this.size += this.sizeStep
      // 時間變短
      this.lifespan -= this.lifeStep
    }
  }
  const ripples = []
  const shadowColor = 'rgba(0,0,0,0.05)'

  function setup() {
    createCanvas(windowWidth, windowHeight)

    background(230)

  }
  function draw() {
    background(230)
      //默認幀率60, 就是每s執行兩次
    if (frameCount % 30 === 0) ripples.push(new Ripple(random(width), random(height)))

    ripples.forEach((r, i) => {
      r.update()
      r.show()
      // 如果沒有啦 再添加一個漣漪
      if (r.lifespan < 0 ) ripples.splice(i, 1)
    })
  }
  function windowResized() {
    resizeCanvas(windowWidth, windowHeight);
    background(230);
  }

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章