diff options
| author | git perforce import user <a@b> | 2016-10-25 12:29:14 -0600 |
|---|---|---|
| committer | Sheikh Dawood Abdul Ajees <Sheikh Dawood Abdul Ajees> | 2016-10-25 18:56:37 -0500 |
| commit | 3dfe2108cfab31ba3ee5527e217d0d8e99a51162 (patch) | |
| tree | fa6485c169e50d7415a651bf838f5bcd0fd3bfbd /KaplaDemo/samples/sampleViewer3/ShadowMap.cpp | |
| download | physx-3.4-3dfe2108cfab31ba3ee5527e217d0d8e99a51162.tar.xz physx-3.4-3dfe2108cfab31ba3ee5527e217d0d8e99a51162.zip | |
Initial commit:
PhysX 3.4.0 Update @ 21294896
APEX 1.4.0 Update @ 21275617
[CL 21300167]
Diffstat (limited to 'KaplaDemo/samples/sampleViewer3/ShadowMap.cpp')
| -rw-r--r-- | KaplaDemo/samples/sampleViewer3/ShadowMap.cpp | 383 |
1 files changed, 383 insertions, 0 deletions
diff --git a/KaplaDemo/samples/sampleViewer3/ShadowMap.cpp b/KaplaDemo/samples/sampleViewer3/ShadowMap.cpp new file mode 100644 index 00000000..cf9caea3 --- /dev/null +++ b/KaplaDemo/samples/sampleViewer3/ShadowMap.cpp @@ -0,0 +1,383 @@ +//---------------------------------------------------------------------------------- +// File: ShadowMapping.cpp +// Original Author: Rouslan Dimitrov +// Modified by: Nuttapong Chentanez and Matthias M�ller-Fischer +// Email: [email protected] +// +// Copyright (c) 2007 NVIDIA Corporation. All rights reserved. +// +// TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, THIS SOFTWARE IS PROVIDED +// *AS IS* AND NVIDIA AND ITS SUPPLIERS DISCLAIM ALL WARRANTIES, EITHER EXPRESS +// OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL NVIDIA OR ITS SUPPLIERS +// BE LIABLE FOR ANY SPECIAL, INCIDENTAL, INDIRECT, OR CONSEQUENTIAL DAMAGES +// WHATSOEVER (INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF BUSINESS PROFITS, +// BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION, OR ANY OTHER PECUNIARY LOSS) +// ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF NVIDIA HAS +// BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. +// +//---------------------------------------------------------------------------------- + +#include "ShadowMap.h" +#include <GL/glew.h> + +//---------------------------------------------------------------------------------- +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<cur_num_splits; i++) + { + float si = i / (float)cur_num_splits; + + f[i].neard = lambda*(nd*powf(ratio, si)) + (1-lambda)*(nd + (fd - nd)*si); + f[i-1].fard = f[i].neard * 1.005f; + } + f[cur_num_splits-1].fard = fd; +} + +//---------------------------------------------------------------------------------- +// this function builds a projection matrix for rendering from the shadow's POV. +// First, it computes the appropriate z-range and sets an orthogonal projection. +// Then, it translates and scales it, so that it exactly captures the bounding box +// of the current frustum slice +float ShadowMap::applyCropMatrix(Frustum &f) +{ + float shad_proj[16]; + float maxX = -1000.0; + float maxY = -1000.0; + float maxZ; + float minX = 1000.0; + float minY = 1000.0; + float minZ; + + Matrix44 nv_mvp; + Vec4 transf; + + // find the z-range of the current frustum as seen from the light + // in order to increase precision + glGetFloatv(GL_MODELVIEW_MATRIX, nv_mvp.elems); + + // note that only the z-component is need and thus + // the multiplication can be simplified + // transf.z = shad_modelview[2] * f.point[0].x + shad_modelview[6] * f.point[0].y + shad_modelview[10] * f.point[0].z + shad_modelview[14]; + transf = nv_mvp * Vec4(f.point[0]); + minZ = transf.z; + maxZ = transf.z; + for(int i=1; i<8; i++) + { + transf = nv_mvp * Vec4(f.point[i]); + if(transf.z > 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 = "<<shadowOff<<endl; +// cout<<"shadowOff2 = "<<shadowOff2<<endl; + glEnable(GL_POLYGON_OFFSET_FILL); + + // compute the z-distances for each split as seen in camera space + updateSplitDist(f, znear, zfar); + + // for all shadow maps: + for(int i=0; i<cur_num_splits; i++) + { + // compute the camera frustum slice boundary points in world space + updateFrustumPoints(f[i], cameraPos, cameraDir); + // adjust the view frustum of the light, so that it encloses the camera frustum slice fully. + // note that this function sets the projection matrix as it sees best fit + // minZ is just for optimization to cull trees that do not affect the shadows + float minZ = applyCropMatrix(f[i]); + // make the current depth map a rendering target + glFramebufferTextureLayerEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, depth_tex_ar, 0, i); + + // clear the depth texture from last time + glClear(GL_DEPTH_BUFFER_BIT); + + // draw the scene + (*renderShadowCasters)(); + + glMatrixMode(GL_PROJECTION); + // store the product of all shadow matries for later + glMultMatrixf(shad_modelview); + glGetFloatv(GL_PROJECTION_MATRIX, shad_cpm[i]); + } + + glDisable(GL_POLYGON_OFFSET_FILL); + glPopAttrib(); + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + + glEnable(GL_TEXTURE_2D); + + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); +} + +//---------------------------------------------------------------------------------- +void ShadowMap::cameraInverse(float dst[16], float src[16]) +{ + dst[0] = src[0]; + dst[1] = src[4]; + dst[2] = src[8]; + dst[3] = 0.0; + dst[4] = src[1]; + dst[5] = src[5]; + dst[6] = src[9]; + dst[7] = 0.0; + dst[8] = src[2]; + dst[9] = src[6]; + dst[10] = src[10]; + dst[11] = 0.0; + dst[12] = -(src[12] * src[0]) - (src[13] * src[1]) - (src[14] * src[2]); + dst[13] = -(src[12] * src[4]) - (src[13] * src[5]) - (src[14] * src[6]); + dst[14] = -(src[12] * src[8]) - (src[13] * src[9]) - (src[14] * src[10]); + dst[15] = 1.0; +} + +//---------------------------------------------------------------------------------- +void ShadowMap::doneRender() +{ + // Unbind texture + for (int i = 0; i < 8; i++) { + glActiveTexture(GL_TEXTURE0 + i); + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + } + glActiveTexture(GL_TEXTURE0); +} + +//---------------------------------------------------------------------------------- +void ShadowMap::prepareForRender(float* cam_modelview, float* cam_proj) +{ + float cam_inverse_modelview[16]; + const float bias[16] = { 0.5, 0.0, 0.0, 0.0, + 0.0, 0.5, 0.0, 0.0, + 0.0, 0.0, 0.5, 0.0, + 0.5, 0.5f, 0.5, 1.0 }; + + // update the camera, so that the user can have a free look + cameraInverse(cam_inverse_modelview, cam_modelview); + + glActiveTexture(GL_TEXTURE0); + + for(int i=cur_num_splits; i<MAX_SPLITS; i++) + far_bound[i] = 0; + + // for every active split + for(int i=0; i<cur_num_splits; i++) + { + // f[i].fard is originally in eye space - tell's us how far we can see. + // Here we compute it in camera homogeneous coordinates. Basically, we calculate + // cam_proj * (0, 0, f[i].fard, 1)^t and then normalize to [0; 1] + far_bound[i] = 0.5f*(-f[i].fard*cam_proj[10]+cam_proj[14])/f[i].fard + 0.5f; + + // compute a matrix that transforms from camera eye space to light clip space + // and pass it to the shader through the OpenGL texture matrices, since we + // don't use them now + glActiveTexture(GL_TEXTURE0 + (GLenum)i + matOffset); + glMatrixMode(GL_TEXTURE); + glLoadMatrixf(bias); + glMultMatrixf(shad_cpm[i]); + // multiply the light's (bias*crop*proj*modelview) by the inverse camera modelview + // so that we can transform a pixel as seen from the camera + glMultMatrixf(cam_inverse_modelview); + } + + glActiveTexture(GL_TEXTURE0); +} + +//---------------------------------------------------------------------------------- +void ShadowMap::init() +{ + //glClearColor(0.8f, 0.8f , 0.9f, 1.0); + //glEnable(GL_CULL_FACE); + glEnable(GL_DEPTH_TEST); + glEnable(GL_LIGHTING); + glEnable(GL_LIGHT0); + + + glGenFramebuffersEXT(1, &depth_fb); + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, depth_fb); + glDrawBuffer(GL_NONE); + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + + glGenTextures(1, &depth_tex_ar); + + glBindTexture(GL_TEXTURE_2D_ARRAY_EXT, depth_tex_ar); + glTexImage3D(GL_TEXTURE_2D_ARRAY_EXT, 0, GL_DEPTH_COMPONENT32, depth_size, depth_size, MAX_SPLITS, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL); + glTexParameteri(GL_TEXTURE_2D_ARRAY_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D_ARRAY_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D_ARRAY_EXT, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D_ARRAY_EXT, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D_ARRAY_EXT, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL); + + for(int i=0; i<MAX_SPLITS; i++) + { + // note that fov is in radians here and in OpenGL it is in degrees. + // the 0.2f factor is important because we might get artifacts at + // the screen borders. + f[i].fov = fov/57.2957795f + 0.2f; + f[i].ratio = (float)width/(float)height; + } + +} + |