use of org.sunflow.math.Vector3 in project joons-renderer by joonhyublee.
the class TriangleMeshLight method getSamples.
public void getSamples(ShadingState state) {
if (numSamples == 0) {
return;
}
Vector3 n = state.getNormal();
Point3 p = state.getPoint();
for (int tri3 = 0, i = 0; tri3 < triangles.length; tri3 += 3, i++) {
// vector towards each vertex of the light source
Vector3 p0 = Point3.sub(getPoint(triangles[tri3 + 0]), p, new Vector3());
// cull triangle if it is facing the wrong way
if (Vector3.dot(p0, ngs[i]) >= 0) {
continue;
}
Vector3 p1 = Point3.sub(getPoint(triangles[tri3 + 1]), p, new Vector3());
Vector3 p2 = Point3.sub(getPoint(triangles[tri3 + 2]), p, new Vector3());
// if all three vertices are below the hemisphere, stop
if (Vector3.dot(p0, n) <= 0 && Vector3.dot(p1, n) <= 0 && Vector3.dot(p2, n) <= 0) {
continue;
}
p0.normalize();
p1.normalize();
p2.normalize();
float dot = Vector3.dot(p2, p0);
Vector3 h = new Vector3();
h.x = p2.x - dot * p0.x;
h.y = p2.y - dot * p0.y;
h.z = p2.z - dot * p0.z;
float hlen = h.length();
if (hlen > 1e-6f) {
h.div(hlen);
} else {
continue;
}
Vector3 n0 = Vector3.cross(p0, p1, new Vector3());
float len0 = n0.length();
if (len0 > 1e-6f) {
n0.div(len0);
} else {
continue;
}
Vector3 n1 = Vector3.cross(p1, p2, new Vector3());
float len1 = n1.length();
if (len1 > 1e-6f) {
n1.div(len1);
} else {
continue;
}
Vector3 n2 = Vector3.cross(p2, p0, new Vector3());
float len2 = n2.length();
if (len2 > 1e-6f) {
n2.div(len2);
} else {
continue;
}
float cosAlpha = MathUtils.clamp(-Vector3.dot(n2, n0), -1.0f, 1.0f);
float cosBeta = MathUtils.clamp(-Vector3.dot(n0, n1), -1.0f, 1.0f);
float cosGamma = MathUtils.clamp(-Vector3.dot(n1, n2), -1.0f, 1.0f);
float alpha = (float) Math.acos(cosAlpha);
float beta = (float) Math.acos(cosBeta);
float gamma = (float) Math.acos(cosGamma);
float area = alpha + beta + gamma - (float) Math.PI;
float cosC = MathUtils.clamp(Vector3.dot(p0, p1), -1.0f, 1.0f);
float salpha = (float) Math.sin(alpha);
float product = salpha * cosC;
// use lower sampling depth for diffuse bounces
int samples = state.getDiffuseDepth() > 0 ? 1 : numSamples;
Color c = Color.mul(area / samples, radiance);
for (int j = 0; j < samples; j++) {
// random offset on unit square
double randX = state.getRandom(j, 0, samples);
double randY = state.getRandom(j, 1, samples);
float phi = (float) randX * area - alpha + (float) Math.PI;
float sinPhi = (float) Math.sin(phi);
float cosPhi = (float) Math.cos(phi);
float u = cosPhi + cosAlpha;
float v = sinPhi - product;
float q = (-v + cosAlpha * (cosPhi * -v + sinPhi * u)) / (salpha * (sinPhi * -v - cosPhi * u));
float q1 = 1.0f - q * q;
if (q1 < 0.0f) {
q1 = 0.0f;
}
float sqrtq1 = (float) Math.sqrt(q1);
float ncx = q * p0.x + sqrtq1 * h.x;
float ncy = q * p0.y + sqrtq1 * h.y;
float ncz = q * p0.z + sqrtq1 * h.z;
dot = p1.dot(ncx, ncy, ncz);
float z = 1.0f - (float) randY * (1.0f - dot);
float z1 = 1.0f - z * z;
if (z1 < 0.0f) {
z1 = 0.0f;
}
Vector3 nd = new Vector3();
nd.x = ncx - dot * p1.x;
nd.y = ncy - dot * p1.y;
nd.z = ncz - dot * p1.z;
nd.normalize();
float sqrtz1 = (float) Math.sqrt(z1);
Vector3 result = new Vector3();
result.x = z * p1.x + sqrtz1 * nd.x;
result.y = z * p1.y + sqrtz1 * nd.y;
result.z = z * p1.z + sqrtz1 * nd.z;
// the right direction
if (Vector3.dot(result, n) > 0 && Vector3.dot(result, state.getGeoNormal()) > 0 && Vector3.dot(result, ngs[i]) < 0) {
// compute intersection with triangle (if any)
Ray shadowRay = new Ray(state.getPoint(), result);
if (!intersectTriangleKensler(tri3, shadowRay)) {
continue;
}
LightSample dest = new LightSample();
dest.setShadowRay(shadowRay);
// prepare sample
dest.setRadiance(c, c);
dest.traceShadow(state);
state.addSample(dest);
}
}
}
}
use of org.sunflow.math.Vector3 in project joons-renderer by joonhyublee.
the class ShadingState method occlusion.
/**
* Ambient occlusion routine, returns a value between bright and dark
* depending on the amount of geometric occlusion in the scene.
*
* @param samples number of sample rays
* @param maxDist maximum length of the rays
* @param bright color when nothing is occluded
* @param dark color when fully occluded
* @return occlusion color
*/
public final Color occlusion(int samples, float maxDist, Color bright, Color dark) {
if (n == null) {
// in case we got called on a geometry without orientation
return bright;
}
// make sure we are on the right side of the material
faceforward();
OrthoNormalBasis onb = getBasis();
Vector3 w = new Vector3();
Color result = Color.black();
for (int i = 0; i < samples; i++) {
float xi = (float) getRandom(i, 0, samples);
float xj = (float) getRandom(i, 1, samples);
float phi = (float) (2 * Math.PI * xi);
float cosPhi = (float) Math.cos(phi);
float sinPhi = (float) Math.sin(phi);
float sinTheta = (float) Math.sqrt(xj);
float cosTheta = (float) Math.sqrt(1.0f - xj);
w.x = cosPhi * sinTheta;
w.y = sinPhi * sinTheta;
w.z = cosTheta;
onb.transform(w);
Ray r = new Ray(p, w);
r.setMax(maxDist);
result.add(Color.blend(bright, dark, traceShadow(r)));
}
return result.mul(1.0f / samples);
}
use of org.sunflow.math.Vector3 in project joons-renderer by joonhyublee.
the class ShadingState method init.
/**
* Create objects needed for surface shading: point, normal, texture
* coordinates and basis.
*/
public final void init() {
p = new Point3();
n = new Vector3();
tex = new Point2();
ng = new Vector3();
basis = null;
}
use of org.sunflow.math.Vector3 in project joons-renderer by joonhyublee.
the class ShadingState method specularPhong.
/**
* Computes a phong specular response to the current light samples and
* global illumination.
*
* @param spec specular color
* @param power phong exponent
* @param numRays number of glossy rays to trace
* @return shaded color
*/
public final Color specularPhong(Color spec, float power, int numRays) {
// integrate a phong specular function
Color lr = Color.black();
if (!includeSpecular || spec.isBlack()) {
return lr;
}
// reflected direction
float dn = 2 * cosND;
Vector3 refDir = new Vector3();
refDir.x = (dn * n.x) + r.dx;
refDir.y = (dn * n.y) + r.dy;
refDir.z = (dn * n.z) + r.dz;
// direct lighting
for (LightSample sample : this) {
float cosNL = sample.dot(n);
float cosLR = sample.dot(refDir);
if (cosLR > 0) {
lr.madd(cosNL * (float) Math.pow(cosLR, power), sample.getSpecularRadiance());
}
}
// indirect lighting
if (numRays > 0) {
int numSamples = getDepth() == 0 ? numRays : 1;
OrthoNormalBasis onb = OrthoNormalBasis.makeFromW(refDir);
float mul = (2.0f * (float) Math.PI / (power + 1)) / numSamples;
for (int i = 0; i < numSamples; i++) {
// specular indirect lighting
double r1 = getRandom(i, 0, numSamples);
double r2 = getRandom(i, 1, numSamples);
double u = 2 * Math.PI * r1;
double s = (float) Math.pow(r2, 1 / (power + 1));
double s1 = (float) Math.sqrt(1 - s * s);
Vector3 w = new Vector3((float) (Math.cos(u) * s1), (float) (Math.sin(u) * s1), (float) s);
w = onb.transform(w, new Vector3());
float wn = Vector3.dot(w, n);
if (wn > 0) {
lr.madd(wn * mul, traceGlossy(new Ray(p, w), i));
}
}
}
lr.mul(spec).mul((power + 2) / (2.0f * (float) Math.PI));
return lr;
}
use of org.sunflow.math.Vector3 in project joons-renderer by joonhyublee.
the class UniformGrid method build.
@Override
public void build(PrimitiveList primitives) {
Timer t = new Timer();
t.start();
this.primitives = primitives;
int n = primitives.getNumPrimitives();
// compute bounds
bounds = primitives.getWorldBounds(null);
// create grid from number of objects
bounds.enlargeUlps();
Vector3 w = bounds.getExtents();
double s = Math.pow((w.x * w.y * w.z) / n, 1 / 3.0);
nx = MathUtils.clamp((int) ((w.x / s) + 0.5), 1, 128);
ny = MathUtils.clamp((int) ((w.y / s) + 0.5), 1, 128);
nz = MathUtils.clamp((int) ((w.z / s) + 0.5), 1, 128);
voxelwx = w.x / nx;
voxelwy = w.y / ny;
voxelwz = w.z / nz;
invVoxelwx = 1 / voxelwx;
invVoxelwy = 1 / voxelwy;
invVoxelwz = 1 / voxelwz;
UI.printDetailed(Module.ACCEL, "Creating grid: %dx%dx%d ...", nx, ny, nz);
IntArray[] buildCells = new IntArray[nx * ny * nz];
// add all objects into the grid cells they overlap
int[] imin = new int[3];
int[] imax = new int[3];
int numCellsPerObject = 0;
for (int i = 0; i < n; i++) {
getGridIndex(primitives.getPrimitiveBound(i, 0), primitives.getPrimitiveBound(i, 2), primitives.getPrimitiveBound(i, 4), imin);
getGridIndex(primitives.getPrimitiveBound(i, 1), primitives.getPrimitiveBound(i, 3), primitives.getPrimitiveBound(i, 5), imax);
for (int ix = imin[0]; ix <= imax[0]; ix++) {
for (int iy = imin[1]; iy <= imax[1]; iy++) {
for (int iz = imin[2]; iz <= imax[2]; iz++) {
int idx = ix + (nx * iy) + (nx * ny * iz);
if (buildCells[idx] == null) {
buildCells[idx] = new IntArray();
}
buildCells[idx].add(i);
numCellsPerObject++;
}
}
}
}
UI.printDetailed(Module.ACCEL, "Building cells ...");
int numEmpty = 0;
int numInFull = 0;
cells = new int[nx * ny * nz][];
int i = 0;
for (IntArray cell : buildCells) {
if (cell != null) {
if (cell.getSize() == 0) {
numEmpty++;
cell = null;
} else {
cells[i] = cell.trim();
numInFull += cell.getSize();
}
} else {
numEmpty++;
}
i++;
}
t.end();
UI.printDetailed(Module.ACCEL, "Uniform grid statistics:");
UI.printDetailed(Module.ACCEL, " * Grid cells: %d", cells.length);
UI.printDetailed(Module.ACCEL, " * Used cells: %d", cells.length - numEmpty);
UI.printDetailed(Module.ACCEL, " * Empty cells: %d", numEmpty);
UI.printDetailed(Module.ACCEL, " * Occupancy: %.2f%%", 100.0 * (cells.length - numEmpty) / cells.length);
UI.printDetailed(Module.ACCEL, " * Objects/Cell: %.2f", (double) numInFull / (double) cells.length);
UI.printDetailed(Module.ACCEL, " * Objects/Used Cell: %.2f", (double) numInFull / (double) (cells.length - numEmpty));
UI.printDetailed(Module.ACCEL, " * Cells/Object: %.2f", (double) numCellsPerObject / (double) n);
UI.printDetailed(Module.ACCEL, " * Build time: %s", t.toString());
}
Aggregations