Spectrum MIPMap::getValue(Float u, Float v, Float dudx, Float dudy, Float dvdx, Float dvdy) const { if (m_filterType == ETrilinear) { ++mipmapLookups; /* Conservatively estimate a square lookup region */ Float width = 2.0f * std::max( std::max(std::abs(dudx), std::abs(dudy)), std::max(std::abs(dvdx), std::abs(dvdy))); Float mipmapLevel = m_levels - 1 + log2(std::max(width, (Float) 1e-8f)); if (mipmapLevel < 0) { /* The lookup is smaller than one pixel */ return triangle(0, u, v); } else if (mipmapLevel >= m_levels - 1) { /* The lookup is larger than the whole texture */ return getTexel(m_levels - 1, 0, 0); } else { /* Tri-linear interpolation */ int level = (int) mipmapLevel; Float delta = mipmapLevel - level; return triangle(level, u, v) * (1.0f - delta) + triangle(level, u, v) * delta; } } else if (m_filterType == EEWA) { if (dudx*dudx + dudy*dudy < dvdx*dvdx + dvdy*dvdy) { std::swap(dudx, dvdx); std::swap(dudy, dvdy); } Float majorLength = std::sqrt(dudx * dudx + dudy * dudy); Float minorLength = std::sqrt(dvdx * dvdx + dvdy * dvdy); if (minorLength * m_maxAnisotropy < majorLength && minorLength > 0.0f) { Float scale = majorLength / (minorLength * m_maxAnisotropy); dvdx *= scale; dvdy *= scale; minorLength *= scale; } if (minorLength == 0) return triangle(0, u, v); // The min() below avoids overflow in the int conversion when lod=inf Float lod = std::min(std::max((Float) 0, m_levels - 1 + log2(minorLength)), (Float) (m_levels-1)); int ilod = floorToInt(lod); Float d = lod - ilod; return EWA(u, v, dudx, dudy, dvdx, dvdy, ilod) * (1-d) + EWA(u, v, dudx, dudy, dvdx, dvdy, ilod+1) * d; } else { int xPos = floorToInt(u*m_levelWidth[0]), yPos = floorToInt(v*m_levelHeight[0]); return getTexel(0, xPos, yPos); } }
Spectrum MIPMap::triangle(int level, Float x, Float y) const { if (m_filterType == ENone) { int xPos = floorToInt(x*m_levelWidth[0]), yPos = floorToInt(y*m_levelHeight[0]); return getTexel(0, xPos, yPos); } else { level = clamp(level, 0, m_levels - 1); x = x * m_levelWidth[level] - 0.5f; y = y * m_levelHeight[level] - 0.5f; int xPos = floorToInt(x), yPos = floorToInt(y); Float dx = x - xPos, dy = y - yPos; return getTexel(level, xPos, yPos) * (1.0f - dx) * (1.0f - dy) + getTexel(level, xPos, yPos + 1) * (1.0f - dx) * dy + getTexel(level, xPos + 1, yPos) * dx * (1.0f - dy) + getTexel(level, xPos + 1, yPos + 1) * dx * dy; } }
Spectrum MIPMap::EWA(Float u, Float v, Float dudx, Float dudy, Float dvdx, Float dvdy, int level) const { ++ewaLookups; if (level >= m_levels) return getTexel(m_levels-1, 0, 0); Spectrum result(0.0f); Float denominator = 0.0f; u = u * m_levelWidth[level]; v = v * m_levelHeight[level]; dudx = dudx * m_levelWidth[level]; dudy = dudy * m_levelHeight[level]; dvdx = dvdx * m_levelWidth[level]; dvdy = dvdy * m_levelHeight[level]; Float A = dudy * dudy + dvdy * dvdy + 1.0f; Float B = -2.0f * (dudx * dudy + dvdx * dvdy); Float C = dudx * dudx + dvdx * dvdx + 1.0f; Float F = A * C - B * B * 0.25f; Float du = std::sqrt(C), dv = std::sqrt(A); int u0 = (int) std::ceil(u - du); int u1 = (int) std::floor(u + du); int v0 = (int) std::ceil(v - dv); int v1 = (int) std::floor(v + dv); Float invF = 1.0f / F; A *= invF; B *= invF; C *= invF; for (int ut = u0; ut <= u1; ++ut) { const Float uu = ut - u; for (int vt = v0; vt <= v1; ++vt) { const Float vv = vt - v; const Float r2 = A*uu*uu + B*uu*vv + C*vv*vv; if (r2 < 1) { const Float weight = m_weightLut[ std::max(0, std::min((int) (r2 * MIPMAP_LUTSIZE), MIPMAP_LUTSIZE - 1))]; result += getTexel(level, ut, vt) * weight; denominator += weight; } } } return result / denominator; }
/** * Returns an interpolated texel using bi-linear texture filtering */ static glm::vec3 readTexture(texture& tex, float s, float t) { float xf = std::min(s*tex.width, tex.width-1.0f); float yf = std::min(t*tex.height, tex.height-1.0f); //// Find the diffrent texture coords. float xMin = glm::floor(xf); float yMin = glm::floor(yf); float xMax = glm::ceil(xf); float yMax = glm::ceil(yf); ////// Find the four nearest texels. glm::vec3 A = getTexel(tex, xMin, yMin); glm::vec3 B = getTexel(tex, xMax, yMin); glm::vec3 C = getTexel(tex, xMax, yMax); glm::vec3 D = getTexel(tex, xMin, yMax); glm::vec3 xTop; glm::vec3 xBot; float divx = xMax - xMin; float divy = yMax - yMin; //horizontal interpolation if (divx > 0.0f) { xTop = ((xMax-xf)/divx)*A + ((xf-xMin)/divx)*B; xBot = ((xMax-xf)/divx)*D + ((xf-xMin)/divx)*C; } else { xTop = 1.0f*A + 0.0f*B; xBot = 1.0f*D + 0.0f*C; } //vertical interpolation if (divy > 0.0f) { return ((yMax-yf)/divy)*xTop + ((yf-yMin)/divy)*xBot; } return 1.0f * xTop + 0.0f * xBot; }
/* Isotropic/anisotropic EWA mip-map texture map class based on PBRT */ MIPMap::MIPMap(int width, int height, Spectrum *pixels, EFilterType filterType, EWrapMode wrapMode, Float maxAnisotropy) : m_width(width), m_height(height), m_filterType(filterType), m_wrapMode(wrapMode), m_maxAnisotropy(maxAnisotropy) { Spectrum *texture = pixels; if (filterType != ENone && (!isPow2(width) || !isPow2(height))) { m_width = (int) roundToPow2((uint32_t) width); m_height = (int) roundToPow2((uint32_t) height); /* The texture needs to be up-sampled */ Spectrum *texture1 = new Spectrum[m_width*height]; /* Re-sample into the X direction */ ResampleWeight *weights = resampleWeights(width, m_width); for (int y=0; y<height; y++) { for (int x=0; x<m_width; x++) { texture1[x+m_width*y] = Spectrum(0.0f); for (int j=0; j<4; j++) { int pos = weights[x].firstTexel + j; if (pos < 0 || pos >= height) { if (wrapMode == ERepeat) pos = modulo(pos, width); else if (wrapMode == EClamp) pos = clamp(pos, 0, width-1); } if (pos >= 0 && pos < width) texture1[x+m_width*y] += pixels[pos+y*width] * weights[x].weight[j]; } } } delete[] weights; delete[] pixels; /* Re-sample into the Y direction */ texture = new Spectrum[m_width*m_height]; weights = resampleWeights(height, m_height); memset(texture, 0, sizeof(Spectrum)*m_width*m_height); for (int x=0; x<m_width; x++) { for (int y=0; y<m_height; y++) { for (int j=0; j<4; j++) { int pos = weights[y].firstTexel + j; if (pos < 0 || pos >= height) { if (wrapMode == ERepeat) pos = modulo(pos, height); else if (wrapMode == EClamp) pos = clamp(pos, 0, height-1); } if (pos >= 0 && pos < height) texture[x+m_width*y] += texture1[x+pos*m_width] * weights[y].weight[j]; } } } for (int y=0; y<m_height; y++) for (int x=0; x<m_width; x++) texture[x+m_width*y].clampNegative(); delete[] weights; delete[] texture1; } if (m_filterType != ENone) m_levels = 1 + log2i((uint32_t) std::max(width, height)); else m_levels = 1; m_pyramid = new Spectrum*[m_levels]; m_pyramid[0] = texture; m_levelWidth = new int[m_levels]; m_levelHeight= new int[m_levels]; m_levelWidth[0] = m_width; m_levelHeight[0] = m_height; /* Generate the mip-map hierarchy */ for (int i=1; i<m_levels; i++) { m_levelWidth[i] = std::max(1, m_levelWidth[i-1]/2); m_levelHeight[i] = std::max(1, m_levelHeight[i-1]/2); m_pyramid[i] = new Spectrum[m_levelWidth[i] * m_levelHeight[i]]; for (int y = 0; y < m_levelHeight[i]; y++) { for (int x = 0; x < m_levelWidth[i]; x++) { m_pyramid[i][x+y*m_levelWidth[i]] = ( getTexel(i-1, 2*x, 2*y) + getTexel(i-1, 2*x+1, 2*y) + getTexel(i-1, 2*x, 2*y+1) + getTexel(i-1, 2*x+1, 2*y+1)) * 0.25f; } } } if (m_filterType == EEWA) { m_weightLut = static_cast<Float *>(allocAligned(sizeof(Float)*MIPMAP_LUTSIZE)); for (int i=0; i<MIPMAP_LUTSIZE; ++i) { Float pos = (Float) i / (Float) (MIPMAP_LUTSIZE-1); m_weightLut[i] = std::exp(-2.0f * pos) - std::exp(-2.0f); } } }
Color lambertian(Ray normal, const Scene* s, Sphere* sphere, Plane* plane) { Color result; result.r = 0; result.g = 0; result.b = 0; bool blocked; for(int i = 0; i < s->lights.size(); i++) { blocked = false; Light current_light = s->lights.at(i); Ray lightRay; lightRay.p0 = current_light.origin; lightRay.dir = sub(normal.p0, current_light.origin); lightRay.dir = normalize(lightRay.dir); // check that point of interest is not shadowed by other objects float distFromLight = dist(normal.p0, current_light.origin); float distClosestObject = fInfinity; // distance between light and closest object along ray to normal initial point for(int k = 0; k < s->planes.size(); k++) { Plane current_plane = s->planes.at(k); if(!areEqual(¤t_plane, plane)) { distClosestObject = plane_intersect(¤t_plane, &lightRay); // check if current object would block the light if(distClosestObject < distFromLight) { blocked = true; break; } } } for(int k = 0; k < s->spheres.size(); k++) { Sphere current_sphere = s->spheres.at(k); if(!areEqual(¤t_sphere, sphere)) { distClosestObject = sphere_intersect(¤t_sphere, &lightRay); // check if current object would block the light if(distClosestObject < distFromLight) { blocked = true; break; } } } // check that the light source isn't behind the object Vec3 lv = lightRay.dir; Vec3 nv = normalize(normal.dir); if(dot(lv, nv) > 0.0) blocked = true; if(!blocked) { // add this light's contribution to the pixel color float coef = abs(dot(lv, nv)); if(sphere != NULL) { result.r += (sphere->material.color.r * current_light.color.r) * coef; result.g += (sphere->material.color.g * current_light.color.g) * coef; result.b += (sphere->material.color.b * current_light.color.b) * coef; } else if(plane != NULL) { if(plane->hastexture) { Color textureColor = getTextureColor(getTexel(normal,plane),plane); result.r += textureColor.r * current_light.color.r * coef; result.g += textureColor.g * current_light.color.g * coef; result.b += textureColor.b * current_light.color.b * coef; } else { result.r += (plane->material.color.r * current_light.color.r) * coef; result.g += (plane->material.color.g * current_light.color.g) * coef; result.b += (plane->material.color.b * current_light.color.b) * coef; } } } } return result; }
/* * @brief integrateTexture Get a texel by computing the mean of the color on a neighborood of size (deltas x deltat) * @param pixels The image to access, organized as a linear array of texel arranged by row * @param width Width of the image * @param height Height of the image * @param depth Depth of the image (number of component by texel) * @param s The column coordinate of the requested texel as a floating point * @param t The row coordinate of the requested texel as a floating point * @param deltas The size, in the column dimension, of the neighborood * @param deltat The size, in the row dimension, of the neighborood * @return * @todo */ Color integrateTexture(unsigned char *pixels, int width, int height, int depth, float s, float t, int deltas, int deltat){ return getTexel(pixels, width, height, depth, (int)std::floor(s), (int)std::floor(t)); }