Texture Splatting

[EDIT]Check out the webstart demo here: http://wwwhomes.uni-bielefeld.de/krabien/jmestuff/textureLayers/JMESplatting.jnlp[/EDIT]



Has anybody created a working real-time texture splatting yet? I managed to finally find the time come up with something along these lines - texture “layers” using distinct alpha maps for color maps, each with their own texture coordinates… well, basically what everybody around seems to call “texture splatting” (not sure if that’s really what it should be called, though, I think the first article I have seen so far that uses that term uses it for something slightly different).

Here’s a pic:



and here’s the code, I’ve aimed at ultra-simple usage for this:

http://wwwhomes.uni-bielefeld.de/krabien/jmestuff/textureLayers/TextureSplattedNode.java

and finally, here’s a quick test using the jmetest textures (just the same you see in the sceenshot)

http://wwwhomes.uni-bielefeld.de/krabien/jmestuff/textureLayers/TestTextureSplatting.java



To be honest, I am a little disappointed with the performance. I use SharedNodes for the different texture layers, each with it’s own TextureState to apply - probably that’s what impacts the framerate most. I was going to use a texture atlas, but that didn’t yield acceptable results with tiled textures and mipmaps (color bleeding, and incorrect tiling). Maybe somebody has some performance hints for me after looking at my code?





Currently, GLSL support is required for swapping the alpha values - this could probably be done using the apply mode AM_COMBINE, I tried that long time ago and failed, but didn’t dig very deep into it then. Any opinions/experience?

Yes, I have built a very nice splatting system + realtime editor here using multiple 2 texture passes and texture combine mode, all using fixed function at the moment.  Any specific questions?

Wow, sounds nice. Here's some specific questions:


  • It's not in cvs yet, is it? Or, will it be at all?

  • So you're using RenderPasses? Or do you override some draw method to paint your passes by yourself? Does that break TextureState batching, or is that ruled out already by having to use TransparentQueue?

  • Would it be possible for you to send me a test of your system so I can compare performance of our approaches? Or, otherwise, could you try to get a vague impression of how they compare by trying out my code?

  • Have you considered using texture atlases for the texture layers to avoid TextureState switching, and [how are you|why aren't you] using them?


  • It's not in CVS and at this point I wouldn't hold my breath for it.  :|

  • I have one Pass that draws the terrain.  I use my own block management scheme instead of a terrainpage.  I send through the visible blocks writing them to the depth buffer, then turn on blending and loop through the splat textures/alphas and redraw visible blocks that contain that texture.  I place alpha in unit 0 and the texture in unit 1.  I also use SKIP for my queue.  No need for transparent queue in this case - that will just double your tri count for no benefit.

  • I wish we could show things off here... :(  Unfortunately at this time we still can't yet.  I'd be happy to try something of yours if you want to put up a webstart or something.  We could test based on triangle count, number of splatted textures, etc.

  • I haven't yet tried to use texture atlases in splatting.  To keep things artist friendly, that would require a post-process to make the atlas.  Because I only switch textures X number of times for the terrain per frame (where X is the number of splats, and has nothing to do with triangle count or block count) I don't think it would give me more than a small boost at best.  Maybe closer to release.  I think my current best possibility for speedup would be combining my current 1 splat per pass into a 4 splat per pass shader technique which I will probably do at some point.  As it is, the terrain currently takes up only a small part of the frame rate compared to the rest of our game code.

Um… couldn't you just put my two classes in a fresh jME project at your site and run TestTextureSplatting? I really don't get along too well with webstart - have to give darkfrog's magical webstart maker a try sometime, if it's not eclipse only by now… Anyway, if you don't think you could arrange that, I'll try and set up a webstart thingie sometime this weekend.

Not a problem…That would fall under the "or something" I mentioned.

Great! Actually, I have half-implemented meanwhile the 4-splat-shader technique you mentioned earlier - a solid double in fps for me! I'll definitely go that way. I think I can even make it a "n-splat-shader" technique, actually, n being an adjustable limit of how many texture units to use. Once those are full, I can fall back to my previous strategy. I'll look into that this weekend. If it works out, I think my needs in texture splatting are quite satisfied, and it might even be of use for some other folks here, too… there seems to be a lot of excitement every time somebody mentions the words "texture splatting" around here!

I have done some work in glsl to texture jme's terrainpages. News will be updated in this thread here:



http://www.jmonkeyengine.com/jmeforum/index.php?topic=3710.30



Texture Splatting is done in beta stage and not public today. (also bumpmapping is done! )



two demos are online. one with jme's bloom effect for the environment. (see on my signatur for a link)

Sigh - After my n-splat-technique mentioned above fleshed out nicely, I just did a quick cvs update for jME, and voila - now my beautiful shader based texture blending is broken! I can't seem to get access to texture units above index 1, apparently. The fallback approach still works, since it only uses texture units 0 and 1. How frustrating! Can any of the devs recall something committed the last few days that might have such an impact on GLSL fragment shaders, and/or texture unit usage?



@BlackBluegL : Looks really nice! I have seen your examples before, but was thinking you were using some tile-based texturing instead of actual splatting. Could you describe your approach, in comparision to renanse's and mine?

Also, normal generation seems to be broken for TerrainBlock - see Issue 230 in bugtracker.

there was a pretty big commit concerning the state system a few days ago, it will probably take some days for it to get it's bugs out…page mojo, renanse or the bugtracker

Apparently, my problem with recent cvs described above only occurs if I use MM_NONE on the alpha map Textures! MM_LINEAR_LINEAR, for example, works fine. I'm not really sure if that's a bug or a feature, so I won't do anything about it (write a test case, post an issue in the bugtracker) for now. Maybe somebody can enlighten me on the intended behavior of MM_NONE, and how it could possibly cause glsl fragment shaders to not use texture units above 1?

Are you sure it's "not using texture units above 1" or perhaps it IS, but with MM_NONE, it has nothing to sample at the current mip? 

It's possible the filters were not being set properly until now and that is why this never came up.  Does MM_LINEAR also allow it to work?

MM_NEAREST and MM_LINEAR give the same problem. But not on all texture units, thus my "not using texture units above 1" (which should have been "2", actually). My texture layers are as follows for this example:

Texture Unit 0 - a dirt texture, no alpha map

Texture Unit 1 - a grass texture

Texture Unit 2 - a greyscale alpha map for TU1

Texture Unit 3 - a checker pattern texture

Texture Unit 4 - a greyscale alpha map for TU3

The layers 0 through 2 are rendered as expected, but 3,4 will only show up if I don't use MM_NONE,MM_NEAREST, or MM_LINEAR. The same goes for any further layers added.

The other MM_ modes seem to work.

Okay, the shader-based blending works great now.

I can’t create a webstart-demo because of version problems with the jME webstart libs, but you can just download the two classes linked above, copy them into a jME project in your IDE, maybe adjust package names, and run TestTextureSplatting.

Left mouse button lets you paint on the first layer’s alpha map, right mouse button enables the first person camera control while held down.

Screenie:



@renanse: Will this do for a very vague performance comparision? There are 5 texture layers in the test class, the rest is shown in the renderer statistics, I think. I’d really like to know if my approach is worth pursuing, or if there are other, sginificantly faster ways to do this.

my shader used in the webstart-demo uses only 2 texture units (unit0 and unit1). The blending is generated by values read from geometry. No precomputed alphamap is used.



current state of development:

texture unit 2 is used for a normalmap. (rock bumpmapping)

texture unit 3 is used for detailtiles, wich where randomly mapped. (mapping are precomputed in the 4 texture channels)





P.S.

The only bug i found is in combinition with MrCoders simplewatershader and nvidia cards. the terrain is not reflected in the water. On ATI cards it words fine. All stuff reflected in waterplane.

@hevee:  I'll take a look later this week, I think I could scale down one of my tests to that range.

Thanks, renanse! I'm really looking forward to see how my little experiment compares to a professional solution!

BTW: LWJGLShaderObjectsState contains a System.out.println in private ByteBuffer load(String data), maybe this could be commented out or redirected to Logger.info() ? (The first way would probably be best performance-wise, the statetment contains one of those expensive implicit StringBuilder calls)

Hmm, but how often does a load happen?

Well, in most cases probably not very often. If so, it makes reading stdout quite annoying, though.

For instance, my texture splatting approach currently generates shader code on the fly, when new texture layers are loaded, so every time a splat layer is added to a TextureSplattedNode, this will be printed to stdout… granted, that's probably the least of the performance problems with that approach.

Still, I personally tend to avoid string concatenation using the + operator (aka implicit StringBuilder usage) whenever something might be called in any time-critical context, because it's just so slow! That's just me, of course, but still, to me this looks like a debug message of little to no use for jME users, so it could probably be left out anyway (in favor of debugger breakpoints!)



OT: if anybody knows why sun prefers to use StringBuilder instead of the much faster StringBuffer for String concatenation, please enlighten me! Memory usage? Or what else?