我正在尝试检测用户点击的对象上纹理的位置,以便我可以重新绘制纹理。
我知道我可以将带有颜色编码纹理的对象渲染到单独的渲染目标,并使用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);
所以对于像这样的对象:
拾取纹理最终看起来像这样:
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:这是默认的纹理过滤设置。
答案 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;
}