Renderer(int line) { startLine = line; if (!scene.antiAliasing) { sampled = false; samples = Math.max(scene.raysPerPixel, DEBUG_samples); // Force it to be a perfect square samples = (int) Math.sqrt(samples); samples = samples * samples; imageSamples = new Double3D[samples]; for (int i = 0; i < samples; i++) imageSamples[i] = new Double3D(0, 0, 0); // Make samples are in the range [0,1] Sample.multiJitter(imageSamples, samples); // Make samples are in the range [-2,2] Sample.cubicSplineFilter(imageSamples, samples); // Scale image samples to [-.5,.5] and adjust to worldCoords this should be +-.5 pixels // since we're in the middle of a pixel to begin with for (int i = 0; i < samples; i++) { imageSamples[i].x = widthRatio / 2 * (imageSamples[i].x / 4.0); imageSamples[i].y = heightRatio / 2 * (imageSamples[i].y / 4.0); // System.out.println("Jitter by: " + imageSamples[i] + " Pixel width: " + widthRatio + " // Pixel height: " + heightRatio ); } imageSamples[0].x++; imageSamples[0].x--; } }
Renderer() { xMin = scene.camera.viewportLeft + 0.5; xMax = scene.camera.viewportRight; yMin = scene.camera.viewportBottom + 0.5; yMax = scene.camera.viewportTop; if (scene.antiAliasing) { samples = Math.max(scene.raysPerPixel, DEBUG_samples); // Force it to be a perfect square samples = (int) Math.sqrt(samples); samples = samples * samples; imageSamples = new Double3D[samples]; for (int i = 0; i < samples; i++) imageSamples[i] = new Double3D(0, 0, 0); Sample.multiJitter(imageSamples, samples); // Samples are in the range [-2,2] Sample.cubicSplineFilter(imageSamples, samples); // Scale image samples to [-1,1] for (int i = 0; i < samples; i++) { imageSamples[i].x = widthRatio * imageSamples[i].x / 2.0; imageSamples[i].y = heightRatio * imageSamples[i].y / 2.0; } } }
/** * Called back immediately after the OpenGL context is initialized. Can be used to perform * one-time initialization. Run only once. */ @Override public void init(GLAutoDrawable drawable) { GL2 gl = drawable.getGL().getGL2(); // get the OpenGL graphics context glu = new GLU(); // get GL Utilities gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // set background (clear) color gl.glClearDepth(1.0f); // set clear depth value to farthest gl.glEnable(GL_DEPTH_TEST); // enables depth testing gl.glDepthFunc(GL_LEQUAL); // the type of depth test to do gl.glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // best perspective correction gl.glShadeModel(GL_SMOOTH); // blends colors nicely, and smoothes out lighting // Load the texture image try { // Create a OpenGL Texture object from (URL, mipmap, file suffix) // Use URL so that can read from JAR and disk file. texture = TextureIO.newTexture( this.getClass().getResource(textureFileName), false, textureFileType); } catch (GLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } // Use linear filter for texture if image is larger than the original texture gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // Use linear filter for texture if image is smaller than the original texture gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); // Texture image flips vertically. Shall use TextureCoords class to retrieve // the top, bottom, left and right coordinates, instead of using 0.0f and 1.0f. TextureCoords textureCoords = texture.getImageTexCoords(); textureTop = textureCoords.top(); textureBottom = textureCoords.bottom(); // textureLeft = textureCoords.left(); // textureRight = textureCoords.right(); // Enable the texture texture.enable(gl); // gl.glEnable(GL_TEXTURE_2D); // we want back facing polygons to be filled completely and that we want front // facing polygons to be outlined only. gl.glPolygonMode(GL_BACK, GL_FILL); // Back Face Is Filled In gl.glPolygonMode(GL_FRONT, GL_LINE); // Front Face Is Drawn With Lines for (int x = 0; x < numPoints; x++) { // Loop Through The Y Plane for (int y = 0; y < numPoints; y++) { // Apply The Wave To Our Mesh // xmax is 45. Get 9 after dividing by 5. Subtract 4.5 to centralize. points[x][y][0] = (float) x / 5.0f - 4.5f; points[x][y][1] = (float) y / 5.0f - 4.5f; // Sine wave pattern points[x][y][2] = (float) (Math.sin(Math.toRadians(x / 5.0f * 40.0f))); } } }
boolean transmissionDirection(Ray ray, HitRecord hit, Ray transmission) { double n = transmission.r.prevR.n; double nt = transmission.r.n; Double3D N = hit.normal; Double3D D = ray.dir; double cosine = -D.dot(N); if (n > nt) { // We're inside, so reverse the normal N = N.sMult(-1); cosine = -D.dot(N); } double nRatio = n / nt; double cosinePSq = 1.0 - nRatio * nRatio * (1.0f - cosine * cosine); // check for total internal refraction here if (cosinePSq < 0.0f) return false; // total internal refraction else { // D - N(N.D) // Double3D pOne = D.minus( N.sMult(N.dot(D)) ).sMult(nRatio); double inside = nRatio * cosine - Math.sqrt(cosinePSq); Double3D temp = D.sMult(nRatio).plus(N.sMult(inside)).getUnit(); transmission.dir.x = temp.x; transmission.dir.y = temp.y; transmission.dir.z = temp.z; } return true; }
// iPoint is the point of intersection with the surface. DoubleColor shade(Ray ray, HitRecord hit, MaterialCell material, boolean background) { DoubleColor color = new DoubleColor(0.0, 0.0, 0.0, 1.0); // Add ambient light only once color.plus( new DoubleColor( (double) (lights[0].ambient[0] * material.ka.r), (double) (lights[0].ambient[1] * material.ka.g), (double) (lights[0].ambient[2] * material.ka.b), (double) (lights[0].ambient[3] * material.ka.a))); // Assign material color? // Local light or directional? If directional then we need to see if it's shining on the // object if (!background) { double d = 2; // L.distanceTo(hit.hitP); for (int i = 0; i < lights.length; i++) { if (lights[i].lightSwitch == 1) { Double3D L = new Double3D( (double) lights[i].position[0], (double) lights[i].position[1], (double) lights[i].position[2]); L = L.minus(hit.hitP).getUnit(); Ray shadowRay = new Ray(hit.hitP, L); // trace shadow ray to light source // Turn shadows on and shadowRay hit nothing if (!scene.shadows || shadowTrace(shadowRay)) { double LdN = Math.max(0, hit.normal.dot(L)); if (LdN > 0) { // -2(-L.N)N + -L Double3D R = hit.normal.sMult(-2 * hit.normal.dot(L.sMult(-1))).plus(L.sMult(-1)); double RdV = Math.max(0, -R.dot(ray.dir)); // If the light is free add the diffuse light // Intensity (Kd * (LdN) + Ks *(RdV)^(shiny)/(r + k) color.plus( new DoubleColor( (double) (lights[i].diffuse[0] * LdN + lights[i].specular[0] * Math.pow(RdV, material.shiny)) / d, (double) (lights[i].diffuse[1] * LdN + lights[i].specular[1] * Math.pow(RdV, material.shiny)) / d, (double) (lights[i].diffuse[2] * LdN + lights[i].specular[2] * Math.pow(RdV, material.shiny)) / d, 1.0)); // */ } // if(LdN > 0) } // if(!scene.shadows || shadowTrace(shadowRay)) } // if(lights[i].lightSwitch == 1){ } // for // Shiny Phong // If IdN > 0 then we find a reflection // If IdN < 0 then we need -normal if (scene.reflections && (material.reflectivity.r > 0 || material.reflectivity.g > 0 || material.reflectivity.b > 0)) { depth++; // R = I - 2 * (I.N)N Double3D R = new Double3D(); Double3D I = ray.dir; // .sMult(-1.0); Double3D N = hit.normal; // double IdN = I.dot(N); // if (IdN > 0){ // N = N.sMult(-1.0); // IdN = -I.dot(N); // }//*/ R = I.plus(N.sMult(-2.0 * I.dot(N))); Ray reflect = new Ray(hit.hitP, R); DoubleColor reflection = trace(reflect); // Scale by distance? // reflection.scale( 1 / reflect.origin().distanceTo(hit.hitP)); reflection.r = reflection.r * material.reflectivity.r; reflection.g = reflection.g * material.reflectivity.g; reflection.b = reflection.b * material.reflectivity.b; color.plus(reflection); depth--; } if (scene.refractions && (material.refractivity.r > 0 || material.refractivity.g > 0 || material.refractivity.b > 0)) // */ { depth++; Ray refract = new Ray(hit.hitP, ray.dir); if (hit.index == ray.r.objectNum) // Hit the object we're already in { // Pop the n off the stack refract.r = ray.r; // Swap the refraction indices double temp = refract.r.n; refract.r.n = refract.r.prevR.n; refract.r.prevR.n = temp; } else // Otherwise we hit a new object push this n onto the stack and get mat index { refract.r.prevR = ray.r; refract.r.n = material.refractiveIndex; refract.r.objectNum = hit.index; } if (transmissionDirection(ray, hit, refract)) { DoubleColor refraction = trace(refract); refraction.r = refraction.r * material.refractivity.r; refraction.g = refraction.g * material.refractivity.g; refraction.b = refraction.b * material.refractivity.b; // Scale for distance? color.plus(refraction); } depth--; } } return color; }
// All rays we deal with here are in world coordinates. // Should take the refractive index of the material it is currently in. DoubleColor trace(Ray ray) { DoubleColor color = new DoubleColor(0.0, 0.0, 0.0, 1.0); HitRecord hit = new HitRecord(); if (depth > Math.max(DEBUG_recursion, scene.maxRecursiveDepth)) return color; double tMin = 0.0001; double tMax = 10000000; // Spheres only for now for (int i = 0; i < numObjects; i++) // Did I hit the bounding sphere for an object? if (spheres[i].hit(ray, tMin, tMax, 0, hit)) if (scene.spheresOnly) { for (PMesh.SurfCell s = shapes[i].surfHead; s != null; s = s.next) for (PMesh.PolyCell poly = s.polyHead; poly != null; poly = poly.next) // Triangles only for now if (poly.numVerts == 3) { Double3D v[] = new Double3D[3]; int j = 0; for (PMesh.VertListCell vert = poly.vert; vert != null; vert = vert.next) v[j++] = shapes[i].vertArray.get(vert.vert).viewPos; // Increment j in the line post access // Check for a hit on this polygon if (Triangle.hit(v[0], v[1], v[2], ray, tMin, tMax, 0, hit)) { tMax = hit.t; hit.normal = poly.viewNorm; hit.matIndex = s.material; hit.index = i; } } else System.out.println( "Need to intersect polygon with " + poly.numVerts + " vertices."); } else { tMax = hit.t; hit.matIndex = i; // May cause an error if object 10 and it only has 3 materials. hit.index = i; } if (hit.index >= 0) // If it intersects then multi-sample { if (!sampled && depth == 0) { // Only sample once per ray from the main loop sampled = true; Double3D dir = ray.dir; DoubleColor antiAlias = trace(ray); for (int i = 0; i < samples; i++) { // Double3D sample = new Double3D(dir.x + imageSamples[i].x, dir.y + imageSamples[i].y, // dir.z).getUnit(); // ray.dir = sample; ray.dir.x = dir.x + imageSamples[i].x; ray.dir.y = dir.y + imageSamples[i].y; antiAlias.plus(trace(ray)); } antiAlias.scale(1.0 / (samples + 1.0)); color.plus(antiAlias); } else if (hit.matIndex < shapes[hit.index].materials.length) color = shade(ray, hit, shapes[hit.index].materials[hit.matIndex], false); else color = shade( ray, hit, shapes[hit.index].materials[shapes[hit.index].materials.length - 1], false); } else // We hit nothing check for intersection with the far clip plane for checker board // pattern. if (scene.checkerBackground) color = shade(ray, hit, checkerBackgroundHit(ray, hit), true); return color; }