Three.js/WebGL: Large spheres appear broken at intersection

前端 未结 3 1131
南笙
南笙 2020-11-29 12:37

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 m

相关标签:
3条回答
  • 2020-11-29 13:04

    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.

    0 讨论(0)
  • 2020-11-29 13:04

    You could also try using a logarithmic depth buffer:

    renderer = new THREE.WebGLRenderer( { logarithmicDepthBuffer: true } );
    
    0 讨论(0)
  • 2020-11-29 13:18

    You're suffering from a precision issue in the depth buffer. The larger the scale of your scene the more pronounced this becomes -- especially for objects that span a large distance. The depth buffer has only 32bits (floating point I believe) to work with. As you increase the Z-Range of your camera the precision drops. A standard camera tends to have increased precision near the "near" plane, and reduces to the distance (though I'm not positive on what matrix three.js is actually using).

    Either you reduce your scene size, or move the near plane further away from the camera. If you search the topic of depth buffers and precision you can find a lot of information on this topic. Refer to the generic OpenGL info as well, not just three.js or WebGL.

    Note: To clarify, the reason the two scenes are different is because you haven't scaled the camera's settings the same. In the scene that works simple set the near plane to "0.00001" and you'll see the same problem.

    0 讨论(0)
提交回复
热议问题