解答:編輯 fragmentShader 變數,在 vec3 normal = normalize( vNormal ); 之後替換為:

for ( int i = 0; i < 2; i++) {
	vec3 offset = (i==0) ? vWorldPosition : -vWorldPosition;
	offset.y = 0.0;
	vec3 jiggledNormal = normalize( normal + uGroove * normalize( offset ) );

	// 漫射:N * L。法向量必須正規化,因為經過內插計算
	float diffuse = max( dot( jiggledNormal, lVector ), 0.0);

	// 漫射貢獻減半,因為有兩個法向量
	gl_FragColor.rgb += 0.5 * uKd * uMaterialColor * uDirLightColor * diffuse;

	// 鏡面反射:N * H 的指數。H 是往光的向量加上往攝影機的向量正規化
	vec3 viewPosition = normalize( vViewPosition );
	vec3 pointHalfVector = normalize( lVector + viewPosition );
	float pointDotNormalHalf = max( dot( jiggledNormal, pointHalfVector ), 0.0 );
	float specular = uKs * pow( pointDotNormalHalf, shininess );
	specular *= diffuse * (2.0 + shininess)/8.0;

	// 這樣可能會切斷高光,但是比奇怪的閃爍好
	if (diffuse <= 0.0) { specular = 0.0; }

	// 鏡面反射貢獻減半,因為有兩個法向量
	gl_FragColor.rgb += 0.5 * uDirLightColor * uSpecularColor * specular;
}