/* ----------------------------------------------------------------------------- GSFramework Copyright 2001-2013 Emmanuel Julien. All Rights Reserved. ----------------------------------------------------------------------------- */ #include "gpu/gpu_material_shader.h" #include "gpu/gpu_material.h" #include "gpu/gpu_renderer.h" #include "log/log.h" using namespace GS; using namespace GS::GPU; using GS::NML::Tag; //------------------------------------------------------------------------------ bool MaterialShader::SetUserUniformValue(const char *name, const Vector4 &v) { uint match_count = 0; for (uint n = 0; n < variants.GetCount(); ++n) if (Shader *shader = variants[n].c_ptr()) if (ShaderInput *input = shader->GetInput(name)) { input->parm_v = v; ++match_count; } return match_count > 0; } bool MaterialShader::SetUserUniformValue(const char *name, Render::Texture *t) { uint match_count = 0; for (uint n = 0; n < variants.GetCount(); ++n) if (Shader *shader = variants[n].c_ptr()) if (ShaderInput *input = shader->GetInput(name)) { input->parm_t = t; ++match_count; } return match_count > 0; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ void MaterialShader::CompileForwardPointLight(Core::Shader &shader, bool shadow) { shader.Define("_POINT_LIGHT", Core::ShaderInput::Pixel); CompileShaderSection("OutputForwardLightModel", shader); if (shadow) { shader.Define("_CAST_SHADOW", Core::ShaderInput::Pixel); shader.DeclareInput("psm", Core::ShaderInput::DataTextureShadow, Core::ShaderInput::LightShadowMap0, Core::ShaderInput::Uniform, Core::ShaderInput::Pixel); for (int n = 0; n < 6; ++n) shader.DeclareInput(String::Format("psm_%d_projection_matrix", n), Core::ShaderInput::Matrix4, Core::ShaderInput::Semantic(Core::ShaderInput::LightShadowMatrix0 + n), Core::ShaderInput::Uniform, Core::ShaderInput::Pixel); shader.DeclareInput("u_iss", Core::ShaderInput::Float, Core::ShaderInput::InverseShadowMapSize, Core::ShaderInput::Uniform, Core::ShaderInput::Pixel); shader.DeclareInput("u_lsb", Core::ShaderInput::Float, Core::ShaderInput::LightShadowBias, Core::ShaderInput::Uniform, Core::ShaderInput::Pixel); shader.DeclareInput("u_vtl", Core::ShaderInput::Matrix4, Core::ShaderInput::ViewToLightMatrix, Core::ShaderInput::Uniform, Core::ShaderInput::Pixel); shader.DeclareInput("u_noise", Core::ShaderInput::DataTexture2D, Core::ShaderInput::NoiseMap, Core::ShaderInput::Uniform, Core::ShaderInput::Pixel); if (Tag *t = renderer.GetPCFShaderTag()) shader.pixel_decl += t->GetString(); if (Tag *t = renderer.GetPSMShaderTag()) shader.pixel.Replace("%psm_pcf_evaluation%", t->GetString()); } } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ void MaterialShader::CompileForwardSpotLight(Core::Shader &shader, bool shadow, bool projection_map) { shader.Define("_SPOT_LIGHT", Core::ShaderInput::Pixel); CompileShaderSection("OutputForwardLightModel", shader); if (shadow) { shader.Define("_CAST_SHADOW", Core::ShaderInput::Pixel); shader.DeclareInput("u_ssm", Core::ShaderInput::DataTextureShadow, Core::ShaderInput::Semantic(Core::ShaderInput::LightShadowMap0), Core::ShaderInput::Uniform, Core::ShaderInput::Pixel); shader.DeclareInput("u_ssm_projection", Core::ShaderInput::Matrix4, Core::ShaderInput::Semantic(Core::ShaderInput::LightShadowMatrix0), Core::ShaderInput::Uniform, Core::ShaderInput::Pixel); shader.DeclareInput("u_iss", Core::ShaderInput::Float, Core::ShaderInput::InverseShadowMapSize, Core::ShaderInput::Uniform, Core::ShaderInput::Pixel); shader.DeclareInput("u_lsb", Core::ShaderInput::Float, Core::ShaderInput::LightShadowBias, Core::ShaderInput::Uniform, Core::ShaderInput::Pixel); shader.DeclareInput("u_noise", Core::ShaderInput::DataTexture2D, Core::ShaderInput::NoiseMap, Core::ShaderInput::Uniform, Core::ShaderInput::Pixel); if (Tag *t = renderer.GetPCFShaderTag()) shader.pixel_decl += t->GetString(); } if (projection_map) { shader.Define("_PROJECTION_MAP", Core::ShaderInput::Pixel); shader.DeclareInput("u_pjm", Core::ShaderInput::DataTexture2D, Core::ShaderInput::Semantic(Core::ShaderInput::LightProjectionMap), Core::ShaderInput::Uniform, Core::ShaderInput::Pixel); shader.DeclareInput("u_pjm_projection", Core::ShaderInput::Matrix4, Core::ShaderInput::Semantic(Core::ShaderInput::LightShadowMatrix0), Core::ShaderInput::Uniform, Core::ShaderInput::Pixel); } } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ void MaterialShader::CompileForwardLinearLight(Core::Shader &shader, bool shadow) { shader.Define("_LINEAR_LIGHT", Core::ShaderInput::Pixel); CompileShaderSection("OutputForwardLightModel", shader); if (shadow) { shader.Define("_CAST_SHADOW", Core::ShaderInput::Pixel); shader.DeclareInput("pssm", Core::ShaderInput::DataTextureShadow, Core::ShaderInput::LightShadowMap0, Core::ShaderInput::Uniform, Core::ShaderInput::Pixel); for (int n = 0; n < renderer.registry.GetInteger("ShadowMapping:PSSM:Split", 3); ++n) { shader.DeclareInput(String::Format("pssm_%d_slice_distance", n), Core::ShaderInput::Float, Core::ShaderInput::Semantic(Core::ShaderInput::LightPSSMSliceDistance0 + n), Core::ShaderInput::Uniform, Core::ShaderInput::Pixel); shader.DeclareInput(String::Format("pssm_%d_projection_matrix", n), Core::ShaderInput::Matrix4, Core::ShaderInput::Semantic(Core::ShaderInput::LightShadowMatrix0 + n), Core::ShaderInput::Uniform, Core::ShaderInput::Pixel); } shader.DeclareInput("u_noise", Core::ShaderInput::DataTexture2D, Core::ShaderInput::NoiseMap, Core::ShaderInput::Uniform, Core::ShaderInput::Pixel); shader.DeclareInput("u_iss", Core::ShaderInput::Float, Core::ShaderInput::InverseShadowMapSize, Core::ShaderInput::Uniform, Core::ShaderInput::Pixel); shader.DeclareInput("u_lsb", Core::ShaderInput::Float, Core::ShaderInput::LightShadowBias, Core::ShaderInput::Uniform, Core::ShaderInput::Pixel); if (Tag *t = renderer.GetPCFShaderTag()) shader.pixel_decl += t->GetString(); if (Tag *t = renderer.GetPSSMShaderTag()) shader.pixel.Replace("%pssm_pcf_evaluation%", t->GetString()); } } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ bool MaterialShader::CompileShaderSection(const char *id, Core::Shader &shader) { Tag *shader_tag = renderer.GetMaterialDict().GetTag(id); if (!shader_tag) __ERR__(__LOG_E__ << "Missing shader section '" << id << "'.\n", false) if (Tag *tag = shader_tag->GetTag("Input;")) shader.ParseInputTag(tag); if (Tag *tag = shader_tag->GetTag("Varying;")) shader.ParseVaryingTag(tag); if (Tag *tag = shader_tag->GetTag("Vertex;")) shader.vertex += tag->GetString(); if (Tag *tag = shader_tag->GetTag("Fragment;")) shader.pixel += tag->GetString(); return true; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ bool MaterialShader::SetupMaterialProgram(Variant p, Core::Shader &shader) { if (parm.use_alpha_test) { shader.DeclareInput("u_alpha_threshold", Core::ShaderInput::Float, Core::ShaderInput::MaterialAlphaThreshold, Core::ShaderInput::Uniform, Core::ShaderInput::Pixel); shader.pixel += "if (%opacity% < u_alpha_threshold) discard;\n"; } if (parm.use_depth_bias) shader.DeclareInput("u_depth_bias", Core::ShaderInput::Float, Core::ShaderInput::MaterialDepthBias, Core::ShaderInput::Uniform, Core::ShaderInput::Pixel); if (parm.use_skinning) { shader.Define("_SKINNED", (Core::ShaderInput::Scope)(Core::ShaderInput::Vertex | Core::ShaderInput::Pixel)); shader.DeclareInput("bone_mtx", Core::ShaderInput::Matrix4, Core::ShaderInput::BoneMatrix, Core::ShaderInput::Uniform, Core::ShaderInput::Vertex, __PL_BONE_LIMIT__); shader.DeclareInput("bone_idx", Core::ShaderInput::Vector4, Core::ShaderInput::BoneIndex, Core::ShaderInput::Attribute, Core::ShaderInput::Vertex); shader.DeclareInput("bone_w", Core::ShaderInput::Vector4, Core::ShaderInput::BoneWeight, Core::ShaderInput::Attribute, Core::ShaderInput::Vertex); shader.DeclareVarying("v_skin_mtx", "mat4"); } CompileShaderSection("InputPosition;", shader); // Assemble program shader. switch (p) { case Depth: CompileShaderSection("OutputDepth;", shader); break; case FS_Constant: CompileShaderSection("OutputForwardConstant;", shader); break; case FS_PointLight: case FS_PointLightShadowMapping: CompileForwardPointLight(shader, p == FS_PointLightShadowMapping); break; case FS_SpotLight: case FS_SpotLightShadowMapping: case FS_SpotLightProjection: case FS_SpotLightProjectionShadowMapping: CompileForwardSpotLight(shader, (p == FS_SpotLightShadowMapping) || (p == FS_SpotLightProjectionShadowMapping), (p == FS_SpotLightProjection) || (p == FS_SpotLightProjectionShadowMapping)); break; case FS_LinearLight: case FS_LinearLightShadowMapping: CompileForwardLinearLight(shader, p == FS_LinearLightShadowMapping); break; case DS_GBufferMRT4: CompileShaderSection("OutputDeferred;", shader); break; case PP_NormalDepth: CompileShaderSection("OutputNormalDepth;", shader); break; case PP_Velocity: if (parm.use_skinning) shader.DeclareInput("previous_bone_mtx", Core::ShaderInput::Matrix4, Core::ShaderInput::PreviousBoneMatrix, Core::ShaderInput::Uniform, Core::ShaderInput::Vertex, __PL_BONE_LIMIT__); CompileShaderSection("OutputVelocity;", shader); break; case Last: break; } return true; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ static bool FindVarAssignation(const String &src, const char *var) // TODO fix with proper regex, this is proof of concept code at best... { if (src.FindString(String::Format("%s =", var))) return true; if (src.FindString(String::Format("%s\t=", var))) return true; return false; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ static void DeclareVertexPosition(Core::Shader &shader, bool declare_varying) { String decl = "vec4 %position%;\n"; if (!asbool(FindVarAssignation(shader.vertex, "%position%"))) { Core::ShaderInput *input = shader.DeclareInput("a_position", Core::ShaderInput::Vector3, Core::ShaderInput::Position, Core::ShaderInput::Attribute, Core::ShaderInput::Vertex); // Core::ShaderInput *input = shader.GetInput(Core::ShaderInput::Position); decl << "%position% = vec4(" << input->name << ", 1.0);\n"; } shader.vertex = decl + shader.vertex; } static void DeclareVertexNormal(Core::Shader &shader) { String decl = "vec3 %normal%;\n"; if (!asbool(FindVarAssignation(shader.vertex, "%normal%"))) { Core::ShaderInput *input = shader.DeclareInput("a_normal", Core::ShaderInput::Vector3, Core::ShaderInput::Normal, Core::ShaderInput::Attribute, Core::ShaderInput::Vertex); // Core::ShaderInput *input = shader.GetInput(Core::ShaderInput::Normal); decl << "%normal% = " << input->name << ";\n"; } shader.vertex = decl + shader.vertex; } static void InitializeShader(Core::Shader &shader) { // Position { bool pixel_consumes = asbool(shader.pixel.FindString("%in.position%")); DeclareVertexPosition(shader, pixel_consumes); } // Normal { bool pixel_consumes = asbool(shader.pixel.FindString("%in.normal%")); bool pixel_provides = FindVarAssignation(shader.pixel, "%normal%"); if (pixel_consumes || !pixel_provides) DeclareVertexNormal(shader); if (!pixel_provides) shader.pixel << "%normal% = %in.normal%;\n"; shader.pixel = String("vec3 %normal%;\n") + shader.pixel; } { struct ShaderDefault { const char *type, *name, *var; Core::ShaderInput::DataType data_type; Core::ShaderInput::Semantic semantic; }; static ShaderDefault ps_ic[] = { { "vec4", "%diffuse%", "_u_mat_diff", Core::ShaderInput::Vector4, Core::ShaderInput::MaterialDiffuse }, { "vec4", "%specular%", "_u_mat_spec", Core::ShaderInput::Vector4, Core::ShaderInput::MaterialSpecular }, { "float", "%glossiness%", "_u_mat_glos", Core::ShaderInput::Float, Core::ShaderInput::MaterialGlossiness }, { "vec4", "%constant%", "_u_mat_const", Core::ShaderInput::Vector4, Core::ShaderInput::MaterialSelf }, { "float", "%opacity%", "_u_mat_opac", Core::ShaderInput::Float, Core::ShaderInput::MaterialOpacity }, { NULL, NULL, NULL } }; // Declare mandatory outputs and set default value for unprovided entries. String decl; for (uint n = 0; ps_ic[n].type; ++n) { decl << ps_ic[n].type << " " << ps_ic[n].name << ";\n"; if (!FindVarAssignation(shader.pixel, ps_ic[n].name)) { Core::ShaderInput *input = shader.DeclareInput(ps_ic[n].var, ps_ic[n].data_type, ps_ic[n].semantic, Core::ShaderInput::Uniform, Core::ShaderInput::Pixel); decl << ps_ic[n].name << " = " << input->name << ";\n"; } } shader.pixel = decl + shader.pixel; } } static void FinalizeShader(Core::Shader &shader) { if (asbool(shader.pixel.FindString("%in.position%"))) { shader.DeclareVarying("_v_position", "vec4"); shader.vertex << "_v_position = %position%;\n"; } if (asbool(shader.pixel.FindString("%in.normal%"))) { shader.DeclareVarying("_v_normal", "vec3"); shader.vertex << "_v_normal = %normal%;\n"; } shader.pixel.ReplaceAll("%in.position%", "_v_position", true); shader.pixel.ReplaceAll("%in.normal%", "_v_normal", true); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ static const char *GetShaderVariantName(MaterialShader::Variant v) { switch (v) { case MaterialShader::Depth: return "Depth"; case MaterialShader::DS_GBufferMRT4: return "DS_GBufferMRT4"; case MaterialShader::FS_Constant: return "FS_Constant"; case MaterialShader::FS_PointLight: return "FS_PointLight"; case MaterialShader::FS_PointLightShadowMapping: return "FS_PointLightShadowMapping"; case MaterialShader::FS_LinearLight: return "FS_LinearLight"; case MaterialShader::FS_LinearLightShadowMapping: return "FS_LinearLightShadowMapping"; case MaterialShader::FS_SpotLight: return "FS_SpotLight"; case MaterialShader::FS_SpotLightShadowMapping: return "FS_SpotLightShadowMapping"; case MaterialShader::FS_SpotLightProjection: return "FS_SpotLightProjection"; case MaterialShader::FS_SpotLightProjectionShadowMapping: return "FS_SpotLightProjectionShadowMapping"; case MaterialShader::PP_NormalDepth: return "PP_NormalDepth"; case MaterialShader::PP_Velocity: return "PP_Velocity"; case MaterialShader::Last: break; } return "UnknownVariant"; } bool MaterialShader::Create(Render::ResourceFactory &rf, const Core::Shader &shader, const MaterialShaderStaticParm &static_parm) { __LOG_V__ << "Creating material shader '" << shader.name << "' variants.\n"; name = shader.name; parm = static_parm; // Build variants. if (!variants.Allocate(Last)) return false; for (int n = 0; n < Last; ++n) { if (renderer.render_technique == Renderer::TechniqueDeferred) if ((n >= FS_Constant) && (n <= FS_SpotLightProjectionShadowMapping)) continue; // no forward if (renderer.render_technique == Renderer::TechniqueForward) if (n == DS_GBufferMRT4) continue; // no deferred if (!renderer.gpu_config.enable_shadow) if ( (n == Depth) || (n == FS_PointLightShadowMapping) || (n == FS_LinearLightShadowMapping) || (n == FS_SpotLightShadowMapping) || (n == FS_SpotLightProjectionShadowMapping) ) continue; // no shadow-mapping if (!renderer.gpu_config.use_rtt) if ( (n == PP_Velocity) || (n == PP_NormalDepth) ) continue; // no post-processes if (parm.no_lighting) if ((n > FS_Constant) && (n <= FS_SpotLightProjectionShadowMapping)) // keep constant! continue; // no lighting pass // Specialize shader for this program. Core::Shader shader_variant; if (!shader.Clone(shader_variant)) return false; InitializeShader(shader_variant); if (!SetupMaterialProgram((Variant)n, shader_variant)) return false; FinalizeShader(shader_variant); shader_variant.name << "_" << GetShaderVariantName((MaterialShader::Variant)n); // Compile it. if ((variants[n] = (Shader *)renderer.NewShader()) != NULL) if (!variants[n]->Create(rf, shader_variant)) return false; } return true; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ MaterialShader::MaterialShader(Renderer &r) : renderer(r) { renderer.material_shaders.Add(this); } MaterialShader::~MaterialShader() { renderer.material_shaders.Remove(this); } //------------------------------------------------------------------------------