# OpenGL Torus

Related Topics: OpenGL Sphere, OpenGL Cylinder, Prism & Pipe

This page describes how to generate a torus geometry using C++ and how to draw it with OpenGL.

## Torus

A torus is a ring-like 3D geometry by revolving a circle around a circular path. The radius of the circle is called the minor radius, r. And, the radius of the circular path is called the major radius, R which is the distance from the origin to the center of the circle.

An arbitrary point (x, y, z) on a torus can be computed by the parametric equations with the major radius R, minor radius r, and the corresponding sector and side angles θ and ϕ.

Due to limited resources, we only use a limited number of sectors for the circular path and the sides for the circle itself to approximate the 3D geometry. The range of sector angles is from 0 to 360 degrees, and the side angle of the circle is from 180 to -180 degrees. The sector and side angle for each step can be calculated by the following;

The following C++ code generates all vertices of the torus for the given radii, sectors and sides. It also creates other vertex attributes; surface normals, texture coordinates and indices. For more details, please refer to buildVerticesSmooth() or buildVerticesFlat() functions in Torus.cpp class.

``````
// x = (R + r * cos(u)) * cos(v) = R * cos(v) + r * cos(u) * cos(v)
// y = (R + r * cos(u)) * sin(v) = R * sin(v) + r * cos(u) * sin(v)
// z = r * sin(u)
// where u: side angle (-180 ~ 180)
//       v: sector angle (0 ~ 360)

// clear memory of prev arrays
std::vector<float>().swap(vertices);
std::vector<float>().swap(normals);
std::vector<float>().swap(texCoords);
std::vector<unsigned int>().swap(indices);

float x, y, z, xy;                              // vertex position
float nx, ny, nz;                               // normal
float lengthInv = 1.0f / minorRadius;           // to normalize normals
float s, t;                                     // texCoord

float sectorStep = 2 * PI / sectorCount;
float sideStep = 2 * PI / sideCount;
float sectorAngle, sideAngle;

for(int i = 0; i <= sideCount; ++i)
{
// start the tube side from the inside where sideAngle = pi
sideAngle = PI - i * sideStep;              // starting from pi to -pi
xy = minorRadius * cosf(sideAngle);         // r * cos(u)
z = minorRadius * sinf(sideAngle);          // r * sin(u)

// add (sectorCount+1) vertices per side
// the first and last vertices have same position and normal,
// but different tex coords
for(int j = 0; j <= sectorCount; ++j)
{
sectorAngle = j * sectorStep;           // starting from 0 to 2pi

// tmp x and y to compute normal vector
x = xy * cosf(sectorAngle);
y = xy * sinf(sectorAngle);

// add normalized vertex normal first
nx = x * lengthInv;
ny = y * lengthInv;
nz = z * lengthInv;

// shift x & y, and vertex position
x += majorRadius * cosf(sectorAngle);   // (R + r * cos(u)) * cos(v)
y += majorRadius * sinf(sectorAngle);   // (R + r * cos(u)) * sin(v)

// vertex tex coord between [0, 1]
s = (float)j / sectorCount;
t = (float)i / sideCount;
}
}

// indices
//  k1--k1+1
//  |  / |
//  | /  |
//  k2--k2+1
unsigned int k1, k2;
for(int i = 0; i < sideCount; ++i)
{
k1 = i * (sectorCount + 1);     // beginning of current side
k2 = k1 + sectorCount + 1;      // beginning of next side

for(int j = 0; j < sectorCount; ++j, ++k1, ++k2)
{
// 2 triangles per sector
}
}

// add vertex attributes to arrays
void Torus::addVertex(float x, float y, float z)
{
vertices.push_back(x);
vertices.push_back(y);
vertices.push_back(z);
}
void Torus::addNormal(float nx, float ny, float nz)
{
normals.push_back(nx);
normals.push_back(ny);
normals.push_back(nz);
}
{
texCoords.push_back(s);
texCoords.push_back(t);
}
void Torus::addIndices(unsigned int i1, unsigned int i2, unsigned int i3)
{
indices.push_back(i1);
indices.push_back(i2);
indices.push_back(i3);
}

``````

## Example: Torus

This example constructs torues with major radius 1, minor radius 0.5, 36 sectors and 18 sides, but with different shadings; flat, smooth or textured. Torus.cpp class provides pre-defined functions; draw(), drawWithLines() and drawLines(), to draw a sphere using OpenGL VertexArray.

By default, the revolving axis of the torus is +Z axis. But, it can be changed by the last parameter of Torus class contructor (X=1, Y=2, or Z=3), or by calling setUpAxis() function after it is constructed.

``````
// create a torus with R=1, r=0.5, sectors=36, stacks=18, smooth=true, up=Z
Torus torus(1.0f, 0.5f, 36, 18);
Torus torus(1.0f, 0.5f, 36, 18, true, 3);  // same as above

// can change parameters later
torus.setSectorCount(72);
torus.setSideCount(24);
torus.setSmooth(false);
torus.setUpAxis(2);                   // X=1, Y=2, Z=3
...

// draw torus using vertexarray
torus.draw();

``````

This C++ class also provides getVertices(), getIndices(), getInterleavedVertices(), etc. in order to access the vertex data in GLSL. The following code draws a torus with interleaved vertex data using VBO, VAO and GLSL. Or, download torusShader.zip for more details.

``````
// create a torus with default params; R=1, r=0.5, sectors=36, sides=18, smooth=true
Torus torus;

// creat VAO to store VBO states
GLuint vaoId;
glGenVertexArrays(1, &vaoId1);
glBindVertexArray(vaoId1);

// copy interleaved vertex data (V/N/T) to VBO
GLuint vboId;
glGenBuffers(1, &vboId);
glBindBuffer(GL_ARRAY_BUFFER, vboId);             // for vertex data
glBufferData(GL_ARRAY_BUFFER,                     // target
torus.getInterleavedVertexSize(),    // data size, # of bytes
torus.getInterleavedVertices(),      // ptr to vertex data
GL_STATIC_DRAW);                     // usage

// copy index data to VBO
GLuint iboId;
glGenBuffers(1, &iboId);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, iboId);     // for index data
glBufferData(GL_ELEMENT_ARRAY_BUFFER,             // target
torus.getIndexSize(),                // data size, # of bytes
torus.getIndices(),                  // ptr to index data
GL_STATIC_DRAW);                     // usage

// enable vertex array attributes for bound VAO
glEnableVertexAttribArray(attribPosition);
glEnableVertexAttribArray(attribNormal);
glEnableVertexAttribArray(attribTexCoord);

// store vertex array pointers to bound VAO
int stride = torus.getInterleavedStride();
glVertexAttribPointer(attribPosition, 3, GL_FLOAT, false, stride, 0);
glVertexAttribPointer(attribNormal, 3, GL_FLOAT, false, stride, (void*)(3 * sizeof(float)));
glVertexAttribPointer(attribTexCoord, 2, GL_FLOAT, false, stride, (void*)(6 * sizeof(float)));
...

// bind VAO before drawing
glBindVertexArray(vaoId);

// draw a torus with VBO
glDrawElements(GL_TRIANGLES,                    // primitive type
torus.getIndexCount(),           // # of indices
GL_UNSIGNED_INT,                 // data type
(void*)0);                       // offset to indices

// unbind VAO
glBindVertexArray(0);

``````

## Example: WebGL Torus (Interactive Demo)

Sector Count
Side Count

It is a JavaScript version of Torus class, Torus.js, and rendering it with WebGL. Drag the sliders to change the parameters of the totus. The fullscreen version is available at WebGL Torus.

The following JavaScript code is to create and to render a torus object.

``````
// create a torus with 5 params: R, r, sectors, sides, smooth
let torus = new Torus(gl, 1, 0.5, 36, 18, false);
...

// change params of torus later
torus.setSectorCount(8);
torus.setSideCount(4);
torus.setSmooth(true);
torus.setUpAxis(2);
...

// draw a torus with interleaved mode
gl.bindBuffer(gl.ARRAY_BUFFER, torus.vboVertex);
gl.vertexAttribPointer(gl.program.attribPosition, 3, gl.FLOAT, false, torus.stride, 0);
gl.vertexAttribPointer(gl.program.attribNormal, 3, gl.FLOAT, false, torus.stride, 12);
gl.vertexAttribPointer(gl.program.attribTexCoord0, 2, gl.FLOAT, false, torus.stride, 24);

gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, torus.vboIndex);
gl.drawElements(gl.TRIANGLES, torus.getIndexCount(), gl.UNSIGNED_SHORT, 0);
...

``````