我想将 Canvas 鼠标坐标传递给一个函数,该函数以鼠标坐标为中心交互式生成一个圆。因此,我正在使用以下函数进行标准化:
var mousePositionX = (2*ev.clientX/canvas.width) - 1;
var mousePositionY = (2*ev.clientY/(canvas.height*-1)) + 1;
但是,这仅适用于屏幕中心。当围绕光标移动鼠标时,光标不再位于圆心:
see the picture here
鼠标光标从屏幕中心移得越远,它从圆心移开的位置就越多。
这是一些相关的代码:
HTML
body {
border: 0;
margin: 0;
}
/* make the canvas the size of the viewport */
canvas {
width: 100vw;
height: 100vh;
display: block;
}
...
<body onLoad="main()">
<canvas id="glContext"></canvas>
</body>
SHADER
<script id="vShaderCircle" type="notjs">
attribute vec4 a_position;
uniform mat4 u_viewMatrix;
void main(){
gl_Position = u_viewMatrix * a_position;
}
</script>
JS
function main(){
// PREPARING CANVAS AND WEBGL-CONTEXT
var canvas = document.getElementById("glContext");
var gl_Original = initWebGL(canvas);
var gl = WebGLDebugUtils.makeDebugContext(gl_Original);
resize(canvas);
gl.viewport(0, 0, canvas.width, canvas.height);
// ----------------------------------
...
// MATRIX SETUP
var viewMatrix = new Matrix4();
viewMatrix.setPerspective(30, canvas.width/canvas.height, 1, 100);
viewMatrix.lookAt(0, 0, 5, 0, 0, 0, 0, 1, 0);
// ----------------------------------
canvas.addEventListener("mousemove", function(){stencilTest(event)});
function stencilTest(ev){
var mousePositionX = (2*ev.clientX/canvas.width) - 1;
var mousePositionY = (2*ev.clientY/(canvas.height*(-1))) + 1;
...
...
drawCircle(..., mousePositionX, mousePositionY, viewMatrix);
...
drawCube(...);
}
}
我该如何解决?
最佳答案
这实际上是far more complicated issue than it sounds。 Canvas 的显示尺寸是否与绘图缓冲区相同? Canvas 上有边框吗?
这是一些代码,假定您在 Canvas 上没有边框或任何填充,将为您提供 Canvas 的相对像素坐标。
function getRelativeMousePosition(event, target) {
target = target || event.target;
var rect = target.getBoundingClientRect();
return {
x: event.clientX - rect.left,
y: event.clientY - rect.top,
}
}
// assumes target or event.target is canvas
function getNoPaddingNoBorderCanvasRelativeMousePosition(event, target) {
target = target || event.target;
var pos = getRelativeMousePosition(event, target);
pos.x = pos.x * target.width / target.clientWidth;
pos.y = pos.y * target.height / target.clientHeight;
return pos;
}
将其转换为WebGL坐标
var pos = getRelativeMousePosition(event, target);
const x = pos.x / gl.canvas.width * 2 - 1;
const y = pos.y / gl.canvas.height * -2 + 1;
工作示例:
function getRelativeMousePosition(event, target) {
target = target || event.target;
var rect = target.getBoundingClientRect();
return {
x: event.clientX - rect.left,
y: event.clientY - rect.top,
}
}
// assumes target or event.target is canvas
function getNoPaddingNoBorderCanvasRelativeMousePosition(event, target) {
target = target || event.target;
var pos = getRelativeMousePosition(event, target);
pos.x = pos.x * target.width / target.clientWidth;
pos.y = pos.y * target.height / target.clientHeight;
return pos;
}
const vs = `
attribute vec4 position;
void main() {
gl_Position = position;
gl_PointSize = 20.;
}
`;
const fs = `
void main() {
gl_FragColor = vec4(1,0,1,1);
}
`;
const gl = document.querySelector("canvas").getContext("webgl");
// compiles and links shaders and assigns position to location
const program = twgl.createProgramFromSources(gl, [vs, fs]);
const positionLoc = gl.getAttribLocation(program, "position");
window.addEventListener('mousemove', e => {
const pos = getNoPaddingNoBorderCanvasRelativeMousePosition(e, gl.canvas);
// pos is in pixel coordinates for the canvas.
// so convert to WebGL clip space coordinates
const x = pos.x / gl.canvas.width * 2 - 1;
const y = pos.y / gl.canvas.height * -2 + 1;
gl.clear(gl.COLOR_BUFFER_BIT);
gl.useProgram(program);
// only drawing a single point so no need to use a buffer
gl.vertexAttrib2f(positionLoc, x, y);
gl.drawArrays(gl.POINTS, 0, 1);
});
canvas {
display: block;
width: 400px;
height: 100px;
}
div {
display: inline-block;
border: 1px solid black;
}
<div><canvas></canvas></div>
<p>move the mouse over the canvas</p>
<script src="https://twgljs.org/dist/3.x/twgl-full.min.js"></script>
请注意,这里没有涉及矩阵。如果您使用矩阵,那么您已经定义了自己的空间,而不是WebGL的空间,该空间始终是剪辑空间。在那种情况下,您要么需要乘以矩阵的倒数,然后选择想要的-1和+1之间的Z值。这样,当您的位置乘以着色器中使用的矩阵时,它将位置反转为正确的webgl剪辑空间坐标。或者,您需要摆脱矩阵或将它们设置为身份。
这是一个示例,请注意,我没有/不知道您的数学库,因此您必须将其翻译为您的数学库
function getRelativeMousePosition(event, target) {
target = target || event.target;
var rect = target.getBoundingClientRect();
return {
x: event.clientX - rect.left,
y: event.clientY - rect.top,
}
}
// assumes target or event.target is canvas
function getNoPaddingNoBorderCanvasRelativeMousePosition(event, target) {
target = target || event.target;
var pos = getRelativeMousePosition(event, target);
pos.x = pos.x * target.width / target.clientWidth;
pos.y = pos.y * target.height / target.clientHeight;
return pos;
}
const vs = `
attribute vec4 position;
uniform mat4 matrix;
void main() {
gl_Position = matrix * position;
}
`;
const fs = `
void main() {
gl_FragColor = vec4(1,0,0,1);
}
`;
const m4 = twgl.m4;
const gl = document.querySelector("canvas").getContext("webgl");
const programInfo = twgl.createProgramInfo(gl, [vs, fs]);
const bufferInfo = twgl.primitives.createSphereBufferInfo(gl, .5, 12, 8);
window.addEventListener('mousemove', e => {
const pos = getNoPaddingNoBorderCanvasRelativeMousePosition(e, gl.canvas);
// pos is in pixel coordinates for the canvas.
// so convert to WebGL clip space coordinates
const x = pos.x / gl.canvas.width * 2 - 1;
const y = pos.y / gl.canvas.height * -2 + 1;
// use a projection and view matrix
const projection = m4.perspective(
30 * Math.PI / 180,
gl.canvas.clientWidth / gl.canvas.clientHeight,
1,
100);
const camera = m4.lookAt([0, 0, 15], [0, 0, 0], [0, 1, 0]);
const view = m4.inverse(camera);
const viewProjection = m4.multiply(projection, view);
// pick a clipsace Z value between -1 and 1
// we'll zNear to zFar and convert back to clip space
const viewZ = -5; // 5 units back from the camera
const clip = m4.transformPoint(projection, [0, 0, viewZ]);
const z = clip[2];
// compute the world space position needed to put the sphere
// under the cursor at this clipspace position
const inverseViewProjection = m4.inverse(viewProjection);
const worldPos = m4.transformPoint(inverseViewProjection, [x, y, z]);
// add that world position to our matrix
const mat = m4.translate(viewProjection, worldPos);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.useProgram(programInfo.program);
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
twgl.setUniforms(programInfo, {
matrix: mat,
});
gl.drawElements(gl.LINES, bufferInfo.numElements, gl.UNSIGNED_SHORT, 0);
});
canvas {
display: block;
width: 400px;
height: 100px;
}
div {
display: inline-block;
border: 1px solid black;
}
<div><canvas></canvas></div>
<p>move the mouse over the canvas</p>
<script src="https://twgljs.org/dist/3.x/twgl-full.min.js"></script>
还要注意,我故意使 Canvas 的显示尺寸与绘图缓冲区的尺寸不匹配,以显示数学效果。
关于javascript - 如何正确地将鼠标坐标传递给WebGL?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42309715/