Skip to content

Commit d79d732

Browse files
Clear coat (playcanvas#1977)
* compiles and runs - needs testing & debug * small fixes and example added TODO: clean up example * clean up example * lint spacing fixes * refactor based on review comments * fixes based on code reviews * typo fix * clearCoatGlossiness range changed to 0 - 1 * more fixes * more typos(!) and clean up * remove unecessary code (no-op) Co-authored-by: Will Eastcott <will@playcanvas.com>
1 parent c4c33f6 commit d79d732

31 files changed

+312
-55
lines changed

examples/assets/textures/flakes5c.png

384 KB
Loading

examples/assets/textures/flakes5n.png

696 KB
Loading

examples/assets/textures/flakes5o.png

436 KB
Loading

examples/examples.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,9 @@ var categories = [
2020
"lights",
2121
"lights-baked",
2222
"loader-obj",
23-
"material-physical",
2423
"material-anisotropic",
24+
"material-clear-coat",
25+
"material-physical",
2526
"model-asset",
2627
"model-box",
2728
"model-outline",
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<title>PlayCanvas Clear Coat Material</title>
5+
<meta charset="utf-8">
6+
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
7+
<link rel="icon" type="image/png" href="../playcanvas-favicon.png" />
8+
<script src="../../build/output/playcanvas.js"></script>
9+
<style>
10+
body {
11+
margin: 0;
12+
overflow-y: hidden;
13+
}
14+
</style>
15+
</head>
16+
17+
<body>
18+
<!-- The canvas element -->
19+
<canvas id="application-canvas"></canvas>
20+
21+
<!-- The script -->
22+
<script>
23+
var canvas = document.getElementById("application-canvas");
24+
25+
// Create the application and start the update loop
26+
var app = new pc.Application(canvas);
27+
28+
// Set the canvas to fill the window and automatically change resolution to be the same as the canvas size
29+
app.setCanvasFillMode(pc.FILLMODE_FILL_WINDOW);
30+
app.setCanvasResolution(pc.RESOLUTION_AUTO);
31+
32+
window.addEventListener("resize", function () {
33+
app.resizeCanvas(canvas.width, canvas.height);
34+
});
35+
36+
app.scene.gammaCorrection = pc.GAMMA_SRGB;
37+
app.scene.toneMapping = pc.TONEMAP_ACES;
38+
// Set the skybox to the 128x128 cubemap mipmap level
39+
app.scene.skyboxMip = 1;
40+
41+
// Create an entity with a camera component
42+
var camera = new pc.Entity();
43+
camera.addComponent("camera");
44+
camera.translate(0, 0, 3);
45+
app.root.addChild(camera);
46+
47+
// Create an entity with a directional light component
48+
var light = new pc.Entity();
49+
light.addComponent("light", {
50+
type: "directional",
51+
color: new pc.Color(1, 0.8, 0.25)
52+
});
53+
app.root.addChild(light);
54+
light.setLocalEulerAngles(85, -100, 0);
55+
56+
// Load a cubemap asset. This DDS file was 'prefiltered' in the
57+
// PlayCanvas Editor and then downloaded.
58+
var cubemapAsset = new pc.Asset('helipad.dds', 'cubemap', {
59+
url: "../assets/cubemaps/helipad.dds"
60+
});
61+
app.assets.add(cubemapAsset);
62+
app.assets.load(cubemapAsset);
63+
cubemapAsset.ready(function () {
64+
// Mark the cubemaps as RGBM format since it's an HDR cubemap
65+
cubemapAsset.resources.forEach(function (cubemap) {
66+
cubemap.rgbm = true;
67+
});
68+
app.scene.setSkybox(cubemapAsset.resources);
69+
});
70+
71+
// function to create sphere
72+
var createSphere = function (x, y, z, material) {
73+
var sphere = new pc.Entity();
74+
75+
sphere.addComponent("model", {
76+
material: material,
77+
type: "sphere"
78+
});
79+
sphere.setLocalPosition(x, y, z);
80+
sphere.setLocalScale(0.7, 0.7, 0.7);
81+
app.root.addChild(sphere);
82+
}
83+
84+
// async load a textures, create materials and spheres, then start app
85+
app.assets.loadFromUrl("../assets/textures/flakes5n.png", "texture", function (err, asset) {
86+
var normalMap = asset.resource;
87+
app.assets.loadFromUrl("../assets/textures/flakes5c.png", "texture", function (err, asset) {
88+
var diffuseMap = asset.resource;
89+
app.assets.loadFromUrl("../assets/textures/flakes5o.png", "texture", function (err, asset) {
90+
var otherMap = asset.resource;
91+
92+
var material = new pc.StandardMaterial();
93+
material.diffuseMap = diffuseMap;
94+
material.metalnessMap = otherMap;
95+
material.metalnessMapChannel ='r';
96+
material.glossMap = otherMap;
97+
material.glossMapChannel ='g';
98+
material.normalMap = normalMap;
99+
material.diffuse = new pc.Color(0.6, 0.6, 0.9);
100+
material.diffuseTint = true;
101+
material.metalness = 1.0;
102+
material.shininess = 90.0;
103+
material.bumpiness = 0.7;
104+
material.useMetalness = true;
105+
material.update();
106+
107+
createSphere(-0.5, 0, 0, material);
108+
109+
var clearCoatMaterial = new pc.StandardMaterial();
110+
clearCoatMaterial.diffuseMap = diffuseMap;
111+
clearCoatMaterial.metalnessMap = otherMap;
112+
clearCoatMaterial.metalnessMapChannel ='r';
113+
clearCoatMaterial.glossMap = otherMap;
114+
clearCoatMaterial.glossMapChannel ='g';
115+
clearCoatMaterial.normalMap = normalMap;
116+
clearCoatMaterial.diffuse = new pc.Color(0.6, 0.6, 0.9);
117+
clearCoatMaterial.diffuseTint = true;
118+
clearCoatMaterial.metalness = 1.0;
119+
clearCoatMaterial.shininess = 90;
120+
clearCoatMaterial.bumpiness = 0.7;
121+
clearCoatMaterial.useMetalness = true;
122+
clearCoatMaterial.clearCoat = 0.25;
123+
clearCoatMaterial.clearCoatGlossiness = 0.9;
124+
clearCoatMaterial.update();
125+
126+
createSphere(0.5, 0, 0, clearCoatMaterial);
127+
128+
app.start();
129+
});
130+
});
131+
});
132+
133+
// update things each frame
134+
var time = 0;
135+
app.on("update", function (dt) {
136+
// rotate camera around the objects
137+
time += dt;
138+
camera.setLocalPosition(3 * Math.sin(time*0.5), 0, 3 * Math.cos(time*0.5));
139+
camera.lookAt(pc.Vec3.ZERO);
140+
});
141+
142+
</script>
143+
</body>
144+
</html>
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
vec3 combineColorCC() {
2+
return combineColor()+(ccSpecularLight*ccSpecularity+ccReflection.rgb*ccSpecularity*ccReflection.a);
3+
}

src/graphics/program-lib/chunks/end.frag

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1+
#ifdef CLEARCOAT
2+
gl_FragColor.rgb = combineColorCC();
3+
#else
14
gl_FragColor.rgb = combineColor();
5+
#endif
26
gl_FragColor.rgb += getEmission();
37
gl_FragColor.rgb = addFog(gl_FragColor.rgb);
48
#ifndef HDR

src/graphics/program-lib/chunks/fresnelSchlick.frag

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,11 @@ void getFresnel() {
66
fresnel *= fresnel2 * fresnel2;
77
fresnel *= dGlossiness * dGlossiness;
88
dSpecularity = dSpecularity + (1.0 - dSpecularity) * fresnel;
9+
#ifdef CLEARCOAT
10+
fresnel = 1.0 - max(dot(ccNormalW, dViewDirW), 0.0);
11+
fresnel2 = fresnel * fresnel;
12+
fresnel *= fresnel2 * fresnel2;
13+
fresnel *= ccGlossiness * ccGlossiness;
14+
ccSpecularity = ccSpecularity + (1.0 - ccSpecularity) * fresnel;
15+
#endif
916
}
10-

src/graphics/program-lib/chunks/gloss.frag

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ uniform float material_shininess;
66
uniform sampler2D texture_glossMap;
77
#endif
88

9+
#ifdef CLEARCOAT
10+
uniform float material_clearCoatGlossiness;
11+
#endif
12+
913
void getGlossiness() {
1014
dGlossiness = 1.0;
1115

@@ -22,5 +26,11 @@ void getGlossiness() {
2226
#endif
2327

2428
dGlossiness += 0.0000001;
29+
30+
#ifdef CLEARCOAT
31+
ccGlossiness = 1.0;
32+
ccGlossiness *= material_clearCoatGlossiness;
33+
ccGlossiness += 0.0000001;
34+
#endif
2535
}
2636

src/graphics/program-lib/chunks/lightSpecularAnisoGGX.frag

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
// Anisotropic GGX
2-
float getLightSpecular() {
2+
float calcLightSpecular(float tGlossiness, vec3 tNormalW) {
33
float PI = 3.141592653589793;
4-
float roughness = max((1.0 - dGlossiness) * (1.0 - dGlossiness), 0.001);
4+
float roughness = max((1.0 - tGlossiness) * (1.0 - tGlossiness), 0.001);
55
float anisotropy = material_anisotropy * roughness;
66

77
float at = max((roughness + anisotropy), roughness / 4.0);
88
float ab = max((roughness - anisotropy), roughness / 4.0);
99

1010
vec3 h = normalize(normalize(-dLightDirNormW) + normalize(dViewDirW));
1111

12-
float NoH = dot(dNormalW, h);
12+
float NoH = dot(tNormalW, h);
1313
float ToH = dot(dTBN[0], h);
1414
float BoH = dot(dTBN[1], h);
1515

@@ -23,8 +23,8 @@ float getLightSpecular() {
2323
float BoV = dot(dTBN[1], dViewDirW);
2424
float ToL = dot(dTBN[0], -dLightDirNormW);
2525
float BoL = dot(dTBN[1], -dLightDirNormW);
26-
float NoV = dot(dNormalW, dViewDirW);
27-
float NoL = dot(dNormalW, -dLightDirNormW);
26+
float NoV = dot(tNormalW, dViewDirW);
27+
float NoL = dot(tNormalW, -dLightDirNormW);
2828

2929
float lambdaV = NoL * length(vec3(at * ToV, ab * BoV, NoV));
3030
float lambdaL = NoV * length(vec3(at * ToL, ab * BoL, NoL));
@@ -33,3 +33,10 @@ float getLightSpecular() {
3333
return D * G;
3434
}
3535

36+
float getLightSpecular() {
37+
return calcLightSpecular(dGlossiness, dNormalW);
38+
}
39+
40+
float getLightSpecularCC() {
41+
return calcLightSpecular(ccGlossiness, ccNormalW);
42+
}
Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
// Energy-conserving (hopefully) Blinn-Phong
2-
float getLightSpecular() {
2+
float calcLightSpecular(float tGlossiness, vec3 tNormalW) {
33
vec3 h = normalize( -dLightDirNormW + dViewDirW );
4-
float nh = max( dot( h, dNormalW ), 0.0 );
4+
float nh = max( dot( h, tNormalW ), 0.0 );
55

6-
float specPow = exp2(dGlossiness * 11.0); // glossiness is linear, power is not; 0 - 2048
6+
float specPow = exp2(tGlossiness * 11.0); // glossiness is linear, power is not; 0 - 2048
77
specPow = antiAliasGlossiness(specPow);
88

99
// Hack: On Mac OS X, calling pow with zero for the exponent generates hideous artifacts so bias up a little
@@ -12,3 +12,10 @@ float getLightSpecular() {
1212
return pow(nh, specPow) * (specPow + 2.0) / 8.0;
1313
}
1414

15+
float getLightSpecular() {
16+
return calcLightSpecular(dGlossiness, dNormalW);
17+
}
18+
19+
float getLightSpecularCC() {
20+
return calcLightSpecular(ccGlossiness, ccNormalW);
21+
}
Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
1-
float getLightSpecular() {
2-
float specPow = dGlossiness;
1+
float calcLightSpecular(float tGlossiness, vec3 tReflDirW) {
2+
float specPow = tGlossiness;
33

44
specPow = antiAliasGlossiness(specPow);
55

66
// Hack: On Mac OS X, calling pow with zero for the exponent generates hideous artifacts so bias up a little
7-
return pow(max(dot(dReflDirW, -dLightDirNormW), 0.0), specPow + 0.0001);
7+
return pow(max(dot(tReflDirW, -dLightDirNormW), 0.0), specPow + 0.0001);
88
}
99

10+
float getLightSpecular() {
11+
return calcLightSpecular(dGlossiness, dReflDirW);
12+
}
13+
14+
float getLightSpecularCC() {
15+
return calcLightSpecular(ccGlossiness, ccReflDirW);
16+
}

src/graphics/program-lib/chunks/metalness.frag

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ uniform float material_metalness;
1212
uniform sampler2D texture_metalnessMap;
1313
#endif
1414

15+
#ifdef CLEARCOAT
16+
uniform float material_clearCoatSpecularity;
17+
#endif
18+
1519
void getSpecularity() {
1620
float metalness = 1.0;
1721

@@ -28,5 +32,10 @@ void getSpecularity() {
2832
#endif
2933

3034
processMetalness(metalness);
35+
36+
#ifdef CLEARCOAT
37+
ccSpecularity = vec3(1.0);
38+
ccSpecularity *= material_clearCoatSpecularity;
39+
#endif
3140
}
3241

src/graphics/program-lib/chunks/normalMap.frag

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,7 @@ void getNormal() {
55
dNormalMap = normalMap;
66
normalMap = mix(vec3(0.0, 0.0, 1.0), normalMap, material_bumpiness);
77
dNormalW = normalize(dTBN * normalMap);
8+
#ifdef CLEARCOAT
9+
ccNormalW = normalize(dVertexNormalW);
10+
#endif
811
}
9-

src/graphics/program-lib/chunks/normalMapFast.frag

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,7 @@ void getNormal() {
44
vec3 normalMap = unpackNormal(texture2D(texture_normalMap, $UV));
55
dNormalMap = normalMap;
66
dNormalW = dTBN * normalMap;
7+
#ifdef CLEARCOAT
8+
ccNormalW = normalize(dVertexNormalW);
9+
#endif
710
}
8-
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
void getNormal() {
22
dNormalW = normalize(dVertexNormalW);
3+
#ifdef CLEARCOAT
4+
ccNormalW = dNormalW;
5+
#endif
36
}
47

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
void getReflDir() {
22
dReflDirW = normalize(-reflect(dViewDirW, dNormalW));
3+
#ifdef CLEARCOAT
4+
ccReflDirW = normalize(-reflect(dViewDirW, ccNormalW));
5+
#endif
36
}
47

src/graphics/program-lib/chunks/reflDirAniso.frag

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,8 @@ void getReflDir() {
66
vec3 anisotropicNormal = cross(anisotropicTangent, anisotropicDirection);
77
vec3 bentNormal = normalize(mix(normalize(dNormalW), normalize(anisotropicNormal), anisotropy));
88
dReflDirW = reflect(-dViewDirW, bentNormal);
9+
#ifdef CLEARCOAT
10+
ccReflDirW = normalize(-reflect(dViewDirW, ccNormalW));
11+
#endif
912
}
1013

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
uniform float material_reflectivity;
2+
#ifdef CLEARCOAT
3+
uniform float material_clearCoatReflectivity;
4+
#endif
5+
void addReflection() {
6+
dReflection += calcReflection(dReflDirW, dGlossiness, material_reflectivity);
7+
#ifdef CLEARCOAT
8+
ccReflection += calcReflection(ccReflDirW, ccGlossiness, material_clearCoatReflectivity);
9+
#endif
10+
}
11+
Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
uniform samplerCube texture_cubeMap;
2-
uniform float material_reflectivity;
3-
void addReflection() {
4-
vec3 lookupVec = fixSeams(cubeMapProject(dReflDirW));
2+
vec4 calcReflection(vec3 tReflDirW, float tGlossiness, float tmaterial_reflectivity) {
3+
vec3 lookupVec = fixSeams(cubeMapProject(tReflDirW));
54
lookupVec.x *= -1.0;
6-
dReflection += vec4($textureCubeSAMPLE(texture_cubeMap, lookupVec).rgb, material_reflectivity);
5+
return vec4($textureCubeSAMPLE(texture_cubeMap, lookupVec).rgb, tmaterial_reflectivity);
76
}

0 commit comments

Comments
 (0)