Orillusion / orillusion

Orillusion is a pure Web3D rendering engine which is fully developed based on the WebGPU standard.
https://www.orillusion.com
MIT License
4.97k stars 621 forks source link

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

Open bestsamsg opened 3 months ago

bestsamsg commented 3 months ago

Bug描述

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

Bug复现流程

通过以下步骤产生bug:

  1. orillusion\src\loader\parser\gltf\GLTFSubParserConverter.ts中的convertprimitives方法,给类型为Object3D的model添加ColliderComponent

    model.addComponent(ColliderComponent);
  2. 给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;
    }
  3. 给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

本机系统 (请填写完整):

本机配置

代码示例

其他信息

meshID可能存在小数点,然后又使用Math.floor读取(假设visibleMap返回meshID结果为324.75, 那么即使加上0.1,经过floor后结果也是324,实际上325才是对的meshID),这是一种情况,可能还有其他原因。

orillusion-admin commented 3 months ago

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

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

bestsamsg commented 3 months ago

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

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

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