Skip to content

Commit 6e8adb4

Browse files
committed
implementing Spec-Gloss workflow and transmission materials.
1 parent 276d1cb commit 6e8adb4

File tree

9 files changed

+537
-79
lines changed

9 files changed

+537
-79
lines changed

attachments/simple_engine/model_loader.cpp

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,120 @@ bool ModelLoader::ParseGLTF(const std::string& filename, Model* model) {
230230
material->emissiveStrength = 1.0f * light_scale;
231231
}
232232

233+
// Alpha mode / cutoff
234+
material->alphaMode = gltfMaterial.alphaMode.empty() ? std::string("OPAQUE") : gltfMaterial.alphaMode;
235+
material->alphaCutoff = static_cast<float>(gltfMaterial.alphaCutoff);
236+
237+
// Transmission (KHR_materials_transmission)
238+
auto transIt = gltfMaterial.extensions.find("KHR_materials_transmission");
239+
if (transIt != gltfMaterial.extensions.end()) {
240+
const tinygltf::Value& ext = transIt->second;
241+
if (ext.Has("transmissionFactor") && ext.Get("transmissionFactor").IsNumber()) {
242+
material->transmissionFactor = static_cast<float>(ext.Get("transmissionFactor").Get<double>());
243+
}
244+
}
245+
246+
// Specular-Glossiness (KHR_materials_pbrSpecularGlossiness)
247+
auto sgIt = gltfMaterial.extensions.find("KHR_materials_pbrSpecularGlossiness");
248+
if (sgIt != gltfMaterial.extensions.end()) {
249+
const tinygltf::Value& ext = sgIt->second;
250+
material->useSpecularGlossiness = true;
251+
// diffuseFactor -> albedo and alpha
252+
if (ext.Has("diffuseFactor") && ext.Get("diffuseFactor").IsArray()) {
253+
const auto& arr = ext.Get("diffuseFactor").Get<tinygltf::Value::Array>();
254+
if (arr.size() >= 3) {
255+
material->albedo = glm::vec3(
256+
arr[0].IsNumber() ? static_cast<float>(arr[0].Get<double>()) : material->albedo.r,
257+
arr[1].IsNumber() ? static_cast<float>(arr[1].Get<double>()) : material->albedo.g,
258+
arr[2].IsNumber() ? static_cast<float>(arr[2].Get<double>()) : material->albedo.b
259+
);
260+
if (arr.size() >= 4 && arr[3].IsNumber()) {
261+
material->alpha = static_cast<float>(arr[3].Get<double>());
262+
}
263+
}
264+
}
265+
// specularFactor (vec3)
266+
if (ext.Has("specularFactor") && ext.Get("specularFactor").IsArray()) {
267+
const auto& arr = ext.Get("specularFactor").Get<tinygltf::Value::Array>();
268+
if (arr.size() >= 3) {
269+
material->specularFactor = glm::vec3(
270+
arr[0].IsNumber() ? static_cast<float>(arr[0].Get<double>()) : material->specularFactor.r,
271+
arr[1].IsNumber() ? static_cast<float>(arr[1].Get<double>()) : material->specularFactor.g,
272+
arr[2].IsNumber() ? static_cast<float>(arr[2].Get<double>()) : material->specularFactor.b
273+
);
274+
}
275+
}
276+
// glossinessFactor (float)
277+
if (ext.Has("glossinessFactor") && ext.Get("glossinessFactor").IsNumber()) {
278+
material->glossinessFactor = static_cast<float>(ext.Get("glossinessFactor").Get<double>());
279+
}
280+
281+
// Load diffuseTexture into albedoTexturePath if present
282+
if (ext.Has("diffuseTexture") && ext.Get("diffuseTexture").IsObject()) {
283+
const auto& diffObj = ext.Get("diffuseTexture");
284+
if (diffObj.Has("index") && diffObj.Get("index").IsInt()) {
285+
int texIndex = diffObj.Get("index").Get<int>();
286+
if (texIndex >= 0 && texIndex < static_cast<int>(gltfModel.textures.size())) {
287+
const auto& texture = gltfModel.textures[texIndex];
288+
int imageIndex = -1;
289+
if (texture.source >= 0 && texture.source < static_cast<int>(gltfModel.images.size())) {
290+
imageIndex = texture.source;
291+
} else {
292+
auto extBasis = texture.extensions.find("KHR_texture_basisu");
293+
if (extBasis != texture.extensions.end()) {
294+
const tinygltf::Value &e = extBasis->second;
295+
if (e.Has("source") && e.Get("source").IsInt()) {
296+
int src = e.Get("source").Get<int>();
297+
if (src >= 0 && src < static_cast<int>(gltfModel.images.size())) imageIndex = src;
298+
}
299+
}
300+
}
301+
if (imageIndex >= 0) {
302+
const auto& image = gltfModel.images[imageIndex];
303+
std::string textureId = "gltf_baseColor_" + std::to_string(texIndex);
304+
if (!image.image.empty()) {
305+
if (renderer->LoadTextureFromMemory(textureId, image.image.data(), image.width, image.height, image.component)) {
306+
material->albedoTexturePath = textureId;
307+
}
308+
} else if (!image.uri.empty()) {
309+
std::vector<uint8_t> data; int w=0,h=0,c=0;
310+
std::string filePath = baseTexturePath + image.uri;
311+
if (LoadKTX2FileToRGBA(filePath, data, w, h, c) && renderer->LoadTextureFromMemory(textureId, data.data(), w, h, c)) {
312+
material->albedoTexturePath = textureId;
313+
}
314+
}
315+
}
316+
}
317+
}
318+
}
319+
// Load specularGlossinessTexture into specGlossTexturePath and mirror to metallicRoughnessTexturePath (binding 2)
320+
if (ext.Has("specularGlossinessTexture") && ext.Get("specularGlossinessTexture").IsObject()) {
321+
const auto& sgObj = ext.Get("specularGlossinessTexture");
322+
if (sgObj.Has("index") && sgObj.Get("index").IsInt()) {
323+
int texIndex = sgObj.Get("index").Get<int>();
324+
if (texIndex >= 0 && texIndex < static_cast<int>(gltfModel.textures.size())) {
325+
const auto& texture = gltfModel.textures[texIndex];
326+
if (texture.source >= 0 && texture.source < static_cast<int>(gltfModel.images.size())) {
327+
std::string textureId = "gltf_specGloss_" + std::to_string(texIndex);
328+
const auto& image = gltfModel.images[texture.source];
329+
if (!image.image.empty()) {
330+
if (renderer->LoadTextureFromMemory(textureId, image.image.data(), image.width, image.height, image.component)) {
331+
material->specGlossTexturePath = textureId;
332+
material->metallicRoughnessTexturePath = textureId; // reuse binding 2
333+
}
334+
} else if (!image.uri.empty()) {
335+
std::vector<uint8_t> data; int w=0,h=0,c=0;
336+
std::string filePath = baseTexturePath + image.uri;
337+
if (LoadKTX2FileToRGBA(filePath, data, w, h, c) && renderer->LoadTextureFromMemory(textureId, data.data(), w, h, c)) {
338+
material->specGlossTexturePath = textureId;
339+
material->metallicRoughnessTexturePath = textureId; // reuse binding 2
340+
}
341+
}
342+
}
343+
}
344+
}
345+
}
346+
}
233347

234348
// Extract texture information and load embedded texture data
235349
if (gltfMaterial.pbrMetallicRoughness.baseColorTexture.index >= 0) {

attachments/simple_engine/model_loader.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,25 @@ class Material {
2525

2626
[[nodiscard]] const std::string& GetName() const { return name; }
2727

28-
// PBR properties
28+
// PBR properties (Metallic-Roughness default)
2929
glm::vec3 albedo = glm::vec3(1.0f);
3030
float metallic = 0.0f;
3131
float roughness = 1.0f;
3232
float ao = 1.0f;
3333
glm::vec3 emissive = glm::vec3(0.0f);
3434
float emissiveStrength = 1.0f; // KHR_materials_emissive_strength extension
3535
float alpha = 1.0f; // Base color alpha (from MR baseColorFactor or SpecGloss diffuseFactor)
36+
float transmissionFactor = 0.0f; // KHR_materials_transmission: 0=opaque, 1=fully transmissive
37+
38+
// Specular-Glossiness workflow (KHR_materials_pbrSpecularGlossiness)
39+
bool useSpecularGlossiness = false;
40+
glm::vec3 specularFactor = glm::vec3(0.04f);
41+
float glossinessFactor = 1.0f;
42+
std::string specGlossTexturePath; // Stored separately; also mirrored to metallicRoughnessTexturePath for binding 2
43+
44+
// Alpha handling (glTF alphaMode and cutoff)
45+
std::string alphaMode = "OPAQUE"; // "OPAQUE", "MASK", or "BLEND"
46+
float alphaCutoff = 0.5f; // Used when alphaMode == MASK
3647

3748
// Texture paths for PBR materials
3849
std::string albedoTexturePath;

attachments/simple_engine/pipeline.cpp

Lines changed: 30 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include "pipeline.h"
2+
#include "mesh_component.h"
23
#include <iostream>
34
#include <fstream>
45

@@ -52,7 +53,7 @@ bool Pipeline::createDescriptorSetLayout() {
5253
bool Pipeline::createPBRDescriptorSetLayout() {
5354
try {
5455
// Create descriptor set layout bindings for PBR shader
55-
std::array<vk::DescriptorSetLayoutBinding, 6> bindings = {
56+
std::array<vk::DescriptorSetLayoutBinding, 7> bindings = {
5657
// Binding 0: Uniform buffer (UBO)
5758
vk::DescriptorSetLayoutBinding{
5859
.binding = 0,
@@ -100,6 +101,14 @@ bool Pipeline::createPBRDescriptorSetLayout() {
100101
.descriptorCount = 1,
101102
.stageFlags = vk::ShaderStageFlagBits::eFragment,
102103
.pImmutableSamplers = nullptr
104+
},
105+
// Binding 6: Light storage buffer (StructuredBuffer<LightData>)
106+
vk::DescriptorSetLayoutBinding{
107+
.binding = 6,
108+
.descriptorType = vk::DescriptorType::eStorageBuffer,
109+
.descriptorCount = 1,
110+
.stageFlags = vk::ShaderStageFlagBits::eFragment,
111+
.pImmutableSamplers = nullptr
103112
}
104113
};
105114

@@ -330,49 +339,28 @@ bool Pipeline::createPBRPipeline() {
330339

331340
vk::PipelineShaderStageCreateInfo shaderStages[] = {vertShaderStageInfo, fragShaderStageInfo};
332341

333-
// Define vertex binding description
334-
vk::VertexInputBindingDescription bindingDescription{
335-
.binding = 0,
336-
.stride = sizeof(float) * (3 + 3 + 2 + 4), // Position(3) + Normal(3) + UV(2) + Tangent(4)
337-
.inputRate = vk::VertexInputRate::eVertex
338-
};
339-
340-
// Define vertex attribute descriptions
341-
std::array<vk::VertexInputAttributeDescription, 4> attributeDescriptions = {
342-
// Position attribute
343-
vk::VertexInputAttributeDescription{
344-
.location = 0,
345-
.binding = 0,
346-
.format = vk::Format::eR32G32B32Sfloat,
347-
.offset = 0
348-
},
349-
// Normal attribute
350-
vk::VertexInputAttributeDescription{
351-
.location = 1,
352-
.binding = 0,
353-
.format = vk::Format::eR32G32B32Sfloat,
354-
.offset = sizeof(float) * 3
355-
},
356-
// UV attribute
357-
vk::VertexInputAttributeDescription{
358-
.location = 2,
359-
.binding = 0,
360-
.format = vk::Format::eR32G32Sfloat,
361-
.offset = sizeof(float) * 6
362-
},
363-
// Tangent attribute
364-
vk::VertexInputAttributeDescription{
365-
.location = 3,
366-
.binding = 0,
367-
.format = vk::Format::eR32G32B32A32Sfloat,
368-
.offset = sizeof(float) * 8
369-
}
370-
};
342+
// Define vertex and instance binding descriptions using MeshComponent layouts
343+
auto vertexBinding = Vertex::getBindingDescription();
344+
auto instanceBinding = InstanceData::getBindingDescription();
345+
std::array<vk::VertexInputBindingDescription, 2> bindingDescriptions = { vertexBinding, instanceBinding };
346+
347+
// Define vertex and instance attribute descriptions
348+
auto vertexAttrArray = Vertex::getAttributeDescriptions();
349+
auto instanceAttrArray = InstanceData::getAttributeDescriptions();
350+
std::array<vk::VertexInputAttributeDescription, 11> attributeDescriptions{};
351+
// Copy vertex attributes (0..3)
352+
for (size_t i = 0; i < vertexAttrArray.size(); ++i) {
353+
attributeDescriptions[i] = vertexAttrArray[i];
354+
}
355+
// Copy instance attributes (4..10)
356+
for (size_t i = 0; i < instanceAttrArray.size(); ++i) {
357+
attributeDescriptions[vertexAttrArray.size() + i] = instanceAttrArray[i];
358+
}
371359

372360
// Create vertex input info
373361
vk::PipelineVertexInputStateCreateInfo vertexInputInfo{
374-
.vertexBindingDescriptionCount = 1,
375-
.pVertexBindingDescriptions = &bindingDescription,
362+
.vertexBindingDescriptionCount = static_cast<uint32_t>(bindingDescriptions.size()),
363+
.pVertexBindingDescriptions = bindingDescriptions.data(),
376364
.vertexAttributeDescriptionCount = static_cast<uint32_t>(attributeDescriptions.size()),
377365
.pVertexAttributeDescriptions = attributeDescriptions.data()
378366
};
@@ -425,7 +413,7 @@ bool Pipeline::createPBRPipeline() {
425413
.sampleShadingEnable = VK_FALSE,
426414
.minSampleShading = 1.0f,
427415
.pSampleMask = nullptr,
428-
.alphaToCoverageEnable = VK_FALSE,
416+
.alphaToCoverageEnable = VK_TRUE,
429417
.alphaToOneEnable = VK_FALSE
430418
};
431419

attachments/simple_engine/pipeline.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ struct MaterialProperties {
2727
alignas(4) float alphaMaskCutoff;
2828
alignas(16) glm::vec3 emissiveFactor; // Emissive factor for HDR emissive sources
2929
alignas(4) float emissiveStrength; // KHR_materials_emissive_strength extension
30+
alignas(4) float transmissionFactor; // KHR_materials_transmission
31+
alignas(4) int useSpecGlossWorkflow; // 1 if using KHR_materials_pbrSpecularGlossiness
32+
alignas(4) float glossinessFactor; // SpecGloss glossiness scalar
33+
alignas(16) glm::vec3 specularFactor; // SpecGloss specular color factor
3034
};
3135

3236
/**

attachments/simple_engine/renderer.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,10 @@ struct MaterialProperties {
9696
alignas(4) float alphaMaskCutoff;
9797
alignas(16) glm::vec3 emissiveFactor; // Emissive factor for HDR emissive sources
9898
alignas(4) float emissiveStrength; // KHR_materials_emissive_strength extension
99+
alignas(4) float transmissionFactor; // KHR_materials_transmission
100+
alignas(4) int useSpecGlossWorkflow; // 1 if using KHR_materials_pbrSpecularGlossiness
101+
alignas(4) float glossinessFactor; // SpecGloss glossiness scalar
102+
alignas(16) glm::vec3 specularFactor; // SpecGloss specular color factor
99103
};
100104

101105
/**
@@ -460,6 +464,7 @@ class Renderer {
460464
vk::raii::Pipeline graphicsPipeline = nullptr;
461465
vk::raii::PipelineLayout pbrPipelineLayout = nullptr;
462466
vk::raii::Pipeline pbrGraphicsPipeline = nullptr;
467+
vk::raii::Pipeline pbrBlendGraphicsPipeline = nullptr;
463468
vk::raii::PipelineLayout lightingPipelineLayout = nullptr;
464469
vk::raii::Pipeline lightingPipeline = nullptr;
465470

attachments/simple_engine/renderer_pipelines.cpp

Lines changed: 56 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -422,15 +422,6 @@ bool Renderer::createPBRPipeline() {
422422

423423
pbrPipelineLayout = vk::raii::PipelineLayout(device, pipelineLayoutInfo);
424424

425-
// Enable alpha blending for translucency (glass)
426-
colorBlendAttachment.blendEnable = VK_TRUE;
427-
colorBlendAttachment.srcColorBlendFactor = vk::BlendFactor::eSrcAlpha;
428-
colorBlendAttachment.dstColorBlendFactor = vk::BlendFactor::eOneMinusSrcAlpha;
429-
colorBlendAttachment.colorBlendOp = vk::BlendOp::eAdd;
430-
colorBlendAttachment.srcAlphaBlendFactor = vk::BlendFactor::eOne;
431-
colorBlendAttachment.dstAlphaBlendFactor = vk::BlendFactor::eOneMinusSrcAlpha;
432-
colorBlendAttachment.alphaBlendOp = vk::BlendOp::eAdd;
433-
434425
// Create pipeline rendering info
435426
vk::Format depthFormat = findDepthFormat();
436427

@@ -444,8 +435,19 @@ bool Renderer::createPBRPipeline() {
444435
.stencilAttachmentFormat = vk::Format::eUndefined
445436
};
446437

447-
// Create a graphics pipeline
448-
vk::GraphicsPipelineCreateInfo pipelineInfo{
438+
// 1) Opaque PBR pipeline (no blending, depth writes enabled)
439+
vk::PipelineColorBlendAttachmentState opaqueBlendAttachment = colorBlendAttachment;
440+
opaqueBlendAttachment.blendEnable = VK_FALSE;
441+
vk::PipelineColorBlendStateCreateInfo colorBlendingOpaque{
442+
.logicOpEnable = VK_FALSE,
443+
.logicOp = vk::LogicOp::eCopy,
444+
.attachmentCount = 1,
445+
.pAttachments = &opaqueBlendAttachment
446+
};
447+
vk::PipelineDepthStencilStateCreateInfo depthStencilOpaque = depthStencil;
448+
depthStencilOpaque.depthWriteEnable = VK_TRUE;
449+
450+
vk::GraphicsPipelineCreateInfo opaquePipelineInfo{
449451
.sType = vk::StructureType::eGraphicsPipelineCreateInfo,
450452
.pNext = &pbrPipelineRenderingCreateInfo,
451453
.flags = vk::PipelineCreateFlags{},
@@ -456,17 +458,57 @@ bool Renderer::createPBRPipeline() {
456458
.pViewportState = &viewportState,
457459
.pRasterizationState = &rasterizer,
458460
.pMultisampleState = &multisampling,
459-
.pDepthStencilState = &depthStencil,
460-
.pColorBlendState = &colorBlending,
461+
.pDepthStencilState = &depthStencilOpaque,
462+
.pColorBlendState = &colorBlendingOpaque,
463+
.pDynamicState = &dynamicState,
464+
.layout = *pbrPipelineLayout,
465+
.renderPass = nullptr,
466+
.subpass = 0,
467+
.basePipelineHandle = nullptr,
468+
.basePipelineIndex = -1
469+
};
470+
pbrGraphicsPipeline = vk::raii::Pipeline(device, nullptr, opaquePipelineInfo);
471+
472+
// 2) Blended PBR pipeline (alpha blending, depth writes disabled for translucency)
473+
vk::PipelineColorBlendAttachmentState blendedAttachment = colorBlendAttachment;
474+
blendedAttachment.blendEnable = VK_TRUE;
475+
blendedAttachment.srcColorBlendFactor = vk::BlendFactor::eSrcAlpha;
476+
blendedAttachment.dstColorBlendFactor = vk::BlendFactor::eOneMinusSrcAlpha;
477+
blendedAttachment.colorBlendOp = vk::BlendOp::eAdd;
478+
blendedAttachment.srcAlphaBlendFactor = vk::BlendFactor::eOne;
479+
blendedAttachment.dstAlphaBlendFactor = vk::BlendFactor::eOneMinusSrcAlpha;
480+
blendedAttachment.alphaBlendOp = vk::BlendOp::eAdd;
481+
vk::PipelineColorBlendStateCreateInfo colorBlendingBlended{
482+
.logicOpEnable = VK_FALSE,
483+
.logicOp = vk::LogicOp::eCopy,
484+
.attachmentCount = 1,
485+
.pAttachments = &blendedAttachment
486+
};
487+
vk::PipelineDepthStencilStateCreateInfo depthStencilBlended = depthStencil;
488+
depthStencilBlended.depthWriteEnable = VK_FALSE;
489+
490+
vk::GraphicsPipelineCreateInfo blendedPipelineInfo{
491+
.sType = vk::StructureType::eGraphicsPipelineCreateInfo,
492+
.pNext = &pbrPipelineRenderingCreateInfo,
493+
.flags = vk::PipelineCreateFlags{},
494+
.stageCount = 2,
495+
.pStages = shaderStages,
496+
.pVertexInputState = &vertexInputInfo,
497+
.pInputAssemblyState = &inputAssembly,
498+
.pViewportState = &viewportState,
499+
.pRasterizationState = &rasterizer,
500+
.pMultisampleState = &multisampling,
501+
.pDepthStencilState = &depthStencilBlended,
502+
.pColorBlendState = &colorBlendingBlended,
461503
.pDynamicState = &dynamicState,
462504
.layout = *pbrPipelineLayout,
463505
.renderPass = nullptr,
464506
.subpass = 0,
465507
.basePipelineHandle = nullptr,
466508
.basePipelineIndex = -1
467509
};
510+
pbrBlendGraphicsPipeline = vk::raii::Pipeline(device, nullptr, blendedPipelineInfo);
468511

469-
pbrGraphicsPipeline = vk::raii::Pipeline(device, nullptr, pipelineInfo);
470512
return true;
471513
} catch (const std::exception& e) {
472514
std::cerr << "Failed to create PBR pipeline: " << e.what() << std::endl;

0 commit comments

Comments
 (0)