Three.js/WebGL:大球体在交叉路口出现破损

2022-01-14 00:00:00 geometry javascript three.js webgl

让我先说我对 3D 图形非常缺乏经验.

问题

我正在使用 Three.js.我有两个球体(故意)在我的 WebGL 模型中发生碰撞.当我的球体非常大时,重叠的球体在它们相交的地方出现破碎",但较小的球体呈现完美.

我有一个非常具体的原因为某些对象使用如此大的单位,而缩小对象并不是一个真正的选择.

示例

这里是一个大领域的小提琴:http://jsfiddle.net/YSX7h/ p>

对于较小的:http://jsfiddle.net/7Lca2/

代码

var radiusUnits = 1790;//179000000变量容器;var 相机、场景、渲染器;var 时钟 = new THREE.Clock();变种交叉;变种平面;var 控件;变量立方体;var cubeMaterial = new THREE.MeshBasicMaterial( { color: 0xffffff, vertexColors: THREE.VertexColors } );在里面();动画();函数初始化(){camera = new THREE.PerspectiveCamera(100, window.innerWidth/window.innerHeight, 0.1, 3500000);控制=新的三个.轨道控制(相机);camera.position.set(2000, 2000, 2000);相机.位置.z = 500;场景 = 新的三个场景();var texture = THREE.ImageUtils.loadTexture('http://i.imgur.com/qDAEVoo.jpg');var material = new THREE.MeshBasicMaterial({颜色:0xFFFFFF,地图:纹理,不透明度:1});var material1 = new THREE.MeshBasicMaterial({ color: 0xFF0000, 线框: true, opacity:1 });var geometry = new THREE.SphereGeometry(radiusUnits, 32, 32);var geometry1 = new THREE.SphereGeometry(radiusUnits, 32, 32);var mesh = new THREE.Mesh(geometry, material);var mesh1 = new THREE.Mesh(geometry1, material1);网格1.position.set(0, 1000, 0);网格.position.set(0, -1000, 0);场景.添加(网格);场景.add(mesh1);renderer = new THREE.WebGLRenderer( { antialias: true, alpha: true } );document.body.appendChild(renderer.domElement);renderer.setSize(window.innerWidth, window.innerHeight);}函数 onWindowResize() {renderer.setSize(window.innerWidth, window.innerHeight);使成为();}函数动画(){控制.更新();requestAnimationFrame( 动画 );}window.requestAnimFrame = (function(){返回 window.requestAnimationFrame ||window.webkitRequestAnimationFrame ||window.mozRequestAnimationFrame ||window.oRequestAnimationFrame ||window.msRequestAnimationFrame ||函数(回调){window.setTimeout(回调, 1000/60);};})();(函数动画循环(){requestAnimFrame(动画循环);使成为();})();函数渲染(){var delta = clock.getDelta();renderer.render(场景,相机);}

为什么会发生这种情况?除了缩小这些对象之外,我还能做些什么来解决这个问题?

提前致谢.

解决方案

简而言之,将你的z近平面设置得更远

改变

camera = new THREE.PerspectiveCamera(100、window.innerWidth/window.innerHeight, 0.1, 3500000);

var zNear = 1000;var zFar = 3500000;相机=新的三.透视相机(100、window.innerWidth/window.innerHeight、zNear、zFar);

注意:我不知道 1000 是否可行,如果不尝试 10000.

zBuffer,过去能够分辨哪些像素在其他先前绘制的像素之前的东西,具有有限的分辨率.在 WebGL 中,它可能是 16 位、24 位或 32 位.我猜 24 位是最常见的.为了便于说明,我们假设它只有 4 位.这意味着对于给定的 z 范围,只有 16 个可能的值.给定用于 3D 投影的标准数学,在 4 位 zbuffer 上,如果范围是 zNear = 0.1zFar = 3500000,则 16 个可能的值类似于

0 = 0.1001 = 0.107 范围:0.0072 = 0.115 范围:0.0083 = 0.125 范围:0.0104 = 0.136 范围:0.0115 = 0.150 范围:0.0146 = 0.167 范围:0.0177 = 0.187 范围:0.0218 = 0.214 范围:0.0279 = 0.250 范围:0.03610 = 0.300 范围:0.05011 = 0.375 范围:0.07512 = 0.500 范围:0.12513 = 0.750 范围:0.25014 = 1.500 范围:0.75015 = 3499999.993 范围:3499998.493

正如您所见,值之间的范围呈指数增长,这意味着远离相机几乎没有分辨率.

如果我们将 zNear 增加到 1000,我们得到

0 = 1000.0001 = 1071.407 范围:71.4072 = 1153.795 范围:82.3893 = 1249.911 范围:96.1154 = 1363.495 范围:113.5845 = 1499.786 范围:136.2916 = 1666.349 范围:166.5647 = 1874.531 范围:208.1828 = 2142.158 范围:267.6269 = 2498.929 范围:356.77110 = 2998.287 范围:499.35811 = 3747.056 范围:748.76912 = 4994.292 范围:1247.23613 = 7486.097 范围:2491.80514 = 14940.239 范围:7454.14215 = 3500000.000 范围:3485059.761

你可以看到它开始散开一点.在 zNear 为 0.1 且 zFar 为 3500000 的 24 位深度缓冲区上,最后 15 个单位之间的范围为

16777201 = 115869.957 范围:7485.45416777202 = 124466.066 范围:8596.10916777203 = 134439.829 范围:9973.76316777204 = 146151.280 范围:11711.45116777205 = 160097.879 范围:13946.59916777206 = 176987.000 范围:16889.12216777207 = 197859.711 范围:20872.71116777208 = 224313.847 范围:26454.13516777209 = 258933.659 范围:34619.81216777210 = 306189.940 范围:47256.28116777211 = 374545.842 范围:68355.90216777212 = 482194.095 范围:107648.25316777213 = 676678.248 范围:194484.15416777214 = 1134094.478 范围:457416.22916777215 = 3499999.993 范围:2365905.515

zNear 一样,它们在 1000 处

16777201 = 3489810.475 范围:725.55316777202 = 3490536.330 范围:725.85516777203 = 3491262.487 范围:726.15716777204 = 3491988.947 范围:726.45916777205 = 3492715.709 范围:726.76216777206 = 3493442.773 范围:727.06416777207 = 3494170.140 范围:727.36716777208 = 3494897.810 范围:727.67016777209 = 3495625.784 范围:727.97316777210 = 3496354.060 范围:728.27716777211 = 3497082.640 范围:728.58016777212 = 3497811.524 范围:728.88416777213 = 3498540.712 范围:729.18816777214 = 3499270.204 范围:729.49216777215 = 3500000.000 范围:729.796

哪个可能更合理一点?这基本上是说当远离相机时,小于~728个单位的2个点可能会被错误地排序.或者从正面的角度来看,只要 2 个点与相机之间的距离至少为 728 个单位,它们就会被正确排序.

所有这些都是为了指出您必须为您的应用程序适当地设置远近剪裁平面.

我可能应该注意到,所应用的数学只是最常见的数学,并且可能与 three.js 默认使用的数学相同.使用您自己的顶点着色器,您可以让 zbuffer 做其他事情.这是一篇很好的文章.p>

Let me preface this with saying I'm very inexperienced with 3D graphics.

Problem

I'm using Three.js. I have two spheres which (deliberately) collide in my WebGL model. When my spheres are incredibly large, the overlapping spheres appear "broken" where they intersect, but smaller spheres render perfectly fine.

I have a very specific reason for using such large units for some objects, and scaling down objects isn't really an option.

Example

Here is a fiddle for the larger spheres: http://jsfiddle.net/YSX7h/

and for the smaller ones: http://jsfiddle.net/7Lca2/

Code

var radiusUnits = 1790; // 179000000
var container;
var camera, scene, renderer;
var clock = new THREE.Clock();
var cross;
var plane;
var controls;
var cube;
var cubeMaterial = new THREE.MeshBasicMaterial( { color: 0xffffff, vertexColors: THREE.VertexColors } );
init();
animate();

function init() {
    camera = new THREE.PerspectiveCamera(100, window.innerWidth / window.innerHeight, 0.1, 3500000);
    controls = new THREE.OrbitControls(camera);
    camera.position.set(2000, 2000, 2000);
    camera.position.z = 500;
    scene = new THREE.Scene();

    var texture = THREE.ImageUtils.loadTexture('http://i.imgur.com/qDAEVoo.jpg');
    var material = new THREE.MeshBasicMaterial({
        color: 0xFFFFFF,
        map: texture,
        opacity:1
    });

    var material1 = new THREE.MeshBasicMaterial({ color: 0xFF0000, wireframe: true, opacity:1 });
    var geometry = new THREE.SphereGeometry(radiusUnits, 32, 32);
    var geometry1 = new THREE.SphereGeometry(radiusUnits, 32, 32);
    var mesh = new THREE.Mesh(geometry, material);
    var mesh1 = new THREE.Mesh(geometry1, material1);
    mesh1.position.set(0, 1000, 0);
    mesh.position.set(0, -1000, 0);

    scene.add(mesh);
    scene.add(mesh1);

    renderer = new THREE.WebGLRenderer( { antialias: true, alpha: true } );

    document.body.appendChild(renderer.domElement);
    renderer.setSize( window.innerWidth, window.innerHeight );
}

function onWindowResize() {
    renderer.setSize( window.innerWidth, window.innerHeight );
    render();
}

function animate() {
    controls.update();
    requestAnimationFrame( animate );
}

window.requestAnimFrame = (function(){
    return  window.requestAnimationFrame       ||
                window.webkitRequestAnimationFrame ||
                window.mozRequestAnimationFrame    ||
                window.oRequestAnimationFrame      ||
                window.msRequestAnimationFrame     ||
                function( callback ){
                  window.setTimeout(callback, 1000 / 60);
                };
})();

(function animloop(){
    requestAnimFrame(animloop);
    render();
})();

function render() {
    var delta = clock.getDelta(); 
    renderer.render( scene, camera );
}

Why, exactly, does this happen? And is there anything I can do to fix this, short of scaling down these objects?

Thanks in advance.

解决方案

The short answer, set your z near plane further away

Change

camera = new THREE.PerspectiveCamera(
    100, window.innerWidth / window.innerHeight, 0.1, 3500000);

to

var zNear = 1000;
var zFar = 3500000;
camera = new THREE.PerspectiveCamera(
    100, window.innerWidth / window.innerHeight, zNear, zFar);

Note: I don't know if 1000 will work, if it doesn't try 10000.

A zBuffer, the thing used to be able to tell which pixels go in front of other previously drawn pixels, has limited resolution. In WebGL it could be 16bits, 24 or 32. I'm guessing 24 is the most common. For the point of illustration let's assume it was just 4 bits though. That would mean for a given z range there are only 16 possible values. Given the standard math used for 3D projection, on a 4 bit zbuffer, if the range was zNear = 0.1 and zFar = 3500000 the 16 possible values are something like

0 = 0.100
1 = 0.107         range: 0.007
2 = 0.115         range: 0.008
3 = 0.125         range: 0.010
4 = 0.136         range: 0.011
5 = 0.150         range: 0.014
6 = 0.167         range: 0.017
7 = 0.187         range: 0.021
8 = 0.214         range: 0.027
9 = 0.250         range: 0.036
10 = 0.300        range: 0.050
11 = 0.375        range: 0.075
12 = 0.500        range: 0.125
13 = 0.750        range: 0.250
14 = 1.500        range: 0.750
15 = 3499999.993  range: 3499998.493 

As you can see the range between values increase exponentially meaning there is almost no resolution far away from the camera.

If we increase zNear to 1000 we get

0 = 1000.000
1 = 1071.407       range: 71.407
2 = 1153.795       range: 82.389
3 = 1249.911       range: 96.115
4 = 1363.495       range: 113.584
5 = 1499.786       range: 136.291
6 = 1666.349       range: 166.564
7 = 1874.531       range: 208.182
8 = 2142.158       range: 267.626
9 = 2498.929       range: 356.771
10 = 2998.287      range: 499.358
11 = 3747.056      range: 748.769
12 = 4994.292      range: 1247.236
13 = 7486.097      range: 2491.805
14 = 14940.239     range: 7454.142
15 = 3500000.000   range: 3485059.761 

You can see it starting to spread out a little. On a 24bit depth buffer with zNear at 0.1 and zFar at 3500000 the range between the last 15 units is

16777201 = 115869.957       range: 7485.454
16777202 = 124466.066       range: 8596.109
16777203 = 134439.829       range: 9973.763
16777204 = 146151.280       range: 11711.451
16777205 = 160097.879       range: 13946.599
16777206 = 176987.000       range: 16889.122
16777207 = 197859.711       range: 20872.711
16777208 = 224313.847       range: 26454.135
16777209 = 258933.659       range: 34619.812
16777210 = 306189.940       range: 47256.281
16777211 = 374545.842       range: 68355.902
16777212 = 482194.095       range: 107648.253
16777213 = 676678.248       range: 194484.154
16777214 = 1134094.478       range: 457416.229
16777215 = 3499999.993       range: 2365905.515

Where as with zNear at 1000 they're

16777201 = 3489810.475       range: 725.553
16777202 = 3490536.330       range: 725.855
16777203 = 3491262.487       range: 726.157
16777204 = 3491988.947       range: 726.459
16777205 = 3492715.709       range: 726.762
16777206 = 3493442.773       range: 727.064
16777207 = 3494170.140       range: 727.367
16777208 = 3494897.810       range: 727.670
16777209 = 3495625.784       range: 727.973
16777210 = 3496354.060       range: 728.277
16777211 = 3497082.640       range: 728.580
16777212 = 3497811.524       range: 728.884
16777213 = 3498540.712       range: 729.188
16777214 = 3499270.204       range: 729.492
16777215 = 3500000.000       range: 729.796

Which is probably a little more reasonable? It's basically saying 2 points that are less than ~728 units different when far away from the camera may be sorted incorrectly. Or to put it in a positive light, as long as 2 points are at least 728 units away from each other in their distance from the camera they'll be sorted correctly.

All of this is to point out that you have to set your near and far clipping planes appropriately for your application.

I should probably note that the math being applied is just the most common math and probably the same math that three.js used by default. With your own vertex shaders you could make the zbuffer do something else. Here's a good article on it.

相关文章