0
votes

I am using non-power-of-two textures within shader programs. I am trying to implement scrolling text with repeats. Scrolling works fine, but at soon as I try to get the texture to repeat via logic in my vertex shader, I suddenly get a quad with a single set of stretched pixels across the entire range. I assume this is happening due to the filtering algorithm.

As background, I want to generate the texture coordinates within the vertex program since I then do further distortion on them in the fragment programs, and it is easier to manage if the inputs to the fragment programs are already correct to account for scroll. Note that I access textureCoordinateVarying in the corresponding fragment shaders

This works, albeit with no repeated texture once the text scrolls through:

attribute vec4 position;
attribute vec2 texcoord;

uniform mat3 matrixUniform;
uniform float horizontalTextureOffsetUniform;

varying vec2 textureCoordinateVarying;

void main() {
   gl_Position = vec4((matrixUniform * vec3(position.x, position.y, 1)).xy, 0, 1);
   textureCoordinateVarying = vec2(
//I get a nice scrolling animation by changing the offset here, but the texture doesn't repeat, since it is NPO2 and therefore doesn't have repeating enabled
        texcoord.x + horizontalTextureOffsetUniform,
        texcoord.y
    );
}

Correct Scroll w/ No Repeate

On the other hand, this gives me a stretched out image, as you can see:

attribute vec4 position;
attribute vec2 texcoord;

uniform mat3 matrixUniform;
uniform float horizontalTextureOffsetUniform;

varying vec2 textureCoordinateVarying;

void main() {
   gl_Position = vec4((matrixUniform * vec3(position.x, position.y, 1)).xy, 0, 1);
   textureCoordinateVarying = vec2(
\\Note how I am using fract here, which should make the texture repeat at the 1.0 texture boundary, but instead renders a blurry stretched texture
        fract(texcoord.x + horizontalTextureOffsetUniform),
        texcoord.y
    );

}

Stretched image during scroll

Any ideas on how I can solve for this?

Thanks!

1
Repeating texture coordinates cannot work with a simple fract(). Imagine one vertex having texture x coordinate 0.0 and the other 1.0. Then when you use an offset of 0.1 you will get 0.1 for both vertices, since fract(0.0 + 0.1) = 0.1 and fract(1.0 + 0.1) = 0.1Kai Burjack
Actually, it seems like maybe the issue is with my use of fract (or mod(x, 1.0) for that matter). If I just pass texcoord.x through as fract(texcoord.x), nothing is rendered. I thought texture coordinates were from 0-1.0? What am I missing? Edit: I posted this right as httpdigest was respondingbaadcafe
Hi httpdigest, what you describe is what I was going for... I assume that the 1.0 coordinate is the edge of the texture, and so if I want it to repeat across the quad, it should be handled the same way as 0.0. I feel like I am missing something pretty elementary here.baadcafe
No, this is not what you want. If you assign 0.1 to both vertices, then texture coordinates will not interpolate between multiple texels, but only sample a single texel, which is what you are seeing. Texture repeating can only be done either in the fragment shader (with unmodified, greater than one texture coordinates) or via texture filtering (GL_REPEAT).Kai Burjack
Interesting, thank you for the explanation. Given that I can't use GL_REPEAT on a non-power-of-two texture, do you think this is feasible in the fragment shader? I will experiment with moving the logic there once I have time. For now, xmas calls.baadcafe

1 Answers

1
votes

You need to do the repeat math in the fragment shader, not the vertex shader.

const gl = document.querySelector('canvas').getContext('webgl');

const vs = `
void main() {
  gl_Position = vec4(0,0,0,1);
  gl_PointSize = 100.0;
}`;

const fs = `
precision highp float;
uniform sampler2D tex;
uniform vec2 offset;
void main() {
  gl_FragColor = texture2D(tex, fract(gl_PointCoord.xy + offset));
}`;

// compile shaders, link program, look up locations
const programInfo = twgl.createProgramInfo(gl, [vs, fs]);

// calls gl.createTexture, gl.texImage2D, gl.texParameteri
const tex = twgl.createTexture(gl, {
  src: 'https://i.imgur.com/v38pV.jpg'
});


function render(time) {
  time *= 0.001;
  
  gl.useProgram(programInfo.program);
  
  // calls gl.activeTexture, gl.bindTexture, gl.uniform
  twgl.setUniformsAndBindTextures(programInfo, {
    tex,
    offset: [time, time * 0.1],
  });

  gl.drawArrays(gl.POINTS, 0, 1);

  requestAnimationFrame(render);
}
requestAnimationFrame(render);
<script src="https://twgljs.org/dist/4.x/twgl.min.js"></script>
<canvas></canvas>