public void getSamples(ShadingState state) { if (getNumSamples() <= 0) return; Vector3 wc = Point3.sub(center, state.getPoint(), new Vector3()); float l2 = wc.LengthSquared(); if (l2 <= r2) return; // inside the sphere? // top of the sphere as viewed from the current shading point float topX = wc.x + state.getNormal().x * radius; float topY = wc.y + state.getNormal().y * radius; float topZ = wc.z + state.getNormal().z * radius; if (state.getNormal().dot(topX, topY, topZ) <= 0) return; // top of the sphere is below the horizon float cosThetaMax = (float)Math.Sqrt(Math.Max(0, 1 - r2 / Vector3.dot(wc, wc))); OrthoNormalBasis basis = OrthoNormalBasis.makeFromW(wc); int samples = state.getDiffuseDepth() > 0 ? 1 : getNumSamples(); float scale = (float)(2 * Math.PI * (1 - cosThetaMax)); Color c = Color.mul(scale / samples, radiance); for (int i = 0; i < samples; i++) { // random offset on unit square double randX = state.getRandom(i, 0, samples); double randY = state.getRandom(i, 1, samples); // cone sampling double cosTheta = (1 - randX) * cosThetaMax + randX; double sinTheta = Math.Sqrt(1 - cosTheta * cosTheta); double phi = randY * 2 * Math.PI; Vector3 dir = new Vector3((float)(Math.Cos(phi) * sinTheta), (float)(Math.Sin(phi) * sinTheta), (float)cosTheta); basis.transform(dir); // check that the direction of the sample is the same as the // normal float cosNx = Vector3.dot(dir, state.getNormal()); if (cosNx <= 0) continue; float ocx = state.getPoint().x - center.x; float ocy = state.getPoint().y - center.y; float ocz = state.getPoint().z - center.z; float qa = Vector3.dot(dir, dir); float qb = 2 * ((dir.x * ocx) + (dir.y * ocy) + (dir.z * ocz)); float qc = ((ocx * ocx) + (ocy * ocy) + (ocz * ocz)) - r2; double[] t = Solvers.solveQuadric(qa, qb, qc); if (t == null) continue; LightSample dest = new LightSample(); // compute shadow ray to the sampled point dest.setShadowRay(new Ray(state.getPoint(), dir)); // FIXME: arbitrary bias, should handle as in other places dest.getShadowRay().setMax((float)t[0] - 1e-3f); // prepare sample dest.setRadiance(c, c); dest.traceShadow(state); state.addSample(dest); } }
public void getSamples(ShadingState state) { if (samples == null) { int n = state.getDiffuseDepth() > 0 ? 1 : numSamples; for (int i = 0; i < n; i++) { // random offset on unit square, we use the infinite version of // getRandom because the light sampling is adaptive double randX = state.getRandom(i, 0, n); double randY = state.getRandom(i, 1, n); int x = 0; while (randX >= colHistogram[x] && x < colHistogram.Length - 1) x++; float[] rowHistogram = imageHistogram[x]; int y = 0; while (randY >= rowHistogram[y] && y < rowHistogram.Length - 1) y++; // sample from (x, y) float u = (float)((x == 0) ? (randX / colHistogram[0]) : ((randX - colHistogram[x - 1]) / (colHistogram[x] - colHistogram[x - 1]))); float v = (float)((y == 0) ? (randY / rowHistogram[0]) : ((randY - rowHistogram[y - 1]) / (rowHistogram[y] - rowHistogram[y - 1]))); float px = ((x == 0) ? colHistogram[0] : (colHistogram[x] - colHistogram[x - 1])); float py = ((y == 0) ? rowHistogram[0] : (rowHistogram[y] - rowHistogram[y - 1])); float su = (x + u) / colHistogram.Length; float sv = (y + v) / rowHistogram.Length; float invP = (float)Math.Sin(sv * Math.PI) * jacobian / (n * px * py); Vector3 dir = getDirection(su, sv); basis.transform(dir); if (Vector3.dot(dir, state.getGeoNormal()) > 0) { LightSample dest = new LightSample(); dest.setShadowRay(new Ray(state.getPoint(), dir)); dest.getShadowRay().setMax(float.MaxValue); Color radiance = texture.getPixel(su, sv); dest.setRadiance(radiance, radiance); dest.getDiffuseRadiance().mul(invP); dest.getSpecularRadiance().mul(invP); dest.traceShadow(state); state.addSample(dest); } } } else { for (int i = 0; i < numSamples; i++) { if (Vector3.dot(samples[i], state.getGeoNormal()) > 0 && Vector3.dot(samples[i], state.getNormal()) > 0) { LightSample dest = new LightSample(); dest.setShadowRay(new Ray(state.getPoint(), samples[i])); dest.getShadowRay().setMax(float.MaxValue); dest.setRadiance(colors[i], colors[i]); dest.traceShadow(state); state.addSample(dest); } } } }
public void getSamples(ShadingState state) { if (lightBounds.contains(state.getPoint()) && state.getPoint().z < maxZ) { int n = state.getDiffuseDepth() > 0 ? 1 : samples; float a = area / n; for (int i = 0; i < n; i++) { // random offset on unit square double randX = state.getRandom(i, 0, n); double randY = state.getRandom(i, 1, n); Point3 p = new Point3(); p.x = (float)(lxmin * (1 - randX) + lxmax * randX); p.y = (float)(lymin * (1 - randY) + lymax * randY); p.z = maxZ - 0.001f; LightSample dest = new LightSample(); // prepare shadow ray to sampled point dest.setShadowRay(new Ray(state.getPoint(), p)); // check that the direction of the sample is the same as the // normal float cosNx = dest.dot(state.getNormal()); if (cosNx <= 0) return; // light source facing point ? // (need to check with light source's normal) float cosNy = dest.getShadowRay().dz; if (cosNy > 0) { // compute geometric attenuation and probability scale // factor float r = dest.getShadowRay().getMax(); float g = cosNy / (r * r); float scale = g * a; // set sample radiance dest.setRadiance(radiance, radiance); dest.getDiffuseRadiance().mul(scale); dest.getSpecularRadiance().mul(scale); dest.traceShadow(state); state.addSample(dest); } } } }