首页 > 解决方案 > 为什么重叠的透明对象在我的 glTF 中经常以错误的顺序呈现?

问题描述

我生成了一个 glTF 文件,其中有两个盒子,每个盒子都有一个透明的纹理(.png 图像是 RGBA 格式)。

我在多个场合都经历过,这个 glTF 渲染不正确。那就是当我从顶部看它时,一切都按原样呈现,但是当我从底部看它时,似乎应该在背景中的盒子被呈现在更近的盒子的顶部: 渲染错误 我观察到这个当我使用完全不透明的RGBA 纹理时的行为

alpha 值 < 1 时效果更微妙,但从底部看时渲染顺序仍然错误: 透明度错误

然而,在使用https://sandbox.babylonjs.com/时工作的两个 glTF 文件中,只有opaque-texture-atlas.gltf失败(可以在下面找到的那个)。

现在我问自己这是否是一个错误,Three.js或者我的 glTF 模型是否构造不正确(我自己在代码中构建它)。例如,我怀疑盒子底面的法线是错误的(当前(0, -1, 0)),或者我错过了一些关于"alphaMode" : "MASK".

为了进一步测试这一点,我构建了自己的小查看器,Three.js并且能够使用版本r.126和不透明的 glTF 重现它(但有趣的是,不是使用部分透明的)。

var camera, controls, scene, renderer;

init();
animate();

function init() {
    // renderer
    renderer = new THREE.WebGLRenderer({ antialias: true });
    renderer.setSize(window.innerWidth, window.innerHeight);
    document.body.appendChild(renderer.domElement);

    // camera
    camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.01, 1000000);
    camera.position.set(4, 4, 6);
    camera.up.set(0, 1, 0);
    camera.lookAt(new THREE.Vector3(0, 0, 0));

    // scene
    scene = new THREE.Scene();
    scene.background = new THREE.Color( 0xdcdcdc );

    //lights
    var pointLight1 = new THREE.PointLight(0xffffff, 5, 1000, 2);
    pointLight1.position.set(30, 30, 40);
    pointLight1.castShadow = true;
    scene.add(pointLight1);

    var pointLight2 = new THREE.PointLight(0xffffff, 1, 1000, 2);
    pointLight2.position.set(-30, -30, -40);
    pointLight2.castShadow = true;
    scene.add(pointLight2);

    var directionalLight = new THREE.DirectionalLight(0xffffff, 1);
    directionalLight.position.set(0, 20, 40);
    directionalLight.castShadow = true;
    scene.add(directionalLight);

    //controls
    controls = new THREE.TrackballControls(camera, renderer.domElement);
    controls.rotateSpeed = 3.0;
    controls.zoomSpeed = 6.8;
    controls.panSpeed = 9.0;
    controls.noZoom = false;
    controls.noPan = false;
    controls.staticMoving = true;
    controls.dynamicDampingFactor = 0.3;
    controls.keys = [ 65, 83, 68 ];
    controls.addEventListener('change', render);

    // load gltf model and texture
    const loader = new THREE.GLTFLoader();
    loader.load('https://gist.githubusercontent.com/FrankenApps/0fe05e47f148cacb38e2509640e1aa54/raw/fad6897ed11d9e7ad8b6f24400b3bbd5bf2849d1/opaque-texture-atlas.gltf', (gltf) => {
        gltf.scene.scale.set(1, 1, 1);
        var mesh = gltf.scene;
        mesh.castShadow = true;
        mesh.receiveShadow = true;

        scene.add(mesh);
    });

    window.addEventListener('resize', onWindowResize, false);
}

function onWindowResize() {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(window.innerWidth, window.innerHeight);
    controls.handleResize();
    renderer.render(scene, camera);
}

function render() {
    renderer.render(scene, camera);
}

function animate() {
    requestAnimationFrame(animate);
    controls.update();
    render();
}
html,body {
    overflow: hidden;
    width: 100%;
    height: 100%;
    margin: 0;
    padding: 0;
}

#canvas {
    width: 100%;
    height: 100%;
    touch-action: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r126/three.min.js"></script>
<script src="https://unpkg.com/three@0.126.0/examples/js/controls/TrackballControls.js"></script>
<script src="https://unpkg.com/three@0.126.0/examples/js/loaders/GLTFLoader.js"></script>

更新

感谢@WestLangley,我基本上知道我的问题与meshes没有偏移有关。这是真的,但基本上我的全部想法是为什么我什至尝试构建一个 .gltf。原因是,我想尽量减少对性能的绘制调用。所以我基本上计算顶点位置然后构建对象。为了使它起作用,我将需要尽可能多的顶点primitive。然后我还计算正确的 uv 坐标并从纹理图集中获取“顶点组”/“三角形网格”的纹理。不幸的是,GPU 有一个最大的纹理大小,这意味着如果我的纹理图集已满,我会继续创建另一个网格 + 图元(以绘制调用为代价)。

这个想法可以在下面的新示例中看到

{
  "accessors": [
    {
      "bufferView": 0,
      "byteOffset": 0,
      "count": 72,
      "componentType": 5126,
      "extras": {},
      "type": "VEC3",
      "min": [
        -0.75,
        -0.5,
        -0.25
      ],
      "max": [
        0.75,
        3.5,
        0.25
      ]
    },
    {
      "bufferView": 0,
      "byteOffset": 12,
      "count": 72,
      "componentType": 5126,
      "extras": {},
      "type": "VEC3"
    },
    {
      "bufferView": 0,
      "byteOffset": 24,
      "count": 72,
      "componentType": 5126,
      "extras": {},
      "type": "VEC2"
    },
    {
      "bufferView": 1,
      "byteOffset": 0,
      "count": 72,
      "componentType": 5125,
      "extras": {},
      "type": "SCALAR"
    }
  ],
  "asset": {
    "extras": {},
    "version": "2.0"
  },
  "buffers": [
    {
      "byteLength": 2592,
      "uri": "data:application/octet-stream;base64,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",
      "extras": {}
    }
  ],
  "bufferViews": [
    {
      "buffer": 0,
      "byteLength": 2592,
      "byteStride": 32,
      "name": "Cube0",
      "target": 34962,
      "extras": {}
    },
    {
      "buffer": 0,
      "byteLength": 288,
      "byteOffset": 2304,
      "name": "Indices0",
      "extras": {}
    }
  ],
  "extras": {},
  "images": [
    {
      "mimeType": "image/png",
      "uri": "data:image/png;base64,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",
      "extras": {}
    }
  ],
  "materials": [
    {
      "alphaMode": "BLEND",
      "doubleSided": false,
      "pbrMetallicRoughness": {
        "baseColorFactor": [
          1.0,
          1.0,
          1.0,
          1.0
        ],
        "baseColorTexture": {
          "index": 0,
          "texCoord": 0,
          "extras": {}
        },
        "metallicFactor": 1.0,
        "roughnessFactor": 1.0,
        "extras": {}
      },
      "emissiveFactor": [
        0.0,
        0.0,
        0.0
      ],
      "extras": {}
    }
  ],
  "meshes": [
    {
      "extras": {},
      "primitives": [
        {
          "attributes": {
            "TEXCOORD_0": 2,
            "POSITION": 0,
            "NORMAL": 1
          },
          "extras": {},
          "indices": 3,
          "material": 0
        }
      ]
    }
  ],
  "nodes": [
    {
      "extras": {},
      "mesh": 0
    }
  ],
  "scenes": [
    {
      "extras": {},
      "nodes": [
        0
      ]
    }
  ],
  "textures": [
    {
      "source": 0,
      "extras": {}
    }
  ]
}

所有使用的 glTF 文件都可以在这个gist 中找到。

正如您从下方看到的那样,即使两个 RGBA .png 纹理的 alpha 值都为 255,上框也会发光。从顶部看模型时也不会发生这种情况。

我检查了@WestLangley 提供的链接中提供的选项,但不幸的是它们对我没有帮助,因为在大多数情况下(除了我的纹理图集已满时)我将不得不处理单个对象。那么是否有一个选项可以强制渲染器检查每个顶点或每个三角形的深度或类似的东西?

标签: javascriptthree.jsgltfbabylonjs

解决方案


推荐阅读