javascript - 为什么重叠的透明对象在我的 glTF 中经常以错误的顺序呈现?
问题描述
我生成了一个 glTF 文件,其中有两个盒子,每个盒子都有一个透明的纹理(.png 图像是 RGBA 格式)。
我在多个场合都经历过,这个 glTF 渲染不正确。那就是当我从顶部看它时,一切都按原样呈现,但是当我从底部看它时,似乎应该在背景中的盒子被呈现在更近的盒子的顶部: 我观察到这个当我使用完全不透明的RGBA 纹理时的行为
- 在带有引擎的 VSCode glTF 扩展的预览器中
Three.js
(但不带有Babylon.js
和Filament
- Three.js glTF 查看器:gltf-viewer.donmccurdy.com
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 提供的链接中提供的选项,但不幸的是它们对我没有帮助,因为在大多数情况下(除了我的纹理图集已满时)我将不得不处理单个对象。那么是否有一个选项可以强制渲染器检查每个顶点或每个三角形的深度或类似的东西?
解决方案
推荐阅读
- java - XMLUnit-2 使用 `withNodeMatcher` API 失败,用于 List 的随机顺序
- python - 缓存结果似乎比重新计算结果慢
- r - 如何根据某些变量绘制条形图的条件彩色轮廓?
- java - 将 MySQL 数据库打包到 java se 应用程序
- r - 如何在 R 中使用 mapview() 标题给出地图?
- clang - 如何在 llvm ir 中创建 thread_local 变量?
- python - python函数应该总是返回一些值有什么原因吗?
- javascript - 如何从返回承诺的回调函数中获取数据?
- python - 将 GeoJSON 加载到 GeoPandas 数据帧中 - 格式错误或给出“引发 TypeError”
- python - 在等待数据库查询响应时从外部停止线程