Skip to content

Instantly share code, notes, and snippets.

@roymacdonald
Last active March 31, 2020 07:29
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save roymacdonald/5a91380ca9ac2404f46f3625b5d514d1 to your computer and use it in GitHub Desktop.
Save roymacdonald/5a91380ca9ac2404f46f3625b5d514d1 to your computer and use it in GitHub Desktop.

The depth test

What is it for

When any geometry gets rendered (remember that even for simply drawing textures you need a geometry to "hold" the texture) it gets drawn on top of what's already been rendered. There are a lot of cases in which this behavior is fine or even desirable, ie. 2D rendering. On the other hand, there are cases in which this behavior might not be what you are looking for, usually when drawing three dimensional objects. For example, you want to draw two spheres that are partially overlapping. Each sphere is a different geometry, so one will get drawn before the other. When the depth test is disabled, one sphere will get drawn on top of the other regardless of its depth - its position in the Z axis-, making it look spatially incorrect. To get the spatially correct looking render what we want to happen is to have drawn only the pixels of the overlapping geometries that have the lowest depth. in order to achieve this is that we use depth testing.

To enable the depth test call

ofEnableDepthTest();

and for disabling it call

ofDisableDepthTest();

Important. The code provided here is for being used with openFrameworks

Test the following code. Just make a new empty OF project and paste into the draw function.

//--------------------------------------------------------------
void ofApp::draw(){

    bool bPressed = ofGetMousePressed();
    
    if(bPressed){
        ofEnableDepthTest();
    }else{
        ofDisableDepthTest();
    }
    
    ofSetColor(ofColor::yellow);
    float y = ofGetHeight()/2;
    float m = ofGetWidth()/2;
    float radius = 200;
    float overlap = ofMap(ofGetMouseX(), 0, ofGetWidth(), 50, radius);
    ofDrawSphere(m - overlap, y, radius);

    ofSetColor(ofColor::red);
    ofDrawSphere(m + overlap, y, radius);
    if(bPressed){
        ofDisableDepthTest();
    }
    
    ofDrawBitmapStringHighlight("Depth test enabled: " + (string)(bPressed?"TRUE":"FALSE"), 20, 20);
    
}
//--------------------------------------------------------------

move the mouse sideways to change the overlap amount. Keep pressing the mouse button to enable depth testing.

How it works

The way this works is quite simple. When the depth test is enabled, there is a buffer- a grayscale group of pixels the same size of the rendering surface- which stores the depth at which each pixel was rendered. So whenever we want to draw something, each pixel gets tested against the depth buffer; if the depth of the new pixel is lower than the correspondent value in the depth buffer it will get drawn, thus passing the test, where also the depth buffer value is updated to the new pixel's depth.

Inspecting the depth buffer

We can get access to the depth buffer for either debuging purposes or for providing some kind of special shading. Make a new openFrameworks project with the project generator, copy and paste de following code into main.cpp file. Delete ofApp.h and ofApp.cpp (this is the single file form for OF projects).

#include "ofMain.h"
#include "ofAppGLFWWindow.h"
#include "ofMain.h"
class ofApp : public ofBaseApp{
public:

    ofFloatPixels depthPix;
    ofImage depth;
//--------------------------------------------------------------
void setup(){
    depthPix.allocate(ofGetWidth(), ofGetHeight(), OF_IMAGE_GRAYSCALE);
    depth.allocate(ofGetWidth(), ofGetHeight(), OF_IMAGE_GRAYSCALE);
}
//--------------------------------------------------------------
void draw(){
    bool bPressed = ofGetMousePressed();
    bool bDrawDepth = ofGetKeyPressed(' ');
    
    if(bPressed || bDrawDepth){
        ofEnableDepthTest();
    }else{
        ofDisableDepthTest();
    }
    
    ofSetColor(ofColor::yellow);
    float y = ofGetHeight()/2;
    float m = ofGetWidth()/2;
    float radius = 200;
    float overlap = ofMap(ofGetMouseX(), 0, ofGetWidth(), 50, radius);
    ofDrawSphere(m - overlap, y, radius);
    
    ofSetColor(ofColor::red);
    ofDrawSphere(m + overlap, y, radius);
    
    
    float mn = std::numeric_limits<float>::max();
    float mx = -mn;
    if(bPressed || bDrawDepth){
        // depth values returned from this function a floats between 0 and 1; 0 is the closest 1 is farthest away
        glReadPixels( 0,0, depthPix.getWidth(), depthPix.getHeight(), GL_DEPTH_COMPONENT, GL_FLOAT, depthPix.getData());        
        
        ofDisableDepthTest();
        
        //find the depth values min and max that are not one or zero, so we get the min and max of the z values of the objects drawn.
        for (auto & pixel : depthPix) {
            if(pixel != 0.0f && pixel != 1.0f){
                mx = MAX(mx, pixel);
                mn = MIN(mn, pixel);			
            }
        }
        
        for (int i = 0; i<depthPix.size(); i++) {
            depth.getPixels().getData()[i] = ofMap(depthPix.getData()[i],mn,mx, 0,255, true);
        }
        depth.update();
    }
    
    if(bDrawDepth){
        ofBackground(0);
        ofSetColor(255);
        depth.draw(0, 0);
    }
    
    // just info for printing to screen....
    stringstream ss;
    ss << "Move the mouse sideways to set the spheres separation" <<endl;
    ss << "Depth test enabled: " << (string)((bPressed||bDrawDepth)?"TRUE":"FALSE") << endl;
    ss << "Keep pressed the mouse button to enable the depth test."<< endl;
    ss << "Keep pressed the space bar key to view the depth buffer" << endl;
    if(bPressed||bDrawDepth){
        ss << "Z max " << mx << "  Z min " << mn << endl;
    }
    ofDrawBitmapStringHighlight(ss.str(), 20, 20);
    
    
}
};

//========================================================================
int main( ){
    ofGLFWWindowSettings settings;
    //this is an ugly hack only for osx in order to get back the whole depth buffer.
    //It is an openGL driver issue....
    settings.numSamples = 0;
    settings.width = 1024;
	settings.height = 768;
	settings.windowMode = OF_WINDOW;
    ofCreateWindow(settings);
	ofRunApp(new ofApp());

}

Enabling and disabling the depth test.

One could be tempted to simply keep the depth test enabled all the time, but this is not a good idea for some reasons.

  • The depth test is expensive. A lot more calculations need to be done than when it is disabled.
  • The results might not be the desired ones. (try drawing an ofxPanel object (part of ofxGui) with the depth test enabled) It is for these reasons that you should only enable the depth test when needed and disable it as soon as you dont.

Problems with the depth Test.

Here are some links for some common problems with depth testing:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment