1 /++ 2 A module for programming open graphics shaders. 3 4 Macros: 5 LREF = <a href="#$1">$1</a> 6 HREF = <a href="$1">$2</a> 7 8 Authors: $(HREF https://github.com/TodNaz,TodNaz) 9 Copyright: Copyright (c) 2020 - 2021, TodNaz. 10 License: $(HREF https://github.com/TodNaz/Tida/blob/master/LICENSE,MIT) 11 +/ 12 module tida.shader; 13 14 import tida.gl; 15 16 enum 17 { 18 Vertex = 0, /// Vertex shader type 19 Fragment, /// Fragment shader type 20 Program /// Program shader type 21 } 22 23 /++ 24 Whether the shader type is a program. 25 +/ 26 template isShaderProgram(int type) 27 { 28 enum isShaderProgram = (type == Program); 29 } 30 31 /++ 32 Whether the shader type is a shader unit. 33 +/ 34 template isShaderUnite(int type) 35 { 36 enum isShaderUnite = !isShaderProgram!type; 37 } 38 39 /++ 40 Whether the shader type is valid to use. 41 +/ 42 template isValidTypeShader(int type) 43 { 44 enum isValidTypeShader = (type < 3); 45 } 46 47 /++ 48 Converts types into a convenient representation for the open graphics library. 49 +/ 50 template glTypeShader(int type) 51 { 52 static assert(isValidTypeShader!type, "Invalid type shader!"); 53 54 static if (type == Vertex) 55 enum glTypeShader = GL_VERTEX_SHADER; 56 else 57 static if (type == Fragment) 58 enum glTypeShader = GL_FRAGMENT_SHADER; 59 } 60 61 private string formatError(string error) @safe pure 62 { 63 import std.array : replace; 64 65 error = error.replace("error","\x1b[1;91merror\x1b[0m"); 66 67 return error; 68 } 69 70 /++ 71 A description object for a shader program or program unit. 72 +/ 73 class Shader(int type) 74 if (isValidTypeShader!type) 75 { 76 import std..string : toStringz; 77 import std.conv : to; 78 79 enum Type = type; 80 81 private: 82 uint glid; 83 84 static if (isShaderProgram!type) 85 { 86 Shader!Vertex vertex; 87 Shader!Fragment fragment; 88 } 89 90 public @trusted: 91 this() 92 { 93 static if (isShaderUnite!type) 94 glid = glCreateShader(glTypeShader!type); 95 else 96 glid = glCreateProgram(); 97 } 98 99 /// Shader identificator 100 @property uint id() 101 { 102 return glid; 103 } 104 105 /++ 106 Binds the shader source directly to the shader itself into memory by compiling it. 107 108 Params: 109 source = Shader source code. 110 111 Throws: `Exception` If the shader contains errors. 112 All errors are displayed in a preformatted message. 113 +/ 114 static if (isShaderUnite!type) 115 Shader!type bindSource(string source) 116 { 117 const int len = cast(const(int)) source.length; 118 glShaderSource(glid, 1, [source.ptr].ptr, &len); 119 glCompileShader(glid); 120 121 char[] error; 122 int result; 123 int lenLog; 124 125 glGetShaderiv(glid, GL_COMPILE_STATUS, &result); 126 if (!result) 127 { 128 glGetShaderiv(glid, GL_INFO_LOG_LENGTH, &lenLog); 129 error = new char[](lenLog); 130 glGetShaderInfoLog(glid, lenLog, null, error.ptr); 131 132 throw new Exception("Shader compile error:\n" ~ error.to!string.formatError); 133 } 134 135 return this; 136 } 137 138 /++ 139 Binds the source code to the shader directly from the specified file. 140 141 Params: 142 path = The path to the shader source code. 143 144 Throws: `Exception` if the file is not found. 145 +/ 146 static if (isShaderUnite!type) 147 Shader!type fromFile(string path) 148 { 149 import std.file : readText; 150 151 return bindSource(readText(path)); 152 } 153 154 /++ 155 Binds a shader to the program. 156 157 Params: 158 shader = Fragment/Vertex shader. 159 +/ 160 static if (isShaderProgram!type) 161 Shader!type attach(T)(T shader) 162 if (isShaderUnite!(T.Type) && (shader.Type == Vertex || shader.Type == Fragment)) 163 { 164 static if (T.Type == Vertex) 165 vertex = shader; 166 else 167 static if (T.Type == Fragment) 168 fragment = shader; 169 170 glAttachShader(glid, shader.id); 171 return this; 172 } 173 174 /++ 175 Links two compiled shaders into a program. 176 177 Throws: `Exception` If linking two shaders did not succeed. The error 178 will be written directly in the exception message. 179 +/ 180 static if (isShaderProgram!type) 181 Shader!type link() 182 { 183 import std.conv : to; 184 185 glLinkProgram(glid); 186 187 int status; 188 glGetProgramiv(glid, GL_LINK_STATUS, &status); 189 if (!status) 190 { 191 int lenLog; 192 char[] error; 193 glGetProgramiv(glid, GL_INFO_LOG_LENGTH, &lenLog); 194 error = new char[](lenLog); 195 196 glGetProgramInfoLog(glid, lenLog, null, error.ptr); 197 198 throw new Exception(error.to!string.formatError); 199 } 200 201 return this; 202 } 203 204 /++ 205 Uses a shader program. 206 +/ 207 static if (isShaderProgram!type) 208 void using() 209 { 210 glUseProgram(glid); 211 } 212 213 /++ 214 Associates a generic vertex attribute index with a named attribute variable 215 216 Params: 217 index = Specifies the index of the generic vertex attribute to be bound. 218 name = Specifies a null terminated string containing the name of the vertex shader 219 attribute variable to which index is to be bound. 220 221 See_Also: $(HTTP https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glBindAttribLocation.xhtml, glBindAttribLocation) 222 +/ 223 static if (isShaderProgram!Type) 224 void bindAttribLocation(uint index, string name) 225 { 226 glBindAttribLocation(glid, index, name.ptr); 227 } 228 229 /++ 230 Enable or disable a generic vertex attribute array 231 232 Params: 233 name = Vertex attribute name. 234 235 See_Also: $(HTTP https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glEnableVertexAttribArray.xhtml, glEnableVertexAttribArray) 236 +/ 237 static if (isShaderProgram!Type) 238 void enableVertex(string name) 239 { 240 glEnableVertexAttribArray(getAttribLocation(name)); 241 } 242 243 /++ 244 Enable or disable a generic vertex attribute array 245 246 Params: 247 id = Vertex identificator. 248 249 See_Also: $(HTTP https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glEnableVertexAttribArray.xhtml, glEnableVertexAttribArray) 250 +/ 251 static if (isShaderProgram!Type) 252 void enableVertex(uint id) 253 { 254 glEnableVertexAttribArray(id); 255 } 256 257 /++ 258 Enable or disable a generic vertex attribute array 259 260 Params: 261 name = Vertex attribute name. 262 263 See_Also: $(HTTP https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glEnableVertexAttribArray.xhtml, glEnableVertexAttribArray) 264 +/ 265 static if (isShaderProgram!Type) 266 void disableVertex(string name) 267 { 268 glDisableVertexAttribArray(getAttribLocation(name)); 269 } 270 271 /++ 272 Enable or disable a generic vertex attribute array 273 274 Params: 275 id = Vertex identificator. 276 277 See_Also: $(HTTP https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glEnableVertexAttribArray.xhtml, glEnableVertexAttribArray) 278 +/ 279 static if (isShaderProgram!Type) 280 void disableVertex(uint id) 281 { 282 glDisableVertexAttribArray(id); 283 } 284 285 /++ 286 Returns the location of an attribute variable 287 288 Params: 289 name = Points to a null terminated string containing the name of the 290 attribute variable whose location is to be queried. 291 292 See_Also: $(HTTP https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glGetAttribLocation.xhtml, glGetAttribLocation) 293 +/ 294 static if(isShaderProgram!Type) 295 int getAttribLocation(string name) 296 { 297 return glGetAttribLocation(glid, name.ptr); 298 } 299 300 import tida.color, tida.vector; 301 302 /++ 303 Return uniform identificator (location) 304 305 Params: 306 name = Uniform name. 307 +/ 308 static if (isShaderProgram!Type) 309 uint getUniformLocation(string name) 310 { 311 return glGetUniformLocation(glid, name.ptr); 312 } 313 314 /++ 315 Sets the color of a 4D vector type uniform. 316 317 Params: 318 name = The name of the uniform variable. 319 color = Color structure. 320 +/ 321 static if (isShaderProgram!Type) 322 void setUniform(string name, Color!ubyte color) 323 { 324 auto uid = glGetUniformLocation(glid, name.ptr); 325 glUniform4f(uid, color.rf, color.gf, color.bf, color.af); 326 } 327 328 /++ 329 Sends a float value to the uniform. 330 331 Params: 332 name = The name of the uniform variable. 333 value = Variable value. 334 +/ 335 static if (isShaderProgram!Type) 336 void setUniform(string name, float value) 337 { 338 auto uid = glGetUniformLocation(glid, name.ptr); 339 glUniform1f(uid, value); 340 } 341 342 /++ 343 Passes a 4-by-4 variable matrix to the uniform. 344 345 Params: 346 name = The name of the uniform variable. 347 value = Matrix. 348 +/ 349 static if (isShaderProgram!Type) 350 void setUniform(string name, float[4][4] value) 351 { 352 auto uid = glGetUniformLocation(glid, name.ptr); 353 glUniformMatrix4fv(uid, 1, false, value[0].ptr); 354 } 355 356 /++ 357 Passes a 3-by-3 variable matrix to the uniform. 358 359 Params: 360 name= The name of the uniform variable. 361 value = Matrix. 362 +/ 363 static if (isShaderProgram!Type) 364 void setUniform(string name, float[3][3] value) 365 { 366 auto uid = glGetUniformLocation(glid, name.ptr); 367 glUniformMatrix3fv(uid, 1, false, cast(const(float)*) value); 368 } 369 370 /// 371 static if (isShaderProgram!Type) 372 void setUniformMat(int Type)(string name, float* matPTR) 373 { 374 auto uid = glGetUniformLocation(glid, name.ptr); 375 376 static if (Type == 4) 377 { 378 glUniformMatrix4fP(uid, matPTR); 379 }else 380 static if (Type == 3) 381 { 382 glUniformMatrix3fP(uid, matPTR); 383 } 384 } 385 386 /// Returns the number of variable uniforms in the program. 387 static if (isShaderProgram!Type) 388 uint lengthUniforms() 389 { 390 int len; 391 glGetProgramiv(glid, GL_ACTIVE_UNIFORMS, &len); 392 393 return len; 394 } 395 396 /// Destroys the structure of a shader or program. 397 void destroy() 398 { 399 if(glid != 0) 400 { 401 static if (isShaderProgram!Type) 402 { 403 glDeleteProgram(glid); 404 }else 405 static if (isShaderUnite!Type) 406 { 407 glDeleteShader(glid); 408 } 409 410 glid = 0; 411 } 412 } 413 414 ~this() 415 { 416 destroy(); 417 } 418 }