Skip to content

Commit 792c7ca

Browse files
committed
Refactor clearcoat extension to look similar to Appendix B.
1 parent f513c9e commit 792c7ca

File tree

1 file changed

+77
-12
lines changed
  • extensions/2.0/Khronos/KHR_materials_clearcoat

1 file changed

+77
-12
lines changed

extensions/2.0/Khronos/KHR_materials_clearcoat/README.md

Lines changed: 77 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@ The PBR clearcoat materials are defined by adding the `KHR_materials_clearcoat`
5353
### Clearcoat
5454

5555
The following parameters are contributed by the `KHR_materials_clearcoat` extension:
56-
5756
| | Type | Description | Required |
5857
|----------------------------------|---------------------------------------------------------------------------------|----------------------------------------|----------------------|
5958
|**clearcoatFactor** | `number` | The clearcoat layer intensity. | No, default: `0.0` |
@@ -62,34 +61,100 @@ The following parameters are contributed by the `KHR_materials_clearcoat` extens
6261
|**clearcoatRoughnessTexture** | [`textureInfo`](/specification/2.0/README.md#reference-textureInfo) | The clearcoat layer roughness texture. | No |
6362
|**clearcoatNormalTexture** | [`normalTextureInfo`](/specification/2.0/README.md#reference-normaltextureinfo) | The clearcoat normal map texture. | No |
6463

65-
The clearcoat BRDF is layered on top of the glTF 2.0 Metallic-Roughness material, including emission and all extensions. The `clearcoatFactor` determines the view-independent intensity of the clearcoat BRDF. If `clearcoatFactor` (in the extension) is zero, the whole clearcoat layer is disabled. Implementations of the BRDF itself can vary based on device performance and resource constraints. As in the specular term of the glTF 2.0 Metallic-Roughness material, the roughness input is squared to make it perceptually linear, and the Fresnel term uses a fixed refractive index of 1.5, corresponding to a F0 of 0.04.
64+
If `clearcoatFactor` (in the extension) is zero, the whole clearcoat layer is disabled.
6665

6766
The values for clearcoat layer intensity and clearcoat roughness can be defined using factors, textures, or both. If the `clearcoatTexture` or `clearcoatRoughnessTexture` is not given, respective texture components are assumed to have a value of 1.0. All clearcoat textures contain RGB components in linear space. If both factors and textures are present, the factor value acts as a linear multiplier for the corresponding texture values.
6867

68+
```
69+
clearcoat = clearcoatFactor * clearcoatTexture.r
70+
clearcoatRoughness = clearcoatRoughnessFactor * clearcoatRoughnessTexture.g
71+
```
72+
6973
If `clearcoatNormalTexture` is not given, no normal mapping is applied to the clear coat layer, even if normal mapping is applied to the base material. Otherwise, `clearcoatNormalTexture` may be a reference to the same normal map used by the base material, or any other compatible normal map.
7074

71-
## Implementation Notes
75+
The clearcoat effect is modeled via a microfacet BRDF. The BRDF is layered on top of the glTF 2.0 Metallic-Roughness material, including emission and all extensions, using a new `fresnel_coat` function:
76+
77+
```
78+
# from glTF 2.0 Metallic-Roughness material (core)
79+
material = mix(dielectric_brdf, metal_brdf, metallic)
80+
81+
# clearcoat
82+
clearcoat_brdf = specular_brdf(
83+
normal = clearcoatNormal,
84+
α = clearcoatRoughness^2)
85+
86+
# layering
87+
coated_material =
88+
fresnel_coat(
89+
normal = clearcoatNormal,
90+
ior = 1.5,
91+
weight = clearcoat,
92+
base = material,
93+
layer = clearcoat_brdf)
94+
```
95+
96+
The `fresnel_coat` function adds a BRDF as a layer on top of another BSDF according to `weight` and a Fresnel term. The layer is weighted with `weight*fresnel(ior)`. The base is weighted with `1-(weight*fresnel(ior))`. `normal` is a float3 vector that affects the top layer, but not the base.
97+
98+
### Implementation
7299

73100
*This section is non-normative.*
74101

75-
All implementations should use the same calculations for the BRDF inputs. See [Appendix B](/specification/2.0/README.md#appendix-b-brdf-implementation) for more details on the BRDF calculations.
102+
Implementations of the BRDF or the layering operator can vary based on device performance and resource constraints.
103+
104+
#### Clearcoat BRDF
105+
106+
The specular BRDF for the clearcoat layer `clearcoat_brdf` is computed using the specular term from the glTF 2.0 Metallic-Roughness material, defined in [Appendix B](/specification/2.0/README.md#appendix-b-brdf-implementation). Roughness and normal are taken from `clearcoatNormal` and `clearcoatRoughness`.
76107

77-
The clearcoat formula `f_clearcoat` is computed using the specular term from the glTF 2.0 Metallic-Roughness material, defined in [Appendix B](/specification/2.0/README.md#appendix-b-brdf-implementation). Use specular F0 equal `0.04`, base color black `0.0, 0.0, 0.0`, metallic value `0.0`, and the clearcoat roughness value defined in this extension as follows:
108+
#### Layering
109+
110+
The `fresnel_coat` function is computed using the Schlick Fresnel term from the glTF 2.0 Metallic-Roughness material, defined in [Appendix B](/specification/2.0/README.md#appendix-b-brdf-implementation).
78111

79112
```
80-
clearcoatRoughness = clearcoatRoughnessFactor * clearcoatRoughnessTexture.g
113+
function fresnel_coat(normal, ior, weight, base, layer) {
114+
f0 = ((1-ior)/(1+ior))^2
115+
fr = f0 + (1 - f0)*(1 - abs(NdotV))^5 // N = normal
116+
return mix(base, layer, weight * fr)
117+
}
81118
```
82119

83-
The following abstract code describes how the base and clearcoat layers should be blended together:
84-
120+
Applying the functions we arrive at the coated material
121+
122+
```
123+
coated_material = mix(material, clearcoat_brdf(clearcoatRughness^2), clearcoat * (0.04 + (1 - 0.04) * (1 - NdotV)^5))
124+
```
125+
126+
and finally, substituting and simplifying, using some symbols from [Appendix B](/specification/2.0/README.md#appendix-b-brdf-implementation) and `Nc` for the clearcoat normal:
127+
85128
```
86-
clearcoatBlendFactor = clearcoatTexture.r * clearcoatFactor
87-
clearcoatFresnel = fresnel(0.04, NdotV)
129+
clearcoatFresnel = 0.04 + (1 - 0.04) * (1 - abs(VdotNc))^5
130+
clearcoatAlpha = clearcoatRoughness^2
131+
132+
f_clearcoat = clearcoatFresnel * D(clearcoatAlpha) * G / (4 * abs(VdotNc) * abs(LdotNc))
88133
89-
color = (f_emissive + f_diffuse + f_specular) * (1.0 - clearcoatBlendFactor * clearcoatFresnel) +
90-
f_clearcoat * clearcoatBlendFactor
134+
coated_material = (f_diffuse + f_specular) * (1 - clearcoat * clearcoatFresnel) +
135+
f_clearcoat * clearcoat
91136
```
92137

138+
#### Emission
139+
140+
The clearcoat layer is on top of emission in the layering stack. Consequently, the emission is darkened by the Fresnel term.
141+
142+
```
143+
coated_emission = emission * (0.04 + (1 - 0.04) * (1 - NdotV)^5)
144+
```
145+
146+
#### Discussion
147+
148+
In order to make the material energy conserving with a simple layering function, we compute the microfacet Fresnel term with `NdotV` instead of `VdotH`. That means that we ignore the orientation of the microsurface. As the clearcoat roughness is usually very low the microfacets orientation is very close to the normal direction, and `NdotV ≈ NdotL`.
149+
150+
The simple layering function ignores many effects that occur between clearcoat and base layer. For example:
151+
* The clearcoat layer is assumed to be infinitely thin. There is no refraction.
152+
* The index of refraction of clearcoat and base layer do not influence each other. The Fresnel terms are computed independently.
153+
* There is no scattering between layers.
154+
* There is no diffraction.
155+
156+
More sophisticated layering techniques that improve the accuracy of the renderings are described in [Appendix B](/specification/2.0/README.md#appendix-b-brdf-implementation).
157+
93158
## Schema
94159

95160
- [glTF.KHR_materials_clearcoat.schema.json](schema/glTF.KHR_materials_clearcoat.schema.json)

0 commit comments

Comments
 (0)