The Sloth
The Sloth

Reputation: 387

Three js texture edge pixels being stretched

I'm trying to display a texture within a sphere where the sphere is acting as a mask. I have my texture correctly displayed but I can't get my head around how to remove the edge pixels that are being stretched out to fill the sphere. I want them to be transparent.

This is what is happening

This is what is happening atm

The idea being that the image is displayed in it's default aspect and on a mouseover the sphere will shrink to mask part of the image. The sphere will later be modified to something else, this is just for the test, so doing the same with a 2d shape is not an option.

this is how it's set up, nothing special but just so you get the idea.

    texture.wrapS = texture.wrapT = THREE.ClampToEdgeWrapping
    texture.minFilter = THREE.LinearFilter

    // uniforms

    const uniforms = {
        texture: {value: texture},
        resolution: {type: 'vec2', value: new THREE.Vector2(window.innnerWidth, window.innerHeight)},
        offset: {type: 'vec2', value: new THREE.Vector2(offset.x, offset.y)},
        size: {type: 'vec2', value: new THREE.Vector2(width, height)},
        scroll: {value: 0}
    }

    // material

    const material = new THREE.ShaderMaterial({
        uniforms: uniforms,
        vertexShader: this.vertex,
        fragmentShader: this.fragment
    })

    const geometry = new THREE.SphereGeometry(5, 32, 32)
    this.mesh = new THREE.Mesh(geometry, material)

and my fragment shader

    uniform sampler2D texture;
    uniform vec2 resolution;
    uniform vec2 offset;
    uniform vec2 size;

    void main() {

        vec2 uv = gl_FragCoord.xy / resolution * 2.0 - 1.0;
        uv = uv / 2.0 + 0.5;

        uv.x = (uv.x - offset.x) / size.x;
        uv.y = (uv.y - offset.y - scroll) / size.y + 1.0 ;

        gl_FragColor = texture2D(texture, uv);
    }

I understand that texture.wrapS = texture.wrapT = THREE.ClampToEdgeWrapping is the default behaviour, and therefor completely useless, the only other alternatives I can find in the documentation is RepeatWrapping or MirroredRepeatWrapping neither of which is what I want. I also can't create a custom texture with transparent edges as the images will be uploaded by the client.

How would I go about fixing this issue, can I somehow detect the boundaries within my shader ?

Upvotes: 1

Views: 1636

Answers (1)

Rabbid76
Rabbid76

Reputation: 210878

You've to respect the aspect ratio and discard the fragments if the distance to the center of the view is above 1.0. Scale the x component by the aspect ration and calculate the length of the vector form the center to the current position:

vec2 p = gl_FragCoord.xy / resolution * 2.0 - 1.0;
p.x *= resolution.x / resolution.y;
if (length(p) > 1.0)
    discard;

Note, the discard keyword can be used within a fragment shader to abandon the operation on the current fragment. This keyword causes the fragment to be discarded and no updates to any buffers will occur.


Note it is even possible to define a point and a radius in pixel coordinates and to show a circular area around this point.
e.g. You can update the center point by the current mouse position. Since the calculation is done in pixel coordinates, there is not need to scale the x component of the distance by the aspect ratio:

uniform vec2  center; // center of the circle in pixel coordinates
uniform float radius; // radius in pixel

void main()
{
   vec2 v = gl_FragCoord.xy - center.xy;
   if (length(v) > radius)
       discard;

   // [...]
}

Upvotes: 2

Related Questions