I made a new method for quick texture swapping, but it only seems to allow 8 texture slots

I was playing around with shaders and the BlenderMaterial module for the game engine, and discovered a trick to quickly swap textures during the runtime. I like this better than swapping out meshes, and it grabs the textures from the texture slots instead of replacing it with a file using the Texture module.

#NEW TEXTURE SWAP LOGIC
vertex_shader = """
void main(void)
{
	 // simple projection of the vertex position to view space
	 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
	
	 // coordinate of the texture channels (seems to be limit of 8)
	 gl_TexCoord[0] = gl_MultiTexCoord0;
	 gl_TexCoord[1] = gl_MultiTexCoord1;
	 gl_TexCoord[2] = gl_MultiTexCoord2;
	 gl_TexCoord[3] = gl_MultiTexCoord3;
	 gl_TexCoord[4] = gl_MultiTexCoord4;
	 gl_TexCoord[5] = gl_MultiTexCoord5;
	 gl_TexCoord[6] = gl_MultiTexCoord6;
	 gl_TexCoord[7] = gl_MultiTexCoord7;
     //gl_TexCoord[8] and above doesn't work
}
""";
fragment_shader="""
uniform sampler2D texture_a;
uniform sampler2D texture_b;
uniform float factor;
void main(void)
{
	vec4 color_0 = texture2D(texture_a, gl_TexCoord[0].st);
	vec4 color_1 = texture2D(texture_b, gl_TexCoord[1].st);
	gl_FragColor = mix(color_0, color_1, factor);
}
""";
for mesh in body.meshes:
	for material in mesh.materials:
		shader = material.getShader()
		shader.setSource(vertex_shader, fragment_shader, True)
		shader.setUniform1f('factor', 1.0)# set blend factor to 1
		shader.setSampler('texture_a', 0)#default slot

#test keys:
if XKEY==acv: #reset to neutral
	shader.setSampler('texture_b', 0)#tex slot to replace with
if CKEY==acv:
	shader.setSampler('texture_b', 1)#tex slot to replace with
if VKEY==acv:
	shader.setSampler('texture_b', 2)#tex slot to replace with
if BKEY==acv:
	shader.setSampler('texture_b', 3)#tex slot to replace with
#...
if NKEY==acv:
	shader.setSampler('texture_b', 6)#tex slot to replace with
if MKEY==acv:
	shader.setSampler('texture_b', 7)#tex slot to replace with

I am not well-versed in OpenGL, but I frankensteined this together and it works for me. This is particularly helpful for animated sprites or facial expressions/lip sync. HOWEVER, I discovered a catch. I am not sure, but it seems like if I try to add another slot, it will be considered invalid, even if there are 8+ frames (texture slots) assigned to the material in a stack in the blend file. I am guessing that since it is 8, this is the limit to the amount of textures that are “allowed” for OpenGL (?).

1 Like

8 is the limit for texture coordinates (UVs), 32 is the limit of textures units (samplers or slots), according to the values returned by this script (although these may vary on different GPUs?):

from bgl import *

MaxUVs = Buffer(GL_BYTE, [1])

glGetIntegerv(GL_MAX_TEXTURE_COORDS, MaxUVs)
print('MaxUVs:', MaxUVs[0])

MaxTextureUnits = Buffer(GL_BYTE, [1])

glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, MaxTextureUnits)
print('MaxTextureUnits:', MaxTextureUnits[0])
2 Likes

I’m confused. You mean texture swapping or uv animation?

If you want to work with sprites just do the object color mambo, it’s cleaner than anything I’ve tried and I’ve tried a LOT of things.

Maybe this can help? It’s for UPBGE 0.2.5, just a plane and a script.
SPRITETEST.blend (1.6 MB)

There’s a few similar files over at resources, but no unified sprite-handling system that I know of, none that are free to use that is. I was in the middle of developing something along those lines as part of DSM but I’ve been caught up with the whole replacing Visual Studio affaire, it’s a long story. It’ll be done when I’m done.

But once I work that out, this’ll be a breeze, for 0.2.5 at least.

2 Likes

The thing this is for is a model that doesn’t animate per-se, but swaps out different textures randomly if that makes sense? It’s for a character’s facial expressions and then lip sync for when they are talking (the face is the part that changes.) I’ll try your approach with a sprite sheet. I was actually messing around with it last night by animating material nodes and attaching them to the UV coords.

Originally my goal was to find a fast way for texture/material swapping and storing textures in memory. Like, while editing for me it is super easy to have all 20 of them assigned to one material and I was trying to figure out if I could switch up the order of the textures (indices in the list of textures/materials for the object, ie the “ID”) during runtime, but that didn’t seem possible.

I looked into the BlenderMaterial module and used this shader to mix the “default” texture with each of the 7 other available slots. It seemed to be a lot faster than using the videoTexture method that loads the image file. The texture I am using is 3072x3072 px.

I’ve also been sticking with the regular BGE (2.79b) since I was having some issues with some of the UPBGE versions’ physics and stuff. But I do have a version of upbge installed and the above file runs in it, but it does not run in regular blender.

I don’t 100% know what this section of code means but I was assuming this was assigning textures to the UVs?
Every time I use a 9th slot I get the following error
image
image
.
Basically my question is, Is there a way to add more textures?

On shaders, you don’t assign textures to UVs, you can use any UV to sample any texture. Also, unless every UV look different, you don’t need multiple UVs.

Alright I’m pretty sleepy, but let me try some numbers here.

3072*3072=9,437,184 pixels

No matter if your image is black and white it’ll still be converted to 16 bytes per element if OpenGL has anything to say about it. So it’s effectively 4 floats per pixel (RGBA) even if the actual file is much smaller. 4 floats, 4 bytes each, 16 bytes.

9,437,184*16=150,994,944 bytes

That’s roughly 144MB, per texture. With 20 textures you’re looking at almost 3 gigs worth of memory.

I’m not a 100% on how Blender pulls off these feats of insanity but I’d guess a good portion of that data is either kept in RAM rather than sent directly to the GPU or is fetched as needed in some funny way, though both things strike me as slow.

What would make this a tad more doable is going with a smaller size and array textures. It’s basically a texture with layers, and you fetch from it with a 3D vector (U, V, layer), like so:

vec4 diffuse     = texture(Surface, vec3(texCoords, 0));
vec4 shadinginfo = texture(Surface, vec3(texCoords, 1));
vec4 normalmap   = texture(Surface, vec3(texCoords, 2));

My engine uses this to save time with texture bindings, plus texture slots are limited as well. For what you want to do then, passing the layer value for the lookup as a uniform would work to swap out textures on the fly. If you’re not updating thousands of uniforms a frame then the overhead is negligible.

The question then is, does vanilla BGE support array textures? I honestly have no idea, but it IS something I should look into it.