Implement KHR_materials_dispersion (#9471)
This change adds support for the KHR_materials_dispersion glTF extension, which introduces a dispersion property for refractive materials. The dispersion property controls the angular separation of colors transmitting through a refractive object, allowing for more realistic rendering of materials like glass and diamonds. Changes include: - Added a dispersion property to the material definition. - Updated the shaders to incorporate the dispersion effect in the refraction calculations. - Added a new test case for dispersion. - Updated the material documentation to include the new dispersion property.
This commit is contained in:
@@ -6,3 +6,5 @@
|
||||
appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md).
|
||||
|
||||
## Release notes for next branch cut
|
||||
|
||||
- materials: added support for the glTF `KHR_materials_dispersion` extension, which adds dispersion for refractive objects
|
||||
@@ -97,6 +97,7 @@ in table [standardProperties].
|
||||
**transmission** | Defines how much of the diffuse light of a dielectric is transmitted through the object, in other words this defines how transparent an object is
|
||||
**ior** | Index of refraction, either for refractive objects or as an alternative to reflectance
|
||||
**microThickness** | Thickness of the thin layer of refractive objects
|
||||
**dispersion** | Strength of the dispersion effect for refractive objects, specified as 20/Abbe number
|
||||
**bentNormal** | A normal pointing in the average unoccluded direction. Can be used to improve indirect lighting quality
|
||||
**shadowStrength** | Strength factor between 0 and 1 for all shadows received by this material
|
||||
[Table [standardProperties]: Properties of the standard model]
|
||||
@@ -126,6 +127,7 @@ The type and range of each property is described in table [standardPropertiesTyp
|
||||
**absorption** | float3 | [0..n] |
|
||||
**microThickness** | float | [0..n] |
|
||||
**thickness** | float | [0..n] |
|
||||
**dispersion** | float | [0..n] | Realistic values are between [0, 1], with the exception of Rutile, which has a value of 2.04
|
||||
[Table [standardPropertiesTypes]: Range and type of the standard model's properties]
|
||||
|
||||
|
||||
@@ -153,13 +155,14 @@ The type and range of each property is described in table [standardPropertiesTyp
|
||||
as-is, which can lead to physically impossible materials, however, this might be desirable
|
||||
for artistic reasons.
|
||||
|
||||
!!! Note: About `thickness` and `microThickness` for refraction
|
||||
!!! Note: About `thickness`, `microThickness` and `dispersion` for refraction
|
||||
`thickness` represents the thickness of solid objects in the direction of the normal, for
|
||||
satisfactory results, this should be provided per fragment (e.g.: as a texture) or at least per
|
||||
vertex. `microThickness` represent the thickness of the thin layer of an object, and can
|
||||
generally be provided as a constant value. For example, a 1mm thin hollow sphere of radius 1m,
|
||||
would have a `thickness` of 1 and a `microThickness` of 0.001. Currently `thickness` is not
|
||||
used when `refractionType` is set to `thin`.
|
||||
would have a `thickness` of 1 and a `microThickness` of 0.001. Dispersion controls the angular
|
||||
separation of colors transmitting through a volume, and can be set by a contant value.
|
||||
Currently `thickness` and `dispersion` are not used when `refractionType` is set to `thin`.
|
||||
|
||||
### Base color
|
||||
|
||||
@@ -651,6 +654,23 @@ the `refractionType` is set to `solid` and `absorption` coefficients are set.
|
||||
![Figure [varyingThickness]: `thickness` varying from 0.0 at the top of the prism to 3.0 at the
|
||||
bottom of the prism](images/material_thickness.png)
|
||||
|
||||
### Dispersion
|
||||
|
||||
The dispersion property controls the angular separation of colors transmitting through a relatively
|
||||
clear volume. It can only be used when `refractionType` is set to `volume`.
|
||||
Its value is specified as 20/Abbe number. When the value is zero, no dispersion is used.
|
||||
|
||||
Table [commonMatDispersion] describes acceptable dispersion values for various types of materials.
|
||||
|
||||
Material | Abbe Number (V) | Dispersion (20/V)
|
||||
--------------------------:|:------------------:|:-----------------
|
||||
Rutile | 9.8 | 2.04
|
||||
Polycarbonate | 32 | 0.625
|
||||
Diamond | 55 | 0.36
|
||||
Water | 55 | 0.36
|
||||
Crown Glass | 59 | 0.33
|
||||
[Table [commonMatDispersion]: Dispersion of common materials]
|
||||
|
||||
## Subsurface model
|
||||
|
||||
### Thickness
|
||||
@@ -2276,6 +2296,7 @@ struct MaterialInputs {
|
||||
float3 absorption; // default float3(0.0, 0.0, 0.0)
|
||||
float ior; // default: 1.5
|
||||
float microThickness; // default: 0.0, not available with refractionType "solid"
|
||||
float dispersion; // default: 0.0, not available with refractionType "thin"
|
||||
}
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
@@ -203,7 +203,7 @@ enum class ReflectionMode : uint8_t {
|
||||
// can't really use std::underlying_type<AttributeIndex>::type because the driver takes a uint32_t
|
||||
using AttributeBitset = utils::bitset32;
|
||||
|
||||
static constexpr size_t MATERIAL_PROPERTIES_COUNT = 30;
|
||||
static constexpr size_t MATERIAL_PROPERTIES_COUNT = 31;
|
||||
enum class Property : uint8_t {
|
||||
BASE_COLOR, //!< float4, all shading models
|
||||
ROUGHNESS, //!< float, lit shading models only
|
||||
@@ -230,6 +230,7 @@ enum class Property : uint8_t {
|
||||
ABSORPTION, //!< float3, how much light is absorbed by the material
|
||||
TRANSMISSION, //!< float, how much light is refracted through the material
|
||||
IOR, //!< float, material's index of refraction
|
||||
DISPERSION, //!< float, material's dispersion
|
||||
MICRO_THICKNESS, //!< float, thickness of the thin layer
|
||||
BENT_NORMAL, //!< float3, all shading models only, except unlit
|
||||
SPECULAR_FACTOR, //!< float, lit shading models only, except subsurface and cloth
|
||||
|
||||
@@ -46,6 +46,7 @@ std::unordered_map<std::string, Property> Enums::mStringToProperty = {
|
||||
{ "absorption", Property::ABSORPTION },
|
||||
{ "transmission", Property::TRANSMISSION },
|
||||
{ "ior", Property::IOR },
|
||||
{ "dispersion", Property::DISPERSION },
|
||||
{ "microThickness", Property::MICRO_THICKNESS },
|
||||
{ "bentNormal", Property::BENT_NORMAL },
|
||||
{ "specularFactor", Property::SPECULAR_FACTOR },
|
||||
|
||||
@@ -1248,6 +1248,7 @@ char const* CodeGenerator::getConstantName(MaterialBuilder::Property property) n
|
||||
case Property::ABSORPTION: return "ABSORPTION";
|
||||
case Property::TRANSMISSION: return "TRANSMISSION";
|
||||
case Property::IOR: return "IOR";
|
||||
case Property::DISPERSION: return "DISPERSION";
|
||||
case Property::MICRO_THICKNESS: return "MICRO_THICKNESS";
|
||||
case Property::BENT_NORMAL: return "BENT_NORMAL";
|
||||
case Property::SPECULAR_FACTOR: return "SPECULAR_FACTOR";
|
||||
|
||||
@@ -475,6 +475,32 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerTransmission) {
|
||||
EXPECT_TRUE(PropertyListsMatch(expected, properties));
|
||||
}
|
||||
|
||||
TEST_F(MaterialCompiler, StaticCodeAnalyzerDispersion) {
|
||||
std::string fragmentCode(R"(
|
||||
void material(inout MaterialInputs material) {
|
||||
prepareMaterial(material);
|
||||
material.absorption = vec3(0.0);
|
||||
material.transmission = 0.96;
|
||||
material.ior = 1.33;
|
||||
material.dispersion = 0.33;
|
||||
}
|
||||
)");
|
||||
|
||||
std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderStage::FRAGMENT,
|
||||
fragmentCode, "", filamat::MaterialBuilder::Shading::LIT,
|
||||
filamat::MaterialBuilder::RefractionMode::CUBEMAP);
|
||||
|
||||
GLSLTools glslTools;
|
||||
MaterialBuilder::PropertyList properties{ false };
|
||||
glslTools.findProperties(ShaderStage::FRAGMENT, shaderCode, properties);
|
||||
MaterialBuilder::PropertyList expected{ false };
|
||||
expected[size_t(filamat::MaterialBuilder::Property::ABSORPTION)] = true;
|
||||
expected[size_t(filamat::MaterialBuilder::Property::TRANSMISSION)] = true;
|
||||
expected[size_t(filamat::MaterialBuilder::Property::IOR)] = true;
|
||||
expected[size_t(filamat::MaterialBuilder::Property::DISPERSION)] = true;
|
||||
EXPECT_TRUE(PropertyListsMatch(expected, properties));
|
||||
}
|
||||
|
||||
TEST_F(MaterialCompiler, StaticCodeAnalyzerClearCoatRoughness) {
|
||||
std::string fragmentCode(R"(
|
||||
void material(inout MaterialInputs material) {
|
||||
|
||||
@@ -94,10 +94,11 @@ struct alignas(4) MaterialKey {
|
||||
bool hasSheen : 1;
|
||||
bool hasIOR : 1;
|
||||
bool hasVolume : 1;
|
||||
bool hasDispersion : 1;
|
||||
bool hasSpecular : 1;
|
||||
bool hasSpecularTexture : 1;
|
||||
bool hasSpecularColorTexture : 1;
|
||||
bool padding : 2;
|
||||
bool padding : 1;
|
||||
// -- 32 bit boundary --
|
||||
uint8_t specularTextureUV;
|
||||
uint8_t specularColorTextureUV;
|
||||
|
||||
@@ -1397,6 +1397,7 @@ MaterialKey FAssetLoader::getMaterialKey(const cgltf_data* srcAsset,
|
||||
.hasSheen = !!inputMat->has_sheen,
|
||||
.hasIOR = !!inputMat->has_ior,
|
||||
.hasVolume = !!inputMat->has_volume,
|
||||
.hasDispersion = !!inputMat->has_dispersion,
|
||||
.hasSpecular = !!inputMat->has_specular,
|
||||
.hasSpecularTexture = spConfig.specular_texture.texture != nullptr,
|
||||
.hasSpecularColorTexture = spConfig.specular_color_texture.texture != nullptr,
|
||||
@@ -1488,6 +1489,7 @@ MaterialInstance* FAssetLoader::createMaterialInstance(const cgltf_material* inp
|
||||
auto sgConfig = inputMat->pbr_specular_glossiness;
|
||||
auto ccConfig = inputMat->clearcoat;
|
||||
auto trConfig = inputMat->transmission;
|
||||
auto dpConfig = inputMat->dispersion;
|
||||
auto shConfig = inputMat->sheen;
|
||||
auto vlConfig = inputMat->volume;
|
||||
auto spConfig = inputMat->specular;
|
||||
@@ -1671,6 +1673,10 @@ MaterialInstance* FAssetLoader::createMaterialInstance(const cgltf_material* inp
|
||||
}
|
||||
}
|
||||
|
||||
if (matkey.hasDispersion) {
|
||||
mi->setParameter("dispersion", dpConfig.dispersion);
|
||||
}
|
||||
|
||||
// IOR can be implemented as either IOR or reflectance because of ubershaders
|
||||
if (matkey.hasIOR) {
|
||||
if (mi->getMaterial()->hasParameter("ior")) {
|
||||
|
||||
@@ -334,6 +334,12 @@ std::string shaderFromKey(const MaterialKey& config) {
|
||||
)SHADER";
|
||||
}
|
||||
|
||||
if (config.hasDispersion) {
|
||||
shader += R"SHADER(
|
||||
material.dispersion = materialParams.dispersion;
|
||||
)SHADER";
|
||||
}
|
||||
|
||||
if (config.hasSpecular) {
|
||||
shader += R"SHADER(
|
||||
material.specularFactor = materialParams.specularStrength;
|
||||
@@ -599,6 +605,10 @@ Material* createMaterial(Engine* engine, const MaterialKey& config, const UvMap&
|
||||
builder.parameter("ior", MaterialBuilder::UniformType::FLOAT);
|
||||
}
|
||||
|
||||
if (config.hasDispersion) {
|
||||
builder.parameter("dispersion", MaterialBuilder::UniformType::FLOAT);
|
||||
}
|
||||
|
||||
if (config.unlit) {
|
||||
builder.shading(Shading::UNLIT);
|
||||
} else if (config.useSpecularGlossiness) {
|
||||
|
||||
@@ -447,22 +447,22 @@ struct Refraction {
|
||||
float d;
|
||||
};
|
||||
|
||||
void refractionSolidSphere(const PixelParams pixel,
|
||||
void refractionSolidSphere(float etaIR, float etaRI, float thickness,
|
||||
const vec3 n, vec3 r, out Refraction ray) {
|
||||
r = refract(r, n, pixel.etaIR);
|
||||
r = refract(r, n, etaIR);
|
||||
float NoR = dot(n, r);
|
||||
float d = pixel.thickness * -NoR;
|
||||
float d = thickness * -NoR;
|
||||
ray.position = vec3(shading_position + r * d);
|
||||
ray.d = d;
|
||||
vec3 n1 = normalize(NoR * r - n * 0.5);
|
||||
ray.direction = refract(r, n1, pixel.etaRI);
|
||||
ray.direction = refract(r, n1, etaRI);
|
||||
}
|
||||
|
||||
void refractionSolidBox(const PixelParams pixel,
|
||||
void refractionSolidBox(float etaIR, float thickness,
|
||||
const vec3 n, vec3 r, out Refraction ray) {
|
||||
vec3 rr = refract(r, n, pixel.etaIR);
|
||||
vec3 rr = refract(r, n, etaIR);
|
||||
float NoR = dot(n, rr);
|
||||
float d = pixel.thickness / max(-NoR, 0.001);
|
||||
float d = thickness / max(-NoR, 0.001);
|
||||
ray.position = vec3(shading_position + rr * d);
|
||||
ray.direction = r;
|
||||
ray.d = d;
|
||||
@@ -473,15 +473,15 @@ void refractionSolidBox(const PixelParams pixel,
|
||||
#endif
|
||||
}
|
||||
|
||||
void refractionThinSphere(const PixelParams pixel,
|
||||
void refractionThinSphere(float etaIR, float uThickness,
|
||||
const vec3 n, vec3 r, out Refraction ray) {
|
||||
float d = 0.0;
|
||||
#if defined(MATERIAL_HAS_MICRO_THICKNESS)
|
||||
// note: we need the refracted ray to calculate the distance traveled
|
||||
// we could use shading_NoV, but we would lose the dependency on ior.
|
||||
vec3 rr = refract(r, n, pixel.etaIR);
|
||||
vec3 rr = refract(r, n, etaIR);
|
||||
float NoR = dot(n, rr);
|
||||
d = pixel.uThickness / max(-NoR, 0.001);
|
||||
d = uThickness / max(-NoR, 0.001);
|
||||
ray.position = vec3(shading_position + rr * d);
|
||||
#else
|
||||
ray.position = vec3(shading_position);
|
||||
@@ -494,63 +494,80 @@ vec3 evaluateRefraction(
|
||||
const PixelParams pixel,
|
||||
const vec3 n0, vec3 E) {
|
||||
|
||||
Refraction ray;
|
||||
#if REFRACTION_TYPE == REFRACTION_TYPE_THIN
|
||||
// For thin surfaces, the light will bounce off at the second interface in the direction of
|
||||
// the reflection, effectively adding to the specular, but this process will repeat itself.
|
||||
// Each time the ray exits the surface on the front side after the first bounce,
|
||||
// it's multiplied by E^2, and we get: E + E(1-E)^2 + E^3(1-E)^2 + ...
|
||||
// This infinite series converges and is easy to simplify.
|
||||
// Note: we calculate these bounces only on a single component,
|
||||
// since it's a fairly subtle effect.
|
||||
E *= 1.0 + pixel.transmission * (1.0 - E.g) / (1.0 + E.g);
|
||||
#endif
|
||||
|
||||
vec3 Ft;
|
||||
|
||||
#if defined(MATERIAL_HAS_DISPERSION) && (REFRACTION_TYPE == REFRACTION_TYPE_SOLID)
|
||||
for (int i = 0; i < 3; i++) {
|
||||
float etaIR = pixel.etaIR[i];
|
||||
float etaRI = pixel.etaRI[i];
|
||||
#else
|
||||
float etaIR = pixel.etaIR;
|
||||
float etaRI = pixel.etaRI;
|
||||
#endif
|
||||
|
||||
Refraction ray;
|
||||
|
||||
#if REFRACTION_TYPE == REFRACTION_TYPE_SOLID
|
||||
refractionSolidSphere(pixel, n0, -shading_view, ray);
|
||||
refractionSolidSphere(etaIR, etaRI, pixel.thickness, n0, -shading_view, ray);
|
||||
#elif REFRACTION_TYPE == REFRACTION_TYPE_THIN
|
||||
refractionThinSphere(pixel, n0, -shading_view, ray);
|
||||
refractionThinSphere(etaIR, pixel.uThickness, n0, -shading_view, ray);
|
||||
#else
|
||||
#error invalid REFRACTION_TYPE
|
||||
#endif
|
||||
|
||||
// compute transmission T
|
||||
// compute transmission T
|
||||
#if defined(MATERIAL_HAS_ABSORPTION)
|
||||
vec3 T = min(vec3(1.0), exp(-pixel.absorption * ray.d));
|
||||
vec3 T = min(vec3(1.0), exp(-pixel.absorption * ray.d));
|
||||
#endif
|
||||
|
||||
// Roughness remapping so that an IOR of 1.0 means no microfacet refraction and an IOR
|
||||
// of 1.5 has full microfacet refraction
|
||||
float perceptualRoughness = mix(pixel.perceptualRoughnessUnclamped, 0.0,
|
||||
saturate(pixel.etaIR * 3.0 - 2.0));
|
||||
#if REFRACTION_TYPE == REFRACTION_TYPE_THIN
|
||||
// For thin surfaces, the light will bounce off at the second interface in the direction of
|
||||
// the reflection, effectively adding to the specular, but this process will repeat itself.
|
||||
// Each time the ray exits the surface on the front side after the first bounce,
|
||||
// it's multiplied by E^2, and we get: E + E(1-E)^2 + E^3(1-E)^2 + ...
|
||||
// This infinite series converges and is easy to simplify.
|
||||
// Note: we calculate these bounces only on a single component,
|
||||
// since it's a fairly subtle effect.
|
||||
E *= 1.0 + pixel.transmission * (1.0 - E.g) / (1.0 + E.g);
|
||||
#endif
|
||||
// Roughness remapping so that an IOR of 1.0 means no microfacet refraction and an IOR
|
||||
// of 1.5 has full microfacet refraction
|
||||
float perceptualRoughness = mix(pixel.perceptualRoughnessUnclamped, 0.0,
|
||||
saturate(etaIR * 3.0 - 2.0));
|
||||
|
||||
/* sample the cubemap or screen-space */
|
||||
/* sample the cubemap or screen-space */
|
||||
#if REFRACTION_MODE == REFRACTION_MODE_CUBEMAP
|
||||
// when reading from the cubemap, we are not pre-exposed so we apply iblLuminance
|
||||
// which is not the case when we'll read from the screen-space buffer
|
||||
vec3 Ft = prefilteredRadiance(ray.direction, perceptualRoughness) * frameUniforms.iblLuminance;
|
||||
// when reading from the cubemap, we are not pre-exposed so we apply iblLuminance
|
||||
// which is not the case when we'll read from the screen-space buffer
|
||||
vec3 t = prefilteredRadiance(ray.direction, perceptualRoughness) * frameUniforms.iblLuminance;
|
||||
#else
|
||||
vec3 Ft;
|
||||
// compute the point where the ray exits the medium, if needed
|
||||
vec4 p = vec4(getClipFromWorldMatrix() * vec4(ray.position, 1.0));
|
||||
p.xy = uvToRenderTargetUV(p.xy * (0.5 / p.w) + 0.5);
|
||||
|
||||
// compute the point where the ray exits the medium, if needed
|
||||
vec4 p = vec4(getClipFromWorldMatrix() * vec4(ray.position, 1.0));
|
||||
p.xy = uvToRenderTargetUV(p.xy * (0.5 / p.w) + 0.5);
|
||||
|
||||
// distance to camera plane
|
||||
const float invLog2sqrt5 = 0.8614;
|
||||
float lod = max(0.0, (2.0 * log2(perceptualRoughness) + frameUniforms.refractionLodOffset) * invLog2sqrt5);
|
||||
Ft = textureLod(sampler0_ssr, vec3(p.xy, 0.0), lod).rgb;
|
||||
// distance to camera plane
|
||||
const float invLog2sqrt5 = 0.8614;
|
||||
float lod = max(0.0, (2.0 * log2(perceptualRoughness) + frameUniforms.refractionLodOffset) * invLog2sqrt5);
|
||||
vec3 t = textureLod(sampler0_ssr, vec3(p.xy, 0.0), lod).rgb;
|
||||
#endif
|
||||
|
||||
// base color changes the amount of light passing through the boundary
|
||||
Ft *= pixel.diffuseColor;
|
||||
// base color changes the amount of light passing through the boundary
|
||||
t *= pixel.diffuseColor;
|
||||
|
||||
// fresnel from the first interface
|
||||
Ft *= 1.0 - E;
|
||||
// fresnel from the first interface
|
||||
t *= 1.0 - E;
|
||||
|
||||
// apply absorption
|
||||
// apply absorption
|
||||
#if defined(MATERIAL_HAS_ABSORPTION)
|
||||
Ft *= T;
|
||||
t *= T;
|
||||
#endif
|
||||
|
||||
#if defined(MATERIAL_HAS_DISPERSION) && (REFRACTION_TYPE == REFRACTION_TYPE_SOLID)
|
||||
Ft[i] = t[i];
|
||||
}
|
||||
#else
|
||||
Ft = t;
|
||||
#endif
|
||||
|
||||
return Ft;
|
||||
|
||||
@@ -62,8 +62,13 @@ struct PixelParams {
|
||||
#endif
|
||||
|
||||
#if defined(MATERIAL_HAS_REFRACTION)
|
||||
#if defined(MATERIAL_HAS_DISPERSION) && (REFRACTION_TYPE == REFRACTION_TYPE_SOLID)
|
||||
vec3 etaRI;
|
||||
vec3 etaIR;
|
||||
#else
|
||||
float etaRI;
|
||||
float etaIR;
|
||||
#endif
|
||||
float transmission;
|
||||
float uThickness;
|
||||
vec3 absorption;
|
||||
|
||||
@@ -77,6 +77,9 @@ struct MaterialInputs {
|
||||
#if defined(MATERIAL_HAS_TRANSMISSION)
|
||||
float transmission;
|
||||
#endif
|
||||
#if defined(MATERIAL_HAS_DISPERSION) && (REFRACTION_TYPE == REFRACTION_TYPE_SOLID)
|
||||
float dispersion;
|
||||
#endif
|
||||
#if defined(MATERIAL_HAS_IOR)
|
||||
float ior;
|
||||
#endif
|
||||
@@ -178,6 +181,9 @@ void initMaterial(out MaterialInputs material) {
|
||||
#if defined(MATERIAL_HAS_TRANSMISSION)
|
||||
material.transmission = 1.0;
|
||||
#endif
|
||||
#if defined(MATERIAL_HAS_DISPERSION) && (REFRACTION_TYPE == REFRACTION_TYPE_SOLID)
|
||||
material.dispersion = 0.0f;
|
||||
#endif
|
||||
#if defined(MATERIAL_HAS_IOR)
|
||||
material.ior = 1.5;
|
||||
#endif
|
||||
|
||||
@@ -104,13 +104,22 @@ void getCommonPixelParams(const MaterialInputs material, inout PixelParams pixel
|
||||
const float airIor = 1.0;
|
||||
#if !defined(MATERIAL_HAS_IOR)
|
||||
// [common case] ior is not set in the material, deduce it from F0
|
||||
float materialor = f0ToIor(pixel.f0.g);
|
||||
float materialIor = f0ToIor(pixel.f0.g);
|
||||
#else
|
||||
// if ior is set in the material, use it (can lead to unrealistic materials)
|
||||
float materialor = max(1.0, material.ior);
|
||||
float materialIor = max(1.0, material.ior);
|
||||
#endif
|
||||
|
||||
#if defined(MATERIAL_HAS_DISPERSION) && (REFRACTION_TYPE == REFRACTION_TYPE_SOLID)
|
||||
float halfSpread = (materialIor - 1.0) * 0.025 * material.dispersion;
|
||||
vec3 iors = vec3(materialIor - halfSpread, materialIor, materialIor + halfSpread);
|
||||
|
||||
pixel.etaIR = vec3(airIor) / iors; // air -> material
|
||||
pixel.etaRI = iors / vec3(airIor); // material -> air
|
||||
#else
|
||||
pixel.etaIR = airIor / materialIor; // air -> material
|
||||
pixel.etaRI = materialIor / airIor; // material -> air
|
||||
#endif
|
||||
pixel.etaIR = airIor / materialor; // air -> material
|
||||
pixel.etaRI = materialor / airIor; // material -> air
|
||||
#if defined(MATERIAL_HAS_TRANSMISSION)
|
||||
pixel.transmission = saturate(material.transmission);
|
||||
#else
|
||||
|
||||
Reference in New Issue
Block a user