Squeeze rectangular texture onto segment of circle

I’m trying to map a rectangular texture on some segment of a circle. A segment of 70° in this context.
My problem is that the texture is just laid on to the circle and not squeezed to fit into it, so it’s probably a problem of texture coordinates.

The texture I’m using is on the top left and the desired result is the top right image.

I’m using the following code to draw the circle. The vertices are fed into a VAO that is drawn using GL_TRIANGLE_FAN. The first parameter of Vertex is the position and the second the texture coordinates.

std::vector<Vertex> drawCircle(GLfloat cx, GLfloat cy, GLfloat radius) {
	std::vector<Vertex> vertices;
	vertices.push_back(Vertex(glm::vec3(cx, cy, 0), glm::vec2(0.5, 0.0)));

	for (float i = 55; i <= 125; i++)
	{
		float angle_radians = i * M_PI / 180.0f;

		float x_val = cos(angle_radians);
		float y_val = sin(angle_radians);

		float x = radius * x_val;
		float y = radius * y_val;
		
		vertices.push_back(Vertex(glm::vec3(cx + x, cy + y, 0),
			glm::vec2(x_val * 0.5 + 0.5, y_val * 0.5 + 0.5)));
	}

	return vertices;
}

Im using a standard vertex shader. Here is the main method my fragment shader:

  void main()
   {
   	float t_inv = 1.0 - texCoord0.t;
   	gl_FragColor = texture2D(texture, vec2(texCoord0.x, t_inv));
   }

This results in the bottom left image.

I found some code online which changes the fragment shader to use a different uv mapping:

vec2 uv = vec2((texCoord0.s - t_inv / 2.0) / (1 - t_inv), t_inv);
gl_FragColor = texture2D(texture, uv);

which results in the bottom right image, which is better but not quite what I want to archieve.

Any help would be kindly appreciated.

With the texture coordinates you’re currently using, you’d need to perform rectangular-to-polar conversion in the fragment shader:

vec2 t = (texCoord0 - 0.5) * 2;
float r = length(t);
float a = atan(t.s, t.t) / (35*M_PI/180) * 0.5 + 0.5;
vec2 texCoord = vec2(a, r);

But it would be more efficient to use polar texture coordinates: vec2(0,0) for the centre vertex and vec2((i-55)/70.0,1) for the outside vertices. In the fragment shader, you just need to undo the interpolation of the S coordinate:

vec2 texCoord = vec2(texCoord0.s/texCoord0.t, texCoord0.t);
1 Like

@GClements Thank you very much, your code solved my problem :+1: .
One question though, what is the purpose of this calculation:

vec2 texCoord = vec2(texCoord0.s/texCoord0.t, texCoord0.t);

I’m having a hard time understanding it.

It counteracts the interpolation of the s coordinate between the exterior vertices and the centre vertex, so that a line from the edge to the centre will have the same s coordinate along its length.

The triangle will have texture coordinates of (s1,1), (s2,1) and (0,0). At a point on the exterior edge, the interpolated texture coordinates will be (s0,1). So along the line from that point to the centre, the texture coordinates will be interpolated between (s0,1) and (0,0), i.e. (s,t)=k*(s0,1)+(1-k)*(0,0) = (k*s0,k); dividing s by t gives (s0,k).

Effectively, it tessellates the texture into rectangular strips and maps each strip to a triangle (so the bottom edge has zero width). Without it, the texture mapping forms a triangle fan in texture space, so half the texture (the half to the lower-right of the main diagonal) never gets used.

1 Like

Thanks for the detailed explanation, that clears things up :+1: