Reputation: 387
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
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
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