/// <summary>
        /// Creates a <see cref="ShaderProgram"/> by compiling the given strings as code.
        /// </summary>
        /// <typeparam name="T">The type of vertex to configure the input of the <see cref="ShaderProgram"/> for.</typeparam>
        /// <param name="graphicsDevice">The <see cref="GraphicsDevice"/> the <see cref="ShaderProgram"/> will use.</param>
        /// <param name="vsCode">The vertex shader code for the <see cref="ShaderProgram"/>.</param>
        /// <param name="fsCode">The fragment shader code for the <see cref="ShaderProgram"/>.</param>
        /// <param name="attribNames">The names of the attributes ordered by attribute index.</param>
        public static ShaderProgram FromCode <T>(GraphicsDevice graphicsDevice, string vsCode, string fsCode, params string[] attribNames) where T : unmanaged, IVertex
        {
            ShaderProgramBuilder builder = new ShaderProgramBuilder()
            {
                VertexShaderCode   = vsCode,
                FragmentShaderCode = fsCode,
            };

            builder.SpecifyVertexAttribs <T>(attribNames);
            return(builder.Create(graphicsDevice));
        }
Exemple #2
0
        /// <summary>
        /// Creates a <see cref="SimpleShaderProgram"/> using the current values on this
        /// <see cref="SimpleShaderProgramBuilder"/>.
        /// </summary>
        /// <param name="graphicsDevice">The <see cref="GraphicsDevice"/> the <see cref="SimpleShaderProgram"/> will use.</param>
        public SimpleShaderProgram Create(GraphicsDevice graphicsDevice)
        {
            const string DifferentVertexAttribIndicesError = "All specified vertex attribute indices must be different.";

            VertexShaderLog   = null;
            FragmentShaderLog = null;
            ProgramLog        = null;

            if (graphicsDevice == null)
            {
                throw new ArgumentNullException(nameof(graphicsDevice));
            }

            if (PositionAttributeIndex < 0)
            {
                throw new InvalidOperationException("A vertex attribute index for Position must always be specified.");
            }

            if (DiscardTransparentFragments && !float.IsFinite(TransparentFragmentThreshold))
            {
                throw new InvalidOperationException(nameof(TransparentFragmentThreshold) + " must be a finite value.");
            }

            if (LightingEnabled)
            {
                if (NormalAttributeIndex < 0)
                {
                    throw new InvalidOperationException("Using lighting requires a vertex Normal attribute.");
                }
                if (NormalAttributeIndex == PositionAttributeIndex)
                {
                    throw new InvalidOperationException(DifferentVertexAttribIndicesError);
                }
            }

            if (VertexColorsEnabled)
            {
                if (ColorAttributeIndex < 0)
                {
                    throw new InvalidOperationException("Using vertex colors requires a vertex Color attribute.");
                }
                if (ColorAttributeIndex == PositionAttributeIndex || ColorAttributeIndex == NormalAttributeIndex)
                {
                    throw new InvalidOperationException(DifferentVertexAttribIndicesError);
                }
            }

            if (TextureEnabled)
            {
                if (TexCoordsAttributeIndex < 0)
                {
                    throw new InvalidOperationException("Using textures requires a vertex TexCoords attribute.");
                }
                if (TexCoordsAttributeIndex == PositionAttributeIndex || TexCoordsAttributeIndex == NormalAttributeIndex ||
                    TexCoordsAttributeIndex == ColorAttributeIndex)
                {
                    throw new InvalidOperationException(DifferentVertexAttribIndicesError);
                }
            }

            StringBuilder        builder        = GetStringBuilder();
            ShaderProgramBuilder programBuilder = new ShaderProgramBuilder();
            uint programHandle = 0;

            try
            {
                bool useLighting = LightingEnabled;

                builder.Append("#version ");
                builder.Append(GLSLVersionString ?? graphicsDevice.GLMajorVersion.ToString() + graphicsDevice.GLMinorVersion.ToString() + "0 core");
                builder.Append("\n\n");

                if (!ExcludeWorldMatrix)
                {
                    builder.Append("uniform mat4 World;\n");
                }
                builder.Append("uniform mat4 View, Projection;\n");

                builder.Append("\nin vec3 vPosition;\n");
                if (useLighting)
                {
                    builder.Append("in vec3 vNormal;\n");
                }
                if (VertexColorsEnabled)
                {
                    builder.Append("in vec4 vColor;\n");
                }
                if (TextureEnabled)
                {
                    builder.Append("in vec2 vTexCoords;\n");
                }
                builder.Append('\n');
                if (useLighting)
                {
                    builder.Append("out vec3 fPosition;\nout vec3 fNormal;\n");
                }
                if (VertexColorsEnabled)
                {
                    builder.Append("out vec4 fColor;\n");
                }
                if (TextureEnabled)
                {
                    builder.Append("out vec2 fTexCoords;\n");
                }

                builder.Append("\nvoid main() {\n");
                if (ExcludeWorldMatrix)
                {
                    builder.Append("vec4 worldPos = vec4(vPosition, 1.0);\n");
                }
                else
                {
                    builder.Append("vec4 worldPos = World * vec4(vPosition, 1.0);\n");
                }
                builder.Append("gl_Position = Projection * View * worldPos;\n\n");
                if (useLighting)
                {
                    builder.Append("fPosition = worldPos.xyz;\n");
                    if (ExcludeWorldMatrix)
                    {
                        builder.Append("fNormal = vNormal;\n");
                    }
                    else
                    {
                        builder.Append("fNormal = (World * vec4(vNormal, 0.0)).xyz;\n");
                    }
                }
                if (VertexColorsEnabled)
                {
                    builder.Append("fColor = vColor;\n");
                }
                if (TextureEnabled)
                {
                    builder.Append("fTexCoords = vTexCoords;\n");
                }

                builder.Append('}');

                programBuilder.VertexShaderCode = builder.ToString();
                builder.Clear();



                builder.Append("#version ");
                builder.Append(GLSLVersionString ?? graphicsDevice.GLMajorVersion.ToString() + graphicsDevice.GLMinorVersion.ToString() + "0 core");
                builder.Append("\n\n");

                builder.Append("uniform vec4 Color;\n\n");

                if (TextureEnabled)
                {
                    builder.Append("uniform sampler2D samp;\n\n");
                }

                if (useLighting)
                {
                    builder.Append("uniform vec3 cameraPos;\n");
                    builder.Append("uniform vec3 ambientLightColor;\n");
                    builder.Append("uniform float reflectivity;\n");
                    builder.Append("uniform float specularPower;\n");
                    for (int i = 0; i < DirectionalLights; i++)
                    {
                        string itostring = i.ToString();
                        builder.Append("uniform vec3 dLightDir");
                        builder.Append(itostring);
                        builder.Append(";\nuniform vec3 dLightDiffColor");
                        builder.Append(itostring);
                        builder.Append(";\nuniform vec3 dLightSpecColor");
                        builder.Append(itostring);
                        builder.Append(";\n");
                    }

                    for (int i = 0; i < PositionalLights; i++)
                    {
                        string itostring = i.ToString();
                        builder.Append("uniform vec3 pLightPos");
                        builder.Append(itostring);
                        builder.Append(";\nuniform vec3 pLightDiffColor");
                        builder.Append(itostring);
                        builder.Append(";\nuniform vec3 pLightSpecColor");
                        builder.Append(itostring);
                        builder.Append(";\nuniform vec3 pAttConfig");
                        builder.Append(itostring);
                        builder.Append(";\n");
                    }
                }

                if (useLighting)
                {
                    builder.Append("\nin vec3 fPosition;\nin vec3 fNormal;\n");
                }
                if (VertexColorsEnabled)
                {
                    builder.Append("in vec4 fColor;\n");
                }
                if (TextureEnabled)
                {
                    builder.Append("in vec2 fTexCoords;\n");
                }
                builder.Append("\nout vec4 FragColor;\n");

                if (useLighting)
                {
                    builder.Append("\nvec3 calcDirLight(in vec3 norm, in vec3 toCamVec, in vec3 lDir, in vec3 diffCol, in vec3 specCol) {\n");
                    builder.Append("float brightness = max(0.0, dot(norm, -lDir));\n");
                    builder.Append("vec3 reflectedDir = reflect(lDir, norm);\n");
                    builder.Append("float specFactor = max(0.0, dot(reflectedDir, toCamVec));\n");
                    builder.Append("float dampedFactor = pow(specFactor, specularPower);\n");
                    builder.Append("return brightness * diffCol + (dampedFactor * reflectivity) * specCol;\n}\n");

                    if (PositionalLights > 0)
                    {
                        builder.Append("\nvec3 calcPosLight(in vec3 norm, in vec3 toCamVec, in vec3 lPos, in vec3 diffCol, in vec3 specCol, in vec3 attConfig) {\n");
                        builder.Append("float d = distance(fPosition, lPos);\n");
                        builder.Append("return calcDirLight(norm, toCamVec, normalize(fPosition - lPos), diffCol, specCol) * clamp(1.0 / (attConfig.x + attConfig.y*d + attConfig.z*d*d), 0.0, 1.0);\n");
                        builder.Append("}\n");
                    }
                }

                builder.Append("\nvoid main() {\n");
                builder.Append("vec4 finalColor = Color;\n");
                if (VertexColorsEnabled)
                {
                    builder.Append("finalColor *= fColor;\n");
                }
                if (TextureEnabled)
                {
                    builder.Append("finalColor *= texture(samp, fTexCoords);\n");
                }

                if (DiscardTransparentFragments)
                {
                    builder.Append("if (finalColor.A < ");
                    builder.Append(TransparentFragmentThreshold);
                    builder.Append(")\ndiscard;\n");
                }

                if (useLighting)
                {
                    builder.Append("vec3 unitNormal = normalize(fNormal);\n");
                    builder.Append("vec3 unitToCameraVec = normalize(cameraPos - fPosition);\n\n");
                    builder.Append("vec3 light = ambientLightColor;\n");

                    for (int i = 0; i < DirectionalLights; i++)
                    {
                        string itostring = i.ToString();
                        builder.Append("light += calcDirLight(unitNormal, unitToCameraVec, dLightDir");
                        builder.Append(itostring);
                        builder.Append(", dLightDiffColor");
                        builder.Append(itostring);
                        builder.Append(", dLightSpecColor");
                        builder.Append(itostring);
                        builder.Append(");\n");
                    }

                    for (int i = 0; i < PositionalLights; i++)
                    {
                        string itostring = i.ToString();
                        builder.Append("light += calcPosLight(unitNormal, unitToCameraVec, pLightPos");
                        builder.Append(itostring);
                        builder.Append(", pLightDiffColor");
                        builder.Append(itostring);
                        builder.Append(", pLightSpecColor");
                        builder.Append(itostring);
                        builder.Append(", pAttConfig");
                        builder.Append(itostring);
                        builder.Append(");\n");
                    }

                    builder.Append("finalColor.xyz *= light;\n");
                }

                builder.Append("FragColor = finalColor;\n}");

                programBuilder.FragmentShaderCode = builder.ToString();

                int maxAttribs = Math.Max(PositionAttributeIndex, Math.Max(NormalAttributeIndex, Math.Max(ColorAttributeIndex, TexCoordsAttributeIndex)));
                SpecifiedShaderAttrib[] attribs = new SpecifiedShaderAttrib[maxAttribs + 1];
                for (int i = 0; i < attribs.Length; i++)
                {
                    attribs[i] = new SpecifiedShaderAttrib(null, AttributeType.Float);
                }
                attribs[PositionAttributeIndex] = new SpecifiedShaderAttrib("vPosition", AttributeType.FloatVec3);
                if (NormalAttributeIndex >= 0)
                {
                    attribs[NormalAttributeIndex] = new SpecifiedShaderAttrib(useLighting ? "vNormal" : null, AttributeType.FloatVec3);
                }
                if (ColorAttributeIndex >= 0)
                {
                    attribs[ColorAttributeIndex] = new SpecifiedShaderAttrib(VertexColorsEnabled ? "vColor" : null, AttributeType.FloatVec4);
                }
                if (TexCoordsAttributeIndex >= 0)
                {
                    attribs[TexCoordsAttributeIndex] = new SpecifiedShaderAttrib(TextureEnabled ? "vTexCoords" : null, AttributeType.FloatVec2);
                }
                programBuilder.SpecifyVertexAttribs(attribs);

                // TODO: Change the getLogs parameter to false
                // Also, once we know the SimpleShaderProgram's code is flawless we should change this
                // somehow so it doesn't raise the GraphicsDevice.ShaderCompiled event.
                programHandle = programBuilder.CreateInternal(graphicsDevice, out ActiveVertexAttrib[] activeAttribs,
                                                              out bool hasVs, out bool hasGs, out bool hasFs, true);
                return(new SimpleShaderProgram(graphicsDevice, programHandle, activeAttribs, hasVs, hasGs, hasFs,
                                               VertexColorsEnabled, TextureEnabled, DirectionalLights, PositionalLights));
            }
            catch
            {
                // If anything goes wrong, we delete the program handle (since we're not returning a
                // ShaderProgram so it'd get lost into oblivion otherwise) and re-throw the exception.
                graphicsDevice.GL.DeleteProgram(programHandle);
                throw;
            }
            finally
            {
                VertexShaderLog   = programBuilder.VertexShaderLog;
                FragmentShaderLog = programBuilder.FragmentShaderLog;
                ProgramLog        = programBuilder.ProgramLog;

                builder.Clear();
                ReturnStringBuilder(builder);
            }
        }