Используя OpenGL ES 2.0 с iOS, как я рисую цилиндр между двумя точками?

Мне дано два GLKVector3, представляющие начальную и конечную точки цилиндра. Используя эти точки и радиус, мне нужно построить и сделать цилиндр. Я могу построить цилиндр с правильным расстоянием между точками, но в фиксированном направлении (в настоящее время всегда в направлении y (0, 1, 0) вверх). Я не уверен, какие вычисления мне нужно сделать, чтобы заставить цилиндр на правильной плоскости между двумя точками, чтобы линия проходила через две конечные точки. Я думаю, что есть какие-то вычисления, которые я могу применить, когда создаю свои данные вершин с вектором направления или углом, который создаст цилиндр, указывающий на правильное направление. Кто-нибудь имеет алгоритм или знает об одном, который поможет?

Ответ 1

Отметьте эту удивительную статью ; он датируется, но после адаптации алгоритма он работает как шарм. Один совет, OpenGL ES 2.0 поддерживает только треугольники, поэтому вместо использования GL_QUAD_STRIP, как это делает метод, вместо этого используйте GL_TRIANGLE_STRIP, и результат будет идентичным. Сайт также содержит кучу другой полезной информации о геометрии OpenGL.

См. приведенный ниже код для решения. Self представляет сетку и содержит вершины, индексы и т.д.

- (instancetype)initWithOriginRadius:(CGFloat)originRadius
                   atOriginPoint:(GLKVector3)originPoint
                    andEndRadius:(CGFloat)endRadius
                      atEndPoint:(GLKVector3)endPoint
                   withPrecision:(NSInteger)precision
                        andColor:(GLKVector4)color
{
self = [super init];

if (self) {
    // normal pointing from origin point to end point
    GLKVector3 normal = GLKVector3Make(originPoint.x - endPoint.x,
                                       originPoint.y - endPoint.y,
                                       originPoint.z - endPoint.z);

    // create two perpendicular vectors - perp and q
    GLKVector3 perp = normal;
    if (normal.x == 0 && normal.z == 0) {
        perp.x += 1;
    } else {
        perp.y += 1;
    }

    // cross product
    GLKVector3 q = GLKVector3CrossProduct(perp, normal);
    perp = GLKVector3CrossProduct(normal, q);

    // normalize vectors
    perp = GLKVector3Normalize(perp);
    q = GLKVector3Normalize(q);

    // calculate vertices
    CGFloat twoPi = 2 * PI;        
    NSInteger index = 0;
    for (NSInteger i = 0; i < precision + 1; i++) {
        CGFloat theta = ((CGFloat) i) / precision * twoPi; // go around circle and get points

        // normals
        normal.x = cosf(theta) * perp.x + sinf(theta) * q.x;
        normal.y = cosf(theta) * perp.y + sinf(theta) * q.y;
        normal.z = cosf(theta) * perp.z + sinf(theta) * q.z;

        AGLKMeshVertex meshVertex;
        AGLKMeshVertexDynamic colorVertex;

        // top vertex
        meshVertex.position.x = endPoint.x + endRadius * normal.x;
        meshVertex.position.y = endPoint.y + endRadius * normal.y;
        meshVertex.position.z = endPoint.z + endRadius * normal.z;
        meshVertex.normal = normal;
        meshVertex.originalColor = color;

        // append vertex
        [self appendVertex:meshVertex];

        // append color vertex
        colorVertex.colors = color;
        [self appendColorVertex:colorVertex];

        // append index
        [self appendIndex:index++];

        // bottom vertex
        meshVertex.position.x = originPoint.x + originRadius * normal.x;
        meshVertex.position.y = originPoint.y + originRadius * normal.y;
        meshVertex.position.z = originPoint.z + originRadius * normal.z;
        meshVertex.normal = normal;
        meshVertex.originalColor = color;

        // append vertex
        [self appendVertex:meshVertex];

        // append color vertex
        [self appendColorVertex:colorVertex];

        // append index
        [self appendIndex:index++];
    }

    // draw command
    [self appendCommand:GL_TRIANGLE_STRIP firstIndex:0 numberOfIndices:self.numberOfIndices materialName:@""];
}

return self;
}

Ответ 2

Вы рисуете более одного из этих цилиндров? Или когда-либо рисовать его в другом положении? Если это так, использование алгоритма из удивительной статьи - это не очень-то замечательная идея. Каждый раз, когда вы загружаете данные геометрии на GPU, вы несете затраты на производительность.

Лучший подход состоит в том, чтобы вычислить геометрию для одного базового цилиндра один раз, скажем, один с единичным радиусом и высотой - и материал, который данные вершин в VBO. Затем, когда вы рисуете, используйте матрицу преобразования модели в мир для масштабирования (независимо от радиуса и длины, если необходимо) и вращайте цилиндр на место. Таким образом, единственные новые данные, которые отправляются на GPU с каждым вызовом вызова, представляют собой матрицу 4x4 вместо всех данных вершин для любого полигонала цилиндра, который вы рисуете.