public ScatteredRay scatter(SurfacePoint x, Vector3 v, boolean adjoint,
WavelengthPacket lambda, double ru, double rv, double rj) {
double n1 = riAbove;
double n2 = riBelow;
Vector3 N = x.getNormal();
double R = Optics.reflectance(v, n1, n2, N);
boolean fromSide = (v.dot(N) < 0.0);
boolean toSide;
Vector3 w;
double specularity;
if (RandomUtil.bernoulli(R, rj)) {
toSide = fromSide;
specularity = fromSide ? n11 : n22;
w = Optics.reflect(v, N);
} else {
toSide = !fromSide;
specularity = fromSide ? n12 : n21;
w = Optics.refract(v, n1, n2, N);
}
if (!Double.isInfinite(specularity)) {
Basis3 basis = Basis3.fromW(w);
do {
SphericalCoordinates perturb = new SphericalCoordinates(
Math.acos(Math.pow(1.0 - ru, 1.0 / (specularity + 1.0))),
2.0 * Math.PI * rv);
w = perturb.toCartesian(basis);
} while ((w.dot(N) > 0.0) != toSide);
}
return new ScatteredRay(new Ray3(x.getPosition(), w),
lambda.getColorModel().getWhite(lambda),
Double.isInfinite(specularity) ? Type.SPECULAR : Type.DIFFUSE,