Skip to content

Instantly share code, notes, and snippets.

@mantissa
Created April 6, 2014 01:03
Show Gist options
  • Save mantissa/10000081 to your computer and use it in GitHub Desktop.
Save mantissa/10000081 to your computer and use it in GitHub Desktop.
Frustrum Culling with Shaders in OF
#version 120
uniform sampler2DRect pointsTexture;
uniform sampler2DRect resultsTexture;
uniform vec4 viewport;
uniform float width;
uniform mat4 mvMatrix;
varying vec4 texCoord0;
void main(){
vec3 pos = texture2DRect(pointsTexture, texCoord0.xy).xyz;
float d = 1.0f / (mvMatrix[0][3] * pos.x + mvMatrix[1][3] * pos.y + mvMatrix[2][3] * pos.z + mvMatrix[3][3]) ;
pos = (mvMatrix * vec4(pos, 1.0f)).yxz;
pos = pos * d;
vec3 screenPos;
screenPos.x = (( pos.x + 1.0f ) / 2.0f ) * viewport.z + viewport.x;
screenPos.y = (( 1.0f - pos.y ) / 2.0f ) * viewport.w + viewport.y;
screenPos.z = pos.z;
if( screenPos.x >= viewport.x && screenPos.x <= viewport.x+viewport.z && screenPos.y >= viewport.y && screenPos.y <= viewport.y+viewport.w ){
gl_FragColor = vec4(screenPos.x, screenPos.y, screenPos.z, 1.0f);
}else{
gl_FragColor = vec4(0.0f, 0.0f, 0.0f, 0.5f);
}
// for debugging
//gl_FragColor = vec4(mvMatrix[0].x, mvMatrix[0].y, mvMatrix[0].z, 1.0);
//gl_FragColor = vec4(screenPos.x, screenPos.y, screenPos.z, 1.0f);
}
#version 120
uniform sampler2DRect pointsTexture;
uniform vec4 viewport;
uniform mat4 mvMatrix;
varying vec4 texCoord0;
void main(){
texCoord0 = gl_MultiTexCoord0;
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}
#include "testApp.h"
//--------------------------------------------------------------
void testApp::setup(){
frustumShader.load("shaders/FrustrumCulling");
shaderImage.allocate( PT_COUNT, 1, OF_IMAGE_COLOR );
frustumShaderFBO.allocate( PT_COUNT, 1, GL_RGBA32F_ARB );
shaderPix.allocate( PT_COUNT, 1, OF_IMAGE_COLOR_ALPHA );
float * imgPixels = shaderImage.getPixels();
int w = ofGetWidth();
int h = ofGetHeight();
// how many pixels outside of the screen
float expandAmt = 400;
// how many points back
float maxZOffset = 800;
for( int i=0; i<PT_COUNT; i++ ){
ofPoint pt;
pt.x = ofRandom( -expandAmt, w+expandAmt );
pt.y = ofRandom( -expandAmt, h+expandAmt );
pt.z = ofRandom( 0, -maxZOffset );
int pix = i * 3;
imgPixels[ pix ] = pt.x;
imgPixels[ pix +1 ] = pt.y;
imgPixels[ pix +2 ] = pt.z;
testPoints.push_back( pt );
}
shaderImage.update();
}
//--------------------------------------------------------------
void testApp::update(){
// update the camera based on the mouse position ...
float mX = ofGetMouseX();
float mY = ofGetMouseY();
ofPoint lookAt;
lookAt.x = ofGetWidth()/2 + ofMap( mX, 0, ofGetWidth(), -300, 300, true);
lookAt.y = ofGetHeight()/2 + ofMap( mY, 0, ofGetHeight(), 300, -300, true);
lookAt.z = -400;
cam.setPosition(ofGetWidth()/2, ofGetHeight()/2, 100);
cam.lookAt( lookAt );
cam.setFov( 50 );
}
//--------------------------------------------------------------
void testApp::draw(){
bool bDoComparison = false;
if( !bDoComparison ){
drawCPUTest();
} else {
long start = ofGetElapsedTimeMillis();
drawCPUTest();
long cpuDraw = ofGetElapsedTimeMillis()-start;
start = ofGetElapsedTimeMillis();
drawGPUTest();
long gpuDraw = ofGetElapsedTimeMillis()-start;
printf("cpu draw: %zd, ", cpuDraw);
printf("gpu draw: %zd, diff %zd (%.2f)\n", gpuDraw, cpuDraw-gpuDraw, float(cpuDraw-gpuDraw)/cpuDraw);
}
ofDrawBitmapStringHighlight( ofToString(ofGetFrameRate(), 2), 20, ofGetHeight()-20 );
}
//--------------------------------------------------------------
void testApp::drawCPUTest(){
ofRectangle viewport = ofRectangle(20, 20, ofGetWidth()-40, ofGetHeight()-40 );
ofMatrix4x4 modelViewMat = cam.getModelViewProjectionMatrix(viewport);
cam.begin( viewport );
ofSetColor(255);
vector<ofPoint>::iterator pIter = testPoints.begin();
while( pIter != testPoints.end() ){
ofVec3f screenPt = (*pIter) * modelViewMat;
screenPt.x = ((screenPt.x + 1.0) / 2.0) * viewport.width + viewport.x;
screenPt.y = ((1.0 - screenPt.y) / 2.0) * viewport.height + viewport.y;
if( viewport.inside(screenPt.x, screenPt.y) ){
ofDrawSphere( (*pIter), 5 );
}
pIter++;
}
cam.end();
bool bShowScreenPts = false;
if( bShowScreenPts ){
ofSetColor(255, 0, 0);
vector<ofPoint>::iterator pIter = testPoints.begin();
while( pIter != testPoints.end() ){
ofVec3f screenPt = (*pIter) * modelViewMat;
screenPt.x = ((screenPt.x + 1.0) / 2.0) * viewport.width + viewport.x;
screenPt.y = ((1.0 - screenPt.y) / 2.0) * viewport.height + viewport.y;
if( viewport.inside(screenPt.x, screenPt.y) ){
ofCircle(screenPt.x, screenPt.y, 5 );
}
pIter++;
}
}
ofSetColor(255, 0, 0);
ofNoFill();
ofRect( viewport );
ofFill();
}
//--------------------------------------------------------------
void testApp::drawGPUTest(){
ofRectangle viewport = ofRectangle(20, 20, ofGetWidth()-40, ofGetHeight()-40 );
ofMatrix4x4 modelViewMat = cam.getModelViewProjectionMatrix(viewport);
frustumShaderFBO.begin();
ofClear(0, 255);
frustumShader.begin();
frustumShader.setUniformMatrix4f( "mvMatrix", modelViewMat );
frustumShader.setUniform4f( "viewport", viewport.x, viewport.y, viewport.width, viewport.height );
frustumShader.setUniformTexture( "pointsTexture", shaderImage, 0 );
ofSetColor(255);
ofxDrawShaderRect(0, 0, shaderImage.width, shaderImage.height );
frustumShader.end();
frustumShaderFBO.end();
// step 2: get the pix
frustumShaderFBO.getTextureReference().bind();
glGetTexImage(frustumShaderFBO.getTextureReference().getTextureData().textureTarget,0,ofGetGlFormat(shaderPix),GL_FLOAT,shaderPix.getPixels());
frustumShaderFBO.getTextureReference().unbind();
float * pix = shaderPix.getPixels();
int nPts = testPoints.size();
cam.begin( viewport );
for(int i=0; i<PT_COUNT; i++){
int pt = i * 4;
float w = pix[pt+3];
if( w >= 1.0 ){
ofDrawSphere( testPoints[i], 5 );
}
}
cam.end();
bool bShowScreenPts = false;
if( bShowScreenPts ){
for(int i=0; i<PT_COUNT; i++){
int pt = i * 4;
float x = pix[pt];
float y = pix[pt+1];
float w = pix[pt+3];
if( w == 1.0 ){
ofSetColor(255, 0, 0);
ofCircle(x, y, 5 );
}
}
}
ofSetColor(255, 0, 0);
ofNoFill();
ofRect( viewport );
ofFill();
bool bDrawTextures = false;
if( bDrawTextures ){
ofSetColor(255);
shaderImage.draw(5, 5, shaderImage.width, 10 );
frustumShaderFBO.draw(5, 20, shaderImage.width, 10 );
}
}
//--------------------------------------------------------------
void testApp::drawNoTest(){
ofRectangle viewport = ofRectangle(20, 20, ofGetWidth()-40, ofGetHeight()-40 );
cam.begin( viewport );
ofSetColor(255);
vector<ofPoint>::iterator pIter = testPoints.begin();
while( pIter != testPoints.end() ){
ofDrawSphere( (*pIter), 5 );
pIter++;
}
cam.end();
}
//--------------------------------------------------------------
void testApp::exit(){
testPoints.clear();
}
//--------------------------------------------------------------
void ofxDrawShaderRect(int x, int y, int w, int h){
ofSetColor(255);
glBegin(GL_QUADS);
glTexCoord2f(0, 0);
glVertex2f(0, 0);
glTexCoord2f(w, 0);
glVertex2f(w, 0);
glTexCoord2f(w, h);
glVertex2f(w, h);
glTexCoord2f(0, h);
glVertex2f(0, h);
glEnd();
}
#include "ofMain.h"
#define PT_COUNT 3000
class testApp : public ofBaseApp {
public:
void setup();
void update();
void draw();
void exit();
void drawCPUTest();
void drawGPUTest();
void drawNoTest();
ofCamera cam;
ofShader frustumShader;
ofFbo frustumShaderFBO;
ofFloatImage shaderImage;
ofFloatImage resultsImage;
ofFloatPixels shaderPix;
vector<ofPoint> testPoints;
};
void ofxDrawShaderRect(int x, int y, int w, int h);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment