[BUG]: mode=pixel时,PickFire对于visibleMap的meshID读取存在误差
Bug描述
我在实现一个拾取功能,但在测试中,PickFire返回的meshID经常错误,从而找不到对应的ColliderComponent,最终导致拾取不到物体
Bug复现流程
通过以下步骤产生bug:
- 在
orillusion\src\loader\parser\gltf\GLTFSubParserConverter.ts中的convertprimitives方法,给类型为Object3D的model添加ColliderComponent
model.addComponent(ColliderComponent);
- 给PickCompute.ts添加log对比
public getPickMeshID(): number {
var meshID = this._outBuffer.outFloat32Array[0];
const result = Math.floor(meshID + 0.1);
console.log(meshID, result, 'DDDDDDDDDDDDD')
return result;
}
- 给PickFire.ts添加测试拾取的方法
PickFire
public pickMeshId(){
this._pickCompute.compute(this._view);
const meshId = this._pickCompute.getPickMeshID();
const collider = this.mouseEnableMap.get(meshId);
//collider经常出现undefined
if(collider) {
return meshId;
}
}
- 在demo中对
POINTER_CLICK进行监听,并主动触发PickFire.pickMeshIddemo.ts
Engine3D.inputSystem.addEventListener(PointerEvent3D.POINTER_CLICK, e=>{
const id= view.pickFire.pickMeshId();
console.log(id);
}, this)
期待的结果
PickFire要拾取到准确的meshId
报错截图
测试引擎版本:
本地运行出错的Orillusion引擎版本v0.7.2
本机系统 (请填写完整):
- OS: win10
- Browser: chrome
- Version: 120
本机配置
- CPU: i7
- Graphics Card: 1660 Ti
代码示例
其他信息
meshID可能存在小数点,然后又使用Math.floor读取(假设visibleMap返回meshID结果为324.75, 那么即使加上0.1,经过floor后结果也是324,实际上325才是对的meshID),这是一种情况,可能还有其他原因。
能不能提供一下案例?现在不太好确定问题~
按道理说是不会出现324.75这种meshID的情况的~
能不能提供一下案例?现在不太好确定问题~
按道理说是不会出现324.75这种meshID的情况的~
我这边确实出现过很多次这种情况,因修改了不少源码,不好给案例。
不过,昨天通过将f32的meshId转4个uint8,存在materialMap.rgba中(pick_cs新增此sampler, 并读取存于原来的pick_Tangent中),在PickCompute.ts中unpack为float32,可以拿到一个误差在0.001内的数,通过Math.round就能拿到准确的meshId。
目前还没出现过点击失败的情况,算是一个方案吧。
大概代码 orillusion\src\assets\shader\lighting\BxDF_frag.ts
//sam-add-begin
fn float32ToUint8(value: f32) -> vec4<u32> {
var intRep = bitcast<u32>(value);
let byte0 = intRep & 0xFFu;
let byte1 = (intRep >> 8) & 0xFFu;
let byte2 = (intRep >> 16) & 0xFFu;
let byte3 = (intRep >> 24) & 0xFFu;
return vec4<u32>(byte0, byte1, byte2, byte3);
}
//sam-add-end
fn BxDFShading(){
...
//sam-replace-begin
#if USE_BATCHID
//ORI_FragmentOutput.material = vec4<f32>(1.0,fragData.Roughness,fragData.Metallic,1.0);
//sam-replace-to
//材质贴图添加meshId输出
let uint8Values = float32ToUint8(ORI_VertexVarying.index);
// 将 uint8 转换为 [0.0, 1.0] 范围内的 f32
let r = f32(uint8Values.x) / 255.0;
let g = f32(uint8Values.y) / 255.0;
let b = f32(uint8Values.z) / 255.0;
let a = f32(uint8Values.w) / 255.0;
ORI_FragmentOutput.material = vec4<f32>(r, g, b, a);
#endif
//sam-replace-end
...
}
orillusion\src\io\picker\PickCompute.ts
public getPickMeshID(): number {
//sam-replace-begin
//结果返回错误
// var meshID = this._outBuffer.outFloat32Array[0] + 0.1;
// return Math.floor(meshID);
//sam-replace-to
const byte0 = this._outBuffer.outFloat32Array[12] * 255;
const byte1 = this._outBuffer.outFloat32Array[13] * 255;
const byte2 = this._outBuffer.outFloat32Array[14] * 255;
const byte3 = this._outBuffer.outFloat32Array[15] * 255;
const uint32Value = (byte3 << 24) | (byte2 << 16) | (byte1 << 8) | byte0
const float32Array = new Float32Array(new Uint32Array([uint32Value]).buffer);
const restoredValue = float32Array[0];
console.log(restoredValue, 'd')
if(window['startDebug']){
debugger
}
return Math.round(restoredValue);
//sam-replace-end
}
后续如果有其他的问题再反馈,感谢回复。
Orillusion_admin 你好, 我也遇到一些pixel pick的問題, 在 PickCompute中的normal計算: public getPickWorldNormal(target?: Vector3): Vector3 { target ||= new Vector3(); var x = this._outBuffer.outFloat32Array[8]; var y = this._outBuffer.outFloat32Array[9]; var z = this._outBuffer.outFloat32Array[10]; target.set(x * 2.0 - 1.0, y * 2.0 - 1.0, z * 2.0 - 1.0).normalize(); return target; }
target.set(x * 2.0 - 1.0, y * 2.0 - 1.0, z * 2.0 - 1.0).normalize(); 這行,如果用一個立方體來驗證,那求得的normal會難以反推pick到立方體的哪個面向. 但是如果改成target.set(x, y, z).normalize(); 就可以.
原本打算繼承PickCompute來override此function,但無奈PickFire/PickCompute幾個關鍵member variables都是private.