如何平滑地并排对齐旋转的位图而不出现抖动?

Oni*_*rom 5 algorithm graphics 2d image-rotation p5.js

我当前的程序绘制一个旋转的位图(64x64),并通过再次绘制它来将其平铺在屏幕上,但根据位图右上角的计算位置(旋转后)添加偏移量,它工作正常,但我遇到了一些抖动网格在运动。

如果我对画布变换执行相同的操作,则不会出现抖动。

这是一个比较两者的示例:https ://editor.p5js.org/onirom/sketches/A5D-0nxBp

将鼠标移动到画布的左侧部分以使用自定义旋转算法,将鼠标移动到右侧部分以使用画布算法。

似乎某些图块因单个像素而错位,从而导致网格抖动。

自定义旋转算法网格抖动与画布旋转

有没有一种方法可以消除网格抖动,而无需将其作为单遍进行并保持相同的插值方案?

这是子像素正确性问题吗?

这是一些代码:

let tileImage = null
function preload() {
  tileImage = loadImage('data:image/png;base64,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W2sfpw9AhrpK3QAHh8BokbLXPd4d6uzt3zOt/n4Aru1yvz0Dqp8AAA0aaVRYdFhNTDpjb20uYWRvYmUueG1wAAAAAAA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/Pgo8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJYTVAgQ29yZSA0LjQuMC1FeGl2MiI+CiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIKICAgIHhtbG5zOnN0RXZ0PSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VFdmVudCMiCiAgICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgICB4bWxuczpHSU1QPSJodHRwOi8vd3d3LmdpbXAub3JnL3htcC8iCiAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgeG1wTU06RG9jdW1lbnRJRD0iZ2ltcDpkb2NpZDpnaW1wOjNlYzk5MTljLWI0OTUtNGExMC1hNTQyLWI1NjQ4ZDc1YzcwYSIKICAgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpmMDczODg5NS1mZDBmLTRjMGUtOTRjOS0yZjA4YjZiYjVjYjEiCiAgIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDoxNjkwYmU3NS02NzE1LTQwMDEtYTdkZS0xMjBjODQ3MjU3MzgiCiAgIGRjOkZvcm1hdD0iaW1hZ2UvcG5nIgogICBHSU1QOkFQST0iMi4wIgogICBHSU1QOlBsYXRmb3JtPSJMaW51eCIKICAgR0lNUDpUaW1lU3RhbXA9IjE2NzA1Mzk0OTE3Nzc3MTUiCiAgIEdJTVA6VmVyc2lvbj0iMi4xMC4zMCIKICAgdGlmZjpPcmllbnRhdGlvbj0iMSIKICAgeG1wOkNyZWF0b3JUb29sPSJHSU1QIDIuMTAiPgogICA8eG1wTU06SGlzdG9yeT4KICAgIDxyZGY6U2VxPgogICAgIDxyZGY6bGkKICAgICAgc3RFdnQ6YWN0aW9uPSJzYXZlZCIKICAgICAgc3RFdnQ6Y2hhbmdlZD0iLyIKICAgICAgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDplMGQ2NWZlOS1lMDQ1LTRhOGItYWZmZS0xZGRjMWI0M2Q3MTciCiAgICAgIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkdpbXAgMi4xMCAoTGludXgpIgogICAgICBzdEV2dDp3aGVuPSIyMDIyLTEyLTA4VDIzOjQ0OjUxKzAxOjAwIi8+CiAgICA8L3JkZjpTZXE+CiAgIDwveG1wTU06SGlzdG9yeT4KICA8L3JkZjpEZXNjcmlwdGlvbj4KIDwvcmRmOlJERj4KPC94OnhtcG1ldGE+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAKPD94cGFja2V0IGVuZD0idyI/PnF4ZZ8AAAAGYktHRAD/AP8A/6C9p5MAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfmDAgWLDOSx0KrAAAAGXRFWHRDb21tZW50AENyZWF0ZWQgd2l0aCBHSU1QV4EOFwAABDVJREFUeNrtmzFrFVEQhSeSwiIEIhJFEUUMQVERBFNYCGKhiJWllVjbCPojLGysLS2tREghgoVFBEFEEIkERRQNYiCksNPGr3jHPW/uJi8iZqZZdt/eve/tnDlzZu59YzevXvgZHbay+iNabGpye0RE7J2eiIiIew9fDXx++/qZiIh48vzjup6vdvTQzs55rl06HhERn5bXho5n3oU3nyMiYltscRs7dmS2EwFzh/cMHfjh62pERHz5PvyN794xMXC+f9fkwHjOQRIe4lzt5eJy07ytVghwCNgsU0T0tVF5vhDw28bxCG+W2NcYdRygsZ3dxzwOCfocxl88fbDzflifLOSygOOOQoDLs8rCsDOfc3Q2e2Cq8/qd+y8GPHF+btCzeBIjX6un3f1nT+3rnJfrb9+vDOiIQoDmfY0hzcuv330bmqcx7sMun5uJiIgTM9MRETG/sNb5fOZXpTi/sNSJGMcJTk+ABDioEOA0vfO044ZMyRF7LsadlgeZ3AcSVKm2KkkMJBYCsiqv1dPu3LF1Vq05HaHX0Q2uusyuFwLIxyurkwOeUmWVxVhrDDp90LfK5HNVlhki4KLqBygH8EbUk6r4WmMZ7Q3bqgfUHj1b2lC1p+O0puB3qD6pfkBrP0Crxv/FigNcp0bZta/nWzs/60XUqBBZCKC6gt2zPK324PHiQBbR5/XVAZotYG2eXxywWRyA4tNqLEMGdT4eylZ8XJWZKUytCltjP+OKQkCm3UGEeo5xIEC1PJ7CAyhC7TtoreFMkTaqLFEI0BjkDbu1QVcjZHmeHqAa84AAagjV9HSTN6o7CgEOAcq2rabr/qzgaHfW1flZTLeyfbbi5DihEKA9N2Lb6QHNCjqeul5Z3/X2Rm0OMaUDNtoP+Fds1H2J4gB9o8qi2tPD3CqsVnPUENoL5DpZhBUfTKvKP5Vo914jOEznhbt0nkKA++DGlZOdnuNNav5Xw3Pc56pKkIRnFIHaxdWdHtzv1iKZj6OOLwQQO7wRF/PZvoGs7ueco8aqruyoQnV7fPT7qgLlHKShVGuHCAjgDbdWU+oJ2DpbhVWk4BnXaXI1hH5PN2/WX6gsAAJgZ1iY/OqMmOM+5wHtEjtEaJbRvK77F521erwQoAhQdndVmyo/p/R4HrEKZ7idpGSFTNu7DhRsrr1H17fQjlNVg1oNZtnA7eXN6m+39vi3q7/igKwabN2bM6q6Xp+70f8TuPHVEXIIoOpz9XxmsL7T6tr3Vx2QrT26bKP/GtNaQles1G7dfVoIiIgYd3W9ru+rh/AE+oCjruBo/gYBenRdaIcE17niedqHqCzQtyOkHnHr+lpvOzbXzk4rtzhE6P8PtSZRhPG9VdEWAvr+h1fvdxodJKgn3W7y7HsoAlSBuue68YWAjAP6GiytypI3rzHZ17SnqL29iOkmTlH9sOUR8AvuXvZlpzhIWwAAAABJRU5ErkJggg==')
}

function setup() {
  createCanvas(512, 512)
  
  frameRate(14)
  
  tileImage.loadPixels()
}

function computeRotatedPoint(c, s, x, y) {
  return { x: x * c - y * s, y: x * s + y * c }
}

currentTileWidth = 0
currentTileHeight = 0

// draw a rotated bitmap at screen position ox, oy
function drawRotatedBitmap(c, s, ox, oy) {
  let dcu = s
  let dcv = c
  let dru = dcv
  let drv = -dcu
  let su = (tileImage.width / 2.0) - (currentTileWidth_d2 * dcv + currentTileHeight_d2 * dcu)
  let sv = (tileImage.height / 2.0) - (currentTileWidth_d2 * drv + currentTileHeight_d2 * dru)
  let ru = su
  let rv = sv

  for (let y = 0; y < currentTileHeight; y += 1) {
    let u = ru
    let v = rv
    for (let x = 0; x < currentTileWidth; x += 1) {
      let ui = u
      let vi = v
      
      if (ui >= 0 && ui < tileImage.width) {
        let index1 = (floor(ui) + floor(vi) * tileImage.width) * 4
        let index2 = (x + ox + (y + oy) * width) * 4
        pixels[index2 + 0] = tileImage.pixels[index1 + 0]
        pixels[index2 + 1] = tileImage.pixels[index1 + 1]
        pixels[index2 + 2] = tileImage.pixels[index1 + 2]
      }
      
      u += dru
      v += drv
    }
    ru += dcu
    rv += dcv
  }
}

let angle = 0

function draw() {
  background(0)

  const s = sin(angle / 256 * PI * 2)
  const c = cos(angle / 256 * PI * 2)

  // compute rotated tile width / height
  let tw = tileImage.width
  let th = tileImage.height
  if (angle % 128 < 64) {
    currentTileWidth = abs(tw * c + th * s)
    currentTileHeight = abs(tw * s + th * c)
  } else {
    currentTileWidth = abs(tw * c - th * s)
    currentTileHeight = abs(tw * s - th * c)
  }

  currentTileWidth_d2 = (currentTileWidth / 2.0)
  currentTileHeight_d2 = (currentTileHeight / 2.0)
  
  // compute rotated point
  const rp = computeRotatedPoint(c, s, tw, 0)

  // draw tiles
  loadPixels()
  for (let i = -3; i <= 3; i += 1) {
    // compute center
    const cx = width / 2 - currentTileWidth_d2
    const cy = height / 2 - currentTileHeight_d2
    // compute tile position
    const ox = rp.x * i
    const oy = rp.y * i
    drawRotatedBitmap(c, s, round(cx + ox), round(cy + oy))
  }
  updatePixels()
  
  angle += 0.5
}
Run Code Online (Sandbox Code Playgroud)
<!DOCTYPE html>
<html lang="en">
  <head>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.5.0/p5.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.5.0/addons/p5.sound.min.js"></script>
    <link rel="stylesheet" type="text/css" href="style.css">
    <meta charset="utf-8" />
  </head>
  <body>
    <main>
    </main>
  </body>
</html>
Run Code Online (Sandbox Code Playgroud)

Oni*_*rom 4

我找到了一个不使用相同算法但使用相同插值方案的解决方案。

三剪切法解决方案

该解决方案使用三遍剪切方法,修复图块抖动的解决方案是在旋转之前添加图块偏移,然后在所有内容都准备好绘制后添加圆角坐标:

平滑三剪旋转平铺

/**
 * Bitmap rotation + stable tiling with 3-shearing method
 * The 3-shearing method is stable between -PI / 2 and PI / 2 only, that is why a flip is needed for a full rotation
 *
 * https://www.ocf.berkeley.edu/~fricke/projects/israel/paeth/rotation_by_shearing.html
 */
let tex = null
let tile = []
function preload() {
  tile = loadImage('data:image/png;base64,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W2sfpw9AhrpK3QAHh8BokbLXPd4d6uzt3zOt/n4Aru1yvz0Dqp8AAA0aaVRYdFhNTDpjb20uYWRvYmUueG1wAAAAAAA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/Pgo8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJYTVAgQ29yZSA0LjQuMC1FeGl2MiI+CiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIKICAgIHhtbG5zOnN0RXZ0PSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VFdmVudCMiCiAgICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgICB4bWxuczpHSU1QPSJodHRwOi8vd3d3LmdpbXAub3JnL3htcC8iCiAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgeG1wTU06RG9jdW1lbnRJRD0iZ2ltcDpkb2NpZDpnaW1wOjNlYzk5MTljLWI0OTUtNGExMC1hNTQyLWI1NjQ4ZDc1YzcwYSIKICAgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpmMDczODg5NS1mZDBmLTRjMGUtOTRjOS0yZjA4YjZiYjVjYjEiCiAgIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDoxNjkwYmU3NS02NzE1LTQwMDEtYTdkZS0xMjBjODQ3MjU3MzgiCiAgIGRjOkZvcm1hdD0iaW1hZ2UvcG5nIgogICBHSU1QOkFQST0iMi4wIgogICBHSU1QOlBsYXRmb3JtPSJMaW51eCIKICAgR0lNUDpUaW1lU3RhbXA9IjE2NzA1Mzk0OTE3Nzc3MTUiCiAgIEdJTVA6VmVyc2lvbj0iMi4xMC4zMCIKICAgdGlmZjpPcmllbnRhdGlvbj0iMSIKICAgeG1wOkNyZWF0b3JUb29sPSJHSU1QIDIuMTAiPgogICA8eG1wTU06SGlzdG9yeT4KICAgIDxyZGY6U2VxPgogICAgIDxyZGY6bGkKICAgICAgc3RFdnQ6YWN0aW9uPSJzYXZlZCIKICAgICAgc3RFdnQ6Y2hhbmdlZD0iLyIKICAgICAgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDplMGQ2NWZlOS1lMDQ1LTRhOGItYWZmZS0xZGRjMWI0M2Q3MTciCiAgICAgIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkdpbXAgMi4xMCAoTGludXgpIgogICAgICBzdEV2dDp3aGVuPSIyMDIyLTEyLTA4VDIzOjQ0OjUxKzAxOjAwIi8+CiAgICA8L3JkZjpTZXE+CiAgIDwveG1wTU06SGlzdG9yeT4KICA8L3JkZjpEZXNjcmlwdGlvbj4KIDwvcmRmOlJERj4KPC94OnhtcG1ldGE+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAKPD94cGFja2V0IGVuZD0idyI/PnF4ZZ8AAAAGYktHRAD/AP8A/6C9p5MAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfmDAgWLDOSx0KrAAAAGXRFWHRDb21tZW50AENyZWF0ZWQgd2l0aCBHSU1QV4EOFwAABDVJREFUeNrtmzFrFVEQhSeSwiIEIhJFEUUMQVERBFNYCGKhiJWllVjbCPojLGysLS2tREghgoVFBEFEEIkERRQNYiCksNPGr3jHPW/uJi8iZqZZdt/eve/tnDlzZu59YzevXvgZHbay+iNabGpye0RE7J2eiIiIew9fDXx++/qZiIh48vzjup6vdvTQzs55rl06HhERn5bXho5n3oU3nyMiYltscRs7dmS2EwFzh/cMHfjh62pERHz5PvyN794xMXC+f9fkwHjOQRIe4lzt5eJy07ytVghwCNgsU0T0tVF5vhDw28bxCG+W2NcYdRygsZ3dxzwOCfocxl88fbDzflifLOSygOOOQoDLs8rCsDOfc3Q2e2Cq8/qd+y8GPHF+btCzeBIjX6un3f1nT+3rnJfrb9+vDOiIQoDmfY0hzcuv330bmqcx7sMun5uJiIgTM9MRETG/sNb5fOZXpTi/sNSJGMcJTk+ABDioEOA0vfO044ZMyRF7LsadlgeZ3AcSVKm2KkkMJBYCsiqv1dPu3LF1Vq05HaHX0Q2uusyuFwLIxyurkwOeUmWVxVhrDDp90LfK5HNVlhki4KLqBygH8EbUk6r4WmMZ7Q3bqgfUHj1b2lC1p+O0puB3qD6pfkBrP0Crxv/FigNcp0bZta/nWzs/60XUqBBZCKC6gt2zPK324PHiQBbR5/XVAZotYG2eXxywWRyA4tNqLEMGdT4eylZ8XJWZKUytCltjP+OKQkCm3UGEeo5xIEC1PJ7CAyhC7TtoreFMkTaqLFEI0BjkDbu1QVcjZHmeHqAa84AAagjV9HSTN6o7CgEOAcq2rabr/qzgaHfW1flZTLeyfbbi5DihEKA9N2Lb6QHNCjqeul5Z3/X2Rm0OMaUDNtoP+Fds1H2J4gB9o8qi2tPD3CqsVnPUENoL5DpZhBUfTKvKP5Vo914jOEznhbt0nkKA++DGlZOdnuNNav5Xw3Pc56pKkIRnFIHaxdWdHtzv1iKZj6OOLwQQO7wRF/PZvoGs7ueco8aqruyoQnV7fPT7qgLlHKShVGuHCAjgDbdWU+oJ2DpbhVWk4BnXaXI1hH5PN2/WX6gsAAJgZ1iY/OqMmOM+5wHtEjtEaJbRvK77F521erwQoAhQdndVmyo/p/R4HrEKZ7idpGSFTNu7DhRsrr1H17fQjlNVg1oNZtnA7eXN6m+39vi3q7/igKwabN2bM6q6Xp+70f8TuPHVEXIIoOpz9XxmsL7T6tr3Vx2QrT26bKP/GtNaQles1G7dfVoIiIgYd3W9ru+rh/AE+oCjruBo/gYBenRdaIcE17niedqHqCzQtyOkHnHr+lpvOzbXzk4rtzhE6P8PtSZRhPG9VdEWAvr+h1fvdxodJKgn3W7y7HsoAlSBuue68YWAjAP6GiytypI3rzHZ17SnqL29iOkmTlH9sOUR8AvuXvZlpzhIWwAAAABJRU5ErkJggg==')

}

function setup() {
  createCanvas(512, 512)
  
  frameRate(12)
}

function computeRotatedPoint(c, s, x, y) {
  return { x: x * c - y * s, y: x * s + y * c }
}

function _shearX(t, x, y) {
  return x - y * t
}

function _shearY(s, x, y) {
  return x * s + y
}

currentTileWidth = 0
currentTileHeight = 0

currentTileLookupFunction = tileLookup1

// regular lookup
function tileLookup1(x, y) {
  return (x + y * tile.width) * 4
}

// flipped lookup
function tileLookup2(x, y) {
  return ((tile.width - 1 - x) + (tile.height - 1 - y) * tile.width) * 4
}

// draw a rotated bitmap at offset ox,oy with cx,cy as center of rotation offset
function drawRotatedBitmap(c, s, t, ox, oy, cx, cy) {
  for (let ty = 0; ty < tile.height; ty += 1) {
    for (let tx = 0; tx < tile.width; tx += 1) {
      // center of rotation
      let scx = tile.width - tx - tile.width / 2
      let scy = tile.height - ty - tile.height / 2
      
      // this is key to a stable rotation without any jerkiness
      scx += cx
      scy += cy
      
      // shear
      let ux = round(_shearX(t, scx, scy))
      let uy = round(_shearY(s, ux, scy))
      ux = round(_shearX(t, ux, uy))
      
      // translate again
      ux = currentTileWidth_d2 - ux
      uy = currentTileHeight_d2 - uy

      // plot with offset
      let index1 = currentTileLookupFunction(tx, ty)
      let index2 = (ox + ux + (oy + uy) * width) * 4

      pixels[index2 + 0] = tile.pixels[index1 + 0]
      pixels[index2 + 1] = tile.pixels[index1 + 1]
      pixels[index2 + 2] = tile.pixels[index1 + 2]
    }
  }
}

let angle = -3.141592653 / 2
function draw() {
  const s = sin(angle)
  const c = cos(angle)
  const t = tan(angle / 2)

  tile.loadPixels()

  background(0)

  // compute rotated tile width / height
  let tw = tile.width
  let th = tile.height

  currentTileWidth = abs(tw * c + th * s)
  currentTileHeight = abs(tw * s + th * c)

  currentTileWidth_d2 = round(currentTileWidth / 2.0)
  currentTileHeight_d2 = round(currentTileHeight / 2.0)

  // draw tiles
  loadPixels()
  for (let j = -2; j <= 2; j += 1) {
    for (let i = -2; i <= 2; i += 1) {
      let ox = round(width / 2 - currentTileWidth_d2)
      let oy = round(height / 2 - currentTileHeight_d2)
      drawRotatedBitmap(c, s, t, ox, oy, i * tw, j * tw)
    }
  }
  updatePixels()
  
  angle += 0.025
  if (angle >= PI / 2) {
    angle -= PI
    
    if (currentTileLookupFunction === tileLookup2) {
      currentTileLookupFunction = tileLookup1
    } else {
      currentTileLookupFunction = tileLookup2
    }
  }
}
Run Code Online (Sandbox Code Playgroud)
<!DOCTYPE html>
<html lang="en">
  <head>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.5.0/p5.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.5.0/addons/p5.sound.min.js"></script>
    <link rel="stylesheet" type="text/css" href="style.css">
    <meta charset="utf-8" />
  </head>
  <body>
    <main>
    </main>
  </body>
</html>
Run Code Online (Sandbox Code Playgroud)


我无法从技术上说明它的工作原理,但它可能与错误累积问题/舍入有关,因为如果我在旋转后添加瓷砖偏移并独立舍入偏移和剪切通道,我可以使用三剪切方法完全重现问题问题作为 :

function drawRotatedBitmap(c, s, t, ox, oy, cx, cy) {
  cx = round(_shearX(t, cx, cy))
  cy = round(_shearY(s, cx, cy))
  cx = round(_shearX(t, cx, cy))
  for (let ty = 0; ty < tex.height; ty += 1) {
    ...
    let ux = round(_shearX(t, scx, scy))
    let uy = round(_shearY(s, ux, scy))
    ux = round(_shearX(t, ux, uy))
    ...
    let index2 = (cx + ux + ox + (cy + uy + oy) * width) * 4
    ...
  }
}
Run Code Online (Sandbox Code Playgroud)

三剪旋转贴砖,瓷砖抖动


如果我同时舍入偏移和剪切结果,该问题就会变得清晰可见,这会导致最终图像中丢失像素,例如:

function drawRotatedBitmap(c, s, t, ox, oy, cx, cy) {
  cx = _shearX(t, cx, cy)
  cy = _shearY(s, cx, cy)
  cx = _shearX(t, cx, cy)
  for (let ty = 0; ty < tex.height; ty += 1) {
    ...
    let ux = _shearX(t, scx, scy)
    let uy = _shearY(s, ux, scy)
    ux = _shearX(t, ux, uy)
    ...
    let index2 = (round(cx + ux) + ox + (round(cy + uy) + oy) * width) * 4
    ...
  }
}
Run Code Online (Sandbox Code Playgroud)

三剪旋转带孔瓷砖


我仍然想对抖动行为进行详细解释,并想知道是否有通过在旋转后添加平铺偏移量来实现平滑的解决方案,似乎抖动是由于旋转中心偏离一两个像素,具体取决于角度。