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