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.1
和 zFar = 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.
相关文章