#Best way to create a sliding texture

28 messages · Page 1 of 1 (latest)

static granite
#

Hey, currently porting a 2d game to three.js, and i'm wondering what the most efficient approach is to render what some might call a "conveyor belt effect"

My game is made out of grids of cubes, each cube having a pixely texture, no shaders used. Each cube is simply a geometry with uvs for each face.

I'd like to avoid complexifying the geometry by splitting each face into four quadrants, each having their own uvs. That's one way to do it, but i will definitely have a huge number of those cubes, and i want them to be as optimised as possible

Take a look:

still pagoda
#

When you say 'no shaders used' — there are always a vertex and fragment shader being used; the scene doesn't render without them. I presume you mean that you're using the default Three shaders and/or not using shaders to achieve your pixellated look?

The first approach that comes to mind is to do a sort of texture offset instancing: for each face, pass in eight values representing the UV offsets for the four quadrants of that face; then have a fragment shader (or piece of a fragment shader, e.g. using Material.onBeforeCompile()) that takes the 'base' UV values for the facet, doubles them, and applies the appropriate offset based on which of the four quadrants it's in.

static granite
#

That sounds like it could do the trick, however i did also have the idea to simply create another texture every frame. Take the stone texture in the above example, offset it, wrap it around, and i get the same result. Bonus points is that it keeps only 4 uvs per face, and it only has to be done once per frame

#

the texture is quite small as well, either 8x8 or 16x16 pixels, so i'm quite confident the texture creation won't take that long. And in order for the texture to not jump on the pixel grid while moving, you just have to upscale the texture to like 64x64

#

lmk what you think

still pagoda
#

I think you're probably underestimating the pain and performance of recompositing textures every frame. If you have a huge number of these cubes, that means a comparably huge number of textures you'll be uploading; the difference is between sending eight numbers per face per frame and sending e.g. 256 or more.

static granite
#

I see, one good thing to note is that you'd have only around 1/3 different sliding textures to create every frame

#

Each sliding tile will share the same texture, if not they won't tile properly

#

However, the uv updating approach would require you to update every cube

#

There's around 10 000 cubes on that grid

still pagoda
#

That's still more data passed in creating the textures than in shipping UVs. Certainly I won't discourage playing around with both! But I think you'll find a lot less hassle in the UV instancing approach.

#

80,000 values (eight per cube x 10K cubes) is actually a relatively modest amount of data to be shipping to the GPU. Also, if all of these boxes are the same size then you might want to look into instancing the geometry itself; for each cube you could ship just its location, which of your textures it's using, and those four uv offsets.

static granite
#

Mhm and to be fair creating buffer texture is not the cleanest way to go

static granite
#

Oh and, i don't know if it's going to use gpu or cpu, but while we're at it, we can simply make the uvs a function of whatever the equation is to calculate the uvs for each quadrant. And i think that's doable in a simply shader, but i'm going into unknown territory here

still pagoda
#

Doubling the UV values is maybe the simplest way of breaking things into quadrants: assuming that one square face has u and v both going from 0 to 1, then by doubling the values you'll have each of u and v going from 0 to 2 over the face of the square, and using standard texture wrapping this is the same as going from 0 to 1 twice.

static granite
#

alright, i can't really wrap my head around how that works, anyway this is how i did it in the 2d version:

#

basically lots of offsetting the image source, top left position, and width/height of the texture, do that four times and you have the sliding tile

#

Tysm btw as you can tell i am quite new to this

#

There's a lot of other features, such as the "blending" effect in those first videos you can see above, that requires me to mask a blending texture over the slide texture, i don't know if i should do that one with shaders

still pagoda
#

Your fragment shader might look something like this (note: this is off the top of my head and will surely not compile!)

in vec2 uv;
in vec2 offsetTopLeft;
in vec2 offsetTopRight;
in vec2 offsetBottomLeft;
in vec2 offsetBottomRight;
out vec4 outColor;

sampler2D faceTexture;

void main() {
  vec2 doubleUV = 2*uv;
  vec2 trueUV;
  float u = doubleUV.x;
  float v = doubleUV.y;
  if (u < 1 && v < 1) {
    trueUV = doubleUV + offsetTopLeft;
  } else if (u >= 1 && v < 1) {
    trueUV = doubleUV + offsetTopRight;
  } else if (u < 1 && v >= 1) {
    trueUV = doubleUV + offsetBottomLeft;
  } else {
    trueUV = doubleUV + offsetBottomRight;
  }
  return texture(faceTexture, mod(trueUV, 1.0));
}
#

We take the passed in (interpolated) uv value from the vertex shader; this'll be a pair with each member in the range [0, 1]. We double it to get a pair with each member in the range [0, 2], then decide which of the four offsets to apply to it based on whether each member is in [0, 1] or [1, 2] — this is how it breaks it into quadrants. Finally, we do a texture lookup using the offset UV value.

static granite
#

I see

#

👁️

#

Yea that's a cool way to do it

#

Thank you for your explanation that'll help me a lot

#

I'll be implementing that, i'll send you back the result