I am trying to get the world coordinates of where I touch on screen. I am trying to use GLKMathUnproject however I have yet to get satisfactory results from it and after days of Googling I have yet to make significant progress. I’ve attached a sample GLKViewController that draws a colored square on screen. I expect that when I tap on the bottom left corner I would get the coordinate (-0.5, -0.5) back out. Can someone help me out?
import GLKit
private struct VertexAttribute {
let name: UnsafePointer<GLchar>!
let index: GLuint
let normalized: GLboolean
let offset: UnsafeRawPointer!
let size: GLint
let type: GLenum
func bindAttribLocation(_ programHandle: GLuint) {
glBindAttribLocation(programHandle, index, name)
}
func enableVertexAttribArray() {
glEnableVertexAttribArray(index)
}
func disableVertexAttribArray() {
glDisableVertexAttribArray(index)
}
func vertexAttribPointer() {
glVertexAttribPointer(index, size, type, normalized, GLsizei(MemoryLayout<Vertex>.stride), offset)
}
}
private struct Vertex {
var x: GLfloat
var y: GLfloat
var z: GLfloat
var r: GLfloat
var g: GLfloat
var b: GLfloat
var a: GLfloat
struct Attributes {
static let position = VertexAttribute(
name: "a_Position",
index: 0,
normalized: GLboolean(GL_FALSE),
offset: UnsafeRawPointer(bitPattern: 0),
size: 3,
type: GLenum(GL_FLOAT)
)
static let color = VertexAttribute(
name: "a_Color",
index: 1,
normalized: GLboolean(GL_FALSE),
offset: UnsafeRawPointer(bitPattern: 3 * MemoryLayout<GLfloat>.size),
size: 4,
type: GLenum(GL_FLOAT)
)
}
}
private func compileShader(shaderFilename: String, shaderType: GLenum) -> GLuint? {
guard let shaderPath = Bundle.main.path(forResource: shaderFilename, ofType: nil) else { return nil }
do {
let shaderHandle = glCreateShader(shaderType)
let shaderSource = try NSString(contentsOfFile: shaderPath, encoding: String.Encoding.utf8.rawValue)
var shaderCString = shaderSource.utf8String
var shaderSourceLength = GLint(shaderSource.length)
glShaderSource(shaderHandle, 1, &shaderCString, &shaderSourceLength)
glCompileShader(shaderHandle)
var compileStatus = GL_FALSE
glGetShaderiv(shaderHandle, GLenum(GL_COMPILE_STATUS), &compileStatus)
if compileStatus == GL_FALSE {
var infoLength = GLsizei()
let bufferLength = GLsizei(1024)
glGetShaderiv(shaderHandle, GLenum(GL_INFO_LOG_LENGTH), &infoLength)
let info = Array<GLchar>(repeating: GLchar(0), count: Int(bufferLength))
var actualLength = GLsizei()
glGetShaderInfoLog(shaderHandle, bufferLength, &actualLength, UnsafeMutablePointer(mutating: info))
print("error compiling shader: \(String(validatingUTF8: info) ?? "error getting info log")")
return nil
}
return shaderHandle
} catch {
return nil
}
}
private class ShaderProgram {
private(set) var programHandle = glCreateProgram()
private var modelViewMatrixUniform = GLint()
private var projectionMatrixUniform = GLint()
var modelViewMatrix = GLKMatrix4Identity
var projectionMatrix = GLKMatrix4Identity
init?(vertexShaderFilename: String, fragmentShaderFilename: String) {
guard let vertexShaderHandle = compileShader(shaderFilename: vertexShaderFilename, shaderType: GLenum(GL_VERTEX_SHADER)) else { return nil }
guard let fragmentShaderHandle = compileShader(shaderFilename: fragmentShaderFilename, shaderType: GLenum(GL_FRAGMENT_SHADER)) else { return nil }
glAttachShader(programHandle, vertexShaderHandle)
glAttachShader(programHandle, fragmentShaderHandle)
Vertex.Attributes.position.bindAttribLocation(programHandle)
Vertex.Attributes.color.bindAttribLocation(programHandle)
glLinkProgram(programHandle)
modelViewMatrixUniform = glGetUniformLocation(programHandle, "u_ModelViewMatrix")
projectionMatrixUniform = glGetUniformLocation(programHandle, "u_ProjectionMatrix")
var linkStatus = GL_FALSE
glGetProgramiv(programHandle, GLenum(GL_LINK_STATUS), &linkStatus)
if linkStatus == GL_FALSE {
var infoLength = GLsizei()
let bufferLength = GLsizei(1024)
glGetProgramiv(programHandle, GLenum(GL_INFO_LOG_LENGTH), &infoLength)
let info = Array<GLchar>(repeating: GLchar(), count: Int(bufferLength))
var actualLength = GLsizei()
glGetProgramInfoLog(programHandle, bufferLength, &actualLength, UnsafeMutablePointer(mutating: info))
print("error linking shader program: \(String(validatingUTF8: info) ?? "error reading info log")")
return nil
}
}
func prepareToDraw() {
glUseProgram(programHandle)
glUniformMatrix4fv(modelViewMatrixUniform, 1, GLboolean(GL_FALSE), modelViewMatrix.array)
glUniformMatrix4fv(projectionMatrixUniform, 1, GLboolean(GL_FALSE), projectionMatrix.array)
}
}
private struct Model {
let shaderProgram: ShaderProgram
let numIndices: GLsizei
private(set) var vao = GLuint()
private var vertexBuffer = GLuint()
private var indexBuffer = GLuint()
var position = GLKVector3()
var rotationX = Float()
var rotationY = Float()
var rotationZ = Float()
var scale = Float(1)
var modelMatrix: GLKMatrix4 {
var modelMatrix = GLKMatrix4Identity
modelMatrix = GLKMatrix4Translate(modelMatrix, position.x, position.y, position.z)
modelMatrix = GLKMatrix4RotateX(modelMatrix, rotationX)
modelMatrix = GLKMatrix4RotateY(modelMatrix, rotationY)
modelMatrix = GLKMatrix4RotateZ(modelMatrix, rotationZ)
modelMatrix = GLKMatrix4Scale(modelMatrix, scale, scale, scale)
return modelMatrix
}
init(shaderProgram: ShaderProgram, vertices: [Vertex], indices: [GLubyte]) {
self.shaderProgram = shaderProgram
numIndices = GLsizei(indices.count)
glGenVertexArraysOES(1, &vao)
glBindVertexArrayOES(vao)
glGenBuffers(1, &vertexBuffer)
glBindBuffer(GLenum(GL_ARRAY_BUFFER), vertexBuffer)
glBufferData(GLenum(GL_ARRAY_BUFFER), GLsizeiptr(vertices.count * MemoryLayout<Vertex>.stride), vertices, GLenum(GL_STATIC_DRAW))
glGenBuffers(1, &indexBuffer)
glBindBuffer(GLenum(GL_ELEMENT_ARRAY_BUFFER), indexBuffer)
glBufferData(GLenum(GL_ELEMENT_ARRAY_BUFFER), GLsizeiptr(indices.count * MemoryLayout<GLubyte>.stride), indices, GLenum(GL_STATIC_DRAW))
Vertex.Attributes.position.enableVertexAttribArray()
Vertex.Attributes.position.vertexAttribPointer()
Vertex.Attributes.color.enableVertexAttribArray()
Vertex.Attributes.color.vertexAttribPointer()
glBindVertexArrayOES(0)
glBindBuffer(GLenum(GL_ARRAY_BUFFER), 0)
glBindBuffer(GLenum(GL_ELEMENT_ARRAY_BUFFER), 0)
}
func render() {
shaderProgram.modelViewMatrix = modelMatrix
shaderProgram.prepareToDraw()
glBindVertexArrayOES(vao)
glDrawElements(GLenum(GL_TRIANGLES), numIndices, GLenum(GL_UNSIGNED_BYTE), nil)
glBindVertexArrayOES(0)
}
}
class UnprojectViewController: GLKViewController {
@IBOutlet weak var glkView: GLKView!
private var shader: ShaderProgram?
private var renderable: Model?
override func viewDidLoad() {
super.viewDidLoad()
guard let context = EAGLContext(api: .openGLES2) else { return }
glkView.context = context
EAGLContext.setCurrent(glkView.context)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
guard let shader = ShaderProgram(vertexShaderFilename: "TransformableModelVertexShader.glsl", fragmentShaderFilename: "TransformableModelFragmentShader.glsl") else { return }
self.shader = shader
shader.projectionMatrix = GLKMatrix4MakePerspective(GLKMathDegreesToRadians(85), Float(view.bounds.width / view.bounds.height), 1, 101)
renderable = Model(shaderProgram: shader, vertices: [
Vertex(x: -0.5, y: 0.5, z: 0, r: 1, g: 0, b: 0, a: 1),
Vertex(x: -0.5, y: -0.5, z: 0, r: 0, g: 0, b: 1, a: 1),
Vertex(x: 0.5, y: -0.5, z: 0, r: 0, g: 1, b: 0, a: 1),
Vertex(x: 0.5, y: 0.5, z: 0, r: 1, g: 1, b: 0, a: 1)
], indices: [0, 1, 2, 0, 2, 3])
renderable?.position.z = -5
}
override func glkView(_ view: GLKView, drawIn rect: CGRect) {
glClear(GLbitfield(GL_COLOR_BUFFER_BIT))
renderable?.render()
}
@IBAction func tap(_ sender: UITapGestureRecognizer) {
let touchInWindow = GLKVector3(v: (Float(sender.location(in: glkView).x), Float(sender.location(in: glkView).y), 0.05))
var viewport = [Int32]([0, 0, 0, 0])
glGetIntegerv(GLenum(GL_VIEWPORT), &viewport)
guard let renderable = renderable, let shader = shader else { return }
print(GLKMathUnproject(touchInWindow, renderable.modelMatrix, shader.projectionMatrix, &viewport, nil).v)
}
}
extension GLKMatrix2 {
var array: [Float] {
return (0..<4).map { i in
self[i]
}
}
}
extension GLKMatrix3 {
var array: [Float] {
return (0..<9).map { i in
self[i]
}
}
}
extension GLKMatrix4 {
var array: [Float] {
return (0..<16).map { i in
self[i]
}
}
}