it's my first question here so I hope it conforms to the guidelines. Also please excuse my bad English.
I'm currently working on a WebGL application based on three.js that displays thousands of cubes and lines between those cubes. I have several performance issues so I decided to merge all the cubes to one geometry. That helped a little but actually the performance is slowed down by those thousands of lines (all having their own geometry (based on the NURBSCurve example of three.js) and shader materials (shader blends two colors from the start to the end of the line and controls opacity). My biggest data includes over 9000 lines and over 5000 cubes. Without the lines the FPS rate is between 45 - 50 (DirectX) or 20 FPS (OpenGL) but with the lines the performance drops to 5 FPS. I only have an Intel HD Graphics (5th Gen) graphic card so the maximum FPS seems to be restricted to 60 FPS but that is totally enough in my case.
As I said before the lines are NURBS curves and they range from short curves to long, complex curves. Another requirement (which gives me a little headache ;-)) is that each line differs in width, so I actually have to use OpenGL, which is slower in my case and also causes some other problems.
Anyway I have tried several approaches but none of them really helped me to solve the problem.
1) Create pipes for each line and merge the geometries. -> This slowed down the creation of the scene from 1 second to several minutes. Besides FPS rate couldn't be improved. This is actually expected due to the mass amount of vertices and faces produced by this approach.
2) Reduce points of curve. -> Reduced from 200 to 50 points which helped to improve FPS a little. A reduction to 25 points didn't bring any more improvements. I was thinking to write a method to reduce unneeded points (like in straight curves, 2 instead of 50 points are enough) but I don't know how to achieve this so I thought about other ways first. Maybe I will come back to this.
3) Use BufferGeometry. -> It seems like BufferGeometry cannot be used together with shader material. At least in my case I didn't get it to work so that none of my tries displayed anything. By the way I used THREE.BufferGeometryUtils to create the buffer geometries from my NURBS curves. I tried to set THREE.VertexColors as a parameter to the function and colorsNeedUpdate in the geometry but still no display. Actually, I also tried buffergeometry with the curbes geometry and didn't get any improvements in performance.
4) Use splines instead of NURBS curves. ->The paths of the curves were not like I would like them to be and there wasn't any improvements.
5) Merge lines to one line with THREE.LinePieces. -> Well, although I had to double the number of vertices, this actually helped a lot. In case of DirectX, performance improved from 5 FPS to 25 FPS (OpenGL still at 4 FPS) but this solution is not appropriate in my case. The reason is that the lines cannot differ in width, which is in case of DirectX restricted to 1 anyway. I was wondering how other people solved the issue of line width restriction and found out that some are creating thicker lines via Geometry Shader. So my new hope was this Geometry Shader. But then I found out that Geometry shaders are not supported by WebGL.
Sorry for the really long explanation of my approaches so far. After trying out all that I cannot come up with any new ideas. Now I would like to know from the experts if I should just live with the FPS drop or if there is still another way which I could try out?
I came up with the idea to bundle lines by width and then merge these bundles using THREE.LinePieces. In the best case I could reduce about 860 lines to just 2. Of course the effectiveness of this solution depends on the data. Besides, I still have the problem that DirectX is much faster than OpenGL (e.g. 50 FPS instead of 8 FPS) but I need the latter one in order to support thicker lines. So if anyone comes up with a solution for my problem supporting DirectX I would highly appreciate it :-).
Related
I would like to inquire some insights into rendering a large amount of meshes with the best performance.
I'm working on generative mine-able planets incorporating marching cube chunked terrain. Currently I'm trying to add vegetation/rocks to spruce up the planet surfaces (get it?). I am using the actual chunk loading to (next to the terrain) also load smaller rocks and some grass stuff. That runs pretty well. I am having issues with tree's and boulders (visible on the entire planet surface but LODed, obviously).
Testing different methods have lead me on the road of;
Custom shaders with material clipping based on camera distance; Works okay for about half a million trees made from 2 perpendicular planes (merged into one single bufferGeometry). But those 'models' are not good enough.
THREE.LOD's; Which sucks up fps like crazy, to slow for large amounts of meshes.
THREE.InstancedMesh's; Works pretty well, however I'd have to disable frustumCulling, since the originpoint of the vegetation is not always on screen. Which makes it inefficient.
THREE.InstancedGeometry combined with the custom clipping shaders; I had high hopes for this, it gives the best performance while using actual models. But it still eats up half of the frameRate. The vertexshader still has to process all the vertices to determine if it is within clipping range. Also the same frustumCulling issue applies.
Material.clippingPlanes? Combined with InstancedMeshes; This is what I'm trying now, did not have any luck with it, still trying to figure out exactly how that works..
Does anyone have experience with rendering large amounts of meshes or has some advice for me? Is there a technique I do not yet know about?
Would it help to split up the trees in multiple InstancedMeshes? Would the clippingPlanes give me better performance?
I have a sphere with texture of earth that I generate on the fly with the canvas element from an SVG file and manipulate it.
The texture size is 16384x8192 , and less than this - it's look blurry on close zoom.
But this is a huge texture size and causing memory problems... (But it's look very good when it is working)
I think a better approach would be to split the sphere into 32 separated textures, each in size of 2048x2048
A few questions:
How can I split the sphere and assign the right textures?
Is this approach better in terms of memory and performance from a single huge texture?
Is there a better solution?
Thanks
You could subdivide a cube, and cubemap this.
Instead of having one texture per face, you would have NxN textures. 32 doesn't sound like a good number, but 24 for example does, (6x2x2).
You will still use the same amount of memory. If the shape actually needs to be spherical you can further subdivide the segments and normalize the entire shape (spherify it).
You probably cant even use such a big texture anyway.
notice the top sphere (cubemap, ignore isocube):
Typically, that's not something you'd do programmatically, but in a 3D program like Blender or 3D max. It involves some trivial mesh separation, UV mapping and material assignment. One other approach that's worth experimenting with would be to have multiple materials but only one mesh - you'd still get (somewhat) progressive loading. BUT
Are you sure you'd be better off with "chunks" loading sequentially rather than one big texture taking a huge amount of time? Sure, it'll improve a bit in terms of timeouts and caching, but the tradeoff is having big chunks of your mesh be textureless, which is noticeable and unasthetic.
There are a few approaches that would mitigate your problem. First, it's important to understand that texture loading optimization techniques - while common in game engines - aren't really part of threejs or what it's built for. You'll never get the near-seamless LODs or GPU optimization techniques that you'll get with UE4 or Unity. Furthermore webGL - while having made many strides over the past decade - is not ideal for handling vast texture sizes, not at the GPU level (since it's based on OpenGL ES, suited primarily for mobile devices) and certainly not at the caching level - we're still dealing with broswers here. You won't find a lot of webGL work done with vast textures of the dimensions you refer to.
Having said that,
A. A loader will let you do other things while your textures are loading so your user isn't staring at an 'unfinished mesh'. It lets you be pretty clever with dynamic loading times and UX design. Additionally, take a look at this gist to give you an idea for what a progressive texture loader could look like. A much more involved technique, that's JPEG specific, can be found here but I wouldn't approach it unless you're comfortable with low-level graphics programming.
B. Threejs does have a basic implementation of LOD although I haven't tinkered with it myself and am not sure it's useful for textures; that said, the basic premise to inquire into is whether you can load progressively higher-resolution files on a per-need basis, just like Google Earth does it for example.
C. This is out of the scope of your question - but I'd look into what happens under the hood in Unity's webgl export (which is based on threejs), and what kind of clever tricks are being employed there for similar purposes.
Finally, does your project have to be in webgl? For something ambitious and demanding, sometimes "proper" openGL / DX makes much more sense.
I'm currently working on a game using Three.js. I've been studying software engineering for four years and have been working professionally on backends for two, but I've barely touched on graphics aside from some simple Unity experimenting.
I currently have ~22,000 vertices and ~8,000 faces according to renderstats.js, and my desktop (above average) can't run it above 20 FPS. I'm using Lambert material as well as a single ambient light, so I feel like this isn't too much to ask.
With these figures in mind, is this the expected behavior for three.js rendering?
I would be pretty sure that is not end of the line and you are probably missing some possibilities for massive performance-improvements.
But just to give you some numbers first,
if you leave everything fancy away (including three.js) and just render an ultra-simple point-cloud with one fragment rendered per point, you can easily get to rendering 10-20 million (yes, million) points/vertices on an average GPU.
just with simple shapes and material, I already got three.js to render something in the range of 500k triangles (at 1080p-resolution) at 60FPS without problem. You can probably take those numbers times 10 for latest high-end GPUs.
However, these kinds of numbers are not really helpful.
Some hints:
if you want to debug your rendering-performance, you should first add some metrics. Renderstats is good, but I'd recommend integrating http://spite.github.io/rstats/ for this (see the example).
generally the choice of material shouldn't matter too much, the GPU is way more capable than most people think. It's more likely a problem somewhere else in the pipeline. EDIT from comment: In some cases, like hi-resolution displays with slow GPUs (think mobile-devices) this might be less true and complicated shader-code can slow down your site, but it might worth be looking at the other points first. As the rendering itself happens off-thread (so you can't measure it's duration using regular tools like the devtools-profiler), you can use the EXT_disjoint_timer_query-extension to get some information about what is going on on the GPU.
the number of drawcalls shouldn't be too high: three.js needs to do a single drawcall for every Mesh and Points-object rendered in the scene and too many objects are generally a far bigger problem than objects with lots of vertices. Reducing the number of drawcalls can be done by merging multiple geometries into one and making use of multi-materials, vertex-colors and things like that.
if you are doing postprocessing, the GPU needs to render every pixel on screen several times. This might as well massively limit your performance. This can be optimized by merging multiple postprocessing-passes into one (I admit, that'd be a lot of hard work..)
another problem could be on the JS side: you should use the profiler or timeline-view from the chrome devtools to see if maybe it's the javascript that is taking too much time per frame (shouldn't be more than 8-12ms per frame). I've been told there are ways to optimize the javascript-performance as well :)
Recently started learning webGL and decided to use the Three.js library.
Currently, in addition to, rendering over 100K of cubes, I'm also trying to render lines between those cubes (over 100K).
The problem with rendering occurs when I try to draw lines, NOT cubes. Rendering 100k cubes was relatively fast. Even rendering those 100K+ lines is relatively fast but when I try to zoom/pan using the TrackballControls, the FPS goes down to almost 0.
I've searched StackOverflow and various other sites in order to improve the performance of my application. I've used the merging geometries technique, the delayed rendering of lines ( basically x number of cubes/lines at a time using a timeout in js), and adjusting the appearance of the lines to require the most minimal rendering time.
Are there any other methods in constructing the lines so that the rendering/fps aren't affected? I'm building a group of lines and THEN adding it to the scene. But is there a way that merging is possible with lines? Should I be constructing my lines using different objects?
I just find it strange that I can easily render/pan/zoom/maintain a high fps with over 100k cubes but with lines (which is the most basic form of geometry besides a point), everything crashes.
You have found out (the hard way) where do graphics chip vendors put their focus on their devise drivers. But that is to be expected in 3D graphics as lines (yes the most basic geometry) are not used my many games so they do not receive much attention as polygons do. You can take a look at the example webgl_buffergeometry_lines.html which is probably the faster way to draw lines.
I'm implementing a simple lightning effect for my 3D game, something like this:
http://www.krazydad.com/bestiary/bestiary_lightning.html
I'm using opengl ES 2.0. I'm pondering what the best looking and most performance efficient way to render this in a 3D environment is though, as the lines making up the electric bolt needs to be looking "solid" when viewed from any angle.
I was thinking to generate two planes for each line segment, in an X cross to create an effect of line thickness. Rendering by disabling depth buffer writes, using some kind off additive blending mode. Texturing each line segment using an electric looking texture with an alpha channel.
I'm a bit worried about the performance hit from generating the necessary triangle lists using this method though, as my game will potentially have a lot of lightning bolts generated at the same time. But as the length and thickness of the lightning bolts will vary a lot, I doubt it would look good to simply use an animated 3D object of an lightning bolt, stretched and pointing to the right location, which was my initial idea.
I was thinking of an alternative approach where I render the lightning bolts using 2D lines between projected end points in a post processing pass. That should work well since the perspective effect in my case is negligible, except then it would be tricky to have the lines appear behind occluding objects.
Any good ideas on the best approach here?
Edit: I found this white paper from nVidia:
http://developer.download.nvidia.com/SDK/10/direct3d/Source/Lightning/doc/lightning_doc.pdf
Which uses an approach with having billboards for each line segment, then apply some filtering to smooth the resulting gaps and overlaps from each billboard.
Seems to yield pretty good visual results, however I am not too happy about the additional filtering pass as the game is for mobile phones where such a step is quite costly. And, as it turns out, billboarding is quite CPU expensive too, due to the additional matrix calculation overhead, which is slow on mobile devices.
I ended up doing something like the nVidia paper suggested, but to prevent the need for a postprocessing step I used different kind of textures for different kind of branching angles, to avoid gaps and overlaps of the segment corners, which turned out quite well. And to avoid the expensive billboard matrix calculation I instead drew the line segments using a more 2D approach, but calculating the depth value manually for each vertex in the segments. This yields both acceptable performance and visuals.
An animated texture, possibly powered by a shader, is likely the fastest way to handle this.
Any geometry generation and rendering will limit the quality of the effect, and may take significantly more CPU time, memory bandwidth and draw calls.
Using a single animated texture on a quad, or a shader creating procedural lightning, will give constant speed and make the effect much simpler to implement. For that, this question may be of interest.