2
votes

I'm drawing my lines filled with a texture i'm repeating in my shader. As show below

enter image description here

The line are single points - that each vertex i'm expanding in my vertex shader with some calculation that always will be same pixel width no matter what zoom we are.

enter image description here

Than i'm creating my triangles and on them i'm drawing my texture repeatedly on the X axis. so the width of the line is always the image height.

The user can zoom in as much as he wants and the shapes getting bigger and smaller as zoom change. Although the texture saving it's size, means that there are more repeats.

When the user zoom in very much i'm starting to get strange results, i guess due to float overflow.

zoom:1

enter image description here

zoom 2

enter image description here

My shaders:

Vertex Shader

uniform float factor;
attribute vec2 texCoords;
varying vec2 vTexCoords;
attribute vec4 texAtlas;
varying vec4 vTexAtlas;
uniform vec4 uPixelWolrdScale;
attribute vec2 outlineOffset;
 
void main() {
   vTexCoords = texCoords;
   vTexAtlas = texAtlas;
   gl_Position = LIGHTGLgl_ModelViewProjectionMatrix * (LIGHTGLgl_Vertex + uWorldOffset) + vec4((outlineOffset.xy) * uPixelWolrdScale.zw, 0,0);
}

uPixelWolrdScale is vec4 with the xy = worldScale.xy / ScreenSize.xy, zw = 2/ ScreenSize.xy.

I'm using the zw for offseting the width of the line and the xy for repeating my texture in the fragment shader

precision highp float;
uniform float factor;
uniform vec4 color;
varying vec2 vTexCoords;
uniform vec4 uPixelWolrdScale;
uniform sampler2D sampler;
varying vec4 vTexAtlas;

void main() {

   // Here is the problem i guess
   vec2 vexelPos = fract(vec2((vTexCoords.s) / (uPixelWolrdScale.x * factor), vTexCoords.t));
   vexelPos = vTexAtlas.xy + vexelPos * vTexAtlas.zw;
   gl_FragColor = texture2D(sampler, vexelPos);
   gl_FragColor *= color;

}

vTexCoords.s is the number of the repeats when the world and the screen is the same units. no zoom.

Is there another way to repeat the texture when (vTexCoords.s) / (uPixelWolrdScale.x * factor) is overflowing?

-----EDITED ------

Tried to change my approach by using mod() instead of fract() for achieving repeats.

After few rounds I've managed to do this, as solution to my problem, but it is still in some zooms starting to freak.

The basic thought is to use homogeneous values for repeating the texture with a additional computing varying - with the help of the texture size and modulus.

In my vertex shader

I've added attribute and varying

attribute vec4 startVertex;
varying float vVertex;
...
// Same calculation i did to the gl_Vertex I do here to the attribute
vec4 coordToRemove = LIGHTGLgl_ModelViewProjectionMatrix * (startVertex + uWorldOffset) + vec4((outlineOffset.xy) * uPixelWolrdScale.zw, 0,0);
vVertex = gl_Position.x - coordToRemove.x;

I want my varying to start from 0 and interpolate all the way to the second homogeneous position. so I've added the first vertex value as an attribute for both vertices - coordForVar- So when the first vertex will get here vVertex will be 0. and the second vVertex will be the difference.

In my fragment shader

varying float vVertex;
...
float hmgSize = 1. / 32.; // Getting the size in homogeneous - For now 32px, later uniform/attribute
vec2 vexelPos = vec2(mod(vVertex, hmgSize) / hmgSize, vTexCoords.t);
vexelPos = vTexAtlas.xy + vexelPos * vTexAtlas.zw;
1
I've not managed to fully understand you, but vec2 uv = gl_FragCoord.xy/resolution; gives you accurate pixel size, if it could help...(resolution is texture size) - j-p
@j-p resolution is not part of WebGL itself, just some common environments. uPixelWolrdScale is doing almost the same job here. - Kevin Reid
resolution is (Tex Width, tex Height). - j-p
Is that really your fragment shader? It's missing a #version ... directive (optional) and a precision ... float statement (not optional in fragment shaders). I'd go with mediump though chances are it will make no difference whatsoever in your typical WebGL implementation (most desktop hardware doesn't care about this). - Andon M. Coleman
@EntityBlack: Support for precision highp float is not guaranteed in fragment shaders in OpenGL ES 2.0 (WebGL). You would have to do some pre-processor tests to use it compliantly, but ES 2.0 does guarantee mediump support so it's a good universal choice. Version, on the other hand, is completely optional and automatically assumed to be 100 by default in ES 2.0. There is no default floating-point precision in a fragment shader, which is why it's required. - Andon M. Coleman

1 Answers

0
votes

Using fract and mod in texture coordinates can do weird things with mipmapping, make sure your texture is using Nearest Filtering for min and mag filters and go from there


Edit:

Regardless what the contents of your texture is texture2D is only really looking up texture coordinates between 0 and 1. The integer portion is for repeats but using it reduces precision on the lookup. Using fract or mod to do the repeat rather than the integer portion can cause a 1px discontinuity in mipmap lookup; but if you are altasing you will have that issue anyway.

So your lookup for an atlas your texture coords should be a range that is a fraction of 0..1

For example a range of 0.0625 if you had 16x16 images. e.g. 3rd texture in X is 0.125 to 0.1875.

You want to deal with the repeats outside the texture coords, then convert the value to that range.


Edit:

When zoomed in rather than making your geometry a million units long, but only showing one millionth of it; which is just causing precision fighting, make your geometry screen sized; then you should have all the precision you need.

For example after doing your perspective calc your geometry x,y coordinates will be in screen space and only displayed where they are in the box -1,-1 to 1,1. At this point you can intersect and trim (or discard/ignore) the geometry so the x,y is in that box then set your texture varying to be the number of screen repeats matching what it should be. This will mean it doesn't need the precision of fractions of a million.