LoginSignup
4
7

More than 5 years have passed since last update.

SwiftでOpenGL その1

Last updated at Posted at 2015-09-13

はじめに

Xcodeでは、3DプログラミングをするのにMetalを使うかOpenGLを使うかを選べる。

ただし、SwiftでOpenGLを使おうとすると、"in progress"などと書かれていて、きちんと動かない。
しょうがないので、とりあえず動くようにソースを書き換えたメモ。

GameViewController.swift

Xcodeで新しくSwift+OpenGL ESのプロジェクトを作ると、GameViewController.swiftというファイルが作成されるが、その中身は動かない。
なので動くように書き換えたのが下記。

割と力技なので動作保証はなし。

GameViewController.swift

import GLKit
import OpenGLES

class GameViewController: GLKViewController {
    enum Uniform_index : Int {
        case UNIFORM_MODELVIEWPROJECTION_MATRIX = 0
        case UNIFORM_NORMAL_MATRIX = 1
        case NUM_UNIFORMS = 2
    }

    var uniforms: [GLint] = [GLint](count: Uniform_index.NUM_UNIFORMS.rawValue, repeatedValue: GLint(0))

    var program: GLuint = 0

    var modelViewProjectionMatrix: GLKMatrix4 = GLKMatrix4Identity
    var normalMatrix: GLKMatrix3 = GLKMatrix3Identity
    var rotation: Float = 0.0

    var vertexArray: GLuint = 0
    var vertexBuffer: GLuint = 0

    var context: EAGLContext? = nil
    var effect: GLKBaseEffect? = nil

    deinit {
        self.tearDownGL()

        if EAGLContext.currentContext() === self.context {
            EAGLContext.setCurrentContext(nil)
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        self.context = EAGLContext(API: .OpenGLES2)

        if self.context == nil {
            println("Failed to create ES context")
        }

        let view = self.view as! GLKView
        view.context = self.context
        view.drawableDepthFormat = .Format24

        self.setupGL()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()

        if self.isViewLoaded() && self.view.window != nil {
            self.view = nil

            self.tearDownGL()

            if EAGLContext.currentContext() === self.context {
                EAGLContext.setCurrentContext(nil)
            }
            self.context = nil
        }
    }

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

    func setupGL() {
        EAGLContext.setCurrentContext(self.context)

        self.loadShaders()

        self.effect = GLKBaseEffect()
        self.effect!.light0.enabled = /*GL_TRUE*/1
        self.effect!.light0.diffuseColor = GLKVector4Make(1.0, 0.4, 0.4, 1.0)

        glEnable(/*GL_DEPTH_TEST*/0x0B71)

        glGenVertexArraysOES(1, &vertexArray)
        glBindVertexArrayOES(vertexArray)

        glGenBuffers(1, &vertexBuffer)
        glBindBuffer(/*GL_ARRAY_BUFFER*/0x8892, vertexBuffer)
        glBufferData(/*GL_ARRAY_BUFFER*/0x8892, sizeof(CGFloat) * gCubeVertexData.count, gCubeVertexData, /*GL_STATIC_DRAW*/0x88E4)

        glEnableVertexAttribArray(GLuint(GLKVertexAttrib.Position.rawValue))
        glVertexAttribPointer(GLuint(GLKVertexAttrib.Position.rawValue), GLint(3), GLenum(GL_FLOAT), GLboolean(GL_FALSE), GLsizei(24), BUFFER_OFFSET(0))
        glEnableVertexAttribArray(GLuint(GLKVertexAttrib.Normal.rawValue))
        glVertexAttribPointer(GLuint(GLKVertexAttrib.Normal.rawValue), GLint(3), GLenum(GL_FLOAT), GLboolean(GL_FALSE), GLsizei(24), BUFFER_OFFSET(12))

        glBindVertexArrayOES(0);
    }

    func tearDownGL() {
        EAGLContext.setCurrentContext(self.context)

        glDeleteBuffers(1, &vertexBuffer)
        glDeleteVertexArraysOES(1, &vertexArray)

        self.effect = nil

        if program != 0 {
            glDeleteProgram(program)
            program = 0
        }
    }

    // MARK: - GLKView and GLKViewController delegate methods

    func update() {
        let aspect = fabsf(Float(self.view.bounds.size.width / self.view.bounds.size.height))
        let projectionMatrix = GLKMatrix4MakePerspective(GLKMathDegreesToRadians(65.0), aspect, 0.1, 100.0)

        self.effect!.transform.projectionMatrix = projectionMatrix

        var baseModelViewMatrix = GLKMatrix4MakeTranslation(0.0, 0.0, -4.0)
        baseModelViewMatrix = GLKMatrix4Rotate(baseModelViewMatrix, rotation, 0.0, 1.0, 0.0)

        // Compute the model view matrix for the object rendered with GLKit
        var modelViewMatrix = GLKMatrix4MakeTranslation(0.0, 0.0, -1.5)
        modelViewMatrix = GLKMatrix4Rotate(modelViewMatrix, rotation, 1.0, 1.0, 1.0)
        modelViewMatrix = GLKMatrix4Multiply(baseModelViewMatrix, modelViewMatrix)

        self.effect!.transform.modelviewMatrix = modelViewMatrix

        // Compute the model view matrix for the object rendered with ES2
        modelViewMatrix = GLKMatrix4MakeTranslation(0.0, 0.0, 1.5)
        modelViewMatrix = GLKMatrix4Rotate(modelViewMatrix, rotation, 1.0, 1.0, 1.0)
        modelViewMatrix = GLKMatrix4Multiply(baseModelViewMatrix, modelViewMatrix)

        normalMatrix = GLKMatrix3InvertAndTranspose(GLKMatrix4GetMatrix3(modelViewMatrix), nil)

        modelViewProjectionMatrix = GLKMatrix4Multiply(projectionMatrix, modelViewMatrix)

        rotation += Float(self.timeSinceLastUpdate * 0.5)
    }

    override func glkView(view: GLKView, drawInRect rect: CGRect) {
        glClearColor(0.65, 0.65, 0.65, 1.0)
        glClear(/*GL_COLOR_BUFFER_BIT*/0x00004000 | /*GL_DEPTH_BUFFER_BIT*/0x00000100)

        glBindVertexArrayOES(vertexArray)

        // Render the object with GLKit
        self.effect?.prepareToDraw()

        glDrawArrays(/*GL_TRIANGLES*/0x0004, 0, 36)

        // Render the object again with ES2
        glUseProgram(program)

        glUniformMatrix4fv(uniforms[Uniform_index.UNIFORM_MODELVIEWPROJECTION_MATRIX.rawValue], 1, 0, modelViewProjectionMatrix.m)
        glUniformMatrix3fv(uniforms[Uniform_index.UNIFORM_NORMAL_MATRIX.rawValue], 1, 0, normalMatrix.m)

        glDrawArrays(/*GL_TRIANGLES*/0x0004, 0, 36)
    }

    // MARK: -  OpenGL ES 2 shader compilation

    func loadShaders() -> Bool {
        var vertShader: GLuint = 0
        var fragShader: GLuint = 0
        var vertShaderPathname: String
        var fragShaderPathname: String

        // Create shader program.
        program = glCreateProgram()

        // Create and compile vertex shader.
        vertShaderPathname = NSBundle.mainBundle().pathForResource("Shader", ofType: "vsh")!
        if !self.compileShader(&vertShader, type: /*GL_VERTEX_SHADER*/0x8B31, file: vertShaderPathname) {
            println("Failed to compile vertex shader")
            return false
        }

        // Create and compile fragment shader.
        fragShaderPathname = NSBundle.mainBundle().pathForResource("Shader", ofType: "fsh")!
        if !self.compileShader(&fragShader, type: /*GL_FRAGMENT_SHADER*/0x8B30, file: fragShaderPathname) {
            println("Failed to compile fragment shader");
            return false
        }

        // Attach vertex shader to program.
        glAttachShader(program, vertShader)

        // Attach fragment shader to program.
        glAttachShader(program, fragShader)

        // Bind attribute locations.
        // This needs to be done prior to linking.
        glBindAttribLocation(program, GLuint(GLKVertexAttrib.Position.rawValue), "position")
        glBindAttribLocation(program, GLuint(GLKVertexAttrib.Normal.rawValue), "normal")

        // Link program.
        if !self.linkProgram(program) {
            println("Failed to link program: \(program)")

            if vertShader != 0 {
                glDeleteShader(vertShader)
                vertShader = 0
            }
            if fragShader != 0 {
                glDeleteShader(fragShader)
                fragShader = 0
            }
            if program != 0 {
                glDeleteProgram(program)
                program = 0
            }

            return false
        }

        // Get uniform locations.
        uniforms[Uniform_index.UNIFORM_MODELVIEWPROJECTION_MATRIX.rawValue] = glGetUniformLocation(program, "modelViewProjectionMatrix")
        uniforms[Uniform_index.UNIFORM_NORMAL_MATRIX.rawValue] = glGetUniformLocation(program, "normalMatrix")

        // Release vertex and fragment shaders.
        if vertShader != 0 {
            glDetachShader(program, vertShader)
            glDeleteShader(vertShader);
        }
        if fragShader != 0 {
            glDetachShader(program, fragShader);
            glDeleteShader(fragShader);
        }

        return true
    }


    func compileShader(inout shader: GLuint, type: GLenum, file: String) -> Bool {
        var status: GLint = 0    // 初期化しないとポインタを引数に持つ関数に渡せないっぽい?
        var source: UnsafePointer<GLchar> = NSString(contentsOfFile: file, encoding: NSUTF8StringEncoding, error: nil)!.UTF8String
        if source == nil {
            println("Failed to load vertex shader")
            return false
        }

        shader = glCreateShader(type)
        glShaderSource(shader, GLsizei(1), &source, nil)
        glCompileShader(shader)

        glGetShaderiv(shader, /*GL_COMPILE_STATUS*/0x8B81, &status)
        if status == 0 {
            glDeleteShader(shader);
            return false
        }

        return true
    }

    func linkProgram(prog: GLuint) -> Bool {
        var status: GLint = 0
        glLinkProgram(prog)

        glGetProgramiv(prog, /*GL_LINK_STATUS*/0x8B82, &status)
        if status == 0 {
            return false
        }

        return true
    }

    func validateProgram(prog: GLuint) -> Bool {
        var logLength: GLint = 0
        var status: GLint = 0

        glValidateProgram(prog)
        glGetProgramiv(prog, /*GL_INFO_LOG_LENGTH*/0x8B84, &logLength)
        if logLength > 0 {
            let log = UnsafeMutablePointer<UInt>.alloc(Int(logLength))
            glGetProgramInfoLog(prog, GLsizei(logLength), &logLength, UnsafeMutablePointer<GLchar>(log))
            println("Program validate log: \n\(log)")
            free(log)
        }

        glGetProgramiv(prog, GLenum(GL_VALIDATE_STATUS), &status)
        if status == 0 {
            return false
        }

        return true
    }

    let gCubeVertexData: [GLfloat] = [
        // Data layout for each line below is:
        // positionX, positionY, positionZ,     normalX, normalY, normalZ,
        0.5, -0.5, -0.5,        1.0, 0.0, 0.0,
        0.5, 0.5, -0.5,         1.0, 0.0, 0.0,
        0.5, -0.5, 0.5,         1.0, 0.0, 0.0,
        0.5, -0.5, 0.5,         1.0, 0.0, 0.0,
        0.5, 0.5, -0.5,         1.0, 0.0, 0.0,
        0.5, 0.5, 0.5,          1.0, 0.0, 0.0,

        0.5, 0.5, -0.5,         0.0, 1.0, 0.0,
        -0.5, 0.5, -0.5,        0.0, 1.0, 0.0,
        0.5, 0.5, 0.5,          0.0, 1.0, 0.0,
        0.5, 0.5, 0.5,          0.0, 1.0, 0.0,
        -0.5, 0.5, -0.5,        0.0, 1.0, 0.0,
        -0.5, 0.5, 0.5,         0.0, 1.0, 0.0,

        -0.5, 0.5, -0.5,        -1.0, 0.0, 0.0,
        -0.5, -0.5, -0.5,      -1.0, 0.0, 0.0,
        -0.5, 0.5, 0.5,         -1.0, 0.0, 0.0,
        -0.5, 0.5, 0.5,         -1.0, 0.0, 0.0,
        -0.5, -0.5, -0.5,      -1.0, 0.0, 0.0,
        -0.5, -0.5, 0.5,        -1.0, 0.0, 0.0,

        -0.5, -0.5, -0.5,      0.0, -1.0, 0.0,
        0.5, -0.5, -0.5,        0.0, -1.0, 0.0,
        -0.5, -0.5, 0.5,        0.0, -1.0, 0.0,
        -0.5, -0.5, 0.5,        0.0, -1.0, 0.0,
        0.5, -0.5, -0.5,        0.0, -1.0, 0.0,
        0.5, -0.5, 0.5,         0.0, -1.0, 0.0,

        0.5, 0.5, 0.5,          0.0, 0.0, 1.0,
        -0.5, 0.5, 0.5,         0.0, 0.0, 1.0,
        0.5, -0.5, 0.5,         0.0, 0.0, 1.0,
        0.5, -0.5, 0.5,         0.0, 0.0, 1.0,
        -0.5, 0.5, 0.5,         0.0, 0.0, 1.0,
        -0.5, -0.5, 0.5,        0.0, 0.0, 1.0,

        0.5, -0.5, -0.5,        0.0, 0.0, -1.0,
        -0.5, -0.5, -0.5,      0.0, 0.0, -1.0,
        0.5, 0.5, -0.5,         0.0, 0.0, -1.0,
        0.5, 0.5, -0.5,         0.0, 0.0, -1.0,
        -0.5, -0.5, -0.5,      0.0, 0.0, -1.0,
        -0.5, 0.5, -0.5,        0.0, 0.0, -1.0
    ]
}

GLKMatrix3+Array.swift
import GLKit

extension GLKMatrix3 {
    var m: [Float] {
        get {
            let ary: [Float] = [
                m00,
                m01,
                m02,
                m10,
                m11,
                m12,
                m20,
                m21,
                m22,
            ]
            return ary
        }
    }
}
GLKMatrix4+Array.swift
import GLKit

extension GLKMatrix4 {
    var m: [Float] {
        get {
            let ary: [Float] = [
                m00,
                m01,
                m02,
                m03,
                m10,
                m11,
                m12,
                m13,
                m20,
                m21,
                m22,
                m23,
                m30,
                m31,
                m32,
                m33
            ]
            return ary
        }
    }
}

つづく?

その2に続くかどうかは未定。

4
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
7