#include "Mesh.h" #include "glInterface.h" #include "OcclusionQuery.h" #include "GlRenderer.h" #include "ViewCellsManager.h" #include "SceneGraph.h" //#include "ObjectPvs.h" #include "Viewcell.h" #include "Beam.h" #include "KdTree.h" #include "Environment.h" #include "Triangle3.h" #include "IntersectableWrapper.h" #include "BvHierarchy.h" #include "KdTree.h" #ifdef USE_CG #include #include #endif namespace GtpVisibilityPreprocessor { static bool arbQuerySupport = false; static bool nvQuerySupport = false; static GLuint frontDepthMap; static GLuint backDepthMap; const int depthMapSize = 512; static void InitExtensions() { GLenum err = glewInit(); if (GLEW_OK != err) { // problem: glewInit failed, something is seriously wrong cerr << "Error: " << glewGetErrorString(err) << endl; exit(1); } if (GLEW_ARB_occlusion_query) arbQuerySupport = true; if (GLEW_NV_occlusion_query) nvQuerySupport = true; if (!arbQuerySupport && !nvQuerySupport) { cout << "I require the GL_ARB_occlusion_query or the GL_NV_occlusion_query OpenGL extension to work.\n"; exit(1); } } GlRenderer::GlRenderer(SceneGraph *sceneGraph, ViewCellsManager *viewCellsManager, KdTree *tree): Renderer(sceneGraph, viewCellsManager), mKdTree(tree), mUseFalseColors(false) { mSceneGraph->CollectObjects(&mObjects); // mViewCellsManager->GetViewPoint(mViewPoint); viewCellsManager->GetViewPoint(mViewPoint); //mSceneGraph->GetBox().Center(); mViewDirection = Vector3(0,0,1); // mViewPoint = Vector3(991.7, 187.8, -271); // mViewDirection = Vector3(0.9, 0, -0.4); // timerId = startTimer(10); // debug coords for atlanta // mViewPoint = Vector3(3473, 6.778, -1699); // mViewDirection = Vector3(-0.2432, 0, 0.97); mFrame = 0; mWireFrame = false; Environment::GetSingleton()->GetBoolValue("Preprocessor.detectEmptyViewSpace", mDetectEmptyViewSpace); mSnapErrorFrames = true; mSnapPrefix = "snap/"; mUseForcedColors = false; mRenderBoxes = false; // mUseGlLists = true; mUseGlLists = false; if (mViewCellsManager->GetViewCellPoints()->size()) mPvsStatFrames = (int)mViewCellsManager->GetViewCellPoints()->size(); else Environment::GetSingleton()->GetIntValue("Preprocessor.pvsRenderErrorSamples", mPvsStatFrames); mPvsErrorBuffer.resize(mPvsStatFrames); ClearErrorBuffer(); } GlRenderer::~GlRenderer() { cerr<<"gl renderer destructor..\n"; //CLEAR_CONTAINER(sQueries); CLEAR_CONTAINER(mOcclusionQueries); cerr<<"done."<GetItem(); glBegin(GL_TRIANGLES); Vector3 normal = t.GetNormal(); glNormal3f(normal.x, normal.y, normal.z); glVertex3f(t.mVertices[0].x, t.mVertices[0].y, t.mVertices[0].z); glVertex3f(t.mVertices[1].x, t.mVertices[1].y, t.mVertices[1].z); glVertex3f(t.mVertices[2].x, t.mVertices[2].y, t.mVertices[2].z); glEnd(); } void GlRenderer::RenderIntersectable(Intersectable *object) { if (!object) return; if (object->Mailed()) return; object->Mail(); glPushAttrib(GL_CURRENT_BIT); if (mUseFalseColors) SetupFalseColor(object->mId); switch (object->Type()) { case Intersectable::MESH_INSTANCE: RenderMeshInstance((MeshInstance *)object); break; case Intersectable::VIEW_CELL: RenderViewCell(static_cast(object)); break; case Intersectable::TRANSFORMED_MESH_INSTANCE: RenderTransformedMeshInstance(static_cast(object)); break; case Intersectable::TRIANGLE_INTERSECTABLE: RenderTriangle(static_cast(object)); break; case Intersectable::BVH_INTERSECTABLE: { BvhNode *node = static_cast(object); if (mRenderBoxes) RenderBox(node->GetBoundingBox()); else RenderBvhNode(node); break; } case Intersectable::KD_INTERSECTABLE: { KdNode *node = (static_cast(object))->GetItem(); if (mRenderBoxes) RenderBox(mKdTree->GetBox(node)); else RenderKdNode(node); break; } default: cerr<<"Rendering this object not yet implemented\n"; break; } glPopAttrib(); } void GlRenderer::RenderRays(const VssRayContainer &rays) { VssRayContainer::const_iterator it = rays.begin(), it_end = rays.end(); glBegin(GL_LINES); for (; it != it_end; ++it) { VssRay *ray = *it; float importance = log10(1e3*ray->mWeightedPvsContribution)/3.0f; // cout<<"w="<mWeightedPvsContribution<<" r="<mWeightedPvsContribution; glColor3f(importance, importance, importance); glVertex3fv(&ray->mOrigin.x); glVertex3fv(&ray->mTermination.x); } glEnd(); } void GlRenderer::RenderViewCell(ViewCell *vc) { if (vc->GetMesh()) { if (!mUseFalseColors) { if (vc->GetValid()) glColor3f(0,1,0); else glColor3f(0,0,1); } RenderMesh(vc->GetMesh()); } else { // render viewcells in the subtree if (!vc->IsLeaf()) { ViewCellInterior *vci = (ViewCellInterior *) vc; ViewCellContainer::iterator it = vci->mChildren.begin(); for (; it != vci->mChildren.end(); ++it) { RenderViewCell(*it); } } else { // cerr<<"Empty viewcell mesh\n"; } } } void GlRenderer::RenderMeshInstance(MeshInstance *mi) { RenderMesh(mi->GetMesh()); } void GlRenderer::RenderTransformedMeshInstance(TransformedMeshInstance *mi) { // apply world transform before rendering Matrix4x4 m; mi->GetWorldTransform(m); glPushMatrix(); /* cout << "\n"; for (int i = 0; i < 4; ++ i) for (int j = 0; j < 4; ++ j) cout << m.x[i][j] << " "; cout << "\n"*/ glMultMatrixf((float *)m.x); /*GLfloat dummy[16]; glGetFloatv(GL_MODELVIEW_MATRIX, dummy); for (int i = 0; i < 16; ++ i) cout << dummy[i] << " "; cout << endl;*/ RenderMesh(mi->GetMesh()); glPopMatrix(); } void GlRenderer::SetupFalseColor(const unsigned int id) { // swap bits of the color glColor3ub(id&255, (id>>8)&255, (id>>16)&255); } unsigned int GlRenderer::GetId(const unsigned char r, const unsigned char g, const unsigned char b) const { return r + (g << 8) + (b << 16); } void GlRenderer::SetupMaterial(Material *m) { if (m) glColor3fv(&(m->mDiffuseColor.r)); } void GlRenderer::RenderMesh(Mesh *mesh) { int i = 0; if (!mUseFalseColors && !mUseForcedColors) SetupMaterial(mesh->mMaterial); for (i=0; i < mesh->mFaces.size(); i++) { if (mWireFrame) glBegin(GL_LINE_LOOP); else glBegin(GL_POLYGON); Face *face = mesh->mFaces[i]; for (int j = 0; j < face->mVertexIndices.size(); j++) { glVertex3fv(&mesh->mVertices[face->mVertexIndices[j]].x); } glEnd(); } } void GlRenderer::InitGL() { mSphere = (GLUquadric *)gluNewQuadric(); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glFrontFace(GL_CCW); glCullFace(GL_BACK); glEnable(GL_CULL_FACE); glShadeModel(GL_FLAT); glDepthFunc( GL_LESS ); glEnable(GL_DEPTH_TEST); glEnable(GL_CULL_FACE); InitExtensions(); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE ); glEnable( GL_NORMALIZE ); glClearColor(0.0f, 0.0f, 1.0f, 1.0f); OcclusionQuery::GenQueries(mOcclusionQueries, 10); } void GlRenderer::SetupProjection(const int w, const int h, const float angle) { glViewport(0, 0, w, h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(angle, 1.0, 0.1, 2.0*Magnitude(mSceneGraph->GetBox().Diagonal())); glMatrixMode(GL_MODELVIEW); } void GlRenderer::SetupCamera() { Vector3 target = mViewPoint + mViewDirection; //cout << "vp: " << mViewPoint << " dr: " << mViewDirection << endl; //cout<<"box: " << mKdTree->GetBox()< 0.99f) up = Vector3(1, 0, 0); glLoadIdentity(); gluLookAt(mViewPoint.x, mViewPoint.y, mViewPoint.z, target.x, target.y, target.z, up.x, up.y, up.z); } void GlRenderer::_RenderScene() { ObjectContainer::const_iterator oi = mObjects.begin(); for (; oi != mObjects.end(); oi++) RenderIntersectable(*oi); } void GlRenderer::_RenderSceneTriangles() { glBegin(GL_TRIANGLES); ObjectContainer::const_iterator oi = mObjects.begin(); for (; oi != mObjects.end(); oi++) { if ((*oi)->Type() == Intersectable::TRIANGLE_INTERSECTABLE) { TriangleIntersectable *object = (TriangleIntersectable *)*oi; Triangle3 &t = object->GetItem(); Vector3 normal = t.GetNormal(); glNormal3f(normal.x, normal.y, normal.z); glVertex3f(t.mVertices[0].x, t.mVertices[0].y, t.mVertices[0].z); glVertex3f(t.mVertices[1].x, t.mVertices[1].y, t.mVertices[1].z); glVertex3f(t.mVertices[2].x, t.mVertices[2].y, t.mVertices[2].z); } } glEnd(); } bool GlRenderer::RenderScene() { Intersectable::NewMail(); static int glList = -1; if (mUseGlLists) { if (glList == -1) { glList = glGenLists(1); glNewList(glList, GL_COMPILE); _RenderSceneTriangles(); glEndList(); } glCallList(glList); } else _RenderSceneTriangles(); return true; } void GlRendererBuffer::EvalQueryWithItemBuffer( //RenderCostSample &sample ) { // read back the texture glReadPixels(0, 0, GetWidth(), GetHeight(), GL_RGBA, GL_UNSIGNED_BYTE, mPixelBuffer); unsigned int *p = mPixelBuffer; for (int y = 0; y < GetHeight(); y++) { for (int x = 0; x < GetWidth(); x++, p++) { unsigned int id = (*p) & 0xFFFFFF; if (id != 0xFFFFFF) { ++ mObjects[id]->mCounter; } } } } /****************************************************************/ /* GlRendererBuffer implementation */ /****************************************************************/ GlRendererBuffer::GlRendererBuffer(SceneGraph *sceneGraph, ViewCellsManager *viewcells, KdTree *tree): GlRenderer(sceneGraph, viewcells, tree) { mPixelBuffer = NULL; // implement width and height in subclasses } void GlRendererBuffer::EvalQueryWithOcclusionQueries( //RenderCostSample &sample ) { glDepthFunc(GL_LEQUAL); glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); glDepthMask(GL_FALSE); // simulate detectemptyviewspace using backface culling if (mDetectEmptyViewSpace) { glEnable(GL_CULL_FACE); //cout << "culling" << endl; } else { //cout << "not culling" << endl; glDisable(GL_CULL_FACE); } //const int numQ = 1; const int numQ = (int)mOcclusionQueries.size(); //glFinish(); #if 0 //-- now issue queries for all objects for (int j = 0; j < (int)mObjects.size(); ++ j) { mOcclusionQueries[j]->BeginQuery(); RenderIntersectable(mObjects[j]); mOcclusionQueries[j]->EndQuery(); unsigned int pixelCount; pixelCount = mOcclusionQueries[j]->GetQueryResult(); mObjects[j]->mCounter += pixelCount; } #else int q = 0; //-- now issue queries for all objects for (int j = 0; j < (int)mObjects.size(); j += q) { for (q = 0; ((j + q) < (int)mObjects.size()) && (q < numQ); ++ q) { //glFinish(); mOcclusionQueries[q]->BeginQuery(); RenderIntersectable(mObjects[j + q]); mOcclusionQueries[q]->EndQuery(); //glFinish(); } //cout << "q: " << q << endl; // collect results of the queries for (int t = 0; t < q; ++ t) { unsigned int pixelCount; //-- reenable other state #if 0 bool available; do { available = mOcclusionQueries[t]->ResultAvailable(); if (!available) cout << "W"; } while (!available); #endif pixelCount = mOcclusionQueries[t]->GetQueryResult(); //if (pixelCount > 0) // cout <<"o="<mCounter += pixelCount; } //j += q; } #endif //glFinish(); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glDepthMask(GL_TRUE); glEnable(GL_CULL_FACE); } void GlRenderer::RandomViewPoint() { // do not use this function since it could return different viewpoints for // different executions of the algorithm // mViewCellsManager->GetViewPoint(mViewPoint); while (1) { Vector3 pVector = Vector3(halton.GetNumber(1), halton.GetNumber(2), halton.GetNumber(3)); mViewPoint = mViewCellsManager->GetViewSpaceBox().GetPoint(pVector); ViewCell *v = mViewCellsManager->GetViewCell(mViewPoint); if (v && v->GetValid()) break; // generate a new vector halton.GenerateNext(); } Vector3 dVector = Vector3(2*M_PI*halton.GetNumber(4), M_PI*halton.GetNumber(5), 0.0f); mViewDirection = Normalize(Vector3(sin(dVector.x), // cos(dVector.y), 0.0f, cos(dVector.x))); halton.GenerateNext(); } void GlRenderer::RenderBox(const AxisAlignedBox3 &box) { glBegin(GL_LINE_LOOP); glVertex3d(box.Min().x, box.Max().y, box.Min().z ); glVertex3d(box.Max().x, box.Max().y, box.Min().z ); glVertex3d(box.Max().x, box.Min().y, box.Min().z ); glVertex3d(box.Min().x, box.Min().y, box.Min().z ); glEnd(); glBegin(GL_LINE_LOOP); glVertex3d(box.Min().x, box.Min().y, box.Max().z ); glVertex3d(box.Max().x, box.Min().y, box.Max().z ); glVertex3d(box.Max().x, box.Max().y, box.Max().z ); glVertex3d(box.Min().x, box.Max().y, box.Max().z ); glEnd(); glBegin(GL_LINE_LOOP); glVertex3d(box.Max().x, box.Min().y, box.Min().z ); glVertex3d(box.Max().x, box.Min().y, box.Max().z ); glVertex3d(box.Max().x, box.Max().y, box.Max().z ); glVertex3d(box.Max().x, box.Max().y, box.Min().z ); glEnd(); glBegin(GL_LINE_LOOP); glVertex3d(box.Min().x, box.Min().y, box.Min().z ); glVertex3d(box.Min().x, box.Min().y, box.Max().z ); glVertex3d(box.Min().x, box.Max().y, box.Max().z ); glVertex3d(box.Min().x, box.Max().y, box.Min().z ); glEnd(); glBegin(GL_LINE_LOOP); glVertex3d(box.Min().x, box.Min().y, box.Min().z ); glVertex3d(box.Max().x, box.Min().y, box.Min().z ); glVertex3d(box.Max().x, box.Min().y, box.Max().z ); glVertex3d(box.Min().x, box.Min().y, box.Max().z ); glEnd(); glBegin(GL_LINE_LOOP); glVertex3d(box.Min().x, box.Max().y, box.Min().z ); glVertex3d(box.Max().x, box.Max().y, box.Min().z ); glVertex3d(box.Max().x, box.Max().y, box.Max().z ); glVertex3d(box.Min().x, box.Max().y, box.Max().z ); glEnd(); } void GlRenderer::RenderBvhNode(BvhNode *node) { if (node->IsLeaf()) { BvhLeaf *leaf = (BvhLeaf *) node; #if 0 if (leaf->mGlList == 0) { leaf->mGlList = glGenLists(1); if (leaf->mGlList != 0) glNewList(leaf->mGlList, GL_COMPILE); for (int i=0; i < leaf->mObjects.size(); i++) RenderIntersectable(leaf->mObjects[i]); if (leaf->mGlList != 0) glEndList(); } if (leaf->mGlList != 0) glCallList(leaf->mGlList); #else for (int i=0; i < leaf->mObjects.size(); i++) RenderIntersectable(leaf->mObjects[i]); #endif } else { BvhInterior *in = (BvhInterior *)node; RenderBvhNode(in->GetBack()); RenderBvhNode(in->GetFront()); } //cout << "leaf obj " << i << endl; } void GlRenderer::RenderKdNode(KdNode *node) { if (node->IsLeaf()) { KdLeaf *leaf = (KdLeaf *) node; for (int i=0; i < leaf->mObjects.size(); i++) { RenderIntersectable(leaf->mObjects[i]); } } else { KdInterior *in = (KdInterior *)node; RenderKdNode(in->mBack); RenderKdNode(in->mFront); } } void GlRendererBuffer::EvalRenderCostSample(RenderCostSample &sample, const bool useOcclusionQueries, const int threshold ) { // choose a random view point mViewCellsManager->GetViewPoint(mViewPoint); sample.mPosition = mViewPoint; //cout << "viewpoint: " << mViewPoint << endl; // take a render cost sample by rendering a cube Vector3 directions[6]; directions[0] = Vector3(1,0,0); directions[1] = Vector3(0,1,0); directions[2] = Vector3(0,0,1); directions[3] = Vector3(-1,0,0); directions[4] = Vector3(0,-1,0); directions[5] = Vector3(0,0,-1); sample.mVisibleObjects = 0; // reset object counters ObjectContainer::const_iterator it, it_end = mObjects.end(); for (it = mObjects.begin(); it != it_end; ++ it) { (*it)->mCounter = 0; } ++ mFrame; //glCullFace(GL_FRONT); glCullFace(GL_BACK); glDisable(GL_CULL_FACE); // query all 6 directions for a full point sample for (int i = 0; i < 6; ++ i) { mViewDirection = directions[i]; SetupCamera(); glClearColor(1.0f, 1.0f, 1.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glDepthMask(GL_TRUE); glDepthFunc(GL_LESS); mUseFalseColors = true; // the actual scene rendering fills the depth (for occlusion queries) // and the frame buffer (for item buffer) RenderScene(); if (0) { char filename[256]; sprintf(filename, "snap/frame-%04d-%d.png", mFrame, i); // QImage im = toImage(); // im.save(filename, "PNG"); } // evaluate the sample if (useOcclusionQueries) { EvalQueryWithOcclusionQueries(); } else { EvalQueryWithItemBuffer(); } } // now evaluate the statistics over that sample // currently only the number of visible objects is taken into account sample.Reset(); for (it = mObjects.begin(); it != it_end; ++ it) { Intersectable *obj = *it; if (obj->mCounter >= threshold) { ++ sample.mVisibleObjects; sample.mVisiblePixels += obj->mCounter; } } //cout << "RS=" << sample.mVisibleObjects << " "; } GlRendererBuffer::~GlRendererBuffer() { #if 0 #ifdef USE_CG if (sCgFragmentProgram) cgDestroyProgram(sCgFragmentProgram); if (sCgContext) cgDestroyContext(sCgContext); #endif #endif } void GlRendererBuffer::SampleRenderCost(const int numSamples, vector &samples, const bool useOcclusionQueries, const int threshold ) { MakeCurrent(); if (mPixelBuffer == NULL) mPixelBuffer = new unsigned int[GetWidth()*GetHeight()]; // using 90 degree projection to capture 360 view with 6 samples SetupProjection(GetHeight(), GetHeight(), 90.0f); //samples.resize(numSamples); halton.Reset(); // the number of queries queried in batch mode const int numQ = 500; //const int numQ = (int)mObjects.size(); if (useOcclusionQueries) { cout << "\ngenerating " << numQ << " queries ... "; OcclusionQuery::GenQueries(mOcclusionQueries, numQ); cout << "finished" << endl; } // sampling queries for (int i = 0; i < numSamples; ++ i) { cout << "."; EvalRenderCostSample(samples[i], useOcclusionQueries, threshold); } DoneCurrent(); } void GlRenderer::ClearErrorBuffer() { for (int i=0; i < mPvsStatFrames; i++) { mPvsErrorBuffer[i].mError = 1.0f; } mPvsStat.maxError = 0.0f; } void GlRendererBuffer::EvalPvsStat() { MakeCurrent(); GlRenderer::EvalPvsStat(); DoneCurrent(); // mRenderingFinished.wakeAll(); } void GlRendererBuffer::EvalPvsStat(const SimpleRayContainer &viewPoints) { MakeCurrent(); GlRenderer::EvalPvsStat(viewPoints); DoneCurrent(); } void GlRendererBuffer::SampleBeamContributions(Intersectable *sourceObject, Beam &beam, const int desiredSamples, BeamSampleStatistics &stat) { // TODO: should be moved out of here (not to be done every time) // only back faces are interesting for the depth pass glShadeModel(GL_FLAT); glDisable(GL_LIGHTING); // needed to kill the fragments for the front buffer glEnable(GL_ALPHA_TEST); glAlphaFunc(GL_GREATER, 0); // assumes that the beam is constructed and contains kd-tree nodes // and viewcells which it intersects // Get the number of viewpoints to be sampled // Now it is a sqrt but in general a wiser decision could be made. // The less viewpoints the better for rendering performance, since less passes // over the beam is needed. // The viewpoints could actually be generated outside of the bounding box which // would distribute the 'efective viewpoints' of the object surface and thus // with a few viewpoints better sample the viewpoint space.... //TODO: comment in //int viewPointSamples = sqrt((float)desiredSamples); int viewPointSamples = max(desiredSamples / (GetWidth() * GetHeight()), 1); // the number of direction samples per pass is given by the number of viewpoints int directionalSamples = desiredSamples / viewPointSamples; Debug << "directional samples: " << directionalSamples << endl; for (int i = 0; i < viewPointSamples; ++ i) { Vector3 viewPoint = beam.mBox.GetRandomPoint(); // perhaps the viewpoint should be shifted back a little bit so that it always lies // inside the source object // 'ideally' the viewpoints would be distributed on the soureObject surface, but this // would require more complicated sampling (perhaps hierarchical rejection sampling of // the object surface is an option here - only the mesh faces which are inside the box // are considered as candidates) SampleViewpointContributions(sourceObject, viewPoint, beam, directionalSamples, stat); } // note: // this routine would be called only if the number of desired samples is sufficiently // large - for other rss tree cells the cpu based sampling is perhaps more efficient // distributing the work between cpu and gpu would also allow us to place more sophisticated // sample distributions (silhouette ones) using the cpu and the jittered once on the GPU // in order that thios scheme is working well the gpu render buffer should run in a separate // thread than the cpu sampler, which would not be such a big problem.... // disable alpha test again glDisable(GL_ALPHA_TEST); } void GlRendererBuffer::SampleViewpointContributions(Intersectable *sourceObject, const Vector3 viewPoint, Beam &beam, const int samples, BeamSampleStatistics &stat) { // 1. setup the view port to match the desired samples glViewport(0, 0, samples, samples); // 2. setup the projection matrix and view matrix to match the viewpoint + beam.mDirBox SetupProjectionForViewPoint(viewPoint, beam, sourceObject); // 3. reset z-buffer to 0 and render the source object for the beam // with glCullFace(Enabled) and glFrontFace(GL_CW) // save result to the front depth map // the front depth map holds ray origins // front depth buffer must be initialised to 0 float clearDepth; glGetFloatv(GL_DEPTH_CLEAR_VALUE, &clearDepth); glClearDepth(0.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); // glFrontFace(GL_CCW); glEnable(GL_CULL_FACE); glCullFace(GL_FRONT); glColorMask(0, 0, 0, 0); // stencil is increased where the source object is located glEnable(GL_STENCIL_TEST); glStencilFunc(GL_ALWAYS, 0x1, 0x1); glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE); #if 0 static int glSourceObjList = -1; if (glSourceObjList != -1) { glSourceObjList = glGenLists(1); glNewList(glSourceObjList, GL_COMPILE); RenderIntersectable(sourceObject); glEndList(); } glCallList(glSourceObjList); #else RenderIntersectable(sourceObject); #endif // copy contents of the front depth buffer into depth texture glBindTexture(GL_TEXTURE_2D, frontDepthMap); glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, depthMapSize, depthMapSize); // reset clear function glClearDepth(clearDepth); // 4. set up the termination depth buffer (= standard depth buffer) // only rays which have non-zero entry in the origin buffer are valid since // they realy start on the object surface (this is tagged by setting a // stencil buffer bit at step 3). glStencilFunc(GL_EQUAL, 0x1, 0x1); glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glDepthMask(1); glEnable(GL_DEPTH_TEST); glEnable(GL_CULL_FACE); glCullFace(GL_BACK); // setup front depth buffer glEnable(GL_TEXTURE_2D); #if 0 #ifdef USE_CG // bind pixel shader implementing the front depth buffer functionality cgGLBindProgram(sCgFragmentProgram); cgGLEnableProfile(sCgFragmentProfile); #endif #endif // 5. render all objects inside the beam // we can use id based false color to read them back for gaining the pvs glColorMask(1, 1, 1, 1); // if objects not stored in beam => extract objects if (beam.mFlags & !Beam::STORE_OBJECTS) { vector::const_iterator it, it_end = beam.mKdNodes.end(); Intersectable::NewMail(); for (it = beam.mKdNodes.begin(); it != it_end; ++ it) { mKdTree->CollectObjects(*it, beam.mObjects); } } // (objects can be compiled to a gl list now so that subsequent rendering for // this beam is fast - the same hold for step 3) // Afterwards we have two depth buffers defining the ray origin and termination #if 0 static int glObjList = -1; if (glObjList != -1) { glObjList = glGenLists(1); glNewList(glObjList, GL_COMPILE); ObjectContainer::const_iterator it, it_end = beam.mObjects.end(); for (it = beam.mObjects.begin(); it != it_end; ++ it) { // render all objects except the source object if (*it != sourceObject) RenderIntersectable(*it); } glEndList(); } glCallList(glObjList); #else ObjectContainer::const_iterator it, it_end = beam.mObjects.end(); for (it = beam.mObjects.begin(); it != it_end; ++ it) { // render all objects except the source object if (*it != sourceObject) RenderIntersectable(*it); } #endif // 6. Use occlusion queries for all viewcell meshes associated with the beam -> // a fragment passes if the corresponding stencil fragment is set and its depth is // between origin and termination buffer // create new queries if necessary OcclusionQuery::GenQueries(mOcclusionQueries, (int)beam.mViewCells.size()); // check whether any backfacing polygon would pass the depth test? // matt: should check both back /front facing because of dual depth buffer // and danger of cutting the near plane with front facing polys. glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); glDepthMask(GL_FALSE); glDisable(GL_CULL_FACE); ViewCellContainer::const_iterator vit, vit_end = beam.mViewCells.end(); int queryIdx = 0; for (vit = beam.mViewCells.begin(); vit != vit_end; ++ vit) { mOcclusionQueries[queryIdx ++]->BeginQuery(); RenderIntersectable(*vit); mOcclusionQueries[queryIdx]->EndQuery(); ++ queryIdx; } // at this point, if possible, go and do some other computation // 7. The number of visible pixels is the number of sample rays which see the source // object from the corresponding viewcell -> remember these values for later update // of the viewcell pvs - or update immediately? queryIdx = 0; for (vit = beam.mViewCells.begin(); vit != vit_end; ++ vit) { // fetch queries unsigned int pixelCount = mOcclusionQueries[queryIdx ++]->GetQueryResult(); if (pixelCount) Debug << "view cell " << (*vit)->GetId() << " visible pixels: " << pixelCount << endl; } // 8. Copmpute rendering statistics // In general it is not neccessary to remember to extract all the rays cast. I hope it // would be sufficient to gain only the intergral statistics about the new contributions // and so the rss tree would actually store no new rays (only the initial ones) // the subdivision of the tree would only be driven by the statistics (the glrender could // evaluate the contribution entropy for example) // However might be an option to extract/store only those the rays which made a contribution // (new viewcell has been discovered) or relative contribution greater than a threshold ... ObjectContainer pvsObj; stat.pvsSize = ComputePvs(beam.mObjects, pvsObj); // to gain ray source and termination // copy contents of ray termination buffer into depth texture // and compare with ray source buffer #if 0 VssRayContainer rays; glBindTexture(GL_TEXTURE_2D, backDepthMap); glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, depthMapSize, depthMapSize); ComputeRays(Intersectable *sourceObj, rays); #endif //////// //-- cleanup // reset gl state glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glDepthMask(GL_TRUE); glEnable(GL_CULL_FACE); glDisable(GL_STENCIL_TEST); #if 0 #ifdef USE_CG cgGLDisableProfile(sCgFragmentProfile); #endif #endif glDisable(GL_TEXTURE_2D); // remove objects from beam if (beam.mFlags & !Beam::STORE_OBJECTS) beam.mObjects.clear(); } void GlRendererBuffer::SetupProjectionForViewPoint(const Vector3 &viewPoint, const Beam &beam, Intersectable *sourceObject) { float left, right, bottom, top, znear, zfar; beam.ComputePerspectiveFrustum(left, right, bottom, top, znear, zfar, mSceneGraph->GetBox()); //Debug << left << " " << right << " " << bottom << " " << top << " " << znear << " " << zfar << endl; glMatrixMode(GL_PROJECTION); glLoadIdentity(); glFrustum(left, right, bottom, top, znear, zfar); //glFrustum(-1, 1, -1, 1, 1, 20000); const Vector3 center = viewPoint + beam.GetMainDirection() * (zfar - znear) * 0.3f; const Vector3 up = Normalize(CrossProd(beam.mPlanes[0].mNormal, beam.mPlanes[4].mNormal)); #ifdef GTP_DEBUG Debug << "view point: " << viewPoint << endl; Debug << "eye: " << center << endl; Debug << "up: " << up << endl; #endif glMatrixMode(GL_MODELVIEW); glLoadIdentity(); gluLookAt(viewPoint.x, viewPoint.y, viewPoint.z, center.x, center.y, center.z, up.x, up.y, up.z); } void GlRendererBuffer::InitGL() { MakeCurrent(); GlRenderer::InitGL(); // initialise dual depth buffer textures glGenTextures(1, &frontDepthMap); glBindTexture(GL_TEXTURE_2D, frontDepthMap); glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, depthMapSize, depthMapSize, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); glGenTextures(1, &backDepthMap); glBindTexture(GL_TEXTURE_2D, backDepthMap); glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, depthMapSize, depthMapSize, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); #if 0 #ifdef USE_CG // cg initialization cgSetErrorCallback(handleCgError); sCgContext = cgCreateContext(); if (cgGLIsProfileSupported(CG_PROFILE_ARBFP1)) sCgFragmentProfile = CG_PROFILE_ARBFP1; else { // try FP30 if (cgGLIsProfileSupported(CG_PROFILE_FP30)) sCgFragmentProfile = CG_PROFILE_FP30; else { Debug << "Neither arbfp1 or fp30 fragment profiles supported on this system" << endl; exit(1); } } sCgFragmentProgram = cgCreateProgramFromFile(sCgContext, CG_SOURCE, "../src/dual_depth.cg", sCgFragmentProfile, NULL, NULL); if (!cgIsProgramCompiled(sCgFragmentProgram)) cgCompileProgram(sCgFragmentProgram); cgGLLoadProgram(sCgFragmentProgram); cgGLBindProgram(sCgFragmentProgram); Debug << "---- PROGRAM BEGIN ----\n" << cgGetProgramString(sCgFragmentProgram, CG_COMPILED_PROGRAM) << "---- PROGRAM END ----\n"; #endif #endif DoneCurrent(); } void GlRendererBuffer::ComputeRays(Intersectable *sourceObj, VssRayContainer &rays) { for (int i = 0; i < depthMapSize * depthMapSize; ++ i) { //todo glGetTexImage() } } bool GlRendererBuffer::ValidViewPoint() { MakeCurrent(); SetupProjection(GetWidth(), GetHeight()); bool result = GlRenderer::ValidViewPoint(); DoneCurrent(); return result; } void GlRenderer::EvalPvsStat() { mPvsStat.Reset(); halton.Reset(); SetupProjection(GetWidth(), GetHeight()); cout<<"mPvsStatFrames="<= 0.0f) { if (err > mPvsStat.maxError) mPvsStat.maxError = err; mPvsStat.sumError += err; mPvsStat.sumPvsSize += mPvsErrorBuffer[i].mPvsSize; if (err == 0.0f) mPvsStat.errorFreeFrames++; mPvsStat.frames++; } } glFinish(); cout< 0.0f) { int pvsSize; // compute the pixel error float error = GetPixelError(pvsSize); mPvsErrorBuffer[i].mError = error; mPvsErrorBuffer[i].mPvsSize = pvsSize; cout << "(" << i << "," << mPvsErrorBuffer[i].mError <<")"; } err = mPvsErrorBuffer[i].mError; if (err >= 0.0f) { if (err > mPvsStat.maxError) mPvsStat.maxError = err; mPvsStat.sumError += err; mPvsStat.sumPvsSize += mPvsErrorBuffer[i].mPvsSize; if (err == 0.0f) ++ mPvsStat.errorFreeFrames; ++ mPvsStat.frames; } } glFinish(); cout << endl << flush; } bool GlRenderer::ValidViewPoint() { //cout<<"VV4 "; if (!mDetectEmptyViewSpace) return true; //cout << "vp: " << mViewPoint << " dir: " << mViewDirection << endl; OcclusionQuery *query = mOcclusionQueries[0]; // now check whether any backfacing polygon would pass the depth test SetupCamera(); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glEnable( GL_CULL_FACE ); glCullFace(GL_BACK); //cout<<"VV1 "; RenderScene(); glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); glDepthMask(GL_FALSE); glDisable( GL_CULL_FACE ); query->BeginQuery(); // cout<<"VV2 "; RenderScene(); // cout<<"VV3 "; query->EndQuery(); // at this point, if possible, go and do some other computation glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glDepthMask(GL_TRUE); glEnable( GL_CULL_FACE ); // int wait = 0; // while (!query.ResultAvailable()) { // wait++; // } // reenable other state unsigned int pixelCount = query->GetQueryResult(); // cout<<"VV4 "; //cout<<"count: " << pixelCount< 0) { return false; // backfacing polygon found -> not a valid viewspace sample } return true; } float GlRenderer::GetPixelError(int &pvsSize) { return -1.0f; } void GlRenderer::RenderViewPoint() { mWireFrame = true; glPushMatrix(); glTranslatef(mViewPoint.x, mViewPoint.y, mViewPoint.z); glScalef(5.0f,5.0f,5.0f); glPushAttrib(GL_CURRENT_BIT); glColor3f(1.0f, 0.0f, 0.0f); gluSphere((::GLUquadric *)mSphere, 1e-3*Magnitude(mViewCellsManager->GetViewSpaceBox().Size()), 6, 6); glPopAttrib(); glPopMatrix(); mWireFrame = false; } }