Closed lorentzo closed 3 years ago
When loading the test scenes as they are, currently the model is set to the respective "standard" model, even though the microfacet version is set in the appleseed file. When loading the 26 - normal mapping - metal brdf - point light.appleseed
, I get the following BSDF setting:
When loading the test scenes as they are, currently the model is set to the respective "standard" model, even though the microfacet version is set in the appleseed file. When loading the
26 - normal mapping - metal brdf - point light.appleseed
, I get the following BSDF setting:
OLD:
OK. I have to investigate this bit more. It seems that only the label is wrong. Regardless of that microfacet normal mapping is applied. So this is just a minor GUI-related bug.
NEW:
I will use metal_brdf
and its microfacet normal mapping alternative microfacet_normal_mapping_metal_brdf
for description of the problem but the same idea applies to other BRDFs and its microfacet based normal mapping alternatives. The problem is following:
I think I have found the source of the problem. First, I will describe how the problem occurs. Then, I will propose (a simple) solution (and maybe not the best one).
appleseed project file contains microfacet_normal_mapping_metal_brdf as BRDF in assembly. Example:
...
<bsdf name="my_metal_BRDF" model="microfacet_normal_mapping_metal_brdf">
<parameter name="anisotropy" value="0.0" />
<parameter name="edge_tint" value="0.0" />
<parameter name="normal_reflectance" value="0.92" />
<parameter name="reflectance_multiplier" value="1.0" />
<parameter name="roughness" value="0.3" />
</bsdf>
...
Opening project file in GUI calls XML project file reader, which (among others) reads "microfacet_normal_mapping_metal_brdf" tag and calls the corresponding factory which is MicrofacetMetalBRDFFactory
.
MicrofacetMetalBRDFFactory
creates metalBRDF
(my_metal_BRDF
) wrapped with microfacetBRDFwrapper
(MicrofacetBRDFWrapper<MetalBRDFImpl>
). Note that created my_metal_BRDF
has "metal_brdf" model name (my_metal_BRDF.get_model()
). my_metal_BRDF
is added to assembly.
GUI then displays assembly elements using get_model()
call. In particular: my_metal_BRDF.get_model()
call. Which results in "metal_brdf" and not "microfacet_normal_mapping_metal_brdf"! (see step 3).
When GUI rendering is performed, we will get a microfacet based normal mapping result because my_metal_BRDF
is wrapped with MicrofacetBRDFWrapper
(see step (3)).
When we use GUI to save this file, XML project writer is called, which writes assembly elements to the file. Saved file will contain "metal_brdf" and not "microfacet_normal_mapping_metal_brdf". This is because my_metal_BRDF.get_model()
is called which returns "metal_brdf" and not "microfacet_normal_mapping_metal_brdf"!
The main problem is that MicrofacetMetalBRDFFactory
has different model name than the model name of BRDF it creates: MicrofacetMetalBRDFFactory.get_name()
= "microfacet_normal_mapping_metal_brdf" vs MetalBRDFImpl.get_name()
= "metal_brdf".
Idea is to have enumeration: {metal_brdf, microfacet_normal_mapping_metal_brdf}
. And in MetalBRDFFactory::create()
as well as MicrofacetMetalBRDFFactory::create()
set Model variable to metal_brdf
or microfacet_normal_mapping_metal_brdf
respectively.
This still has to be tested! Also, a better solution might be always possible.
UPDATE
I can confirm that the above problem description is correct. Simply replacing Model
variable containing "metal_brdf" with MicrofacetModel
containing "microfacet_normal_mapping_metal_brdf" in MetalBRDFImpl::get_model()
results in correct reading and writing of microfacet based normal mapping brdf. Of course, this is not a solution, just a test. Therefore, I am proposing new solution which seems quite elegant.
(1) Create MetalBRDFImplMicrofacet
class which is inheriting MetalBRDFImpl
class. MetalBRDFImplMicrofacet
has all the same functions except MetalBRDFImplMicrofacet::get_model()
. Function MetalBRDFImplMicrofacet::get_model()
must return MicrofacetModel
variable instead of Model
variable. (2) Use MetalBRDFImplMicrofacet
class as a child class for MicrofacetBRDFWrapper
.
Example in code. Only change is required in metalbrdf.cpp
:
MetalBRDFImplMicrofacet
as described above:class MetalBRDFImplMicrofacet : public MetalBRDFImpl
{
using MetalBRDFImpl::MetalBRDFImpl;
const char* get_model() const override
{
return MicrofacetModel;
}
};
MetalBRDFImplMicrofacet
class as a child class for MicrofacetBRDFWrapper
.typedef MicrofacetBRDFWrapper<MetalBRDFImplMicrofacet> MicrofacetMetalBRDF;
Intro
This PR contains the implementation of microfacet based normal mapping for more robust normal mapping. It is connected to the issue "Investigate more robust normal mapping" #2427. It is implemented during Google Summer of Code 2020.
Implementation and further discussion is based on paper Microfacet-based Normal Mapping for Robust Monte Carlo path Tracing V. Schussler, E. Heitz, J. Hanika, C. Dachsbacher.
PR contains complete code and test scenes:
src/appleseed/renderer/modeling/bsdf/microacetbrdfwrapper.h
.sandbox/tests/test scenes/basis modifiers/
.Problem description
Regular normal mapping causes several problems for Monte Carlo path tracing:
ws
) sampled from normal map which cause inconsistencies with geometric (wg
) hemisphere. This effect is visible as black fringes on final render.Solution introduction
Authors introduced microfacet based surface model. Profile of this surface model contains two facets per shading point. Instead of just replacing geometric normal, the sampled shading normal (perturbed normal) from normal map determines the orientation of one of the facets (perturbed facet,
wp
). Other facet is used so that the average microfacet normal in shading point equals the geometric normal (tangent facet,wt
).Other properties of this model are similar to microfacet models: distribution of normals, projected areas and intersection probabilities, masking and shadowing function. Using this properties macrosurface single and multiple scattering BRDF is derived with assumption of arbitrary perturbed and tangent facet BRDF.
Multiple scattering BRDF model evaluation and sapling is solved using random walk with arbitrary amount of scatterings. In shading point, perturbed facet has BRDF specified by the user. For example, if user decides on glossy BRDF with particular normal map, then perturbed facet will contain glossy BRDF and the direction of perturbed normal map will ne equal to sampled normal from that normal map. Tangent facet, on the other hand, can be more arbitrary. One choice is that tangent facet can contain same BRDF as perturbed facet. Other choice is that tangent facet has the specular BRDF (note that tangent facet has the little influence as possible, it is only used so that average normal in shading point equals to geometrical normal).
The case where tangent facet has specular BRDF is chosen in this implementation. There are several reasons for that:
Analytical model with 2nd order scattering has three distinct cases due to two facets per shading normal:
Implementation
As discussed this PR contributes with analytical 2nd order scattering model for microfacet based normal mapping for more robust normal mapping. Implementation is based on:
Idea was that microfacet based normal mapping can be applied to arbitrary BRDF (note R for reflective materials). Therefore it is implemented as wrapper agnostic of particular BRDF. Main implementation is in
microfacetbrdfwrapper.h
asMicrofacetBRDFWRapper
class located with other BRDFs insrc/appleseed/renderer/modeling/bsdf
.MicrofacetBRDFWRapper
is a template class which can be specified with arbitrary BRDF. This class has access toBRDF::sample()
,BRDF::evaluate()
andBRDF::evaluate_pdf()
. Using these BRDF functions it is reimplementing them inMicrofacetBRDFWRapper::sample()
,MicrofacetBRDFWRapper::evaluate()
andMicrofacetBRDFWRapper::evaluate_pdf()
so that microfacet based normal mapping is applied.Microfacet normal mapping wrapper is currently (this PR) added to
metal
,glossy
,plastic
,lambertian
,blinn
andsheen
BRDFs. To add microfacet normal mapping to new BRDF it is recommended to see how it is done in this PR. General steps are as follow (with metalBRDF as an example):typedef MicrofacetBRDFWrapper<MetalBRDFImpl> MicrofacetMetalBRDF;
(metalbrdf.cpp
)MicrofacetMetalBRDFFactory
which is constructingMicrofacetMetalBRDF
(metalbrdf.h
andmetalbrdf.cpp
)MicrofacetMetalBRDFFactory
inbsdffactoryregistar.cpp
(steps, for now, enable usage of microfacet based normal mapping formetal
BRDF in appleseed built-in system -- effectively new BRDF is added, but this BRDF is actually using metalBRDF with microfacet normal mapping wrapper. See test scenes for usage)MicrofacetMetalID
in closures.hMicrofacetMetalClosure
in closures.cppas_microfacet_metal
closure inas_osl_extensions.h.in
MicrofacetMetalBRDF
inoslbsdf.cpp
as_microfacet_metal.osl
to use it in scene construction (These steps allow using microfacet normal mapping wrappedmetalBRDF
as new closure: as_microfacet_metal. See test scenes for usage)Results
With this contribution, black areas due to regular normal mapping are fixed. Authors have provided code in Mitsuba which was used to compare the results.
Appleseed, original normal mapping:
Appleseed, microfacet based normal mapping:
Mitsuba, microfacet based normal mapping:
Important note:
Issue: Double wrapping of child BSDFs in BSDFBlend, BSDFMix and OSLBSDF #1243 is also affecting the results when microfacet normal mapping OSL closure is used. This PR: Fixing double wrapping for OSLBRDF children #2889 is fixing the problem of double wrapping for OSLBSDF children. Therefore, PR #2889 should be merged after the current PR.