获取碰撞检测点的大小

Getting size of point for collision detection

如果我的术语有误,请原谅我,但我只是想在 WebGL 中做一些简单的点碰撞检测。我有一堆 gl.POINTS 漂浮在 canvas 周围(640x480,设置为 HTML 属性),我在我的顶点着色器中设置了 gl_PointSize = 10.0,并且正在尝试转换这到正确的坐标系。

我只是想根据每个渲染点的四个角进行一些基本的碰撞检测(假设我已经增加了它们的大小来表示一个正方形,只是为了让这些点相互反弹)。

我遇到的问题是我似乎无法理解如何计算它们的确切大小。我有它们的顶点位置,我想我可以简单地在 canvas 大小、点大小和 [-1, 1].

的 WebGL 坐标之间进行归一化

基本上,有没有'simple'方法来计算点的精确大小?

一个点的大小是它的中心 +/- 它大小的一半

+-[canvas]-----------------------+
|                                |
|                                |
|                                |
|  +---+                         |
|  | + |                         |
|  +---+                         |
|                                |
+--------------------------------+

在上面的示例中 canvas 32x7 该点的中心位于 4x2 像素处。它的 gl_PointSize 是 3。它的剪辑 space 位置是

cx = px / canvasWidth * 2 - 1
cx = 4 / 32 * 2 - 1 = -0.75
cy = py / canvasHeight * 2 - 1
cy = 2 /  7 * 2 - 1 = -0.43

其剪辑space宽高为

clipWidth  = gl_PointSize / canvasWidth
clipHeight = gl_PointSize / canvasHeight

还要记住 WebGL 中的 +Y

const gl = document.querySelector('canvas').getContext('webgl');
const vs =`
attribute vec4 position;
attribute float pointSize;
attribute vec4 color;

varying vec4 v_color;

void main() {
  gl_Position = position;
  gl_PointSize = pointSize;
  v_color = color;
}
`;

const fs = `
precision mediump float;
varying vec4 v_color;
void main() {
  gl_FragColor = v_color;
}
`;

const prg = twgl.createProgram(gl, [vs, fs]);

const posLoc = gl.getAttribLocation(prg, 'position');
const sizeLoc = gl.getAttribLocation(prg, 'pointSize');
const colorLoc = gl.getAttribLocation(prg, 'color');

const numPoints = 12;
const points = [];
for (let i = 0; i < numPoints; ++i) {
  points.push({
    x: r(-1, 1),
    y: r(-1, 1),
   color: [0, r(0, 1), r(0, 1), 1],
   size: r(15, 55),
  });
}

function r(min, max) {
  return Math.random() * (max - min) + min;
}

let px = -10;
let py = -10;
function render() {
  gl.useProgram(prg);
  
  // convert mouse to clipspace
  const cx = px / gl.canvas.width  *  2 - 1;
  const cy = py / gl.canvas.height * -2 + 1;

  for (const point of points) {
    const {x, y, color, size} = point;
    // size to clip size
    const halfClipWidth = size / gl.canvas.width;
    const halfClipHeight = size / gl.canvas.height;
    
    const left   = x - halfClipWidth;
    const right  = x + halfClipWidth;
    const top    = y + halfClipHeight;
    const bottom = y - halfClipHeight;
    
    const hit = cx >= left && cx <= right &&
                cy >= bottom && cy <= top;    
    
    gl.vertexAttrib2f(posLoc, x, y);
    gl.vertexAttrib1f(sizeLoc, size);
    gl.vertexAttrib4fv(colorLoc, hit ? [1, 0, 0, 1] : color);
    gl.drawArrays(gl.POINTS, 0, 1);
  }
}

render();

gl.canvas.addEventListener('mousemove', (e) => {
  const rect = gl.canvas.getBoundingClientRect();
  px = (e.clientX - rect.left) * gl.canvas.width / gl.canvas.clientWidth;
  py = (e.clientY - rect.top) * gl.canvas.height / gl.canvas.clientHeight;
  render();
});
  
body { margin: 40px; }
canvas { border: 1px solid black; }
<canvas></canvas>
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>

另请记住,WebGL 中的位置是边缘而不是像素。如果你有一个 2x2 canvas 剪辑 space 中左下角像素的中心是 -0.5,-0.5

  -1   0   1
   |   |   |
   +---+---+-- 1
   |   |   |
   |   |   |
   |   |   |
   +---+---+-- 0
   |   |   |
   | + |   |     <--- you can see the center of that pixel is at -0.5, -0.5
   |   |   |
   +---+---+-- -1

在像素 space 中,相同的 canvas 将是

   0   1   2
   |   |   |
   +---+---+-- 2
   |   |   |
   |   |   |
   |   |   |
   +---+---+-- 1
   |   |   |
   | + |   |     <--- you can see the center of that pixel is at 0.5, 0.5
   |   |   |
   +---+---+-- 0

gl.POINTS 以像素 space 中的点为中心,加上 +/- gl_PointSize / 2 以形成一个矩形。中心位于该矩形内的任何像素都将被渲染(或者更确切地说考虑渲染给定所有其他测试 depth/stencil/discard,等等...)