纹理坐标采摘

时间:2015-01-28 20:14:19

标签: javascript three.js

我正在尝试检测用户点击的对象上纹理的位置,以便我可以重新绘制纹理。

我知道我可以将带有颜色编码纹理的对象渲染到单独的渲染目标,并使用gl.readPixels查看单击了哪个编码像素,然后计算回纹理中我的X和Y坐标应该是什么

我似乎可以为Y轴可靠地做到这一点,但不是X轴。

这是我的(删除了多余的代码)Three.js设置:

var canvas = document.getElementByID("output"),
renderer = new THREE.WebGLRenderer({
    canvas: canvas,
    alpha: true,
    antialias: true
}),
back = new THREE.WebGLRenderTarget(canvas.width, canvas.height),
scene = new THREE.Scene(),
pickingScene = new THREE.Scene(),
pickingPixelBuffer = new Uint8Array(4),
camera = new THREE.PerspectiveCamera(50, canvas.width / canvas.height, 0.1, 1000),
textureWidth = 1024,
textureHeight = 1024,
texture = generateTexture(textureWidth, textureHeight),
pickingTexture = generatePickingTexture(textureWidth, textureHeight), 
obj = textured(shell(w, 5, 10), texture),
objPicker = textured(shell(w, 5, 10), pickingTexture);

back.generateMipmaps = false;

scene.add(camera);
scene.add(obj);
pickingScene.add(objPicker);

所以对于像这样的对象: enter image description here

拾取纹理最终看起来像这样: the picking texture

generateTexture功能并不是那么重要。 textured函数只是将纹理应用于几何对象的简写:

function textured(geometry, txt){
    var material = new THREE.MeshBasicMaterial({
        color: 0xffffff,
        map: txt,
        transparent: false,
        shading: THREE.FlatShading,
        side: THREE.DoubleSide
    });

    var obj = new THREE.Mesh(geometry, material);
    return obj;        
}

因此generatePickingTexture函数是:

function generatePickingTexture(w, h){
    var canvas = document.createElement("canvas");
    canvas.width = w;
    canvas.height = h;
    var texture = new THREE.Texture(canvas);
    var gfx = texture.image.getContext("2d"),
            l = w * h,
            pixels = gfx.createImageData(w, h);
    for(var i = 0, p = 0; 
            i < l; 
            ++i, p += 4){
        pixels.data[p]   = (0xff0000 & i) >> 16;
        pixels.data[p+1] = (0x00ff00 & i) >> 8;
        pixels.data[p+2] = 0x0000ff & i;
        pixels.data[p+3] = 0xff;
    }
    gfx.putImageData(pixels, 0, 0);

    texture.needsUpdate = true;
    return texture;
}

然后我尝试进行拣选:

function pick(){
    renderer.render(pickingScene, camera, back, true);

    var gl = renderer.getContext();
    gl.readPixels(pointerX, canvas.height - pointerY, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pickingPixelBuffer);
    var i = (pickingPixelBuffer[0] << 16) | 
            (pickingPixelBuffer[1] << 8) | 
            pickingPixelBuffer[2],
            // x = i % textureWidth,
            x = (i - Math.floor(textureWidth / 512) * 256) % textureWidth, // EDIT2: this is the hack
            y = i / textureWidth;
    console.log(x, y);
}

正如我所说,y坐标完美无缺。但是x坐标很远。当我将鼠标向下拖动到屏幕上时,x坐标向右弯曲,跳跃约为纹理宽度的1/4。当我在屏幕上拖动鼠标时,如果我可以避免任何垂直鼠标移动,则x坐标会在右偏移处移动,但不在正确的位置。每当我击中1/4标记中的一个时,它似乎都会跳转位置。

因为 1/4,所以我生成纹理的数学可能是错误的。但是对于我的生活,我看不到它。

编辑:的确,如果我将纹理限制为仅256像素宽,则效果非常好。

EDIT2:我已经找到了解决问题的pick函数的黑客攻击,但我不明白它为什么会起作用。

EDIT3:嗯,“工作”条件是对象与屏幕的显示比率为1比1。不同方向仍然存在一些问题,但这与其他问题无关。我怀疑它与重新采样纹理有关。

EDIT4:这是默认的纹理过滤设置。

1 个答案:

答案 0 :(得分:1)

OH!整个时间都是纹理过滤。我需要将generatePickingTexture功能更改为:

function generatePickingTexture(w, h){
    var canvas = document.createElement("canvas");
    canvas.width = w;
    canvas.height = h;
    var texture = new THREE.Texture(canvas, THREE.UVMapping, THREE.RepeatWrapping, THREE.RepeatWrapping, THREE.NearestFilter, THREE.NearestFilter);
    var gfx = canvas.getContext("2d"),
            l = w * h,
            pixels = gfx.createImageData(w, h);
    for(var i = 0, p = 0; i < l; ++i, p += 4){
        pixels.data[p]   = (0xff0000 & i) >> 16;
        pixels.data[p+1] = (0x00ff00 & i) >> 8;
        pixels.data[p+2] = (0x0000ff & i) >> 0;
        pixels.data[p+3] = 0xff;
    }
    gfx.putImageData(pixels, 0, 0);

    texture.needsUpdate = true;
    return texture;
}