anchor.diff:      return max(vec2(a2, b2) / a2b2, vec2(EPSILON));
   }
   
  -float schlick(vec3 dir, vec3 normal, float n1, float n2){
  -  float r0 = (n1-n2) / (n1+n2);
  -  r0 *= r0;
  -  float cosX = -dot(normal, dir);
  -  if (n1 > n2)
  -  {
  -    float n = n1/n2;
  -    float sinT2 = n*n*(1.0-cosX*cosX);
  -    // Total internal reflection
  -    if (sinT2 > 1.0)
  -       return 1.0;
  -    cosX = sqrt(1.0-sinT2);
  -  }
  -  float x = 1.0-cosX;
  -  return r0+(1.0-r0)*x*x*x*x*x;
  -}
  -
  -vec3 cosineWeightedRandomVec(vec3 normal){
  -  float rr = rnd();
  -  float r = sqrt(rr - EPSILON);
  -  float theta = M_TAU * rnd();
  -  float x = r * cos(theta);
  -  float y = r * sin(theta);
  -  vec3 u = normalize(cross(normal,vec3(0.0,0.0,1.0)));
  -  vec3 v = normalize(cross(u, normal));
  -  return x*u + y*v + sqrt(1.0 - rr)*normal;
  +//-----------------------------------------------------------------------
  +vec3 CosineSampleHemisphere(float u1, float u2)
  +//-----------------------------------------------------------------------
  +{
  +	vec3 dir;
  +	float r = sqrt(u1);
  +	float phi = M_TAU * u2;
  +	dir.x = r * cos(phi);
  +	dir.y = r * sin(phi);
  +	dir.z = sqrt(max(0.0, 1.0 - dir.x*dir.x - dir.y*dir.y));
  +
  +	return dir;
  +}
  +
  +//-----------------------------------------------------------------------
  +float SchlickFresnel(float u)
  +//-----------------------------------------------------------------------
  +{
  +	float m = clamp(1.0 - u, 0.0, 1.0);
  +	float m2 = m * m;
  +	return m2 * m2*m; // pow(m,5)
  +}
  +
  +//-----------------------------------------------------------------------
  +float GTR2(float NDotH, float a)
  +//-----------------------------------------------------------------------
  +{
  +	float a2 = a * a;
  +	float t = 1.0 + (a2 - 1.0)*NDotH*NDotH;
  +	return a2 / (M_PI * t*t);
  +}
  +
  +//-----------------------------------------------------------------------
  +float SmithG_GGX(float NDotv, float alphaG)
  +//-----------------------------------------------------------------------
  +{
  +	float a = alphaG * alphaG;
  +	float b = NDotv * NDotv;
  +	return 1.0 / (NDotv + sqrt(a + b - a * b));
  +}
  +
  +//-----------------------------------------------------------------------
  +float UE4Pdf(vec3 incident, vec3 normal, vec2 matParams, in vec3 bsdfDir)
  +//-----------------------------------------------------------------------
  +{
  +	vec3 n = normal;
  +	vec3 V = -incident;
  +	vec3 L = bsdfDir;
  +
  +	float specularAlpha = max(0.001, matParams.y);
  +
  +	float diffuseRatio = 0.5 * (1.0 - matParams.x);
  +	float specularRatio = 1.0 - diffuseRatio;
  +
  +	vec3 halfVec = normalize(L + V);
  +
  +	float cosTheta = abs(dot(halfVec, n));
  +	float pdfGTR2 = GTR2(cosTheta, specularAlpha) * cosTheta;
  +
  +	// calculate diffuse and specular pdfs and mix ratio
  +	float pdfSpec = pdfGTR2 / (4.0 * abs(dot(L, halfVec)));
  +	float pdfDiff = abs(dot(L, n)) * INV_PI;
  +
  +	// weight pdfs according to ratios
  +	return diffuseRatio * pdfDiff + specularRatio * pdfSpec;
  +}
  +
  +//-----------------------------------------------------------------------
  +vec3 UE4Sample(vec3 incident, vec3 normal, vec2 matParams)
  +//-----------------------------------------------------------------------
  +{
  +	vec3 N = normal;
  +	vec3 V = -incident;
  +
  +	vec3 dir;
  +
  +	float probability = rnd();
  +	float diffuseRatio = 0.5 * (1.0 - matParams.x);
  +
  +	float r1 = rnd();
  +	float r2 = rnd();
  +
  +	vec3 UpVector = abs(N.z) < 0.999 ? vec3(0, 0, 1) : vec3(1, 0, 0);
  +	vec3 TangentX = normalize(cross(UpVector, N));
  +	vec3 TangentY = cross(N, TangentX);
  +
  +	if (probability < diffuseRatio) // sample diffuse
  +	{
  +		dir = CosineSampleHemisphere(r1, r2);
  +		dir = TangentX * dir.x + TangentY * dir.y + N * dir.z;
  +	}
  +	else
  +	{
  +		float a = max(0.001, matParams.y);
  +
  +		float phi = r1 * M_TAU;
  +
  +		float cosTheta = sqrt((1.0 - r2) / (1.0 + (a*a - 1.0) *r2));
  +		float sinTheta = clamp(sqrt(1.0 - (cosTheta * cosTheta)), 0.0, 1.0);
  +		float sinPhi = sin(phi);
  +		float cosPhi = cos(phi);
  +
  +		vec3 halfVec = vec3(sinTheta*cosPhi, sinTheta*sinPhi, cosTheta);
  +		halfVec = TangentX * halfVec.x + TangentY * halfVec.y + N * halfVec.z;
  +
  +		dir = 2.0*dot(V, halfVec)*halfVec - V;
  +	}
  +	return dir;
  +}
  +
  +//-----------------------------------------------------------------------
  +vec3 UE4Eval(vec3 incident, vec3 normal, vec3 diffuseColor, vec2 matParams, in vec3 bsdfDir)
  +//-----------------------------------------------------------------------
  +{
  +	vec3 N = normal;
  +	vec3 V = -incident;
  +	vec3 L = bsdfDir;
  +
  +	float NDotL = dot(N, L);
  +	float NDotV = dot(N, V);
  +	if (NDotL <= 0.0 || NDotV <= 0.0)
  +		return vec3(0.0);
  +
  +	vec3 H = normalize(L + V);
  +	float NDotH = dot(N, H);
  +	float LDotH = dot(L, H);
  +
  +	// specular	
  +	float specular = 0.5;
  +	vec3 specularCol = mix(vec3(0.2), diffuseColor, matParams.x);
  +	float a = max(0.001, matParams.y);
  +	float Ds = GTR2(NDotH, a);
  +	float FH = SchlickFresnel(LDotH);
  +	vec3 Fs = mix(specularCol, vec3(1.0), FH);
  +	float roughg = (matParams.y*0.5 + 0.5);
  +	roughg = roughg * roughg;
  +	float Gs = SmithG_GGX(NDotL, roughg) * SmithG_GGX(NDotV, roughg);
  +
  +	return diffuseColor * INV_PI * (1.0 - matParams.x) + Gs * Fs*Ds;
   }
   
   // Moller-Trumbore
  
positive.code: const int NUM_BOUNCES = 5;
  const float MAX_T = 100000.0;
  const float EPSILON = 0.000001;
  const float SQRT_EPSILON = sqrt(EPSILON);
  const float M_PI = 3.14159265;
  const float M_TAU = M_PI * 2.0;
  const float INV_PI = 1.0 / M_PI;
  
negative.code:     #endif
      TexCoords texCoords = createTexCoords(result.index);
      vec2 texCoord = barycentricTexCoord(weights, texCoords);
  	  vec4 texRaw = texture(texArray, vec3(texCoord, mat.mapIndices.diffuse));
  	  vec3 texEmmissive = texture(texArray, vec3(texCoord, mat.mapIndices.specular)).rgb;
      vec3 texDiffuse = texRaw.rgb;
  	  vec4 texMetallicRoughness = texture(texArray, vec3(texCoord, mat.mapIndices.roughness));
  	  float texEmmissiveScale = texMetallicRoughness.b;
      vec3 texNormal = (texture(texArray, vec3(texCoord, mat.mapIndices.normal)).rgb - vec3(0.5, 0.5, 0.0)) * vec3(2.0, 2.0, 1.0);
      texMetallicRoughness.g *= texMetallicRoughness.g;
      seed = origin.x * randBase + origin.y * 1.396529836 + origin.z * 4761.52835;
      vec3 baryNormal;
      vec3 macroNormal = barycentricNormal(weights, createNormals(result.index), texNormal, baryNormal);
      vec3 lightDir;
      ray.origin = origin + baryNormal * EPSILON * 2.0;
  
      //texDiffuse = specular ? vec3(1.0) : texDiffuse;
      // TODO: make this configurable in the materials
      color += accumulatedReflectance * texEmmissive * 10.0;
  
      #ifdef USE_EXPLICIT
      vec3 direct = getDirectEmission(ray.origin, macroNormal, lightDir);
      #else
  