Archived
1
Fork 0
This repository has been archived on 2025-04-12. You can view files and clone it, but cannot push or open issues or pull requests.
prism/engine/renderer/src/materialcompiler.cpp
Joshua Goins ca2c2c9d3d Move all engine-specific models, materials etc. to a new base directory
This is a huge change, and basically breaks everything (as per usual!)

First of, this includes stuff like shaders so anything involving those
are broken and then fixed. A new BuildAssets cmake file is added to
aid in running AssetCompiler, and it seems to work fine on the engine
base assets.

The File API will eventually be revamped to handle this new way of
organizing the files and domains will eventually be gotten rid of all
together since I probably will replace it with game directory
priorities. As it stands right now, there isn't a way to easily
replace say - render_options.cfg with your own game-specific version.

Apple builds are probably broken by this commit (since I'm moving
around content and shader directories) to be fixed later.
2022-05-21 18:28:48 -04:00

380 lines
16 KiB
C++
Executable file

#include "materialcompiler.hpp"
#include "file.hpp"
#include "log.hpp"
#include "engine.hpp"
#include "string_utils.hpp"
#include "shadercompiler.hpp"
#include "renderer.hpp"
ShaderSource get_shader(const std::string& filename, bool skinned, bool cubemap) {
auto shader_file = prism::open_file(prism::base_domain / filename);
if(!shader_file.has_value()) {
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
const std::string vertex_path = fmt::format("{}.glsl", createInfo.shaders.vertex_src.as_path().string());
if (positions_only)
createInfo.label += " (shadow)";
if (cubemap)
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}
};
} else {
createInfo.vertex_input.inputs = {
{position_buffer_index, sizeof(prism::float3)},
{normal_buffer_index, sizeof(prism::float3)},
{texcoord_buffer_index, sizeof(prism::float2)},
{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},
{texcoord_buffer_index, 2, 0, GFXVertexFormat::FLOAT2},
{tangent_buffer_index, 3, 0, GFXVertexFormat::FLOAT3},
{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)";
// take vertex src
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},
{bone_buffer_index, 5, offsetof(BoneVertexData, weights), GFXVertexFormat::FLOAT4}
};
} else {
createInfo.vertex_input.inputs = {
{position_buffer_index, sizeof(prism::float3)},
{normal_buffer_index, sizeof(prism::float3)},
{texcoord_buffer_index, sizeof(prism::float2)},
{tangent_buffer_index, sizeof(prism::float3)},
{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},
{texcoord_buffer_index, 2, 0, GFXVertexFormat::FLOAT2},
{tangent_buffer_index, 3, 0, GFXVertexFormat::FLOAT3},
{bitangent_buffer_index, 4, 0, GFXVertexFormat::FLOAT3},
{bone_buffer_index, 5, offsetof(BoneVertexData, ids), GFXVertexFormat::INT4},
{bone_buffer_index, 6, offsetof(BoneVertexData, weights), GFXVertexFormat::FLOAT4},
};
}
return engine->get_gfx()->create_graphics_pipeline(createInfo);
}
std::tuple<GFXPipeline*, GFXPipeline*> 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 =
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;
auto src = fmt::memory_buffer();
switch(render_options.shadow_filter) {
case ShadowFilter::None:
format_to(std::back_inserter(src), "#define SHADOW_FILTER_NONE\n");
break;
case ShadowFilter::PCF:
format_to(std::back_inserter(src), "#define SHADOW_FILTER_PCF\n");
break;
case ShadowFilter::PCSS:
format_to(std::back_inserter(src), "#define SHADOW_FILTER_PCSS\n");
break;
}
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) {
format_to(std::back_inserter(src),
R"(#define POINT_SHADOWS_SUPPORTED
layout (binding = 3) uniform samplerCubeArray point_shadow;
)");
}
format_to(std::back_inserter(src), struct_info);
if(use_ibl) {
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;
)");
}
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;
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;
format_to(std::back_inserter(src), "layout(binding = {}) uniform sampler2D normalTexture;\n", sampler_index++);
}
if(use_ibl) {
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) {
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) {
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;
}}
)");
}
format_to(std::back_inserter(src), "void main() {{\n");
if(material.colorProperty.type == DataType::Vector3) {
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) {
format_to(std::back_inserter(src), "vec3 Color = texture(colorTexture, in_uv).rgb;\n");
} else if(material.colorProperty.type == DataType::Float) {
format_to(std::back_inserter(src),"vec3 Color = vec3({}});\n", material.colorProperty.float_value);
}
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) {
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 {
format_to(std::back_inserter(src), "vec3 final_normal = in_normal;\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) {
format_to(std::back_inserter(src), "light_info.radiance *= calculate_normal_lighting(normalTexture, final_normal, light_info.direction);\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) {
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 {
format_to(std::back_inserter(src), "frag_output = vec4(Lo, 1.0);\n");
}
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());
}