1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
|
// Copyright 2009 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include "BSDF.ih"
#include "Fresnel.ih"
#include "GGXDistribution.ih"
#include "MicrofacetAlbedoTables.ih"
OSPRAY_BEGIN_ISPC_NAMESPACE
// Microfacet conductor BRDF with the Smith microsurface model and approximate
// multiple scattering. [Kulla and Conty, 2017, "Revisiting Physically Based
// Shading at Imageworks"] [Jakob et al., 2014, "A Comprehensive Framework for
// Rendering Layered Materials", Extended Technical Report]
struct MicrofacetConductor
{
BSDF super;
Fresnel *uniform fresnel;
GGXDistribution microfacet;
float roughness;
const MicrofacetAlbedoTables *uniform microfacetAlbedoTables;
// Energy compensation [Kulla and Conty, 2017]
float Eavg;
vec3f fmsScale;
};
inline BSDF_EvalRes MicrofacetConductor_eval(
const varying MicrofacetConductor *uniform self,
const vec3f &wo,
const vec3f &wi)
{
BSDF_EvalRes res;
float cosThetaO = dot(wo, getN(&self->super));
float cosThetaI = dot(wi, getN(&self->super));
if (cosThetaO <= 0.f || cosThetaI <= 0.f)
return make_BSDF_EvalRes_zero();
// Compute the microfacet normal
vec3f wh = normalize(wi + wo);
float cosThetaOH = dot(wo, wh);
float cosThetaIH = dot(wi, wh);
linear3f toLocal = transposed(getFrame(&self->super));
vec3f wo0 = toLocal * wo;
vec3f wi0 = toLocal * wi;
vec3f wh0 = toLocal * wh;
vec3f F = Fresnel_dispatch_eval(self->fresnel, cosThetaOH);
float whPdf;
float D = evalVisible(self->microfacet, wh0, wo0, cosThetaOH, whPdf);
float G = evalG2(self->microfacet, wo0, wi0, cosThetaOH, cosThetaIH);
// Energy compensation
float Eo = MicrofacetAlbedoTable_eval(
self->microfacetAlbedoTables, cosThetaO, self->roughness);
float Ei = MicrofacetAlbedoTable_eval(
self->microfacetAlbedoTables, cosThetaI, self->roughness);
vec3f fms = self->fmsScale
* ((1.f - Eo) * (1.f - Ei) * rcp((float)pi * (1.f - self->Eavg))
* cosThetaI);
res.pdf = whPdf * rcp(4.f * abs(cosThetaOH));
res.value = F * (D * G * rcp(4.f * cosThetaO)) + fms;
return res;
}
inline BSDF_SampleRes MicrofacetConductor_sample(
const varying MicrofacetConductor *uniform self,
const vec3f &wo,
const vec2f &s,
float)
{
BSDF_SampleRes res;
float cosThetaO = dot(wo, getN(&self->super));
if (cosThetaO <= 0.f)
return make_BSDF_SampleRes_zero();
linear3f toGlobal = getFrame(&self->super);
linear3f toLocal = transposed(getFrame(&self->super));
vec3f wo0 = toLocal * wo;
// Sample the microfacet normal
float whPdf;
vec3f wh = toGlobal * sampleVisible(self->microfacet, wo0, whPdf, s);
res.wi = reflect(wo, wh);
float cosThetaI = dot(res.wi, getN(&self->super));
if (cosThetaI <= 0.f)
return make_BSDF_SampleRes_zero();
float cosThetaOH = dot(wo, wh);
float cosThetaIH = dot(res.wi, wh);
vec3f wi0 = toLocal * res.wi;
vec3f F = Fresnel_dispatch_eval(self->fresnel, cosThetaOH);
float G = evalG2(self->microfacet, wo0, wi0, cosThetaOH, cosThetaIH);
// Energy compensation
float Eo = MicrofacetAlbedoTable_eval(
self->microfacetAlbedoTables, cosThetaO, self->roughness);
float Ei = MicrofacetAlbedoTable_eval(
self->microfacetAlbedoTables, cosThetaI, self->roughness);
vec3f fms = self->fmsScale
* ((1.f - Eo) * (1.f - Ei) * rcp((float)pi * (1.f - self->Eavg))
* cosThetaI);
res.type = BSDF_GLOSSY_REFLECTION;
res.pdf = whPdf * rcp(4.f * abs(cosThetaOH));
res.weight = F * (G * rcp_safe(evalG1(self->microfacet, wo0, cosThetaOH)))
+ (fms * rcp(res.pdf));
return res;
}
inline void MicrofacetConductor_Constructor(
varying MicrofacetConductor *uniform self,
MicrofacetAlbedoTables *uniform microfacetAlbedoTables,
const varying linear3f *uniform frame,
Fresnel *uniform fresnel,
float roughness,
float anisotropy)
{
// Energy compensation
self->Eavg = MicrofacetAlbedoTable_evalAvg(microfacetAlbedoTables, roughness);
vec3f Favg = Fresnel_dispatch_evalAvg(fresnel);
self->fmsScale = sqr(Favg) * self->Eavg
/ (1.f - Favg * (1.f - self->Eavg)); // Stephen Hill's tweak
BSDF_Constructor(&self->super,
Favg * self->Eavg, // TODO better estimate
BSDF_GLOSSY_REFLECTION,
BSDF_TYPE_MICROFACET_CONDUCTOR,
frame);
self->fresnel = fresnel;
self->microfacet =
make_GGXDistribution(roughnessToAlpha(roughness, anisotropy));
self->roughness = roughness;
self->microfacetAlbedoTables = microfacetAlbedoTables;
}
inline varying BSDF *uniform MicrofacetConductor_create(
uniform ShadingContext *uniform ctx,
MicrofacetAlbedoTables *uniform microfacetAlbedoTables,
const varying linear3f *uniform frame,
Fresnel *uniform fresnel,
float roughness,
float anisotropy)
{
varying MicrofacetConductor *uniform self =
(varying MicrofacetConductor * uniform)
ShadingContext_alloc(ctx, sizeof(MicrofacetConductor));
MicrofacetConductor_Constructor(
self, microfacetAlbedoTables, frame, fresnel, roughness, anisotropy);
return &self->super;
}
OSPRAY_END_ISPC_NAMESPACE
|