orillusion icon indicating copy to clipboard operation
orillusion copied to clipboard

[BUG]: mode=pixel时,PickFire对于visibleMap的meshID读取存在误差

Open bestsamsg opened this issue 1 year ago • 3 comments

Bug描述

我在实现一个拾取功能,但在测试中,PickFire返回的meshID经常错误,从而找不到对应的ColliderComponent,最终导致拾取不到物体

Bug复现流程

通过以下步骤产生bug:

  1. orillusion\src\loader\parser\gltf\GLTFSubParserConverter.ts中的convertprimitives方法,给类型为Object3D的model添加ColliderComponent
model.addComponent(ColliderComponent);
  1. 给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;
}
  1. 给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;
  }
}
  1. 在demo中对POINTER_CLICK进行监听,并主动触发PickFire.pickMeshId demo.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),这是一种情况,可能还有其他原因。

bestsamsg avatar Jul 18 '24 02:07 bestsamsg

能不能提供一下案例?现在不太好确定问题~

按道理说是不会出现324.75这种meshID的情况的~

orillusion-admin avatar Jul 18 '24 13:07 orillusion-admin

能不能提供一下案例?现在不太好确定问题~

按道理说是不会出现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
}

后续如果有其他的问题再反馈,感谢回复。

bestsamsg avatar Jul 19 '24 02:07 bestsamsg

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.

Richard74Huang avatar Nov 02 '24 13:11 Richard74Huang