How do I offset and scale a texture within ShaderMaterial?

Preamble
I’m working on a strategy game in which I need to draw borders based upon a bitmap. I understand how to draw the borders and I have implemented it, but it looks off. In some places it looks great but in others it looks like this:

image

When I really want it to look like this:

image

I figured the reason for this is due to the texture being cut off on the edges which would make sense. It makes sense because my terrain is divided into chunks and the shader is being projected onto the chunk mesh via DecalGeometry. Also note that Decal geometry is the same size as the chunk mesh. The texture likely doesn’t know the neighboring pixel past the edges (that’s what I think).

My current idea on a solution
My idea is to make the texture I send to the shader 18x18 instead of 16x16. Then I want to make it so the shader only displays the 16 central pixels instead of all 18. Below is how I would represent that visually:

image

The question…
How would I do this? I would love if the solution still kept my border-drawing code in the shader :slight_smile: Please read carefully before answering. I am open to questions. As per helping, here is my current code for the texture and material.

                const _Tex = new THREE.CanvasTexture(this.canvases[y][x].Sub.Elem)
                _Tex.minFilter = THREE.NearestFilter
                _Tex.magFilter = THREE.NearestFilter

                const _Mat = new THREE.ShaderMaterial({
                    transparent: true,
                    depthTest: true,
                    depthWrite: false,
                    polygonOffset: true,
                    polygonOffsetFactor: -1,
                    uniforms: {
                        tex1: {value: _Tex},
                    },
                    vertexShader: `
                        varying vec2 vUv; 

                        void main() {
                            vUv = uv; 
            
                            vec4 modelViewPosition = modelViewMatrix * vec4(position, 1.0);
                            gl_Position = projectionMatrix * modelViewPosition; 
                        }
                    `,
                    fragmentShader: `
                        uniform sampler2D tex1;
                        varying vec2 vUv;
            
                        void main() {
                            vec4 color = texture2D(tex1, vUv);

                            vec2 pixel_size = 1.0 / vec2(128., 128.);

                            gl_FragColor = vec4(1.0, 1.0, 1.0, 0.0);

                            for (int y = -1; y <= 1; y++) {
                                for (int x = -1; x <= 1; x++) {
                                    vec2 pixel_off = vec2(float(x), float(y));
                                    vec4 tex = texture2D(tex1, vUv + pixel_off * pixel_size);

                                    if (tex.rgba != color.rgba)
                                    {
                                        gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);
                                        break;
                                    }
                                }
                            }
                        }
                    `
                })

Thank you so much for the help :grin:

Fixed it myself!
Learned a little more about how UVs work and I got it working. Here’s the result:

Here’s the new code (I only changed the fragment shader):

                        uniform sampler2D tex1;
                        varying vec2 vUv;

                        vec3 black = vec3(0., 0., 0.);
            
                        void main() {

                            float zoom = 0.89;
                            vec2 scaleCenter = vec2(0.5);
                            vec2 newUv = (vUv - scaleCenter) * zoom + scaleCenter;
                        
                            vec2 pixel_size = 1. / vec2(128., 128.);
                            vec4 color = texture2D(tex1, newUv);

                            gl_FragColor = vec4(1.0, 1.0, 1.0, 0.0);

                            for (int y = -1; y <= 1; y++) {
                                for (int x = -1; x <= 1; x++) {
                                    vec2 pixel_off = vec2(float(x), float(y));
                                    vec4 tex = texture2D(tex1, newUv + pixel_off * pixel_size);

                                    if (tex.rgb != color.rgb && tex.rgb != black && color.rgb != black)
                                    {
                                        gl_FragColor = vec4(black, 1.0);
                                        break;
                                    }
                                }
                            }
                        }

If anyone was already attempting to solve this, I appreciate your help :+1: