I'm trying to get a light shining from the camera onto a custom mesh I have created. However, no matter what I do to position a Light Node it always seems to stick to the original pivot point (0,0) of the custom mesh.
This is the code I use for creating my scene, light, camera and mesh nodes, and then finally adding an event handler to rotate the mesh on the x axis.
let scene = SCNScene() //Instantiate Scene for 3D object
SceneView.scene = scene
let ambientLightNode = SCNNode()
ambientLightNode.light = SCNLight()
ambientLightNode.light!.type = SCNLight.LightType.ambient
ambientLightNode.light!.color = UIColor(white: 0.67, alpha: 1.0)
scene.rootNode.addChildNode(ambientLightNode)
let omniLightNode = SCNNode()
omniLightNode.light = SCNLight()
omniLightNode.light!.castsShadow = true
omniLightNode.light!.type = SCNLight.LightType.omni
omniLightNode.light!.color = UIColor(white: 0.75, alpha: 1.0)
omniLightNode.position = SCNVector3Make(0, 0, 0)
scene.rootNode.addChildNode(omniLightNode)
let cameraNode = SCNNode()
cameraNode.camera = SCNCamera()
cameraNode.position = SCNVector3Make(0, 2, 0)
scene.rootNode.addChildNode(cameraNode)
//Create the mesh node.
let environmentMeshNode = SCNNode()
environmentMeshNode.position = SCNVector3Make(0, 0, -8)
scene.rootNode.addChildNode(environmentMeshNode)
//Create the custom mesh, move pivot point and add material.
environmentMesh.geometry = EnvironmentMesh.createEnvironmentMesh(10, 10)
environmentMesh.pivot = SCNMatrix4MakeTranslation(4.5, 0.5, -4.5)
environmentMesh.geometry?.firstMaterial?.diffuse.contents = UIColor.gray
environmentMeshNode.addChildNode(environmentMesh)
//Adding recognisers to manipulate rotation on x axis.
let panXRecognizer = UIPanGestureRecognizer(target: self, action: #selector(self.rotateXGesture(_:)))
SceneView.addGestureRecognizer(panXRecognizer)
The gestureRecognizer then rotate the mesh in the following way:
func rotateXGesture (_ sender: UIPanGestureRecognizer) { //X rotation recognizer
let translation = sender.translation(in: sender.view)
var newXAngle = (Float)(translation.x)*(Float)(Double.pi)/180.0
newXAngle += currentXAngle
environmentMesh.transform = SCNMatrix4MakeRotation(newXAngle, 0, 1, 0)
if(sender.state == UIGestureRecognizerState.ended){
currentXAngle = newXAngle
}
}
This is the class used for creating the custom mesh which is in this case just a 10x10 grid where a few vertices are elevated slightly.
class EnvironmentMesh{
//Custom function for creating the plane mesh. Takes in mesh vertex arguments.
static func createEnvironmentMesh(_ xSize: Int, _ zSize: Int) -> SCNGeometry {
var vertices:[SCNVector3] = []
for x in stride(from:0, to:xSize, by:1){
for z in stride(from:0, to:-zSize, by:-1) { // Creates a X*Y square of vertices.
vertices.append(SCNVector3Make(Float(x), 0, Float(z)))
}
}
vertices[55].y = 5
vertices[27].y = 3
vertices[85].y = 2
vertices[58].y = 1.3
let vertexData = NSData(bytes: vertices, length: vertices.count * MemoryLayout<SCNVector3>.size)
let vertexSource = SCNGeometrySource(data: vertexData as Data, semantic: SCNGeometrySource.Semantic.vertex, vectorCount: vertices.count, usesFloatComponents: true, componentsPerVector: 3, bytesPerComponent: MemoryLayout<Float>.size, dataOffset: 0, dataStride: MemoryLayout<SCNVector3>.size)
let normalSource = SCNGeometrySource(data: vertexData as Data, semantic: SCNGeometrySource.Semantic.normal, vectorCount: vertices.count, usesFloatComponents: true, componentsPerVector: 3, bytesPerComponent: MemoryLayout<Float>.size, dataOffset: 0, dataStride: MemoryLayout<SCNVector3>.size)
var indices:[Int32] = []
for x in stride(from:0, to:xSize-1, by:1){
for z in stride(from:0, to: zSize-1, by:1){
indices.append(Int32((x * 10) + z)) //0
indices.append(Int32(((x + 1) * 10) + z)) //10
indices.append(Int32((x * 10) + (z + 1))) //1
indices.append(Int32((x * 10) + (z + 1))) //1
indices.append(Int32(((x + 1) * 10) + z)) //10
indices.append(Int32(((x + 1) * 10) + (z + 1))) //11
}
}
let indexData = NSData(bytes: indices, length: MemoryLayout<Int32>.size * indices.count)
let indexElement = SCNGeometryElement(data: indexData as Data, primitiveType: SCNGeometryPrimitiveType.triangles, primitiveCount: indices.count / 3, bytesPerIndex: MemoryLayout<CInt>.size)
let geometry = SCNGeometry(sources: [vertexSource, normalSource], elements: [indexElement])
return geometry
}
}
This is my first questions here, I hope I have provided the needed information, else. Please let me know.
Related
What I am trying to do:
Make an empty 3D image (.dcm in this case) with image direction as
[1,0,0;
0,1,0;
0,0,1].
In this image, I insert an oblique trajectory, which essentially represents a cuboid. Now I wish to insert a hollow hemisphere in this cuboid (cuboid with all white pixels - constant value, hemisphere can be anything but differentiable), so that it is aligned along the axis of the trajectory.
What I am getting
So I used the general formula for a sphere:
x = x0 + r*cos(theta)*sin(alpha)
y = y0 + r*sin(theta)*sin(alpha)
z = z0 + r*cos(alpha)
where, 0 <= theta <= 2 * pi, 0 <= alpha <= pi / 2, for hemisphere.
What I tried to achieve this
So first I thought to just get the rotation matrix, between the image coordinate system and the trajectory coordinate system and multiply all points on the sphere with it. This didn't give me desired results as the rotated sphere was scaled and translated. I don't get why this was happening as I checked the points myself.
Then I thought why not make a hemisphere out of a sphere, which is cut at by a plane lying parallel to the y,z plane of the trajectory coordinate system. For this, I calculated the angle between x,y and z axes of the image with that of the trajectory. Then, I started to get hemisphere coordinates for theta_rotated and alpha_rotated. This didn't work either as instead of a hemisphere, I was getting a rather weird sphere.
This is without any transformations
This is with the angle transformation (second try)
For reference,
The trajectory coordinate system :
[-0.4744, -0.0358506, -0.8553;
-0.7049, 0.613244, 0.3892;
-0.5273, -0.787537, 0.342;];
which gives angles:
x_axis angle 2.06508 pi
y_axis angle 2.2319 pi
z_axis angle 1.22175 pi
Code to generate the cuboid
Vector3d getTrajectoryPoints(std::vector<Vector3d> &trajectoryPoints, Vector3d &target1, Vector3d &tangent1){
double distanceFromTarget = 10;
int targetShift = 4;
target -= z_vector;
target -= (tangent * targetShift);
Vector3d vector_x = -tangent;
y_vector = z_vector.cross(vector_x);
target -= y_vector;
Vector3d start = target - vector_x * distanceFromTarget;
std::cout << "target = " << target << "start = " << start << std::endl;
std::cout << "x " << vector_x << " y " << y_vector << " z " << z_vector << std::endl;
double height = 0.4;
while (height <= 1.6)
{
double width = 0.4;
while (width <= 1.6){
distanceFromTarget = 10;
while (distanceFromTarget >= 0){
Vector3d point = target + tangent * distanceFromTarget;
//std::cout << (point + (z_vector*height) - (y_vector * width)) << std::endl;
trajectoryPoints.push_back(point + (z_vector * height) + (y_vector * width));
distanceFromTarget -= 0.09;
}
width += 0.09;
}
height += 0.09;
}
}
The height and width as incremented with respect to voxel spacing.
Do you guys know how to achieve this and what am I doing wrong? Kindly let me know if you need any other info.
EDIT 1
After the answer from #Dzenan, I tried the following:
target = { -14.0783, -109.8260, -136.2490 }, tangent = { 0.4744, 0.7049, 0.5273 };
typedef itk::Euler3DTransform<double> TransformType;
TransformType::Pointer transform = TransformType::New();
double centerForTransformation[3];
const double pi = std::acos(-1);
try{
transform->SetRotation(2.0658*pi, 1.22175*pi, 2.2319*pi);
// transform->SetMatrix(transformMatrix);
}
catch (itk::ExceptionObject &excp){
std::cout << "Exception caught ! " << excp << std::endl;
transform->SetIdentity();
}
transform->SetCenter(centerForTransformation);
Then I loop over all the points in the hemisphere and transform them using,
point = transform->TransformPoint(point);
Although, I'd prefer to give the matrix which is equal to the trajectory coordinate system (mentioned above), the matrix isn't orthogonal and itk wouldn't take it. It must be said that I used the same matrix for resampling this image and extracting the cuboid and this was fine. Thence, I found the angles between x_image - x_trajectory, y_image - y_trajectory and z_image - z_trajectory and used SetRotation instead which gives me the following result (still incorrect):
EDIT 2
I tried to get the sphere coordinates without actually using the polar coordinates. Following discussion with #jodag, this is what I came up with:
Vector3d center = { -14.0783, -109.8260, -136.2490 };
height = 0.4;
while (height <= 1.6)
{
double width = 0.4;
while (width <= 1.6){
distanceFromTarget = 5;
while (distanceFromTarget >= 0){
// Make sure the point lies along the cuboid direction vectors
Vector3d point = center + tangent * distanceFromTarget + (z_vector * height) + (y_vector * width);
double x = std::sqrt((point[0] - center[0]) * (point[0] - center[0]) + (point[1] - center[1]) * (point[1] - center[1]) + (point[2] - center[2]) * (point[2] - center[2]));
if ((x <= 0.5) && (point[2] >= -136.2490 ))
orientation.push_back(point);
distanceFromTarget -= 0.09;
}
width += 0.09;
}
height += 0.09;
}
But this doesn't seem to work either.
This is the output
I'm a little confused about your first plot because it appears that the points being displayed are not defined in the image coordinates. The example I'm posting below assumes that voxels must be part of the image coordinate system.
The code below transforms the voxel coordinates in the image space into the trajectory space by using an inverse transformation. It then rasterises a 2x2x2 cube centered around 0,0,0 and a 0.9 radius hemisphere sliced along the xy axis.
Rather than continuing a long discussion in the comments I've decided to post this. Please comment if you're looking for something different.
% define trajectory coordinate matrix
R = [-0.4744, -0.0358506, -0.8553;
-0.7049, 0.613244, 0.3892;
-0.5273, -0.787537, 0.342]
% initialize 50x50x50 3d image
[x,y,z] = meshgrid(linspace(-2,2,50));
sz = size(x);
x = reshape(x,1,[]);
y = reshape(y,1,[]);
z = reshape(z,1,[]);
r = ones(size(x));
g = ones(size(x));
b = ones(size(x));
blue = [0,1,0];
green = [0,0,1];
% transform image coordinates to trajectory coordinates
vtraj = R\[x;y;z];
xtraj = vtraj(1,:);
ytraj = vtraj(2,:);
ztraj = vtraj(3,:);
% rasterize 2x2x2 cube in trajectory coordinates
idx = (xtraj <= 1 & xtraj >= -1 & ytraj <= 1 & ytraj >= -1 & ztraj <= 1 & ztraj >= -1);
r(idx) = blue(1);
g(idx) = blue(2);
b(idx) = blue(3);
% rasterize radius 0.9 hemisphere in trajectory coordinates
idx = (sqrt(xtraj.^2 + ytraj.^2 + ztraj.^2) <= 0.9) & (ztraj >= 0);
r(idx) = green(1);
g(idx) = green(2);
b(idx) = green(3);
% plot just the blue and green voxels
green_idx = (r == green(1) & g == green(2) & b == green(3));
blue_idx = (r == blue(1) & g == blue(2) & b == blue(3));
figure(1); clf(1);
plot3(x(green_idx),y(green_idx),z(green_idx),' *g')
hold('on');
plot3(x(blue_idx),y(blue_idx),z(blue_idx),' *b')
axis([1,100,1,100,1,100]);
axis('equal');
axis('vis3d');
You can generate you hemisphere in some physical space, then transform it (translate and rotate) by using e.g. RigidTransform's TransformPoint method. Then use TransformPhysicalPointToIndex method in itk::Image. Finally, use SetPixel method to change intensity. Using this approach you will have to control the resolution of your hemisphere to fully cover all the voxels in the image.
Alternative approach is to construct a new image into which you create you hemisphere, then use resample filter to create a transformed version of the hemisphere in an arbitrary image.
I'm researching how to find the inertia for a 2D shape. The contour of this shape is meshed with several points, the x and y coordinate of each point is already known.
I know the expression of Ixx, Iyy and Ixy but the body has no mass. How do I proceed?
For whatever shape you have, you need to split it into triangles and handle each triangle separately. Then in the end combined the results using the following rules
Overall
% Combined total area of all triangles
total_area = SUM( area(i), i=1:n )
total_mass = SUM( mass(i), i=1:n )
% Combined centroid (center of mass) coordinates
combined_centroid_x = SUM( mass(i)*centroid_x(i), i=1:n)/total_mass
combined_centroid_y = SUM( mass(i)*centroid_y(i), i=1:n)/total_mass
% Each distance to triangle (squared)
centroid_distance_sq(i) = centroid_x(i)*centroid_x(i)+centroid_y(i)*centroid_y(i)
% Combined mass moment of inertia
combined_mmoi = SUM(mmoi(i)+mass(i)*centroid_distance_sq(i), i=1:n)
Now for each triangle.
Consider the three corner vertices with vector coordinates, points A, B and C
a=[ax,ay]
b=[bx,by]
c=[cx,cy]
and the following dot and cross product (scalar) combinations
a·a = ax*ax+ay*ay
b·b = bx*bx+by*by
c·c = cx*cx+cy*cy
a·b = ax*bx+ay*by
b·c = bx*cx+by*cy
c·a = cx*ax+cy*ay
a×b = ax*by-ay*bx
b×c = bx*cy-by*cx
c×a = cx*ay-cy*ax
The properties of the triangle are (with t(i) the thickness and rho the mass density)
area(i) = 1/2*ABS( a×b + b×c + c×a )
mass(i) = rho*t(i)*area(i)
centroid_x(i) = 1/3*(ax + bx + cx)
centroid_y(i) = 1/3*(ay + by + cy)
mmoi(i) = 1/6*mass(i)*( a·a + b·b + c·c + a·b + b·c + c·a )
By component the above are
area(i) = 1/2*ABS( ax*(by-cy)+ay*(cx-bx)+bx*cy-by*cx)
mmoi(i) = mass(i)/6*(ax^2+ax*(bx+cx)+bx^2+bx*cx+cx^2+ay^2+ay*(by+cy)+by^2+by*cy+cy^2)
Appendix
A little theory here. The area of each triangle is found using
Area = 1/2 * || (b-a) × (c-b) ||
where × is a vector cross product, and || .. || is vector norm (length function).
The triangle is parametrized by two variables t and s such that the double integral A = INT(INT(1,dx),dy) gives the total area
% position r(s,t) = [x,y]
[x,y] = [ax,ay] + t*[bx-ax, by-zy] + t*s*[cx-bx,cy-by]
% gradient directions along s and t
(dr/dt) = [bx-ax,by-ay] + s*[cx-bx,cy-by]
(dr/ds) = t*[cx-bx,cy-by]
% Integration area element
dA = || (dr/ds)×(dr/dt) || = (2*A*t)*ds*dt
%
% where A = 1/2*||(b-a)×(c-b)||
% Check that the integral returns the area
Area = INT( INT( 2*A*t,s=0..1), t=0..1) = 2*A*(1/2) = A
% Mass moment of inertia components
/ / / | y^2+z^2 -x*y -x*z |
I = 2*m*| | | t*| -x*y x^2+z^2 -y*z | dz ds dt
/ / / | -x*z -y*z x^2+y^2 |
% where [x,y] are defined from the parametrization
I want to slightly correct excellent John Alexiou answer:
Triangle mmoi algorithm expects corners to be defined relative to triangle center of mass (centroid). So subtract centroid from corners before calculating mmoi
Shape mmoi algorithm expects centroids of each single triangle to be defined relative to shape center of mass (combined centroid). So subtract combined centroid from each triangle centroid before calculating mmoi.
So result code would look like this:
public static float CalculateMMOI(Triangle[] triangles, float thickness, float density)
{
float[] mass = new float[triangles.Length];
float[] mmoi = new float[triangles.Length];
Vector2[] centroid = new Vector2[triangles.Length];
float combinedMass = 0;
float combinedMMOI = 0;
Vector2 combinedCentroid = new Vector2(0, 0);
for (var i = 0; i < triangles.Length; ++i)
{
mass[i] = triangles[i].CalculateMass(thickness, density);
mmoi[i] = triangles[i].CalculateMMOI(mass[i]);
centroid[i] = triangles[i].CalculateCentroid();
combinedMass += mass[i];
combinedCentroid += mass[i] * centroid[i];
}
combinedCentroid /= combinedMass;
for (var i = 0; i < triangles.Length; ++i) {
combinedMMOI += mmoi[i] + mass[i] * (centroid[i] - combinedCentroid).LengthSquared();
}
return combinedMMOI;
}
public struct Triangle
{
public Vector2 A, B, C;
public float CalculateMass(float thickness, float density)
{
var area = CalculateArea();
return area * thickness * density;
}
public float CalculateArea()
{
return 0.5f * Math.Abs(Vector2.Cross(A - B, A - C));
}
public float CalculateMMOI(float mass)
{
var centroid = CalculateCentroid()
var a = A - centroid;
var b = B - centroid;
var c = C - centroid;
var aa = Vector2.Dot(a, a);
var bb = Vector2.Dot(b, b);
var cc = Vector2.Dot(c, c);
var ab = Vector2.Dot(a, b);
var bc = Vector2.Dot(b, c);
var ca = Vector2.Dot(c, a);
return (aa + bb + cc + ab + bc + ca) * mass / 6f;
}
public Vector2 CalculateCentroid()
{
return (A + B + C) / 3f;
}
}
public struct Vector2
{
public float X, Y;
public float LengthSquared()
{
return X * X + Y * Y;
}
public static float Dot(Vector2 a, Vector2 b)
{
return a.X * b.X + a.Y * b.Y;
}
public static float Cross(Vector2 a, Vector2 b)
{
return a.X * b.Y - a.Y * b.X;
}
}
I am trying the calculate normal per vertex.
But I do something wrong. When I run the code I see this:
Here Is my code, Note that vertex1 is vertex before the current vertex and vertex2 is vertex after the current vertex.
for (int j = 0; j < meshes[t].face[i].numOfPoints; j++)
{
if (normalSetChange)
{
vector3D vertex1, vertex2;
if ((j < meshes[t].face[i].numOfPoints - 1) && (j > 0))
{
vertex1 = vertexes[meshes[t].face[i].vertex[j + 1]] - vertexes[meshes[t].face[i].vertex[j]];
vertex2 = vertexes[meshes[t].face[i].vertex[j - 1]] - vertexes[meshes[t].face[i].vertex[j]];
}
else if (j < meshes[t].face[i].numOfPoints - 1)
{
vertex1 = vertexes[meshes[t].face[i].vertex[j + 1]] - vertexes[meshes[t].face[i].vertex[j]];
vertex2 = vertexes[meshes[t].face[i].vertex[meshes[t].face[i].numOfPoints - 1]] - vertexes[meshes[t].face[i].vertex[j]];
}
else if (j > 0)
{
vertex1 = vertexes[meshes[t].face[i].vertex[0]] - vertexes[meshes[t].face[i].vertex[j]];
vertex2 = vertexes[meshes[t].face[i].vertex[j - 1]] - vertexes[meshes[t].face[i].vertex[j]];
}
normalSet = vector3D(vertex1.y * vertex2.z - vertex1.z * vertex2.y,
vertex1.z * vertex2.x - vertex1.x * vertex2.z,
vertex1.x * vertex2.y - vertex1.y * vertex2.x);
normalLength = sqrt(normalSet.x * normalSet.x + normalSet.y * normalSet.y + normalSet.z * normalSet.z);
normalSet.x /= normalLength;
normalSet.y /= normalLength;
normalSet.z /= normalLength;
writePolygonLineVCN(PolygonLineVCN(vertexes[meshes[t].face[i].vertex[j]], vertexestexCoordinate[meshes[t].face[i].texCoordinate[j]], normalSet), newFile[workOnCPU]);
}
else
writePolygonLineVCN(PolygonLineVCN(vertexes[meshes[t].face[i].vertex[j]], vertexestexCoordinate[meshes[t].face[i].texCoordinate[j]], vertexesNormals[meshes[t].face[i].normal[j]]), newFile[workOnCPU]);
}
You are computing normals per triangle, not per vertex. In fact you can clearly see "solid" normals in the image you posted.
In order to compute "smooth" normals, you need to assign to each vertex a normal which is an average of the normals of the triangles adjacent to that vertex.
Here's some pseudocode, which computed the weighted average of the normals based on the angle between the two edges adjacent to the vertex. (Maybe someone uses the area of the triangle as weight, I don't know if there is an universally accepted way to do it).
vector3D triangleNormalFromVertex(int face_id, int vertex_id) {
//This assumes that A->B->C is a counter-clockwise ordering
vector3D A = mesh.face[face_id].vertex[vertex_id];
vector3D B = mesh.face[face_id].vertex[(vertex_id+1)%3];
vector3D C = mesh.face[face_id].vertex[(vertex_id+2)%3];
vector3D N = cross(B-A,C-A);
float sin_alpha = length(N) / (length(B-A) * length(C-A) );
return normalize(N) * asin(sin_alpha);
}
void computeNormals() {
for (vertex v in mesh) {
vector3D N (0,0,0);
for (int i = 0;i < NumOfTriangles;++i) {
if (mesh.face[i].contains(v) ) {
int VertexID = index_of_v_in_triangle(i,v); //Can be 0,1 or 2
N = N + triangleNormalFromVertex(i,VertexID);
}
}
N = normalize(N);
add_N_to_normals_for_vertex_v(N,v);
}
}
I have a problem when trying to tesselate a polygon using GLU. The vertex callback always calls back with the last vertex defined by gluTessVertex. It seems as though the values stored in GLdouble v[3] are getting GC'd in each iteration of the for loop. How can I store each GLdouble v[3] so it does not get GC'd?
for(int i = 0; i < vtxcnt; i++)
{
float lon = dbls[i * 2];
float lat = dbls[(i * 2)+1];
GLdouble v[3] = {lon, lat, 0.0f};
gluTessVertex(tess, v, v);
}
* EDIT: This seems to fix the problem... *
GLdouble *vtxs = new GLdouble[vtxcnt * 3];
for(int i = 0; i < vtxcnt; i++)
{
lon = dbls[i * 2];
lat = dbls[(i * 2)+1];
vtxs[(i * 3) + 0] = (double)lon;
vtxs[(i * 3) + 1] = (double)lat;
vtxs[(i * 3) + 2] = (double)0;
gluTessVertex(tess, &vtxs[(i * 3) + 0], &vtxs[(i * 3) + 0]);
}
gluTessVertex only stores the vertex pointer. The pointer must stay valid until the tesselation is performed. This is not the case in your code, so it fails.
How do I draw a cylinder with OpenGL in OpenTK?
Sample code from an older project of mine. This creates an "uncapped" cylinder (top and bottom are empty).
int segments = 10; // Higher numbers improve quality
int radius = 3; // The radius (width) of the cylinder
int height = 10; // The height of the cylinder
var vertices = new List<Vector3>();
for (double y = 0; y < 2; y++)
{
for (double x = 0; x < segments; x++)
{
double theta = (x / (segments - 1)) * 2 * Math.PI;
vertices.Add(new Vector3()
{
X = (float)(radius * Math.Cos(theta)),
Y = (float)(height * y),
Z = (float)(radius * Math.Sin(theta)),
});
}
}
var indices = new List<int>();
for (int x = 0; x < segments - 1; x++)
{
indices.Add(x);
indices.Add(x + segments);
indices.Add(X + segments + 1);
indices.Add(x + segments + 1);
indices.Add(x + 1);
indices.Add(x);
}
You can now render the cylinder like this:
GL.Begin(BeginMode.Triangles);
foreach (int index in indices)
GL.Vertex3(vertices[index]);
GL.End();
You can also upload vertices and indices into a vertex buffer object to improve performance.
Generating the geometry for a cylinder is quite simple (let's consider a Z-aligned cylinder). Let me use pseudocode:
points = list of (x,y,z)
where x = sin(a)*RADIUS, y = cos(a)*RADIUS, z = b,
for each a in [0..2*PI) with step StepA,
for each b in [0..HEIGHT] with step StepB
About the indices: Let us assume N equal to the number of "levels" or "slices" of the cylinder (which depends on HEIGHT and StepB) and M equal to the number of points on every "slice" (which depends on StepA).
The cylinder contains some quads, each spanning 2 neighbouring slices, so the indices would look like:
indices = list of (a,b,c,d)
where a = M * slice + point,
b = M * slice + (point+1) % M,
c = (M+1) * slice + (point+1) % M,
d = (M+1) * slice + point
for each slice in [0..N-2]
for each point in [0..M-1]
If you need normals for the cylinder, they are simple to generate:
normals = (x/RADIUS,y/RADIUS,0)
for each (x,y,z) in points
That's it for the round part of the cylinder, you might also want the "caps" but I believe they are easy to do.
I'll leave the fun part of translating my pseudocode into your language of choice for you. :)
The rest is to create/bind the VBO, load up the geometry, set pointers, use your shader of choice and call glDrawArrays(...) - any OpenGL 3 tutorial should cover this; are you familiar with that part?