Swift、OpenGL 和 glVertexAttribPointer

Swift, OpenGL and glVertexAttribPointer

我正在将 this repo 中的 Objective-C 代码翻译成 Swift,以学习 OpenGL 的一些基础知识。我对它完全陌生。我有一个工作项目可以编译并生成一个带有浮动矩形的工作 NSOpenGLView,但是颜色是错误的。我已将问题缩小到我使用指向顶点和颜色数据的 glVertexAttribPointer 函数。

以下是我存储顶点和颜色数据的方式:

struct Vertex {
    var position: (x: GLfloat, y: GLfloat, z: GLfloat, w: GLfloat)
    var color: (r: GLfloat, g: GLfloat, b: GLfloat, a: GLfloat)
}

struct Vertices {
    var v1: Vertex
    var v2: Vertex
    var v3: Vertex
    var v4: Vertex
}

var vertexData = Vertices(
    v1: Vertex( position:   (x: -0.5, y: -0.5, z:  0.0, w:  1.0),
                color:      (r:  1.0, g:  0.0, b:  0.0, a:  1.0)),
    v2: Vertex( position:   (x: -0.5, y:  0.5, z:  0.0, w:  1.0),
                color:      (r:  0.0, g:  1.0, b:  0.0, a:  1.0)),
    v3: Vertex( position:   (x:  0.5, y:  0.5, z:  0.0, w:  1.0),
                color:      (r:  0.0, g:  0.0, b:  1.0, a:  1.0)),
    v4: Vertex( position:   (x:  0.5, y: -0.5, z:  0.0, w:  1.0),
                color:      (r:  1.0, g:  1.0, b:  1.0, a:  1.0)) )

我尝试翻译的 glVertexAttribPointer 函数的 Objective-C 版本如下所示:

glVertexAttribPointer((GLuint)positionAttribute, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), (const GLvoid *)offsetof(Vertex, position));
glVertexAttribPointer((GLuint)colourAttribute  , 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), (const GLvoid *)offsetof(Vertex, colour  ));

Objective-C版本使用offsetof宏来设置这些函数的指针参数。 Swift 不允许使用宏,所以我想弄清楚用什么来代替它。我试过通过 nil,像这样:

glVertexAttribPointer(GLuint(positionAttribute), 4, UInt32(GL_FLOAT), UInt8(GL_FALSE), GLsizei(sizeof(Vertex)), nil)

glVertexAttribPointer(GLuint(colorAttribute), 4, UInt32(GL_FLOAT), UInt8(GL_FALSE), GLsizei(sizeof(Vertex)), nil)

但是如果我这样做,颜色数据数组将填充位置数据 - 不考虑偏移量,因此它使用位置数据作为位置和颜色属性。

我发现 建议使用 withUnsafePointer 并试了一下,像这样:

withUnsafePointer(&vertexData.v1.position) { ptr in
    glVertexAttribPointer(GLuint(positionAttribute), 4, UInt32(GL_FLOAT), UInt8(GL_FALSE), GLsizei(sizeof(Vertex)), ptr)
}

withUnsafePointer(&vertexData.v1.color) { ptr in
    glVertexAttribPointer(GLuint(colorAttribute), 4, UInt32(GL_FLOAT), UInt8(GL_FALSE), GLsizei(sizeof(Vertex)), ptr)
}

但这会使整个显示崩溃,需要强制关机并重新启动。

我不确定接下来要尝试什么。我正在处理的完整代码可用 here.

编辑:

我也试过将指针指向第一个数据点并将其前进 4 GLfloats,如下所示:

let ptr = UnsafePointer<GLfloat>([vertexData.v1.position.x])
glVertexAttribPointer(GLuint(positionAttribute), 4, UInt32(GL_FLOAT), UInt8(GL_FALSE), GLsizei(sizeof(Vertex)), ptr)

glVertexAttribPointer(GLuint(colorAttribute), 4, UInt32(GL_FLOAT), UInt8(GL_FALSE), GLsizei(sizeof(Vertex)), ptr.advancedBy(4))

显示没有崩溃,但屏幕上根本没有绘制任何内容。

要复制 offsetof 功能,您只需要知道每个字段在您的结构中的字节偏移量。

在您的情况下,假设您的结构紧凑,第一个调用应该接收 0,第二个调用应该接收 4 * GLfloat,因为这是第一个组件的大小。我不确定如何直接从结构中提取这些数据,尤其是因为您使用的是元组。

为了说明,你的结构:

struct Vertex {
    var position: (x: GLfloat, y: GLfloat, z: GLfloat, w: GLfloat)
    var color: (r: GLfloat, g: GLfloat, b: GLfloat, a: GLfloat)
}

很可能是这样的:

GLfloat // <-- structure start // <-- position
GLfloat
GLfloat
GLfloat
GLfloat // <-- color
GLfloat
GLfloat
GLfloat

因此 position 位于偏移量 0,而 color 位于 4 * GLfloat.


要创建一个值为 16 的指针,this answer 看起来相关:

let ptr = UnsafePointer<()> + 16

所以我想这也应该有效:

let ptr = UnsafePointer<()> + sizeof(GLfloat) * 4

我定义了如下顶点结构:

struct Vertex {
var x : GLfloat = 0.0
var y : GLfloat = 0.0
var z : GLfloat = 0.0

var r : GLfloat = 0.0
var g : GLfloat = 0.0
var b : GLfloat = 0.0
var a : GLfloat = 1.0


init(_ x : GLfloat, _ y : GLfloat, _ z : GLfloat, _ r : GLfloat = 0.0, _ g : GLfloat = 0.0, _ b : GLfloat = 0.0, _ a : GLfloat = 1.0) {
    self.x = x
    self.y = y
    self.z = z

    self.r = r
    self.g = g
    self.b = b
    self.a = a
}

然后我设置顶点

let vertices : [Vertex] = [
    Vertex( 1.0, -1.0, 0, 1.0, 0.0, 0.0, 1.0),
    Vertex( 1.0,  1.0, 0, 0.0, 1.0, 0.0, 1.0),
    Vertex(-1.0,  1.0, 0, 0.0, 0.0, 1.0, 1.0),
    Vertex(-1.0, -1.0, 0, 1.0, 1.0, 0.0, 1.0)
]

let indices : [GLubyte] = [
    0, 1, 2,
    2, 3, 0
]

我使用 GLKViewController,所以 glkView(view:drawInRect:) 函数如下所示:

override func glkView(view: GLKView, drawInRect rect: CGRect) {
    glClearColor(1.0, 0.0, 0.0, 1.0);
    glClear(GLbitfield(GL_COLOR_BUFFER_BIT))

    shader.prepareToDraw()

    glEnableVertexAttribArray(VertexAttributes.Position.rawValue)
    glVertexAttribPointer(
        VertexAttributes.Position.rawValue,
        3,
        GLenum(GL_FLOAT),
        GLboolean(GL_FALSE),
        GLsizei(sizeof(Vertex)), BUFFER_OFFSET(0))


    glEnableVertexAttribArray(VertexAttributes.Color.rawValue)
    glVertexAttribPointer(
        VertexAttributes.Color.rawValue,
        4,
        GLenum(GL_FLOAT),
        GLboolean(GL_FALSE),
        GLsizei(sizeof(Vertex)), BUFFER_OFFSET(3 * sizeof(GLfloat))) // x, y, z | r, g, b, a :: offset is 3*sizeof(GLfloat)

    glBindBuffer(GLenum(GL_ARRAY_BUFFER), vertexBuffer)
    glBindBuffer(GLenum(GL_ELEMENT_ARRAY_BUFFER), indexBuffer)
    glDrawElements(GLenum(GL_TRIANGLES), GLsizei(indices.count), GLenum(GL_UNSIGNED_BYTE), nil)

    glDisableVertexAttribArray(VertexAttributes.Position.rawValue)

}

BUFFER_OFFSET函数定义如下:

func BUFFER_OFFSET(n: Int) -> UnsafePointer<Void> {
    let ptr: UnsafePointer<Void> = nil
    return ptr + n
}

我可以得到结果

非常感谢@Bartek!