Как создать 2D развевающийся флаг в webGL? - PullRequest
0 голосов
/ 06 июля 2018

Я только что написал программу, которая реализует эффект волнения в canvas2D. Вот моя реализация:
1. Сохраните данные изображения исходного холста.
2. Рассчитайте новую позицию каждой точки и присвойте ей цвет старой позиции.
3. Увеличьте амплитуду колебаний каждой точки на расстоянии.
4. Увеличение яркости монотонно уменьшающейся области и уменьшение яркости монотонно уменьшающейся области.

var IMG_MAX_WIDTH = 600
var IMG_MAX_HEIGHT = 600
var imgWidth, imgHeight
var oImgData, imgData
var oPixels, pixels
var ctx, canvasWidth, canvasHeight

var image = new Image()
image.crossOrigin = 'anonymous'
image.src = 'https://i.imgur.com/ZKMnXce.png'

var amplitude = 15
var period = 2
var coX

image.onload = function () {

imgWidth = Math.floor(image.width)
imgHeight = Math.floor(image.height)

var canvas = document.getElementById('flagCanvas')
var scale = 1
if (imgWidth > IMG_MAX_WIDTH) {
  scale = IMG_MAX_WIDTH / imgWidth
}
if (imgHeight > IMG_MAX_HEIGHT) {
  scale = scale * IMG_MAX_HEIGHT / imgHeight
}

canvasWidth = imgWidth
canvasHeight = imgHeight + amplitude * 2
canvas.width = canvasWidth
canvas.height = canvasHeight
canvas.style.transform = 'translate3d(-50%,-50%,0) scale(' + scale + ')'

// offscreenCtx = offscreenCanvas.getContext('2d')
ctx = canvas.getContext('2d')
ctx.drawImage(image, 0, amplitude, imgWidth, imgHeight)
imgData = ctx.getImageData(0, 0, canvasWidth, canvasHeight)
pixels = imgData.data

oImgData = ctx.createImageData(canvasWidth, canvasHeight)
oPixels = pixels.slice()
oImgData.data = oPixels

coX = 2 * Math.PI / (imgWidth / period)

tick()
}

var stop = false
var timeNow = Date.now()
var timeLast = timeNow
var delta = 0
var interval
var fps = 70

var offset = 10

interval = 1000 / fps

var tick = function () {
if (stop) return false
timeNow = Date.now()
delta = timeNow - timeLast
if (delta > interval) {
  timeLast = timeNow

  ctx.clearRect(0, 0, canvasWidth, canvasHeight)
  var y0 = amplitude * (1 / imgWidth) * Math.sin(timeNow / 200)
  var yBuf = new Array(canvasWidth)
  var lastY = 0
  var r
  var g
  var b
  var a
  var oR
  var oG
  var oB
  var oA
  for (var i = 0; i < canvasHeight; i++) {
    for (var j = 0; j < canvasWidth; j++) {
      if (i === 0) {
        yBuf[j] = amplitude * (j / imgWidth) * Math.sin(j * coX - timeNow / 200) + y0
      }
      r = (i * canvasWidth + j) * 4
      g = r + 1
      b = r + 2
      a = r + 3
      oR = r + (~~(0.5 + yBuf[j])) * canvasWidth * 4
      oG = oR + 1
      oB = oR + 2
      oA = oR + 3
      offset = j === 0 ? 0 : (yBuf[j] - lastY) * 100
      pixels[r] = oPixels[oR] + offset
      pixels[g] = oPixels[oG] + offset
      pixels[b] = oPixels[oB] + offset
      pixels[a] = oPixels[oA]
      lastY = yBuf[j]
    }
  }
  ctx.putImageData(imgData, 0, 0)
}
requestAnimationFrame(tick)
}
* {
    margin: 0;
    padding: 0;
}

html, body {
    width: 100%;
    height: 100%;
}

body {
    position: relative;
    background: lightgrey;
}

#flagCanvas {
    position: absolute;
    top: 50%;
    left: 50%;
    transform-origin: center;
    transform: translate3d(-50%, -50%, 0);
}
<canvas id="flagCanvas"></canvas>

Хотя после этого я обнаружил, что производительность низкая.

Так что я попытался переписать его через webGL, надеюсь, это поможет.

Теперь я могу использовать webGL для создания простого куба, сделать какое-то преобразование, загрузить текстуру в прямоугольник.

Кто-нибудь может дать идею? Лучше всего показать какой-нибудь основной код js или glsl.

ТНХ.

+++++++++++++++ обновление ++++++++++++++++++

спасибо за помощь. Я имел успех! https://codepen.io/oj8kay/pen/PBZjpe

1 Ответ

0 голосов
/ 12 июля 2018

Вы можете добиться "волнового" эффекта в WebGL, передав все вычисления на графический процессор. Для этого необходимо разделить квадрат (или прямоугольник) на треугольную полосу, как показано в следующей схеме:

1  3  5  7
| /| /| /|
|/ |/ |/ |
2  4  6  8

Поместите вершину 1 в координатах (0,0), вершину 2 в (0,1), вершину 7 в (1,0) и вершину 8 в (1,1). Вы можете легко вывести координаты для остальных вершин. Эти координаты - это UV, используемые в текстуре для отображения вашего изображения Более того, вы можете использовать эти координаты для размещения вершины на экране. Чтобы получить эффект «волны», вы можете перемещаться вверх и вниз по вершинам в зависимости от времени.

Вершинный шейдер будет выполняться для каждой вершины и параллельно. Его код может быть очень простым. Вот пример:

// The time is the only variable value.
uniform float uniTime;
// Coords of the current vertex.
attribute vec2 attCoords;
// Propagate UV coords to the fragment shader
varying vec2 varUV;   

void main() {
  // Propagate UV coords to the fragment shader
  varUV = attCoords;
  // Separate X and Y coordinates.
  float x = attCoords.x;
  float y = attCoords.y;
  // Compute the vertical shift of the vertex according to the time.
  float h = cos( uniTime + x * 10.0 );
  // The following line is not mandatory, but it make the move look
  // more random.
  h += cos( x * 3.0 - uniTime * 0.1751 );
  // Define the size of the wave and make it steady on the left
  // to simulate a fixing point on a stick.
  y += h * x * 0.2;
  // In WebGL, the visble space is between -1 and +1 in each direction.
  gl_Position = vec4( 2.0 * x - 1.0, 0.5 - y, 0.0, 1.0 );
}

Вы можете увидеть живой пример здесь: https://jsfiddle.net/63sj1rpk/53/

...