/Sources/app/Shader.swift

https://github.com/sakrist/Swift_OpenGL_Example · Swift · 248 lines · 169 code · 52 blank · 27 comment · 37 complexity · 592b3d9bb1345520b8ad455bb218aeb9 MD5 · raw file

  1. //
  2. // Shader.swift
  3. // OpenGL_example
  4. //
  5. // Created by Volodymyr Boichentsov on 30/12/2015.
  6. //
  7. //
  8. #if os(Linux)
  9. import Glibc
  10. import OpenGL
  11. #elseif os(OSX)
  12. import Darwin.C
  13. import GLKit
  14. #elseif os(iOS)
  15. import OpenGLES
  16. import OpenGLES.ES3
  17. #elseif os(Android)
  18. import Glibc
  19. import GL.ES3
  20. #endif
  21. public func isGLOn() -> Bool {
  22. let v = glGetString(GLenum(GL_VERSION))
  23. if (v != nil) {
  24. return true
  25. }
  26. return false
  27. }
  28. public let UNIFORM_MODELVIEWPROJECTION_MATRIX = 0
  29. public let UNIFORM_NORMAL_MATRIX = 1
  30. public class Shader {
  31. public static var positionAttribute:GLuint = 0
  32. public static var normalAttribute:GLuint = 1
  33. public var program:UInt32 = 0
  34. public var uniforms = [GLint](repeating: 0, count: 2)
  35. public init?(vertexShader: String, fragmentShader: String) {
  36. var vertexShader_ = vertexShader;
  37. var fragmentShader_ = fragmentShader;
  38. let GL_version_cstring = glGetString(GLenum(GL_VERSION)) as UnsafePointer<UInt8>
  39. let GL_version_string = String(cString:GL_version_cstring)
  40. debugPrint(GL_version_string)
  41. let glsl_version_cstring = glGetString(GLenum(GL_SHADING_LANGUAGE_VERSION)) as UnsafePointer<UInt8>
  42. var glsl_version_string = String(cString:glsl_version_cstring)
  43. // test if it's ES
  44. let isES = (glsl_version_string.rangesOfString("ES").count != 0)
  45. if let lastString = glsl_version_string.split(separator: " ").last {
  46. glsl_version_string = String.init(lastString)
  47. }
  48. glsl_version_string.remove(at:glsl_version_string.index(glsl_version_string.startIndex, offsetBy: 1)) // delete point 1.30 -> 130
  49. // get version into Int value
  50. let glslVersion = Int(glsl_version_string) ?? 0
  51. // form string for replacement
  52. glsl_version_string = "version " + glsl_version_string + ((isES && glslVersion > 100) ? " es" : "" )
  53. // replace version
  54. var range = vertexShader_.rangesOfString("version 000")
  55. for r in range {
  56. vertexShader_.replaceSubrange(r, with: glsl_version_string)
  57. }
  58. range = fragmentShader_.rangesOfString("version 000")
  59. for r in range {
  60. fragmentShader_.replaceSubrange(r, with: glsl_version_string)
  61. }
  62. // create macro for version
  63. let glslVersionMacro = "version \(glslVersion)"
  64. // create macorses string
  65. let macroses = "#define " + glslVersionMacro + "\n"
  66. // set macroses
  67. range = vertexShader_.rangesOfString("// macroses")
  68. for r in range {
  69. vertexShader_.replaceSubrange(r, with: macroses)
  70. }
  71. range = fragmentShader_.rangesOfString("// macroses")
  72. for r in range {
  73. fragmentShader_.replaceSubrange(r, with: macroses)
  74. }
  75. var vertShader: GLuint = 0
  76. var fragShader: GLuint = 0
  77. // Create shader program.
  78. program = glCreateProgram()
  79. // Create and compile vertex shader.
  80. if self.compileShader(shader: &vertShader, type: GLenum(GL_VERTEX_SHADER), shaderString: vertexShader_) == false {
  81. print("Failed to compile vertex shader")
  82. print(vertexShader_)
  83. return nil
  84. }
  85. // Create and compile fragment shader.
  86. if !self.compileShader(shader: &fragShader, type: GLenum(GL_FRAGMENT_SHADER), shaderString: fragmentShader_) {
  87. print("Failed to compile fragment shader")
  88. return nil
  89. }
  90. // Attach vertex shader to program.
  91. glAttachShader(self.program, vertShader)
  92. // Attach fragment shader to program.
  93. glAttachShader(self.program, fragShader)
  94. // Bind attribute locations.
  95. // This needs to be done prior to linking.
  96. glBindAttribLocation(self.program, Shader.positionAttribute, "position")
  97. glBindAttribLocation(self.program, Shader.normalAttribute, "normal")
  98. #if os(iOS) || os(Android)
  99. glGetFragDataLocation(self.program, "glFragData0");
  100. #else
  101. glBindFragDataLocation(self.program, 0, "glFragData0");
  102. #endif
  103. // Link program.
  104. if !self.linkProgram(self.program) {
  105. print("Failed to link program: \(self.program)")
  106. if vertShader != 0 {
  107. glDeleteShader(vertShader)
  108. vertShader = 0
  109. }
  110. if fragShader != 0 {
  111. glDeleteShader(fragShader)
  112. fragShader = 0
  113. }
  114. if program != 0 {
  115. glDeleteProgram(self.program)
  116. self.program = 0
  117. }
  118. return nil
  119. }
  120. // Get uniform locations.
  121. self.uniforms[UNIFORM_MODELVIEWPROJECTION_MATRIX] = glGetUniformLocation(program, "modelViewProjectionMatrix")
  122. self.uniforms[UNIFORM_NORMAL_MATRIX] = glGetUniformLocation(program, "normalMatrix")
  123. // Release vertex and fragment shaders.
  124. if vertShader != 0 {
  125. glDetachShader(program, vertShader)
  126. glDeleteShader(vertShader)
  127. }
  128. if fragShader != 0 {
  129. glDetachShader(program, fragShader)
  130. glDeleteShader(fragShader)
  131. }
  132. // self.validateProgram(program)
  133. }
  134. func compileShader(shader: inout GLuint, type: GLenum, shaderString: String) -> Bool {
  135. var status: GLint = 0
  136. let source = UnsafeMutablePointer<Int8>.allocate(capacity: shaderString.count)
  137. let size = shaderString.count
  138. var idx = 0
  139. for u in shaderString.utf8 {
  140. if idx == size - 1 { break }
  141. source[idx] = Int8(u)
  142. idx += 1
  143. }
  144. source[idx] = 0 // NUL-terminate the C string in the array.
  145. var castSource: UnsafePointer<GLchar>? = UnsafePointer<GLchar>(source)
  146. shader = glCreateShader(type)
  147. glShaderSource(shader, 1, &castSource, nil)
  148. glCompileShader(shader)
  149. source.deallocate()
  150. // #if defined(DEBUG)
  151. var logLength: GLint = 0
  152. glGetShaderiv(shader, GLenum(GL_INFO_LOG_LENGTH), &logLength)
  153. if logLength > 0 {
  154. var log = [GLchar](repeating: 0, count: 512)
  155. glGetShaderInfoLog(shader, 512, &logLength, &log)
  156. let logString = String(cString:log)
  157. print(logString)
  158. }
  159. // #endif
  160. glGetShaderiv(shader, GLenum(GL_COMPILE_STATUS), &status)
  161. if status == 0 {
  162. glDeleteShader(shader)
  163. return false
  164. }
  165. return true
  166. }
  167. func linkProgram(_ prog: GLuint) -> Bool {
  168. var status: GLint = 0
  169. glLinkProgram(prog)
  170. glGetProgramiv(prog, GLenum(GL_LINK_STATUS), &status)
  171. if status == 0 {
  172. return false
  173. }
  174. return true
  175. }
  176. func validateProgram(prog: GLuint) -> Bool {
  177. var logLength: GLsizei = 0
  178. var status: GLint = 0
  179. glValidateProgram(prog)
  180. glGetProgramiv(prog, GLenum(GL_INFO_LOG_LENGTH), &logLength)
  181. if logLength > 0 {
  182. var log: [GLchar] = [GLchar](repeating: 0, count: Int(logLength))
  183. glGetProgramInfoLog(prog, logLength, &logLength, &log)
  184. print("Program validate log: \n\(String(cString:log))")
  185. }
  186. glGetProgramiv(prog, GLenum(GL_VALIDATE_STATUS), &status)
  187. var returnVal = true
  188. if status == 0 {
  189. returnVal = false
  190. }
  191. return returnVal
  192. }
  193. func use() {
  194. glUseProgram(self.program)
  195. }
  196. }