*/
public static final void pathTrace(Scene scene, Ray ray, WorkerState state,
int addEmitted, boolean first) {
Random random = state.random;
Ray reflected = state.rayPool.get();
Ray transmitted = state.rayPool.get();
Ray refracted = state.rayPool.get();
Vector3d ox = state.vectorPool.get(ray.x);
Vector3d od = state.vectorPool.get(ray.d);
double s = 0;
while (true) {
if (!RayTracer.nextIntersection(scene, ray, state)) {
if (ray.depth == 0) {
// direct sky hit
scene.sky.getSkyColorInterpolated(ray, scene.waterHeight > 0);
} else if (ray.specular) {
// sky color
scene.sky.getSkySpecularColor(ray, scene.waterHeight > 0);
} else {
scene.sky.getSkyColor(ray, scene.waterHeight > 0);
}
break;
}
double pSpecular = 0;
Block currentBlock = ray.getCurrentBlock();
Block prevBlock = ray.getPrevBlock();
if (!scene.stillWater && ray.n.y != 0 &&
((currentBlock == Block.WATER && prevBlock == Block.AIR) ||
(currentBlock == Block.AIR && prevBlock == Block.WATER))) {
WaterModel.doWaterDisplacement(ray);
if (currentBlock == Block.AIR) {
ray.n.y = -ray.n.y;
}
}
if (currentBlock.isShiny) {
if (currentBlock == Block.WATER) {
pSpecular = Scene.WATER_SPECULAR;
} else {
pSpecular = Scene.SPECULAR_COEFF;
}
}
double pDiffuse = ray.color.w;
float n1 = prevBlock.ior;
float n2 = currentBlock.ior;
if (pDiffuse + pSpecular < Ray.EPSILON && n1 == n2)
continue;
if (first) {
s = ray.distance;
first = false;
}
if (currentBlock.isShiny &&
random.nextDouble() < pSpecular) {
reflected.specularReflection(ray);
if (!scene.kill(reflected, random)) {
pathTrace(scene, reflected, state, 1, false);
if (reflected.hit) {
ray.color.x *= reflected.color.x;
ray.color.y *= reflected.color.y;
ray.color.z *= reflected.color.z;
ray.hit = true;
}
}
} else {
if (random.nextDouble() < pDiffuse) {
reflected.set(ray);
if (!scene.kill(reflected, random)) {
double emittance = 0;
if (scene.emittersEnabled && currentBlock.isEmitter) {
emittance = addEmitted;
ray.emittance.x = ray.color.x * ray.color.x *
currentBlock.emittance * scene.emitterIntensity;
ray.emittance.y = ray.color.y * ray.color.y *
currentBlock.emittance * scene.emitterIntensity;
ray.emittance.z = ray.color.z * ray.color.z *
currentBlock.emittance * scene.emitterIntensity;
ray.hit = true;
}
if (scene.sunEnabled) {
scene.sun.getRandomSunDirection(reflected, random, state.vectorPool);
double directLightR = 0;
double directLightG = 0;
double directLightB = 0;
boolean frontLight = reflected.d.dot(ray.n) > 0;
if (frontLight || (currentBlock.subSurfaceScattering &&
random.nextDouble() < Scene.fSubSurface)) {
if (!frontLight) {
reflected.x.scaleAdd(-Ray.OFFSET, ray.n, reflected.x);
}
reflected.currentMaterial = ray.prevMaterial;
getDirectLightAttenuation(scene, reflected, state);
Vector4d attenuation = state.attenuation;
if (attenuation.w > 0) {
double mult = QuickMath.abs(reflected.d.dot(ray.n));
directLightR = attenuation.x*attenuation.w * mult;
directLightG = attenuation.y*attenuation.w * mult;
directLightB = attenuation.z*attenuation.w * mult;
ray.hit = true;
}
}
reflected.diffuseReflection(ray, random);
pathTrace(scene, reflected, state, 0, false);
ray.hit = ray.hit || reflected.hit;
if (ray.hit) {
ray.color.x = ray.color.x
* (emittance + directLightR * scene.sun.emittance.x
+ (reflected.color.x + reflected.emittance.x));
ray.color.y = ray.color.y
* (emittance + directLightG * scene.sun.emittance.y
+ (reflected.color.y + reflected.emittance.y));
ray.color.z = ray.color.z
* (emittance + directLightB * scene.sun.emittance.z
+ (reflected.color.z + reflected.emittance.z));
}
} else {
reflected.diffuseReflection(ray, random);
pathTrace(scene, reflected, state, 0, false);
ray.hit = ray.hit || reflected.hit;
if (ray.hit) {
ray.color.x = ray.color.x
* (emittance + (reflected.color.x + reflected.emittance.x));
ray.color.y = ray.color.y
* (emittance + (reflected.color.y + reflected.emittance.y));
ray.color.z = ray.color.z
* (emittance + (reflected.color.z + reflected.emittance.z));
}
}
}
} else if (n1 != n2) {
boolean doRefraction =
currentBlock == Block.WATER ||
prevBlock == Block.WATER ||
currentBlock == Block.ICE ||
prevBlock == Block.ICE;
// refraction
float n1n2 = n1 / n2;
double cosTheta = - ray.n.dot(ray.d);
double radicand = 1 - n1n2*n1n2 * (1 - cosTheta*cosTheta);
if (doRefraction && radicand < Ray.EPSILON) {
// total internal reflection
reflected.specularReflection(ray);
if (!scene.kill(reflected, random)) {
pathTrace(scene, reflected, state, 1, false);
if (reflected.hit) {
ray.color.x = reflected.color.x;
ray.color.y = reflected.color.y;
ray.color.z = reflected.color.z;
ray.hit = true;
}
}
} else {
refracted.set(ray);
if (!scene.kill(refracted, random)) {
// Calculate angle-dependent reflectance using
// Fresnel equation approximation
// R(theta) = R0 + (1 - R0) * (1 - cos(theta))^5