Skip to content

Commit

Permalink
Day 26: Better material integration!
Browse files Browse the repository at this point in the history
Material data is now in a separate buffer so that extra material
parameters do not bloat MeshDraw structure (which regresses
culling/rendering performance when we have lots of draw calls); we now
preserve various material related factors from glTF data, and integrate
them into shading a little better with more sRGB correct results and
nicer specular.

We will need to rework BRDF integration completely in the future,
ideally switching to metal/roughness/GGX et al, and also there's some
normal banding issues that remain that we should investigate.
  • Loading branch information
zeux committed Dec 8, 2024
1 parent 9d9ea4c commit 2d9fdc6
Show file tree
Hide file tree
Showing 7 changed files with 129 additions and 58 deletions.
20 changes: 15 additions & 5 deletions src/niagara.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -713,9 +713,14 @@ int main(int argc, const char** argv)
createBuffer(scratch, device, memoryProperties, 128 * 1024 * 1024, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);

Geometry geometry;
std::vector<Material> materials;
std::vector<MeshDraw> draws;
std::vector<std::string> texturePaths;

// material index 0 is always dummy
materials.resize(1);
materials[0].diffuseFactor = vec4(1);

Camera camera;
camera.position = { 0.0f, 0.0f, 0.0f };
camera.orientation = { 0.0f, 0.0f, 0.0f, 1.0f };
Expand All @@ -731,7 +736,7 @@ int main(int argc, const char** argv)
const char* ext = strrchr(argv[1], '.');
if (ext && (strcmp(ext, ".gltf") == 0 || strcmp(ext, ".glb") == 0))
{
if (!loadScene(geometry, draws, texturePaths, camera, sunDirection, argv[1], meshShadingSupported, fastMode))
if (!loadScene(geometry, materials, draws, texturePaths, camera, sunDirection, argv[1], meshShadingSupported, fastMode))
{
printf("Error: scene %s failed to load\n", argv[1]);
return 1;
Expand Down Expand Up @@ -858,7 +863,10 @@ int main(int argc, const char** argv)
: 0;

Buffer mb = {};
createBuffer(mb, device, memoryProperties, 128 * 1024 * 1024, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
createBuffer(mb, device, memoryProperties, 4 * 1024 * 1024, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);

Buffer mtb = {};
createBuffer(mtb, device, memoryProperties, 4 * 1024 * 1024, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);

Buffer vb = {};
createBuffer(vb, device, memoryProperties, 128 * 1024 * 1024, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | raytracingBufferFlags, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
Expand All @@ -875,6 +883,7 @@ int main(int argc, const char** argv)
}

uploadBuffer(device, commandPool, commandBuffer, queue, mb, scratch, geometry.meshes.data(), geometry.meshes.size() * sizeof(Mesh));
uploadBuffer(device, commandPool, commandBuffer, queue, mtb, scratch, materials.data(), materials.size() * sizeof(Material));

uploadBuffer(device, commandPool, commandBuffer, queue, vb, scratch, geometry.vertices.data(), geometry.vertices.size() * sizeof(Vertex));
uploadBuffer(device, commandPool, commandBuffer, queue, ib, scratch, geometry.indices.data(), geometry.indices.size() * sizeof(uint32_t));
Expand Down Expand Up @@ -1329,7 +1338,7 @@ int main(int argc, const char** argv)
{
vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, postPass >= 1 ? clusterpostPipeline : clusterPipeline);

DescriptorInfo descriptors[] = { dcb.buffer, db.buffer, mlb.buffer, mdb.buffer, vb.buffer, cib.buffer, DescriptorInfo(), textureSampler };
DescriptorInfo descriptors[] = { dcb.buffer, db.buffer, mlb.buffer, mdb.buffer, vb.buffer, cib.buffer, DescriptorInfo(), textureSampler, mtb.buffer };
vkCmdPushDescriptorSetWithTemplateKHR(commandBuffer, clusterProgram.updateTemplate, clusterProgram.layout, 0, descriptors);

vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, clusterProgram.layout, 1, 1, &textureSet.second, 0, nullptr);
Expand All @@ -1343,7 +1352,7 @@ int main(int argc, const char** argv)
: meshtaskPipeline);

DescriptorInfo pyramidDesc(depthSampler, depthPyramid.imageView, VK_IMAGE_LAYOUT_GENERAL);
DescriptorInfo descriptors[] = { dcb.buffer, db.buffer, mlb.buffer, mdb.buffer, vb.buffer, mvb.buffer, pyramidDesc, textureSampler };
DescriptorInfo descriptors[] = { dcb.buffer, db.buffer, mlb.buffer, mdb.buffer, vb.buffer, mvb.buffer, pyramidDesc, textureSampler, mtb.buffer };
vkCmdPushDescriptorSetWithTemplateKHR(commandBuffer, meshtaskProgram.updateTemplate, meshtaskProgram.layout, 0, descriptors);

vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, meshtaskProgram.layout, 1, 1, &textureSet.second, 0, nullptr);
Expand All @@ -1355,7 +1364,7 @@ int main(int argc, const char** argv)
{
vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, postPass >= 1 ? meshpostPipeline : meshPipeline);

DescriptorInfo descriptors[] = { dcb.buffer, db.buffer, vb.buffer, DescriptorInfo(), DescriptorInfo(), DescriptorInfo(), DescriptorInfo(), textureSampler };
DescriptorInfo descriptors[] = { dcb.buffer, db.buffer, vb.buffer, DescriptorInfo(), DescriptorInfo(), DescriptorInfo(), DescriptorInfo(), textureSampler, mtb.buffer };
vkCmdPushDescriptorSetWithTemplateKHR(commandBuffer, meshProgram.updateTemplate, meshProgram.layout, 0, descriptors);

vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, meshProgram.layout, 1, 1, &textureSet.second, 0, nullptr);
Expand Down Expand Up @@ -1624,6 +1633,7 @@ int main(int argc, const char** argv)
vkDestroyImageView(device, swapchainImageViews[i], 0);

destroyBuffer(mb, device);
destroyBuffer(mtb, device);

destroyBuffer(db, device);
destroyBuffer(dvb, device);
Expand Down
62 changes: 43 additions & 19 deletions src/scene.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,7 @@ static void loadVertices(std::vector<Vertex>& vertices, const cgltf_primitive& p
}
}

bool loadScene(Geometry& geometry, std::vector<MeshDraw>& draws, std::vector<std::string>& texturePaths, Camera& camera, vec3& sunDirection, const char* path, bool buildMeshlets, bool fast)
bool loadScene(Geometry& geometry, std::vector<Material>& materials, std::vector<MeshDraw>& draws, std::vector<std::string>& texturePaths, Camera& camera, vec3& sunDirection, const char* path, bool buildMeshlets, bool fast)
{
clock_t timer = clock();

Expand Down Expand Up @@ -396,6 +396,9 @@ bool loadScene(Geometry& geometry, std::vector<MeshDraw>& draws, std::vector<std

assert(primitiveMaterials.size() + firstMeshOffset == geometry.meshes.size());

size_t materialOffset = materials.size();
assert(materialOffset > 0); // index 0 = dummy materials

for (size_t i = 0; i < data->nodes_count; ++i)
{
const cgltf_node* node = &data->nodes[i];
Expand Down Expand Up @@ -424,24 +427,7 @@ bool loadScene(Geometry& geometry, std::vector<MeshDraw>& draws, std::vector<std

cgltf_material* material = primitiveMaterials[range.first + j - firstMeshOffset];

draw.albedoTexture =
material && material->pbr_metallic_roughness.base_color_texture.texture
? 1 + int(cgltf_texture_index(data, material->pbr_metallic_roughness.base_color_texture.texture))
: material && material->pbr_specular_glossiness.diffuse_texture.texture
? 1 + int(cgltf_texture_index(data, material->pbr_specular_glossiness.diffuse_texture.texture))
: 0;
draw.normalTexture =
material && material->normal_texture.texture
? 1 + int(cgltf_texture_index(data, material->normal_texture.texture))
: 0;
draw.specularTexture =
material && material->pbr_specular_glossiness.specular_glossiness_texture.texture
? 1 + int(cgltf_texture_index(data, material->pbr_specular_glossiness.specular_glossiness_texture.texture))
: 0;
draw.emissiveTexture =
material && material->emissive_texture.texture
? 1 + int(cgltf_texture_index(data, material->emissive_texture.texture))
: 0;
draw.materialIndex = material ? materialOffset + int(cgltf_material_index(data, material)) : 0;

if (material && material->alpha_mode != cgltf_alpha_mode_opaque)
draw.postPass = 1;
Expand Down Expand Up @@ -476,6 +462,44 @@ bool loadScene(Geometry& geometry, std::vector<MeshDraw>& draws, std::vector<std
}
}

for (size_t i = 0; i < data->materials_count; ++i)
{
cgltf_material* material = &data->materials[i];
Material mat = {};

mat.diffuseFactor = vec4(1);

if (material->has_pbr_metallic_roughness)
{
if (material->pbr_metallic_roughness.base_color_texture.texture)
mat.albedoTexture = 1 + int(cgltf_texture_index(data, material->pbr_metallic_roughness.base_color_texture.texture));

mat.diffuseFactor = vec4(material->pbr_metallic_roughness.base_color_factor[0], material->pbr_metallic_roughness.base_color_factor[1], material->pbr_metallic_roughness.base_color_factor[2], material->pbr_metallic_roughness.base_color_factor[3]);
}
else if (material->has_pbr_specular_glossiness)
{
if (material->pbr_specular_glossiness.diffuse_texture.texture)
mat.albedoTexture = 1 + int(cgltf_texture_index(data, material->pbr_specular_glossiness.diffuse_texture.texture));

mat.diffuseFactor = vec4(material->pbr_specular_glossiness.diffuse_factor[0], material->pbr_specular_glossiness.diffuse_factor[1], material->pbr_specular_glossiness.diffuse_factor[2], material->pbr_specular_glossiness.diffuse_factor[3]);

if (material->pbr_specular_glossiness.specular_glossiness_texture.texture)
mat.specularTexture = 1 + int(cgltf_texture_index(data, material->pbr_specular_glossiness.specular_glossiness_texture.texture));

mat.specularFactor = vec4(material->pbr_specular_glossiness.specular_factor[0], material->pbr_specular_glossiness.specular_factor[1], material->pbr_specular_glossiness.specular_factor[2], material->pbr_specular_glossiness.glossiness_factor);
}

if (material->normal_texture.texture)
mat.normalTexture = 1 + int(cgltf_texture_index(data, material->normal_texture.texture));

if (material->emissive_texture.texture)
mat.emissiveTexture = 1 + int(cgltf_texture_index(data, material->emissive_texture.texture));

mat.emissiveFactor = vec3(material->emissive_factor[0], material->emissive_factor[1], material->emissive_factor[2]);

materials.push_back(mat);
}

for (size_t i = 0; i < data->textures_count; ++i)
{
cgltf_texture* texture = &data->textures[i];
Expand Down
21 changes: 14 additions & 7 deletions src/scene.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,18 @@ struct alignas(16) Meshlet
uint8_t triangleCount;
};

struct alignas(16) Material
{
int albedoTexture;
int normalTexture;
int specularTexture;
int emissiveTexture;

vec4 diffuseFactor;
vec4 specularFactor;
vec3 emissiveFactor;
};

struct alignas(16) MeshDraw
{
vec3 position;
Expand All @@ -29,12 +41,7 @@ struct alignas(16) MeshDraw
uint32_t meshIndex;
uint32_t meshletVisibilityOffset;
uint32_t postPass;
uint32_t flags;

int albedoTexture;
int normalTexture;
int specularTexture;
int emissiveTexture;
uint32_t materialIndex;
};

struct Vertex
Expand Down Expand Up @@ -84,4 +91,4 @@ struct Camera
};

bool loadMesh(Geometry& geometry, const char* path, bool buildMeshlets, bool fast = false);
bool loadScene(Geometry& geometry, std::vector<MeshDraw>& draws, std::vector<std::string>& texturePaths, Camera& camera, vec3& sunDirection, const char* path, bool buildMeshlets, bool fast = false);
bool loadScene(Geometry& geometry, std::vector<Material>& materials, std::vector<MeshDraw>& draws, std::vector<std::string>& texturePaths, Camera& camera, vec3& sunDirection, const char* path, bool buildMeshlets, bool fast = false);
7 changes: 7 additions & 0 deletions src/shaders/math.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,10 @@ vec4 fromsrgb(vec4 c)
{
return vec4(pow(c.xyz, vec3(2.2)), c.w);
}

// Gradient noise from Jorge Jimenez's presentation:
// http://www.iryoku.com/next-generation-post-processing-in-call-of-duty-advanced-warfare
float gradientNoise(vec2 uv)
{
return fract(52.9829189 * fract(dot(uv, vec2(0.06711056, 0.00583715))));
}
38 changes: 23 additions & 15 deletions src/shaders/mesh.frag.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#extension GL_EXT_nonuniform_qualifier: require

#include "mesh.h"
#include "math.h"

layout (constant_id = 2) const int POST = 0;

Expand All @@ -31,6 +32,11 @@ layout(location = 4) in vec3 wpos;

layout(binding = 7) uniform sampler textureSampler;

layout(binding = 8) readonly buffer Materials
{
Material materials[];
};

layout(binding = 0, set = 1) uniform texture2D textures[];

#define SAMP(id) sampler2D(textures[nonuniformEXT(id)], textureSampler)
Expand All @@ -49,31 +55,33 @@ uint hash(uint a)
void main()
{
MeshDraw meshDraw = draws[drawId];
Material material = materials[meshDraw.materialIndex];

vec4 albedo = vec4(0.5f, 0.5f, 0.5f, 1);
if (meshDraw.albedoTexture > 0)
albedo = texture(SAMP(meshDraw.albedoTexture), uv);
vec4 albedo = material.diffuseFactor;
if (material.albedoTexture > 0)
albedo *= fromsrgb(texture(SAMP(material.albedoTexture), uv));

vec3 nmap = vec3(0, 0, 1);
if (meshDraw.normalTexture > 0)
nmap = texture(SAMP(meshDraw.normalTexture), uv).rgb * 2 - 1;
if (material.normalTexture > 0)
nmap = texture(SAMP(material.normalTexture), uv).rgb * 2 - 1;

vec4 specgloss = vec4(0.0f);
if (meshDraw.specularTexture > 0)
specgloss = texture(SAMP(meshDraw.specularTexture), uv);
vec4 specgloss = material.specularFactor;
if (material.specularTexture > 0)
specgloss *= fromsrgb(texture(SAMP(material.specularTexture), uv));

vec3 emissive = vec3(0.0f);
if (meshDraw.emissiveTexture > 0)
emissive = texture(SAMP(meshDraw.emissiveTexture), uv).rgb;
vec3 emissive = material.emissiveFactor;
if (material.emissiveTexture > 0)
emissive *= fromsrgb(texture(SAMP(material.emissiveTexture), uv).rgb);

vec3 bitangent = cross(normal, tangent.xyz) * tangent.w;

vec3 nrm = normalize(nmap.r * tangent.xyz + nmap.g * bitangent + nmap.b * normal);

// TODO: emissive encoding should support colored & HDR emissive
gbuffer[0] = vec4(albedo.rgb, emissive.r);
// TODO: specular glossiness encoding is missing; we should encode roughness+metalness somehow in gbuffer1.z and use oct encoding for normal
gbuffer[1] = vec4(nrm * 0.5 + 0.5, 0.0);
float emissivef = dot(emissive, vec3(0.3, 0.6, 0.1)) / (dot(albedo.rgb, vec3(0.3, 0.6, 0.1)) + 1e-3);

// TODO: reconstruct metalness from specular texture
gbuffer[0] = vec4(tosrgb(albedo).rgb, log2(1 + emissivef) / 5);
gbuffer[1] = vec4(encodeOct(nrm) * 0.5 + 0.5, specgloss.a, 0.0);

if (POST > 0 && albedo.a < 0.5)
discard;
Expand Down
19 changes: 13 additions & 6 deletions src/shaders/mesh.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,18 @@ struct Mesh
MeshLod lods[8];
};

struct Material
{
uint albedoTexture;
uint normalTexture;
uint specularTexture;
uint emissiveTexture;

vec4 diffuseFactor;
vec4 specularFactor;
vec3 emissiveFactor;
};

struct MeshDraw
{
vec3 position;
Expand All @@ -79,12 +91,7 @@ struct MeshDraw
uint meshIndex;
uint meshletVisibilityOffset;
uint postPass;
uint flags;

int albedoTexture;
int normalTexture;
int specularTexture;
int emissiveTexture;
uint materialIndex;
};

struct MeshDrawCommand
Expand Down
20 changes: 14 additions & 6 deletions src/shaders/shade.comp.glsl
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
#version 460

#extension GL_GOOGLE_include_directive: require

#include "math.h"

#define RAYTRACE 1

layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
Expand Down Expand Up @@ -39,9 +43,9 @@ void main()
vec4 gbuffer1 = texture(gbufferImage1, uv);
float depth = texture(depthImage, uv).r;

vec3 albedo = gbuffer0.rgb;
vec3 emissive = vec3(gbuffer0.a);
vec3 normal = gbuffer1.rgb * 2 - 1;
vec3 albedo = fromsrgb(gbuffer0.rgb);
vec3 emissive = albedo * (exp2(gbuffer0.a * 5) - 1);
vec3 normal = decodeOct(gbuffer1.rg * 2 - 1);

float ndotl = max(dot(normal, shadeData.sunDirection), 0.0);

Expand All @@ -52,7 +56,10 @@ void main()
vec3 view = normalize(shadeData.cameraPosition - wpos);
vec3 halfv = normalize(view + shadeData.sunDirection);
float ndoth = max(dot(normal, halfv), 0.0);
float specular = pow(ndoth, 64);
float gloss = gbuffer1.b;

// TODO: this is not the BRDF we want
float specular = pow(ndoth, mix(1, 64, gloss)) * gloss;

float shadow = 1;

Expand All @@ -66,7 +73,8 @@ void main()
shadow = (rayQueryGetIntersectionTypeEXT(rq, true) == gl_RayQueryCommittedIntersectionNoneEXT) ? 1.0 : 0.0;
#endif

vec3 outputColor = albedo.rgb * sqrt(ndotl * shadow + 0.05) + vec3(specular * shadow) + emissive;
vec3 outputColor = albedo.rgb * (ndotl * shadow + 0.05) + vec3(specular * shadow) + emissive;

imageStore(outImage, ivec2(pos), vec4(outputColor, 1.0));
float deband = gradientNoise(vec2(pos));
imageStore(outImage, ivec2(pos), vec4(tosrgb(outputColor) + (deband * 2 - 1) / 256, 1.0));
}

0 comments on commit 2d9fdc6

Please sign in to comment.