private void CompileShader(Retyped.dom.WebGLShader shader, string source) { // Populate the shader and compile it GL.shaderSource(shader, source); GL.compileShader(shader); // Check for shader compile errors bool status = (bool)GL.getShaderParameter(shader, GL.COMPILE_STATUS); if (!status) { throw new CKGLException("Shader compile error: " + GL.getShaderInfoLog(shader)); } }
private void Compile(string source) { int vertex = source.IndexOf("#vertex", StringComparison.Ordinal); int geometry = source.IndexOf("#geometry", StringComparison.Ordinal); int fragment = source.IndexOf("#fragment", StringComparison.Ordinal); if (vertex == -1) { throw new CKGLException("Shader source must contain a vertex shader definition."); } bool hasGeometryDefinition = geometry != -1; bool hasFragmentDefinition = fragment != -1; if (geometry != -1) { throw new CKGLException("WebGL 2.0 does not support Geometry Shaders."); } string vertSource = hasGeometryDefinition ? source.Substring(vertex, geometry - vertex) : hasFragmentDefinition ? source.Substring(vertex, fragment - vertex) : source.Substring(vertex); //string geomSource = hasGeometryDefinition // ? hasFragmentDefinition // ? source.Substring(geometry, fragment - geometry) // : source.Substring(geometry) // : ""; string fragSource = hasFragmentDefinition ? source.Substring(fragment) : ""; // Debug //Output.WriteLine($"\nVertex Shader:\n{vertSource}"); //Output.WriteLine($"\nGeometry Shader:\n{geomSource}"); //Output.WriteLine($"\nFragment Shader:\n{fragSource}"); // Create the shaders and compile them Retyped.dom.WebGLShader vertID = GL.createShader(GL.VERTEX_SHADER); //Output.WriteLine(vertSource.Replace("#vertex", ShaderIncludes.Vertex)); CompileShader(vertID, ConvertVertex(vertSource.Replace("#vertex", ShaderIncludes.Vertex))); //Retyped.dom.WebGLShader geomID = null; //if (hasGeometryDefinition) //{ // geomID = GL.createShader(GL.GEOMETRY_SHADER); // //Output.WriteLine(geomSource.Replace("#geometry", ShaderIncludes.Geometry)); // CompileShader(geomID, ConvertGeometry(geomSource.Replace("#geometry", ShaderIncludes.Geometry))); //} Retyped.dom.WebGLShader fragID = null; if (hasFragmentDefinition) { fragID = GL.createShader(GL.FRAGMENT_SHADER); //Output.WriteLine(fragSource.Replace("#fragment", ShaderIncludes.Fragment)); CompileShader(fragID, ConvertFragment(fragSource.Replace("#fragment", ShaderIncludes.Fragment))); } // Create the program and attach the shaders to it shader = GL.createProgram(); GL.attachShader(shader, vertID); //if (hasGeometryDefinition) // GL.attachShader(shader, geomID); if (hasFragmentDefinition) { GL.attachShader(shader, fragID); } // WebGL 1.0 only - Automatically bind attributes based on layout qualifiers try { string[] lines = vertSource.Split('\n'); for (int i = 0; i < lines.Length; i++) { if (lines[i].Contains("layout(location") && (lines[i].Contains("in ") || lines[i].Contains("attribute "))) { string[] line = lines[i].Replace("layout(location =", "").Replace("layout(location=", "").Replace(";", " ").Trim().Split(' '); int attribID = int.Parse(line[0].Substring(0, line[0].Length - 1).Trim()); string name = line[3].Trim(); //Output.WriteLine($"Shader - Bind Attribute | id: {attribID}, {name} ({line[2]})"); // Debug GL.bindAttribLocation(shader, attribID, name); } } } catch (Exception e) { Output.WriteLine($"WebGL 1.0 - Automatic Shader Attribute binding failed: {e.Message}"); } // Link the program and check for errors GL.linkProgram(shader); bool linkStatus = (bool)GL.getProgramParameter(shader, GL.LINK_STATUS); if (!linkStatus) { throw new CKGLException("Program link error: " + GL.getProgramInfoLog(shader)); } // Validate the program and check for errors GL.validateProgram(shader); bool validateStatus = (bool)GL.getProgramParameter(shader, GL.VALIDATE_STATUS); if (!validateStatus) { throw new CKGLException("Program validate error: " + GL.getProgramInfoLog(shader)); } // Once linked, we can detach and delete the shaders GL.detachShader(shader, vertID); GL.deleteShader(vertID); //if (hasGeometryDefinition) //{ // GL.detachShader(shader, geomID); // GL.deleteShader(geomID); //} if (hasFragmentDefinition) { GL.detachShader(shader, fragID); GL.deleteShader(fragID); } // Get all the uniforms the shader has and store their information int numUniforms = (int)GL.getProgramParameter(shader, GL.ACTIVE_UNIFORMS); for (int i = 0; i < numUniforms; ++i) { var uniformInfo = GL.getActiveUniform(shader, i); string name = uniformInfo.name; uint type = uniformInfo.type; int count = uniformInfo.size; if (count > 0 && name != null) { if (count > 1) { name = name.Substring(0, name.LastIndexOf('[')); string arrName; for (int n = 0; n < count; ++n) { arrName = $"{name}[{n}]"; WebGLUniformLocation loc = GL.getUniformLocation(shader, arrName); //Output.WriteLine($"index:{i} name:{arrName} type:{type} loc:{loc} count:{count}"); var uniform = new Uniform(i, arrName, type, loc); uniforms.Add(arrName, uniform); } } else { WebGLUniformLocation loc = GL.getUniformLocation(shader, name); //Output.WriteLine($"index:{i} name:{name} type:{type} loc:{loc} count:{count}"); var uniform = new Uniform(i, name, type, loc); uniforms.Add(name, uniform); } } } }