【问题标题】:How to create a 2D waving flag in webGL?如何在 webGL 中创建 2D 挥动旗帜?
【发布时间】:2018-07-06 10:53:39
【问题描述】:

我刚刚写了一个在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);
}
&lt;canvas id="flagCanvas"&gt;&lt;/canvas&gt;

完成之后,我发现性能很差。

所以我尝试用 webGL 重写它,希望能有所帮助。

现在我可以使用 webGL 创建一个简单的立方体,进行一些转换,将纹理加载到矩形中。

谁能提供一个想法?最好展示一些核心 js 或 glsl 代码..

谢谢。

+++++++++++++++++更新++++++++++++++++++++

谢谢帮助。 我成功了! https://codepen.io/oj8kay/pen/PBZjpe

【问题讨论】:

  • 那么...webgl 代码在哪里?
  • 如果您希望您的画布版本更快,请考虑使用drawImage。它需要一个源矩形和一个目标矩形。没有理由使用慢速getImageDataputImageData。向您展示如何在 WebGL 中执行此操作可以说是一个太大的话题。 Consider reading some tutorials.
  • @gman 但是我想模拟反射效果,所以我需要改变每个像素的颜色,drawImage 做不到。你可以认为我可以理解 webgl 并给我一些想法或者代码,我研究一下。
  • 您可以通过使用fillRect 和alpha
  • 您可以使用一些techniques covered here 来制作类似于您上面的挥舞旗帜。

标签: canvas webgl


【解决方案1】:

您可以通过将所有计算委托给 GPU 来实现 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/

【讨论】:

  • 这太棒了!正是我想要的!很抱歉这么久才回复,谢谢!
猜你喜欢
  • 2012-10-06
  • 2013-01-16
  • 1970-01-01
  • 2012-05-09
  • 1970-01-01
  • 1970-01-01
  • 2011-05-20
  • 2015-12-30
  • 2014-09-01
相关资源
最近更新 更多