Multiple Textures in Surface Shader - Sampler limit exceeded

Hi

I’m working on a very special surface shader in Unity 4 for a science project. I need to stitch multiple textures (currently 81 unique textures) depending on uv-coordinates. I know this sounds weird, but stitching on CPU is no option.

Now, Unity complains about “Sampler limit exceeded; more than 16 samplers needed to compile …”.
According to Shader Reference (if I understand it correctly) it should be possible to raise the amount of textures by using
#pragma target 3.0
#pragma profileoption MaxTexIndirections=256

Unfortunatelly, this doesn’t work as Unity does not accept MaxTexIndirections. And there is no indication, if target 3.0 is accepted…

What I basically need is: a shader which takes an arbitrary amount of textures which will be stitched/blended together according to some rules.

Any Ideas?

ps_3_0 only guarantees support for up to 16 samplers (And it looks like that’s the same for ps_5_0). You could consider combining two or four textures in the shader, and rendering to a RenderTexture multiple times. First combining pairs of textures, then combining pairs of combined rendertextures, etc. until all textures are combined.
You should keep in mind that there’s also a limit to the maximum width and height of these textures.

That’s an interesting approach. So far, I haven’t worked with RenderTextures and didn’t thought about them.
Considering runtime: Would that happen in GPU memory or does it force data transfer to GPU memory for every cycle?
I assume the combining loop is meant to happens in a script on CPU. But if no transfer is happening, this should be fast enough.

As I’m relatively new to the Shader stuff, would a multi pass approach help, eventually? Each pass receiving its own set of textures, generated in previous passes…

The texture data of the RenderTexture stays on the GPU. The RenderTexture object you access through scripts simply helps you modify it. It might not be extremely fast, because the CPU and the GPU have to wait for each other to finish, but it shouldn’t be too big of a problem. And the problem you’re trying to solve doesn’t seem like the kind of thing you’d need to do within a millisecond.

Since you’re talking about an arbitrary number of textures, I don’t think you should go for a shader with one pass for each combination, because you’d have to update your shader to change the amount of passes.

Maybe I misunderstand the question but couldn’t you put each of the textures in a different z layer of a Texture3D and stitch them on the gpu?

Hmmm, that sounds like an interesting approach, I didn’t consider the fact that we can use 3D textures now.

I already thought about using them. An example on how to use them in a shader would help, especially as there is not much information in the manual. As far as I see it, they have to be built manually, pixel by pixel, which would be a drawback. Another point is the indexing, which may raise some problems inside the shaders…

If your 81 2Dtextures are static, you can just build the 3Dtexture once at start-up.

For code, Aras gave a fine example for Texture3d here
Obviously in your case for the x and y you would take each Texture2D one at a time along the z and do a GetPixel( x, y ) on it.

But you’re right about the challenge with indexing inside the frag or surf shader. Also note they will try to mip-map automatically (yes, 3Dtexture mipmaps) which will complicate using the z coordinate this way. Something extra to keep in mind.

But if there is an answer, it’d be nice to find it :wink:

Ok, to sum up situation so far:

Texture3D would be a nice approach, but unfortunatelly my textures are not static, but view dependent. As a consequence, it should be possible to pack all textures ever occuring into it, which will blow up memory… Maybe, I will come back to this, when I have a solution to break up data into junks with different rules.

The RenderTexture approach seems to be the most promising. As I am relatively new to Unity and Shader stuff, I have no idea how to build such a CPU-GPU iteration thing…
I have managed to mix up to 9 textures in my shader. But I have no idea how to put that into a RenderTexture and do further iterations. Can someone, eventually, give me a very basic example? Would be much appreciated! :slight_smile:

Thank you both for the help so far!

I managed to have a 2 stage prototypic solution where groups of textures are rendered to renderTextures, which then are combined to a final result. Things work pretty well so far, GPU is around 2.5ms and CPU doesn’t even know, she is working… :wink:

But recently a problem popped up and I cannot say at which point it first occured. Unity complains about m_Memory.renderTextureBytes<0. The message is generated 5 times when the scene is played and another 5 times when it is stopped. However, everything works just fine and looks as expected. Is this a known problem, a bug or a serious problem I should solve before going any further?

Sounds like some bug. Can you report it with a repro project?

After restarting Unity, the error has gone… at least for the moment… :wink:
In fact, it popped up only once more after restart with a second message about some GUI mechanisms. But it never came back since then.
If it happens again, and if I find some free minutes, I will try to build a small project to report it. And I will go for an update now… :roll_eyes:

Thanks anyway!

For those interested in a solution to the problem, I have found another cool one. :smile:

I use a multi-pass fragment shader consecutively adding more textures. Currently, I have 8 passes, each getting a different set of 8 textures. Because the choice of texture depends on u/v and some uniform parameters, this perfectly fits my requirements. At least for current project state… :roll_eyes:

Only thing I wonder so far: is there an upper limit of passes? I assume GPU time will raise at some certain point. Currently it is around 2ms, regardless of using either 1 or 8 passes…

I am not aware of a limit in passes.
Each pass adds a draw call per material used though.
So, if you are using a 2 different 8 pass shaders on 2 different materials on a mesh, you get 28 = 16 drawcalls.
Suppose you have 6 of those meshes, you get 6
16 = 96 draw calls. Which may or may not be a bottleneck in your project.