From 7b67ed73d84fc62fcc9d3fd731fe0707777753db Mon Sep 17 00:00:00 2001 From: Joshua Goins Date: Mon, 21 Feb 2022 13:20:55 -0500 Subject: [PATCH] Use fmt::memory_buffer in material compiler --- engine/renderer/src/materialcompiler.cpp | 422 ++++++++++++----------- 1 file changed, 219 insertions(+), 203 deletions(-) diff --git a/engine/renderer/src/materialcompiler.cpp b/engine/renderer/src/materialcompiler.cpp index 0e1b51a..3538ccc 100755 --- a/engine/renderer/src/materialcompiler.cpp +++ b/engine/renderer/src/materialcompiler.cpp @@ -13,42 +13,41 @@ ShaderSource get_shader(const std::string& filename, bool skinned, bool cubemap) prism::log("Failed to open shader file {}!", filename); return {}; } - + ShaderStage stage; if(filename.find("vert") != std::string::npos) { stage = ShaderStage::Vertex; } else { stage = ShaderStage::Fragment; } - + CompileOptions options; if(skinned) options.add_definition("BONE"); - + if(cubemap) options.add_definition("CUBEMAP"); - + return *shader_compiler.compile(ShaderLanguage::GLSL, stage, ShaderSource(shader_file->read_as_string()), engine->get_gfx()->accepted_shader_language(), options); } GFXPipeline* MaterialCompiler::create_static_pipeline(GFXGraphicsPipelineCreateInfo createInfo, bool positions_only, bool cubemap) { // take vertex src - std::string vertex_path = createInfo.shaders.vertex_src.as_path().string(); - vertex_path += ".glsl"; + const std::string vertex_path = fmt::format("{}.glsl", createInfo.shaders.vertex_src.as_path().string();); if (positions_only) - createInfo.label += "shadow ver"; + createInfo.label += " (shadow)"; if (cubemap) - createInfo.label += "cubemap ver"; - + createInfo.label += " (cubemap)"; + createInfo.shaders.vertex_src = get_shader(vertex_path, false, cubemap); - + if(positions_only) { createInfo.vertex_input.inputs = { {position_buffer_index, sizeof(prism::float3)} }; - + createInfo.vertex_input.attributes = { {position_buffer_index, 0, 0, GFXVertexFormat::FLOAT3} }; @@ -60,7 +59,7 @@ GFXPipeline* MaterialCompiler::create_static_pipeline(GFXGraphicsPipelineCreateI {tangent_buffer_index, sizeof(prism::float3)}, {bitangent_buffer_index, sizeof(prism::float3)} }; - + createInfo.vertex_input.attributes = { {position_buffer_index, 0, 0, GFXVertexFormat::FLOAT3}, {normal_buffer_index, 1, 0, GFXVertexFormat::FLOAT3}, @@ -69,27 +68,26 @@ GFXPipeline* MaterialCompiler::create_static_pipeline(GFXGraphicsPipelineCreateI {bitangent_buffer_index, 4, 0, GFXVertexFormat::FLOAT3} }; } - + return engine->get_gfx()->create_graphics_pipeline(createInfo); } GFXPipeline* MaterialCompiler::create_skinned_pipeline(GFXGraphicsPipelineCreateInfo createInfo, bool positions_only) { - createInfo.label += " (Skinned)"; - + createInfo.label += " (skinned)"; + // take vertex src - std::string vertex_path = createInfo.shaders.vertex_src.as_path().string(); - vertex_path += ".glsl"; - + const std::string vertex_path = fmt::format("{}.glsl", createInfo.shaders.vertex_src.as_path().string()); + createInfo.shaders.vertex_src = get_shader(vertex_path, true, false); - + createInfo.shader_input.bindings.push_back({ 14, GFXBindingType::StorageBuffer }); - + if(positions_only) { createInfo.vertex_input.inputs = { {position_buffer_index, sizeof(prism::float3)}, {bone_buffer_index, sizeof(BoneVertexData)} }; - + createInfo.vertex_input.attributes = { {position_buffer_index, 0, 0, GFXVertexFormat::FLOAT3}, {bone_buffer_index, 4, offsetof(BoneVertexData, ids), GFXVertexFormat::INT4}, @@ -104,7 +102,7 @@ GFXPipeline* MaterialCompiler::create_skinned_pipeline(GFXGraphicsPipelineCreate {bitangent_buffer_index, sizeof(prism::float3)}, {bone_buffer_index, sizeof(BoneVertexData)} }; - + createInfo.vertex_input.attributes = { {position_buffer_index, 0, 0, GFXVertexFormat::FLOAT3}, {normal_buffer_index, 1, 0, GFXVertexFormat::FLOAT3}, @@ -115,252 +113,270 @@ GFXPipeline* MaterialCompiler::create_skinned_pipeline(GFXGraphicsPipelineCreate {bone_buffer_index, 6, offsetof(BoneVertexData, weights), GFXVertexFormat::FLOAT4}, }; } - + return engine->get_gfx()->create_graphics_pipeline(createInfo); } std::tuple MaterialCompiler::create_pipeline_permutations(GFXGraphicsPipelineCreateInfo& createInfo, bool positions_only) { auto st = create_static_pipeline(createInfo, positions_only); auto ss = create_skinned_pipeline(createInfo, positions_only); - + return {st, ss}; } constexpr std::string_view struct_info = -"layout (constant_id = 0) const int max_materials = 25;\n \ -layout (constant_id = 1) const int max_lights = 25;\n \ -layout (constant_id = 2) const int max_spot_lights = 4;\n \ -layout (constant_id = 3) const int max_probes = 4;\n \ -struct Material {\n \ - vec4 color, info;\n \ -};\n \ -struct Light {\n \ - vec4 positionType;\n \ - vec4 directionPower;\n \ - vec4 colorSize;\n \ - vec4 shadowsEnable;\n \ -};\n \ -struct Probe {\n \ - vec4 position, size;\n \ -};\n \ -layout(std430, binding = 1) buffer readonly SceneInformation {\n \ - vec4 options;\n \ - vec4 camPos;\n \ - mat4 vp, lightSpace;\n \ - mat4 spotLightSpaces[max_spot_lights];\n \ - Material materials[max_materials];\n \ - Light lights[max_lights];\n \ - Probe probes[max_probes];\n \ - int numLights;\n \ -} scene;\n \ -layout (binding = 2) uniform sampler2D sun_shadow;\n \ -layout (binding = 6) uniform sampler2DArray spot_shadow;\n \ -layout(push_constant) uniform PushConstant {\n \ - mat4 model;\n \ -};\n"; +R"(layout (constant_id = 0) const int max_materials = 25; +layout (constant_id = 1) const int max_lights = 25; +layout (constant_id = 2) const int max_spot_lights = 4; +layout (constant_id = 3) const int max_probes = 4; +struct Material {{ + vec4 color, info; +}}; +struct Light {{ + vec4 positionType; + vec4 directionPower; + vec4 colorSize; + vec4 shadowsEnable; +}}; +struct Probe {{ + vec4 position, size; +}}; +layout(std430, binding = 1) buffer readonly SceneInformation {{ + vec4 options; + vec4 camPos; + mat4 vp, lightSpace; + mat4 spotLightSpaces[max_spot_lights]; + Material materials[max_materials]; + Light lights[max_lights]; + Probe probes[max_probes]; + int numLights; +}} scene; +layout (binding = 2) uniform sampler2D sun_shadow; +layout (binding = 6) uniform sampler2DArray spot_shadow; +layout(push_constant) uniform PushConstant {{ + mat4 model; +}}; +)"; ShaderSource MaterialCompiler::compile_material_fragment(Material& material, bool use_ibl) { if(!render_options.enable_ibl) use_ibl = false; - - std::string src; - + + auto src = fmt::memory_buffer(); + switch(render_options.shadow_filter) { case ShadowFilter::None: - src += "#define SHADOW_FILTER_NONE\n"; + format_to(std::back_inserter(src), "#define SHADOW_FILTER_NONE\n"); break; case ShadowFilter::PCF: - src += "#define SHADOW_FILTER_PCF\n"; + format_to(std::back_inserter(src), "#define SHADOW_FILTER_PCF\n"); break; case ShadowFilter::PCSS: - src += "#define SHADOW_FILTER_PCSS\n"; + format_to(std::back_inserter(src), "#define SHADOW_FILTER_PCSS\n"); break; } - - src += "layout (location = 0) in vec3 in_frag_pos;\n"; - src += "layout(location = 1) in vec3 in_normal;\n"; - src += "layout(location = 2) in vec2 in_uv;\n"; - src += "layout(location = 0) out vec4 frag_output;\n"; - + format_to(std::back_inserter(src), + R"(layout (location = 0) in vec3 in_frag_pos; + layout(location = 1) in vec3 in_normal; + layout(location = 2) in vec2 in_uv; + layout(location = 0) out vec4 frag_output; + )"); + if(render_options.enable_point_shadows) { - src += "#define POINT_SHADOWS_SUPPORTED\n"; - src += "layout (binding = 3) uniform samplerCubeArray point_shadow;\n"; + format_to(std::back_inserter(src), + R"(#define POINT_SHADOWS_SUPPORTED + layout (binding = 3) uniform samplerCubeArray point_shadow; + )"); } - src += struct_info; - + format_to(std::back_inserter(src), struct_info); + if(use_ibl) { - src += "layout (binding = 7) uniform samplerCubeArray irrandianceSampler;\n \ - layout (binding = 8) uniform samplerCubeArray prefilterSampler;\n \ - layout (binding = 9) uniform sampler2D brdfSampler;\n"; + format_to(std::back_inserter(src), + R"(layout (binding = 7) uniform samplerCubeArray irrandianceSampler; + layout (binding = 8) uniform samplerCubeArray prefilterSampler; + layout (binding = 9) uniform sampler2D brdfSampler; + )"); } - - src += "layout(location = 4) in vec4 fragPosLightSpace;\n"; - src += "layout(location = 5) in mat3 in_tbn;\n"; - src += "layout(location = 14) in vec4 fragPostSpotLightSpace[max_spot_lights];\n"; - src += "layout(location = 13) in flat int inMaterialIndex;\n"; - src += "#include \"common.glsl\"\n"; - src += "#include \"rendering.glsl\"\n"; - + format_to(std::back_inserter(src), + R"(layout(location = 4) in vec4 fragPosLightSpace; + layout(location = 5) in mat3 in_tbn; + layout(location = 14) in vec4 fragPostSpotLightSpace[max_spot_lights]; + layout(location = 13) in flat int inMaterialIndex; + #include "common.glsl" + #include "rendering.glsl" + )"); + material.bound_textures.clear(); - + // insert samplers as needed int sampler_index = 10; if(material.colorProperty.type == DataType::AssetTexture) { material.bound_textures[sampler_index] = material.colorProperty.value_tex; - src += "layout(binding = " + std::to_string(sampler_index++) + ") uniform sampler2D colorTexture;\n"; + format_to(std::back_inserter(src), "layout(binding = {}) uniform sampler2D colorTexture;\n", sampler_index++); } if(material.normalProperty.type == DataType::AssetTexture) { material.bound_textures[sampler_index] = material.normalProperty.value_tex; - src += "layout(binding = " + std::to_string(sampler_index++) + ") uniform sampler2D normalTexture;\n"; + format_to(std::back_inserter(src), "layout(binding = {}) uniform sampler2D normalTexture;\n", sampler_index++); } if(use_ibl) { - src += "vec3 get_reflect(int i, vec3 final_normal) {\n \ - const vec3 direction = normalize(in_frag_pos - scene.camPos.xyz);\n \ - const vec3 reflection = reflect(direction, normalize(final_normal));\n \ - vec3 box_max = scene.probes[i].position.xyz + (scene.probes[i].size.xyz / 2.0f);\n \ - vec3 box_min = scene.probes[i].position.xyz + -(scene.probes[i].size.xyz / 2.0f);\n \ - vec3 unitary = vec3(1.0);\n \ - vec3 first_plane_intersect = (box_max - in_frag_pos) / reflection;\n \ - vec3 second_plane_intersect = (box_min - in_frag_pos) / reflection;\n \ - vec3 furthest_plane = max(first_plane_intersect, second_plane_intersect);\n \ - float distance = min(furthest_plane.x, min(furthest_plane.y, furthest_plane.z));\n \ - vec3 intersect_position_world = in_frag_pos + reflection * distance; \n \ - return intersect_position_world - scene.probes[i].position.xyz;\n}\n"; + format_to(std::back_inserter(src), + R"(vec3 get_reflect(int i, vec3 final_normal) {{ + const vec3 direction = normalize(in_frag_pos - scene.camPos.xyz); + const vec3 reflection = reflect(direction, normalize(final_normal)); + vec3 box_max = scene.probes[i].position.xyz + (scene.probes[i].size.xyz / 2.0f); + vec3 box_min = scene.probes[i].position.xyz + -(scene.probes[i].size.xyz / 2.0f); + vec3 unitary = vec3(1.0); + vec3 first_plane_intersect = (box_max - in_frag_pos) / reflection; + vec3 second_plane_intersect = (box_min - in_frag_pos) / reflection; + vec3 furthest_plane = max(first_plane_intersect, second_plane_intersect); + float distance = min(furthest_plane.x, min(furthest_plane.y, furthest_plane.z)); + vec3 intersect_position_world = in_frag_pos + reflection * distance; + return intersect_position_world - scene.probes[i].position.xyz; + }} + )"); } - + if(render_options.enable_normal_shadowing) { - src += "float calculate_normal_lighting(in sampler2D normal_map, const vec3 normal, const vec3 light_dir) {\n \ - float height_scale = 0.8;\n \ - float sample_count = 100.0;\n \ - float inv_sample_count = 1.0 / sample_count;\n \ - float hardness = 50 * 0.5;\n \ - float lighting = clamp(dot(light_dir, normal), 0.0, 1.0);\n \ - float slope = -lighting;\n \ - vec2 dir = light_dir.xy * vec2(1.0, -1.0) * height_scale;\n \ - float max_slope = 0.0;\n \ - float step = inv_sample_count;\n \ - float pos = step;\n \ - pos = (-lighting >= 0.0) ? 1.001 : pos;\n \ - vec2 noise = fract(in_frag_pos.xy * 0.5);\n \ - noise.x = noise.x + noise.y * 0.5;\n \ - pos = step - step * noise.x;\n \ - float shadow = 0.0;\n \ - while(pos <= 1.0) {\n \ - vec3 tmp_normal = texture(normal_map, in_uv + dir * pos).rgb;\n \ - tmp_normal = in_tbn * (tmp_normal * 2.0 - 1.0);\n \ - float tmp_lighting = dot(light_dir, tmp_normal);\n \ - float shadowed = -tmp_lighting;\n \ - slope += shadowed;\n \ - if(slope > max_slope) {\n \ - shadow += hardness * (1.0 - pos);\n \ - }\n \ - max_slope = max(max_slope, slope);\n \ - pos += step;\n \ - }\n \ - return clamp(1.0 - shadow * inv_sample_count, 0.0, 1.0);\n \ - }\n"; + format_to(std::back_inserter(src), + R"(float calculate_normal_lighting(in sampler2D normal_map, const vec3 normal, const vec3 light_dir) {{ + float height_scale = 0.8; + float sample_count = 100.0; + float inv_sample_count = 1.0 / sample_count; + float hardness = 50 * 0.5; + float lighting = clamp(dot(light_dir, normal), 0.0, 1.0); + float slope = -lighting; + vec2 dir = light_dir.xy * vec2(1.0, -1.0) * height_scale; + float max_slope = 0.0; + float step = inv_sample_count; + float pos = step; + pos = (-lighting >= 0.0) ? 1.001 : pos; + vec2 noise = fract(in_frag_pos.xy * 0.5); + noise.x = noise.x + noise.y * 0.5; + pos = step - step * noise.x; + float shadow = 0.0; + while(pos <= 1.0) {{ + vec3 tmp_normal = texture(normal_map, in_uv + dir * pos).rgb; + tmp_normal = in_tbn * (tmp_normal * 2.0 - 1.0); + float tmp_lighting = dot(light_dir, tmp_normal); + float shadowed = -tmp_lighting; + slope += shadowed; + if(slope > max_slope) {{ + shadow += hardness * (1.0 - pos); + }} + max_slope = max(max_slope, slope); + pos += step; + }} + return clamp(1.0 - shadow * inv_sample_count, 0.0, 1.0); + }} + )"); } - + if(use_ibl) { - src += "vec3 ibl(const int probe, const ComputedSurfaceInfo surface_info, const float intensity) {\n \ - const vec3 F = fresnel_schlick_roughness(surface_info.NdotV, surface_info.F0, surface_info.roughness);\n \ - const vec3 R = get_reflect(probe, surface_info.N);\n \ - const vec2 brdf = texture(brdfSampler, vec2(surface_info.NdotV, surface_info.roughness)).rg; \n \ - const vec3 sampledIrradiance = texture(irrandianceSampler, vec4(surface_info.N, probe)).xyz;\n \ - const vec3 prefilteredColor = textureLod(prefilterSampler, vec4(R, probe), surface_info.roughness * 4).xyz;\n \ - const vec3 diffuse = sampledIrradiance * surface_info.diffuse_color;\n \ - const vec3 specular = prefilteredColor * (F * brdf.x + brdf.y);\n \ - return (diffuse + specular) * intensity;\n \ - }\n"; + format_to(std::back_inserter(src), + R"(vec3 ibl(const int probe, const ComputedSurfaceInfo surface_info, const float intensity) {{ + const vec3 F = fresnel_schlick_roughness(surface_info.NdotV, surface_info.F0, surface_info.roughness); + const vec3 R = get_reflect(probe, surface_info.N); + const vec2 brdf = texture(brdfSampler, vec2(surface_info.NdotV, surface_info.roughness)).rg; + const vec3 sampledIrradiance = texture(irrandianceSampler, vec4(surface_info.N, probe)).xyz; + const vec3 prefilteredColor = textureLod(prefilterSampler, vec4(R, probe), surface_info.roughness * 4).xyz; + const vec3 diffuse = sampledIrradiance * surface_info.diffuse_color; + const vec3 specular = prefilteredColor * (F * brdf.x + brdf.y); + return (diffuse + specular) * intensity; + }} + )"); } - - src += "void main() {\n"; + + format_to(std::back_inserter(src), "void main() {{\n"); if(material.colorProperty.type == DataType::Vector3) { - src += "vec3 Color = vec3(" + std::to_string(material.colorProperty.value.x) + "," - + std::to_string(material.colorProperty.value.y) + "," - + std::to_string(material.colorProperty.value.z) + ");\n"; + format_to(std::back_inserter(src), "vec3 Color = vec3({}, {}, {});\n", material.colorProperty.value.x, material.colorProperty.value.y, material.colorProperty.value.z); } else if(material.colorProperty.type == DataType::AssetTexture) { - src += "vec3 Color = texture(colorTexture, in_uv).rgb;\n"; + format_to(std::back_inserter(src), "vec3 Color = texture(colorTexture, in_uv).rgb;\n"); } else if(material.colorProperty.type == DataType::Float) { - src += "vec3 Color = vec3(" + std::to_string(material.colorProperty.float_value) + ");\n"; + format_to(std::back_inserter(src),"vec3 Color = vec3({}});\n", material.colorProperty.float_value); } - src += "vec3 final_diffuse_color = from_srgb_to_linear(Color);\n"; - src += "float final_roughness = scene.materials[inMaterialIndex].info.y;\n"; - src += "float final_metallic = scene.materials[inMaterialIndex].info.x;\n"; + format_to(std::back_inserter(src), + R"(vec3 final_diffuse_color = from_srgb_to_linear(Color); + float final_roughness = scene.materials[inMaterialIndex].info.y; + float final_metallic = scene.materials[inMaterialIndex].info.x; + )"); if(material.normalProperty.type == DataType::AssetTexture) { prism::log("enabling normal mapping on material.."); - src += "vec3 final_normal = texture(normalTexture, in_uv).rgb;\n"; - src += "final_normal = final_normal * 2.0 - 1.0;\n"; - src += "final_normal = in_tbn * final_normal;\n"; + format_to(std::back_inserter(src), + R"(vec3 final_normal = texture(normalTexture, in_uv).rgb; + final_normal = final_normal * 2.0 - 1.0; + final_normal = in_tbn * final_normal; + )"); } else { - src += "vec3 final_normal = in_normal;\n"; + format_to(std::back_inserter(src), "vec3 final_normal = in_normal;\n"); } - - src += - "ComputedSurfaceInfo surface_info = compute_surface(final_diffuse_color.rgb, final_normal, final_metallic, final_roughness);\n \ - vec3 Lo = vec3(0);\n \ - for(int i = 0; i < scene.numLights; i++) {\n \ - const int type = int(scene.lights[i].positionType.w);\n \ - ComputedLightInformation light_info;\n \ - switch(type) {\n \ - case 0:\n \ - light_info = calculate_point(scene.lights[i]);\n \ - break;\n \ - case 1:\n \ - light_info = calculate_spot(scene.lights[i]);\n \ - break;\n \ - case 2:\n \ - light_info = calculate_sun(scene.lights[i]);\n \ - break;\n \ - }\n \ - SurfaceBRDF surface_brdf = brdf(light_info.direction, surface_info);\n"; + + format_to(std::back_inserter(src), + R"(ComputedSurfaceInfo surface_info = compute_surface(final_diffuse_color.rgb, final_normal, final_metallic, final_roughness); + vec3 Lo = vec3(0); + for(int i = 0; i < scene.numLights; i++) {{ + const int type = int(scene.lights[i].positionType.w); + ComputedLightInformation light_info; + switch(type) {{ + case 0: + light_info = calculate_point(scene.lights[i]); + break; + case 1: + light_info = calculate_spot(scene.lights[i]); + break; + case 2: + light_info = calculate_sun(scene.lights[i]); + break; + }} + SurfaceBRDF surface_brdf = brdf(light_info.direction, surface_info); + )"); if(render_options.enable_normal_mapping && material.normalProperty.type == DataType::AssetTexture && render_options.enable_normal_shadowing) { - src += std::string("light_info.radiance *= calculate_normal_lighting(normalTexture, final_normal, light_info.direction);\n"); + format_to(std::back_inserter(src), "light_info.radiance *= calculate_normal_lighting(normalTexture, final_normal, light_info.direction);\n"); } - src += "Lo += ((surface_brdf.specular + surface_brdf.diffuse) * light_info.radiance * surface_brdf.NdotL) * scene.lights[i].colorSize.rgb;\n \ - }\n"; - + format_to(std::back_inserter(src), "Lo += ((surface_brdf.specular + surface_brdf.diffuse) * light_info.radiance * surface_brdf.NdotL) * scene.lights[i].colorSize.rgb;\n \ + }}\n"); + if(use_ibl) { - src += - "vec3 ambient = vec3(0.0); \ - float sum = 0.0;\n \ - for(int i = 0; i < max_probes; i++) { \ - if(scene.probes[i].position.w == 1) {\n \ - const vec3 position = scene.probes[i].position.xyz; \ - const vec3 probe_min = position - (scene.probes[i].size.xyz / 2.0); \ - const vec3 probe_max = position + (scene.probes[i].size.xyz / 2.0); \ - if(all(greaterThan(in_frag_pos, probe_min)) && all(lessThan(in_frag_pos, probe_max))) { \ - float intensity = 1.0 - length(abs(in_frag_pos - position) / (scene.probes[i].size.xyz / 2.0));\n \ - intensity = clamp(intensity, 0.0, 1.0) * scene.probes[i].size.w;\n \ - ambient += ibl(i, surface_info, intensity);\n \ - sum += intensity; \n \ - } \ - } else if(scene.probes[i].position.w == 2) {\n \ - ambient += ibl(i, surface_info, scene.probes[i].size.w);\n \ - sum += scene.probes[i].size.w; \n \ - }\n \ - }\n \ - ambient /= sum;\n"; - - src += "frag_output = vec4(ambient + Lo, 1.0);\n"; + format_to(std::back_inserter(src), + R"(vec3 ambient = vec3(0.0); + float sum = 0.0; + for(int i = 0; i < max_probes; i++) {{ + if(scene.probes[i].position.w == 1) {{ + const vec3 position = scene.probes[i].position.xyz; + const vec3 probe_min = position - (scene.probes[i].size.xyz / 2.0); + const vec3 probe_max = position + (scene.probes[i].size.xyz / 2.0); + if(all(greaterThan(in_frag_pos, probe_min)) && all(lessThan(in_frag_pos, probe_max))) {{ + float intensity = 1.0 - length(abs(in_frag_pos - position) / (scene.probes[i].size.xyz / 2.0)); + intensity = clamp(intensity, 0.0, 1.0) * scene.probes[i].size.w; + ambient += ibl(i, surface_info, intensity); + sum += intensity; + }} + }} else if(scene.probes[i].position.w == 2) {{ + ambient += ibl(i, surface_info, scene.probes[i].size.w); + sum += scene.probes[i].size.w; + }} + }} + ambient /= sum; + )"); + + format_to(std::back_inserter(src), "frag_output = vec4(ambient + Lo, 1.0);\n"); } else { - src += "frag_output = vec4(Lo, 1.0);\n"; + format_to(std::back_inserter(src), "frag_output = vec4(Lo, 1.0);\n"); } - - src += "}\n"; - - return *shader_compiler.compile(ShaderLanguage::GLSL, ShaderStage::Fragment, ShaderSource(src), engine->get_gfx()->accepted_shader_language()); + + format_to(std::back_inserter(src), "}}\n"); + + return *shader_compiler.compile(ShaderLanguage::GLSL, ShaderStage::Fragment, ShaderSource(to_string(src)), engine->get_gfx()->accepted_shader_language()); }