Skip to content

Commit

Permalink
[Data] The sheen attributes & texture are loaded
Browse files Browse the repository at this point in the history
- The OBJ & glTF loaders handle the factors & map if available

- The sheen isn't used for now but will be in the future

- Added a 2x2 RGB image with pixels of different colors, equal to the RGBA one without alpha
  • Loading branch information
Razakhel committed Feb 8, 2025
1 parent 2b6f11c commit 50e2b65
Show file tree
Hide file tree
Showing 18 changed files with 244 additions and 17 deletions.
2 changes: 1 addition & 1 deletion include/RaZ/Data/Mesh.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class Mesh : public Component {
Mesh(const Plane& plane, float width, float depth);
/// Creates a mesh from a Sphere.
/// \param sphere Sphere to create the mesh with.
/// \param subdivCount Amount of subdivisions (for an UV sphere, represents both the amount of vertical & horizontal lines to be created).
/// \param subdivCount Amount of subdivisions (for a UV sphere, represents both the amount of vertical & horizontal lines to be created).
/// \param type Type of the sphere mesh to create.
Mesh(const Sphere& sphere, uint32_t subdivCount, SphereMeshType type);
Mesh(const Triangle& triangle, const Vec2f& firstTexcoords, const Vec2f& secondTexcoords, const Vec2f& thirdTexcoords);
Expand Down
6 changes: 3 additions & 3 deletions include/RaZ/Math/Matrix.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,13 +72,13 @@ class Matrix {
template <typename... VecsTs>
static constexpr Matrix fromColumns(VecsTs&&... vecs) noexcept;

/// Transposed matrix computation.
/// Computes the transpose of the matrix.
/// \return Transposed matrix.
constexpr Matrix<T, H, W> transpose() const noexcept;
/// Determinant computation.
/// Computes the determinant of the matrix.
/// \return Matrix's determinant.
constexpr T computeDeterminant() const noexcept;
/// Inverse matrix computation.
/// Compute the inverse of the matrix.
/// \return Matrix's inverse.
constexpr Matrix inverse() const noexcept;
/// Recovers the values in the row at the given index.
Expand Down
2 changes: 2 additions & 0 deletions include/RaZ/Render/Material.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ static constexpr const char* Emissive = "uniMaterial.emissive"; ///< Emissive
// PBR attributes
static constexpr const char* Metallic = "uniMaterial.metallicFactor"; ///< Metallic factor.
static constexpr const char* Roughness = "uniMaterial.roughnessFactor"; ///< Roughness factor.
static constexpr const char* Sheen = "uniMaterial.sheenFactors"; ///< Sheen color (RGB) & roughness (A) factors.

// Legacy attributes
static constexpr const char* Ambient = "uniMaterial.ambient"; ///< Ambient factor.
Expand All @@ -44,6 +45,7 @@ static constexpr const char* Ambient = "uniMaterial.ambientMap"; ///< Ambien
static constexpr const char* Normal = "uniMaterial.normalMap"; ///< Normal map.
static constexpr const char* Metallic = "uniMaterial.metallicMap"; ///< Metalness map.
static constexpr const char* Roughness = "uniMaterial.roughnessMap"; ///< Roughness map.
static constexpr const char* Sheen = "uniMaterial.sheenMap"; ///< Sheen color (RGB) & roughness (A) map.

// Legacy textures
static constexpr const char* Specular = "uniMaterial.specularMap"; ///< Specular map
Expand Down
8 changes: 4 additions & 4 deletions include/RaZ/Render/ShaderProgram.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -366,11 +366,11 @@ class ShaderProgram {

class RenderShaderProgram final : public ShaderProgram {
public:
RenderShaderProgram() noexcept : ShaderProgram() {}
RenderShaderProgram(VertexShader&& vertShader, FragmentShader&& fragShader) noexcept
RenderShaderProgram() = default;
RenderShaderProgram(VertexShader&& vertShader, FragmentShader&& fragShader)
: RenderShaderProgram() { setShaders(std::move(vertShader), std::move(fragShader)); }
#if !defined(USE_OPENGL_ES)
RenderShaderProgram(VertexShader&& vertShader, FragmentShader&& fragShader, GeometryShader&& geomShader) noexcept
RenderShaderProgram(VertexShader&& vertShader, FragmentShader&& fragShader, GeometryShader&& geomShader)
: RenderShaderProgram() { setShaders(std::move(vertShader), std::move(geomShader), std::move(fragShader)); }
#endif

Expand Down Expand Up @@ -433,7 +433,7 @@ class RenderShaderProgram final : public ShaderProgram {
#if !defined(USE_WEBGL)
class ComputeShaderProgram final : public ShaderProgram {
public:
ComputeShaderProgram() : ShaderProgram() {}
ComputeShaderProgram() = default;
explicit ComputeShaderProgram(ComputeShader&& compShader) : ComputeShaderProgram() { setShader(std::move(compShader)); }

const ComputeShader& getShader() const noexcept { return m_compShader; }
Expand Down
1 change: 0 additions & 1 deletion include/RaZ/Render/SubmeshRenderer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

#include "RaZ/Data/Submesh.hpp"
#include "RaZ/Render/GraphicObjects.hpp"
#include "RaZ/Utils/Shape.hpp"

#include <functional>

Expand Down
88 changes: 85 additions & 3 deletions src/RaZ/Data/GltfLoad.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ std::vector<std::optional<Image>> loadImages(const std::vector<fastgltf::Image>&
std::vector<std::optional<Image>> loadedImages;
loadedImages.reserve(images.size());

static auto loadFailure = [&loadedImages] (const auto&) {
const auto loadFailure = [&loadedImages] (const auto&) {
Logger::error("[GltfLoad] Cannot find a suitable way of loading an image.");
loadedImages.emplace_back(std::nullopt);
};
Expand All @@ -227,7 +227,7 @@ std::vector<std::optional<Image>> loadImages(const std::vector<fastgltf::Image>&
const auto* imgBytes = reinterpret_cast<const unsigned char*>(imgData.bytes.data());
loadedImages.emplace_back(ImageFormat::loadFromData(imgBytes, imgData.bytes.size()));
},
[&bufferViews, &buffers, &loadedImages] (const fastgltf::sources::BufferView& bufferViewSource) {
[&bufferViews, &buffers, &loadedImages, &loadFailure] (const fastgltf::sources::BufferView& bufferViewSource) {
const fastgltf::BufferView& imgView = bufferViews[bufferViewSource.bufferViewIndex];
const fastgltf::Buffer& imgBuffer = buffers[imgView.bufferIndex];

Expand Down Expand Up @@ -286,6 +286,50 @@ std::pair<Image, Image> extractMetalnessRoughnessImages(const Image& metalRoughI
return { std::move(metalnessImg), std::move(roughnessImg) };
}

Image mergeImages(const Image& image1, const Image& image2) {
if (image1.isEmpty())
return image2;

if (image2.isEmpty() || image1 == image2)
return image1;

if (image1.getWidth() != image2.getWidth()
|| image1.getHeight() != image2.getHeight()
|| image1.getDataType() != image2.getDataType()) {
throw std::invalid_argument("[GltfLoad] The images' attributes need to be the same in order to be merged");
}

if (image1.getDataType() != ImageDataType::BYTE)
throw std::invalid_argument("[GltfLoad] Images with a floating-point data type cannot be merged");

// TODO: the channels to copy from each image should be definable
const uint8_t totalChannelCount = image1.getChannelCount() + image2.getChannelCount();
assert("Error: There shouldn't be only one channel to be merged." && totalChannelCount > 1);

if (totalChannelCount > 4)
throw std::invalid_argument("[GltfLoad] Too many channels to merge images into");

const bool isSrgb = (image1.getColorspace() == ImageColorspace::SRGB || image2.getColorspace() == ImageColorspace::SRGB);
const ImageColorspace colorspace = (totalChannelCount == 2 ? ImageColorspace::GRAY_ALPHA
: (totalChannelCount == 3 ? ImageColorspace::RGB
: (isSrgb ? ImageColorspace::SRGBA
: ImageColorspace::RGBA)));

Image res(image1.getWidth(), image1.getHeight(), colorspace, image1.getDataType());

for (unsigned int heightIndex = 0; heightIndex < image1.getHeight(); ++heightIndex) {
for (unsigned int widthIndex = 0; widthIndex < image1.getWidth(); ++widthIndex) {
for (uint8_t channelIndex = 0; channelIndex < image1.getChannelCount(); ++channelIndex)
res.setByteValue(widthIndex, heightIndex, channelIndex, image1.recoverByteValue(widthIndex, heightIndex, channelIndex));

for (uint8_t channelIndex = 0; channelIndex < image2.getChannelCount(); ++channelIndex)
res.setByteValue(widthIndex, heightIndex, channelIndex + image1.getChannelCount(), image2.recoverByteValue(widthIndex, heightIndex, channelIndex));
}
}

return res;
}

template <template <typename> typename OptionalT, typename TextureInfoT, typename FuncT>
void loadTexture(const OptionalT<TextureInfoT>& textureInfo,
const std::vector<fastgltf::Texture>& textures,
Expand All @@ -306,6 +350,40 @@ void loadTexture(const OptionalT<TextureInfoT>& textureInfo,
callback(*images[*imgIndex]);
}

void loadSheen(const fastgltf::MaterialSheen& matSheen,
const std::vector<fastgltf::Texture>& textures,
const std::vector<std::optional<Image>>& images,
RenderShaderProgram& matProgram) {
ZoneScopedN("[GltfLoad]::loadSheen");

const Vec4f sheenFactors(matSheen.sheenColorFactor.x(), matSheen.sheenColorFactor.y(), matSheen.sheenColorFactor.z(), matSheen.sheenRoughnessFactor);
matProgram.setAttribute(sheenFactors, MaterialAttribute::Sheen);

if (!matSheen.sheenColorTexture.has_value() && !matSheen.sheenRoughnessTexture.has_value())
return;

// If the textures are the same, load either of them
if (matSheen.sheenColorTexture && matSheen.sheenRoughnessTexture
&& matSheen.sheenColorTexture->textureIndex == matSheen.sheenRoughnessTexture->textureIndex) {
loadTexture(matSheen.sheenColorTexture, textures, images, [&matProgram] (const Image& img) {
matProgram.setTexture(Texture2D::create(img, true, true), MaterialTexture::Sheen);
});

return;
}

// If either only one texture is set or they are different, merge them
Image sheenColorImg;
Image sheenRoughnessImg;
loadTexture(matSheen.sheenColorTexture, textures, images, [&sheenColorImg] (Image img) {
sheenColorImg = std::move(img);
});
loadTexture(matSheen.sheenRoughnessTexture, textures, images, [&sheenRoughnessImg] (Image img) {
sheenRoughnessImg = std::move(img);
});
matProgram.setTexture(Texture2D::create(mergeImages(sheenColorImg, sheenRoughnessImg), true, true), MaterialTexture::Sheen);
}

void loadMaterials(const std::vector<fastgltf::Material>& materials,
const std::vector<fastgltf::Texture>& textures,
const std::vector<std::optional<Image>>& images,
Expand Down Expand Up @@ -352,6 +430,9 @@ void loadMaterials(const std::vector<fastgltf::Material>& materials,
matProgram.setTexture(Texture2D::create(roughnessImg), MaterialTexture::Roughness);
});

if (mat.sheen)
loadSheen(*mat.sheen, textures, images, matProgram);

loadedMat.loadType(MaterialType::COOK_TORRANCE);
}

Expand All @@ -376,7 +457,8 @@ std::pair<Mesh, MeshRenderer> load(const FilePath& filePath) {

const FilePath parentPath = filePath.recoverPathToFile();

fastgltf::Parser parser;
constexpr fastgltf::Extensions extensions = fastgltf::Extensions::KHR_materials_sheen;
fastgltf::Parser parser(extensions);

fastgltf::Expected<fastgltf::Asset> asset = parser.loadGltf(data.get(),
parentPath.getPath(),
Expand Down
13 changes: 11 additions & 2 deletions src/RaZ/Data/ObjLoad.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,17 @@ inline void loadMtl(const FilePath& mtlFilePath,
} else if (tag[0] == 'P') { // PBR properties [P*]
const float factor = std::stof(nextValue);

if (tag[1] == 'm') // Metallic factor [Pm]
if (tag[1] == 'm') { // Metallic factor [Pm]
material.getProgram().setAttribute(factor, MaterialAttribute::Metallic);
else if (tag[1] == 'r') // Roughness factor [Pr]
} else if (tag[1] == 'r') { // Roughness factor [Pr]
material.getProgram().setAttribute(factor, MaterialAttribute::Roughness);
} else if (tag[1] == 's') { // Sheen factors [Ps]
std::string secondValue;
std::string thirdValue;
std::string fourthValue;
file >> secondValue >> thirdValue >> fourthValue;
material.getProgram().setAttribute(Vec4f(factor, std::stof(secondValue), std::stof(thirdValue), std::stof(fourthValue)), MaterialAttribute::Sheen);
}

materialType = MaterialType::COOK_TORRANCE;
} else if (tag[0] == 'm') { // Import texture [map_*]
Expand All @@ -96,6 +103,8 @@ inline void loadMtl(const FilePath& mtlFilePath,
material.getProgram().setTexture(loadTexture(textureFilePath, ColorPreset::Red), MaterialTexture::Metallic);
else if (tag[5] == 'r') // Roughness map [map_Pr]
material.getProgram().setTexture(loadTexture(textureFilePath, ColorPreset::Red), MaterialTexture::Roughness);
else if (tag[5] == 's') // Sheen map [map_Ps]
material.getProgram().setTexture(loadTexture(textureFilePath, ColorPreset::White, true), MaterialTexture::Sheen); // TODO: should be an RGBA texture with an alpha of 1

materialType = MaterialType::COOK_TORRANCE;
} else if (tag[4] == 'd') { // Opacity (dissolve) map [map_d]
Expand Down
2 changes: 2 additions & 0 deletions src/RaZ/Data/ObjSave.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ void saveMtl(const FilePath& mtlFilePath, const std::vector<Material>& materials
writeAttribute<float, 1>(mtlFile, "d", matProgram, MaterialAttribute::Opacity);
writeAttribute<float, 1>(mtlFile, "Pm", matProgram, MaterialAttribute::Metallic);
writeAttribute<float, 1>(mtlFile, "Pr", matProgram, MaterialAttribute::Roughness);
writeAttribute<float, 4>(mtlFile, "Ps", matProgram, MaterialAttribute::Sheen);

#if !defined(USE_OPENGL_ES)
writeTexture(mtlFile, "map_Kd", materialName, "baseColor", matProgram, MaterialTexture::BaseColor);
Expand All @@ -85,6 +86,7 @@ void saveMtl(const FilePath& mtlFilePath, const std::vector<Material>& materials
writeTexture(mtlFile, "norm", materialName, "normal", matProgram, MaterialTexture::Normal);
writeTexture(mtlFile, "map_Pm", materialName, "metallic", matProgram, MaterialTexture::Metallic);
writeTexture(mtlFile, "map_Pr", materialName, "roughness", matProgram, MaterialTexture::Roughness);
writeTexture(mtlFile, "map_Ps", materialName, "sheen", matProgram, MaterialTexture::Sheen);
#endif
}
}
Expand Down
4 changes: 4 additions & 0 deletions src/RaZ/Render/Material.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ void Material::loadType(MaterialType type) {
m_program.setAttribute(0.f, MaterialAttribute::Metallic);
if (!m_program.hasAttribute(MaterialAttribute::Roughness))
m_program.setAttribute(1.f, MaterialAttribute::Roughness);
if (!m_program.hasAttribute(MaterialAttribute::Sheen))
m_program.setAttribute(Vec4f(0.f), MaterialAttribute::Sheen);

if (!m_program.hasTexture(MaterialTexture::BaseColor))
m_program.setTexture(Texture2D::create(ColorPreset::White), MaterialTexture::BaseColor);
Expand All @@ -58,6 +60,8 @@ void Material::loadType(MaterialType type) {
m_program.setTexture(Texture2D::create(ColorPreset::Red), MaterialTexture::Roughness);
if (!m_program.hasTexture(MaterialTexture::Ambient))
m_program.setTexture(Texture2D::create(ColorPreset::Red), MaterialTexture::Ambient);
if (!m_program.hasTexture(MaterialTexture::Sheen))
m_program.setTexture(Texture2D::create(ColorPreset::White), MaterialTexture::Sheen); // TODO: should be an RGBA texture with an alpha of 1

break;

Expand Down
2 changes: 2 additions & 0 deletions src/RaZ/Script/LuaRender.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ void LuaWrapper::registerRenderTypes() {
{ "Emissive", MaterialAttribute::Emissive },
{ "Metallic", MaterialAttribute::Metallic },
{ "Roughness", MaterialAttribute::Roughness },
{ "Sheen", MaterialAttribute::Sheen },
{ "Ambient", MaterialAttribute::Ambient },
{ "Specular", MaterialAttribute::Specular },
{ "Opacity", MaterialAttribute::Opacity }
Expand All @@ -175,6 +176,7 @@ void LuaWrapper::registerRenderTypes() {
{ "Normal", MaterialTexture::Normal },
{ "Metallic", MaterialTexture::Metallic },
{ "Roughness", MaterialTexture::Roughness },
{ "Sheen", MaterialTexture::Sheen },
{ "Specular", MaterialTexture::Specular },
{ "Opacity", MaterialTexture::Opacity },
{ "Bump", MaterialTexture::Bump }
Expand Down
1 change: 1 addition & 0 deletions tests/assets/materials/çûbè_BP.mtl
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ newmtl cube_BP
Ke 0.75 0.75 0.75
Ka 0.50 0.50 0.50
Ks 0.25 0.25 0.25
d 0.01

map_Kd ../textures/ŔĜBŖĀ.png
map_Ke ../textures/ŔŖȒȐ.png
Expand Down
2 changes: 2 additions & 0 deletions tests/assets/materials/çûbè_CT.mtl
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ newmtl cube_CT
Ke 0.75 0.75 0.75
Pm 0.50
Pr 0.25
Ps 0.1 0.2 0.3 0.4

map_Kd ../textures/ŔĜBŖĀ.png
map_Ke ../textures/ŔŖȒȐ.png
norm ../textures/BƁḂɃ.png
map_Pm ../textures/₁₁₁₁.png
map_Pr ../textures/₀₀₀₀.png
map_Ps ../textures/ŔĜBŖĀ.png
map_Ka ../textures/₁₀₀₁.png
36 changes: 36 additions & 0 deletions tests/assets/meshes/çûbè.gltf
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,12 @@
"uri": "çûbè.bin"
}
],
"extensionsRequired": [
"KHR_materials_sheen"
],
"extensionsUsed": [
"KHR_materials_sheen"
],
"images": [
{
"uri": "../textures/ŔĜBŖĀ.png"
Expand All @@ -133,6 +139,12 @@
},
{
"uri": "../textures/ĜƓGǦ.png"
},
{
"uri": "../textures/ŔĜBŖ.png"
},
{
"uri": "../textures/₁₀₀₁.png"
}
],
"materials": [
Expand Down Expand Up @@ -167,6 +179,22 @@
],
"emissiveTexture": {
"index": 0
},
"extensions": {
"KHR_materials_sheen": {
"sheenColorFactor": [
0.1,
0.2,
0.3
],
"sheenColorTexture": {
"index": 3
},
"sheenRoughnessFactor": 0.4,
"sheenRoughnessTexture": {
"index": 4
}
}
}
}
],
Expand Down Expand Up @@ -311,6 +339,14 @@
{
"sampler": 0,
"source": 0
},
{
"sampler": 0,
"source": 3
},
{
"sampler": 0,
"source": 4
}
]
}
Binary file added tests/assets/textures/ŔĜBŖ.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 50e2b65

Please sign in to comment.