Add clearCoatNormal property (#131)

* Add clear coat normal map support

This change allows to set a separate normal map for the clear coat layer of a material.

* Document the new clearCoatNormal property

* Fix typo
This commit is contained in:
Romain Guy
2018-08-24 08:56:44 -07:00
committed by GitHub
parent 7a14aadb28
commit 4585b8f896
16 changed files with 179 additions and 74 deletions
+59 -26
View File
@@ -57,11 +57,13 @@ static std::vector<MeshIO::Mesh> g_meshes;
static const Material* g_material;
static Entity g_light;
static Texture* g_normalMap = nullptr;
static Texture* g_clearCoatNormalMap = nullptr;
static Texture* g_baseColorMap = nullptr;
static Config g_config;
static struct NormalConfig {
std::string normalMap;
std::string clearCoatNormalMap;
std::string baseColorMap;
} g_normalConfig;
@@ -82,7 +84,9 @@ static void printUsage(char* name) {
" Applies uniform scale\n\n"
" --normal-map=<path to PNG/JPG/BMP/GIF/TGA/PSD>, -n <path>\n"
" Normal map to apply to the loaded meshes\n\n"
" --basecolor-map=<path to PNG/JPG/BMP/GIF/TGA/PSD>, -c <path>\n"
" --clear-coatnormal-map=<path to PNG/JPG/BMP/GIF/TGA/PSD>, -c <path>\n"
" Normal map to apply to the clear coat layer\n\n"
" --basecolor-map=<path to PNG/JPG/BMP/GIF/TGA/PSD>, -b <path>\n"
" Base color map to apply to the loaded meshes\n\n"
);
const std::string from("SAMPLE_NORMAL_MAP");
@@ -93,15 +97,16 @@ static void printUsage(char* name) {
}
static int handleCommandLineArgments(int argc, char* argv[], Config* config) {
static constexpr const char* OPTSTR = "hi:vs:n:a:c:";
static constexpr const char* OPTSTR = "hi:vs:n:a:c:b:";
static const struct option OPTIONS[] = {
{ "help", no_argument, 0, 'h' },
{ "ibl", required_argument, 0, 'i' },
{ "split-view", no_argument, 0, 'v' },
{ "scale", required_argument, 0, 's' },
{ "normal-map", required_argument, 0, 'n' },
{ "basecolor-map", required_argument, 0, 'c' },
{ 0, 0, 0, 0 } // termination of the option list
{ "help", no_argument, nullptr, 'h' },
{ "ibl", required_argument, nullptr, 'i' },
{ "split-view", no_argument, nullptr, 'v' },
{ "scale", required_argument, nullptr, 's' },
{ "normal-map", required_argument, nullptr, 'n' },
{ "clear-coat-normal-map", required_argument, nullptr, 'c' },
{ "basecolor-map", required_argument, nullptr, 'b' },
{ nullptr, 0, nullptr, 0 } // termination of the option list
};
int opt;
int option_index = 0;
@@ -131,6 +136,9 @@ static int handleCommandLineArgments(int argc, char* argv[], Config* config) {
g_normalConfig.normalMap = arg;
break;
case 'c':
g_normalConfig.clearCoatNormalMap = arg;
break;
case 'b':
g_normalConfig.baseColorMap = arg;
break;
}
@@ -139,8 +147,9 @@ static int handleCommandLineArgments(int argc, char* argv[], Config* config) {
return optind;
}
static void cleanup(Engine* engine, View* view, Scene* scene) {
static void cleanup(Engine* engine, View*, Scene*) {
engine->destroy(g_normalMap);
engine->destroy(g_clearCoatNormalMap);
for (auto material : g_materialInstances) {
engine->destroy(material.second);
}
@@ -156,14 +165,14 @@ static void cleanup(Engine* engine, View* view, Scene* scene) {
em.destroy(g_light);
}
void loadNormalMap(Engine* engine) {
if (!g_normalConfig.normalMap.empty()) {
Path path(g_normalConfig.normalMap);
if (path.exists()) {
void loadNormalMap(Engine* engine, Texture** normalMap, const std::string& path) {
if (!path.empty()) {
Path p(path);
if (p.exists()) {
int w, h, n;
unsigned char* data = stbi_load(path.getAbsolutePath().c_str(), &w, &h, &n, 3);
unsigned char* data = stbi_load(p.getAbsolutePath().c_str(), &w, &h, &n, 3);
if (data != nullptr) {
g_normalMap = Texture::Builder()
*normalMap = Texture::Builder()
.width(uint32_t(w))
.height(uint32_t(h))
.levels(0xff)
@@ -172,13 +181,13 @@ void loadNormalMap(Engine* engine) {
Texture::PixelBufferDescriptor buffer(data, size_t(w * h * 3),
Texture::Format::RGB, Texture::Type::UBYTE,
(driver::BufferDescriptor::Callback)&stbi_image_free);
g_normalMap->setImage(*engine, 0, std::move(buffer));
g_normalMap->generateMipmaps(*engine);
(*normalMap)->setImage(*engine, 0, std::move(buffer));
(*normalMap)->generateMipmaps(*engine);
} else {
std::cout << "The normal map " << path << " could not be loaded" << std::endl;
std::cout << "The normal map " << p << " could not be loaded" << std::endl;
}
} else {
std::cout << "The normal map " << path << " does not exist" << std::endl;
std::cout << "The normal map " << p << " does not exist" << std::endl;
}
}
}
@@ -210,17 +219,23 @@ void loadBaseColorMap(Engine* engine) {
}
}
static void setup(Engine* engine, View* view, Scene* scene) {
loadNormalMap(engine);
static void setup(Engine* engine, View*, Scene* scene) {
loadNormalMap(engine, &g_normalMap, g_normalConfig.normalMap);
loadNormalMap(engine, &g_clearCoatNormalMap, g_normalConfig.clearCoatNormalMap);
loadBaseColorMap(engine);
bool hasNormalMap = g_normalMap != nullptr;
bool hasClearCoatNormalMap = g_clearCoatNormalMap != nullptr;
bool hasBaseColorMap = g_baseColorMap != nullptr;
std::string shader = R"SHADER(
void material(inout MaterialInputs material) {
#if defined(MATERIAL_HAS_NORMAL)
material.normal = texture(materialParams_normalMap, getUV0()).xyz * 2.0 - 1.0;
#endif
#if defined(MATERIAL_HAS_CLEAR_COAT_NORMAL)
material.clearCoatNormal =
texture(materialParams_clearCoatNormalMap, getUV0()).xyz * 2.0 - 1.0;
#endif
prepareMaterial(material);
)SHADER";
@@ -233,11 +248,16 @@ static void setup(Engine* engine, View* view, Scene* scene) {
)SHADER";
} else {
shader += R"SHADER(
material.baseColor.rgb = float3(0.93, 0.51, 0.34);
material.baseColor.rgb = float3(0.48, 0.0, 0.0);
material.metallic = 1.0;
material.roughness = 0.3;
material.roughness = 0.7;
)SHADER";
}
if (hasClearCoatNormalMap) {
shader += " material.clearCoat = 1.0;\n";
}
shader += "}\n";
MaterialBuilder builder = MaterialBuilder()
@@ -255,6 +275,14 @@ static void setup(Engine* engine, View* view, Scene* scene) {
.set(Property::NORMAL);
}
if (hasClearCoatNormalMap) {
builder
.require(VertexAttribute::UV0)
.parameter(MaterialBuilder::SamplerType::SAMPLER_2D, "clearCoatNormalMap")
.set(Property::CLEAR_COAT)
.set(Property::CLEAR_COAT_NORMAL);
}
if (hasBaseColorMap) {
builder
.require(VertexAttribute::UV0)
@@ -274,12 +302,17 @@ static void setup(Engine* engine, View* view, Scene* scene) {
if (hasNormalMap) {
g_materialInstances["DefaultMaterial"]->setParameter("normalMap", g_normalMap, sampler);
}
if (hasClearCoatNormalMap) {
g_materialInstances["DefaultMaterial"]->setParameter(
"clearCoatNormalMap", g_clearCoatNormalMap, sampler);
}
if (hasBaseColorMap) {
g_materialInstances["DefaultMaterial"]->setParameter("baseColorMap", g_baseColorMap, sampler);
g_materialInstances["DefaultMaterial"]->setParameter(
"baseColorMap", g_baseColorMap, sampler);
}
auto& tcm = engine->getTransformManager();
for (auto filename : g_filenames) {
for (const auto& filename : g_filenames) {
MeshIO::Mesh mesh = MeshIO::loadMeshFromFile(engine, filename, g_materialInstances);
if (mesh.renderable) {
auto ei = tcm.getInstance(mesh.renderable);