You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用键盘方向键在WebGL中移动3D对象

使用键盘方向键在WebGL中移动3D对象

嘿,我仔细看了你的代码和问题,核心问题出在两个地方:一是你误解了gl.viewport的作用,二是没有在按键更新后重新应用视口设置(不过这其实不是移动3D物体的正确方式)。先给你理清楚思路,再一步步解决问题:

先纠正一个核心概念

gl.viewport是用来控制WebGL在Canvas上的绘制区域,不是用来移动3D空间里的物体的——手动修改它的x/y参数看起来物体动了,其实是把整个绘制画面在Canvas上偏移了,这种方式会导致画面被裁剪,完全不符合3D场景的逻辑。正确的做法是用**模型矩阵(Model Matrix)**来控制物体在3D空间中的位置,这是WebGL里移动、旋转、缩放物体的标准方案。

具体修改步骤

1. 替换平移变量,修改按键事件逻辑

先把原来的xPos/yPos换成3D空间的平移变量,然后在按键事件里更新这个变量(用小数值而不是像素,因为3D空间用的是标准化坐标):

// 在init函数里替换原来的xPos/yPos定义
var translate = vec3(0, 0, 0); // 存储物体在3D空间的x/y/z平移量

// 修改按键事件处理
document.addEventListener("keydown", function(e) {
    e.preventDefault();
    const key = e.key;
    switch (key) {
        case "ArrowLeft":
            translate[0] -= 0.1; // 沿X轴负方向移动
            break;
        case "ArrowRight":
            translate[0] += 0.1; // 沿X轴正方向移动
            break;
        case "ArrowUp":
            translate[1] += 0.1; // 沿Y轴正方向移动
            break;
        case "ArrowDown":
            translate[1] -= 0.1; // 沿Y轴负方向移动
            break;
    }
});

2. 补充矩阵运算函数

因为需要生成旋转和平移矩阵,添加几个基础的矩阵生成函数(如果你的代码依赖的库没有这些的话):

// 生成X轴旋转矩阵(角度转弧度)
function rotateXMat(angle) {
    const rad = angle * Math.PI / 180;
    const c = Math.cos(rad);
    const s = Math.sin(rad);
    return mat4(
        1, 0, 0, 0,
        0, c, -s, 0,
        0, s, c, 0,
        0, 0, 0, 1
    );
}

// 生成Y轴旋转矩阵
function rotateYMat(angle) {
    const rad = angle * Math.PI / 180;
    const c = Math.cos(rad);
    const s = Math.sin(rad);
    return mat4(
        c, 0, s, 0,
        0, 1, 0, 0,
        -s, 0, c, 0,
        0, 0, 0, 1
    );
}

// 生成Z轴旋转矩阵
function rotateZMat(angle) {
    const rad = angle * Math.PI / 180;
    const c = Math.cos(rad);
    const s = Math.sin(rad);
    return mat4(
        c, -s, 0, 0,
        s, c, 0, 0,
        0, 0, 1, 0,
        0, 0, 0, 1
    );
}

// 生成平移矩阵
function translateMat(x, y, z) {
    return mat4(
        1, 0, 0, 0,
        0, 1, 0, 0,
        0, 0, 1, 0,
        x, y, z, 1
    );
}

3. 修改顶点着色器,添加模型矩阵uniform

更新你的顶点着色器,用模型矩阵来变换顶点位置(替换原来的旋转逻辑):

attribute vec4 aPosition;
attribute vec4 aColor;
attribute vec2 aTexCoord;

uniform mat4 uModelMatrix; // 新增模型矩阵uniform
uniform sampler2D uTex0;

varying vec4 vColor;
varying vec2 vTexCoord;

void main() {
    gl_Position = uModelMatrix * aPosition; // 用模型矩阵完成顶点变换
    vColor = aColor;
    vTexCoord = aTexCoord;
}

然后在JS的init函数里获取这个uniform的位置:

// 在thetaLoc定义之后添加
var modelMatrixLoc = gl.getUniformLocation(program, "uModelMatrix");

4. 在render函数中计算并传递模型矩阵

修改render函数,把旋转和平移结合成模型矩阵,传递给着色器:

var render = function() {
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

    if(flag) theta[axis] += 2.0;

    // 生成各个旋转矩阵
    const rotateX = rotateXMat(theta[0]);
    const rotateY = rotateYMat(theta[1]);
    const rotateZ = rotateZMat(theta[2]);
    // 生成平移矩阵
    const translateM = translateMat(translate[0], translate[1], translate[2]);

    // 模型矩阵:先旋转再平移(让物体绕自身中心旋转后移动位置)
    // 矩阵乘法顺序很重要,WebGL是列主序,所以顺序是 平移 * 旋转Z * 旋转Y * 旋转X
    const modelMatrix = mult(translateM, mult(rotateZ, mult(rotateY, rotateX)));

    // 传递模型矩阵给着色器
    gl.uniformMatrix4fv(modelMatrixLoc, false, flatten(modelMatrix));

    gl.drawArrays(gl.TRIANGLES, 0, numPositions);

    requestAnimationFrame(render);
}

为什么原来的方法不行?

你之前只在init里调用了一次gl.viewport(xPos, yPos, canvas.width, canvas.height),之后按键更新xPos/yPos后没有重新调用这个方法,所以画面不会变化。就算你把gl.viewport移到render里,这种方式也只是偏移了WebGL在Canvas上的绘制区域,不是真正移动3D物体——比如当你把xPos设为25,WebGL会从Canvas的(25,0)位置开始绘制,超出Canvas的部分会被裁剪,这显然不是你想要的3D物体移动效果。

用模型矩阵的方式,是在3D空间中真正改变物体的位置,和旋转逻辑完美结合,也是WebGL开发的标准做法,以后你做更复杂的3D场景也会用到这个思路。

备注:内容来源于stack exchange,提问作者s73c

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.22 14:09:30