// // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions // are met: // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // * Neither the name of NVIDIA CORPORATION nor the names of its // contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY // OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // Copyright (c) 2007-2018 NVIDIA Corporation. All rights reserved. //---------------------------------------------------------------------------------- // File: ShadowMapping.cpp // Original Author: Rouslan Dimitrov // Modified by: Nuttapong Chentanez and Matthias Mueller-Fischer //---------------------------------------------------------------------------------- #include "ShadowMap.h" #include //---------------------------------------------------------------------------------- struct ShadowMap::Vec4 { Vec4() {} Vec4(const PxVec3 &v, float vw = 1.0f) { x = v.x; y = v.y; z = v.z; w = vw; } float x,y,z,w; }; //---------------------------------------------------------------------------------- struct ShadowMap::Matrix44 { Vec4 operator*(const Vec4& v) const { Vec4 res; res.x = elems[0] * v.x + elems[4] * v.y + elems[8] * v.z + elems[12] * v.w; res.y = elems[1] * v.x + elems[5] * v.y + elems[9] * v.z + elems[13] * v.w; res.z = elems[2] * v.x + elems[6] * v.y + elems[10] * v.z + elems[14] * v.w; res.w = elems[3] * v.x + elems[7] * v.y + elems[11] * v.z + elems[15] * v.w; return res; } float &element(int i, int j) { return elems[i + 4*j]; } void zero() { for (int i = 0; i < 16; i++) elems[i] = 0.0f; } void id() { zero(); elems[0] = 1.0f; elems[5] = 1.0f; elems[10] = 1.0f; elems[15] = 1.0f; } float elems[16]; }; //---------------------------------------------------------------------------------- ShadowMap::ShadowMap( int w, int h, float fovi, int matOffseti, int resolution) { shadowOff = 1.0f; shadowOff2 = 2048.0f; fov = fovi; cur_num_splits = 1; //cur_num_splits = 3; width = w; height = h; depth_size = resolution; split_weight = 0.75; matOffset = matOffseti; minZAdd = 0; maxZAdd = 30.0f; init(); } //---------------------------------------------------------------------------------- void ShadowMap::updateFrustumPoints(Frustum &f, const PxVec3 ¢er, const PxVec3 &view_dir) { PxVec3 up(0.0, 1.0, 0.0); PxVec3 right = view_dir.cross(up); PxVec3 fc = center + view_dir*f.fard; PxVec3 nc = center + view_dir*f.neard; right.normalize(); up = right.cross(view_dir); up.normalize(); // these heights and widths are half the heights and widths of // the near and far plane rectangles float near_height = tanf(f.fov/2.0f) * f.neard; float near_width = near_height * f.ratio; float far_height = tanf(f.fov/2.0f) * f.fard; float far_width = far_height * f.ratio; f.point[0] = nc - up*near_height - right*near_width; f.point[1] = nc + up*near_height - right*near_width; f.point[2] = nc + up*near_height + right*near_width; f.point[3] = nc - up*near_height + right*near_width; f.point[4] = fc - up*far_height - right*far_width; f.point[5] = fc + up*far_height - right*far_width; f.point[6] = fc + up*far_height + right*far_width; f.point[7] = fc - up*far_height + right*far_width; } //---------------------------------------------------------------------------------- // updateSplitDist computes the near and far distances for every frustum slice // in camera eye space - that is, at what distance does a slice start and end void ShadowMap::updateSplitDist(Frustum f[MAX_SPLITS], float nd, float fd) { float lambda = split_weight; float ratio = fd/nd; f[0].neard = nd; for(int i=1; i maxZ) maxZ = transf.z; if(transf.z < minZ) minZ = transf.z; } minZ += minZAdd; maxZ += maxZAdd; glMatrixMode(GL_PROJECTION); glLoadIdentity(); // set the projection matrix with the new z-bounds // note the inversion because the light looks at the neg. z axis // gluPerspective(LIGHT_FOV, 1.0, maxZ, minZ); // for point lights glOrtho(-1.0, 1.0, -1.0, 1.0, -maxZ, -minZ); glGetFloatv(GL_PROJECTION_MATRIX, shad_proj); glPushMatrix(); glMultMatrixf(nv_mvp.elems); glGetFloatv(GL_PROJECTION_MATRIX, nv_mvp.elems); glPopMatrix(); // find the extends of the frustum slice as projected in light's homogeneous coordinates for(int i=0; i<8; i++) { transf = nv_mvp * Vec4(f.point[i]); transf.x /= transf.w; transf.y /= transf.w; if(transf.x > maxX) maxX = transf.x; if(transf.x < minX) minX = transf.x; if(transf.y > maxY) maxY = transf.y; if(transf.y < minY) minY = transf.y; } float scaleX = 2.0f/(maxX - minX); float scaleY = 2.0f/(maxY - minY); float offsetX = -0.5f*(maxX + minX)*scaleX; float offsetY = -0.5f*(maxY + minY)*scaleY; // apply a crop matrix to modify the projection matrix we got from glOrtho. nv_mvp.id(); nv_mvp.element(0,0) = scaleX; nv_mvp.element(1,1) = scaleY; nv_mvp.element(0,3) = offsetX; nv_mvp.element(1,3) = offsetY; glLoadMatrixf(nv_mvp.elems); glMultMatrixf(shad_proj); glMatrixMode(GL_MODELVIEW); return minZ; } //---------------------------------------------------------------------------------- // here all shadow map textures and their corresponding matrices are created void ShadowMap::makeShadowMap(const PxVec3 &cameraPos, const PxVec3 &cameraDir, const PxVec3 &lightDir, float znear, float zfar, void (*renderShadowCasters)()) { float shad_modelview[16]; glDisable(GL_TEXTURE_2D); glMatrixMode(GL_PROJECTION); glPushMatrix(); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); gluLookAt( cameraPos.x, cameraPos.y, cameraPos.z, cameraPos.x-lightDir.x, cameraPos.y-lightDir.y, cameraPos.z-lightDir.z, -1.0, 0.0, 0.0); glGetFloatv(GL_MODELVIEW_MATRIX, shad_modelview); // redirect rendering to the depth texture glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, depth_fb); // store the screen viewport glPushAttrib(GL_VIEWPORT_BIT); // and render only to the shadowmap glViewport(0, 0, depth_size, depth_size); // offset the geometry slightly to prevent z-fighting // note that this introduces some light-leakage artifacts glPolygonOffset(shadowOff, shadowOff2); // cout<<"shadowOff = "<