Area light shader

hey all,

i’m trying to port west langley’s three.js super area light example to OF (0.93), but to no avail.

http://jsfiddle.net/hh74z2ft/1/

i suspect the issue has to do with my surface’s normals or the test to see if the vertex is under the light, but i don’t completely understand the test that langley has written.

here’s what i have so far (using GLSL shader version 120). would be grateful if anybody could, ahem, shed some light on why it’s not working.

basic.vert

#version 120

varying vec3 vNormal;                // in camera space
varying vec3 vViewPosition;            // in camera space

void main() {

    vec4 mViewPosition = (gl_ModelViewMatrix * gl_Vertex);
    vViewPosition = -mViewPosition.xyz;
    
    vNormal = normalize(gl_NormalMatrix * gl_Normal);

    gl_TexCoord[0] = gl_MultiTexCoord0;
    gl_Position = ftransform();
}

areaLight.frag

#version 120

#define NVERTS 4

uniform vec3 color;
uniform vec3 lightColor;
uniform float lightIntensity;
uniform vec3 lightverts[ NVERTS ];    // in local space
uniform mat4 viewMatrix;
uniform mat4 lightMatrixWorld;

varying vec3 vNormal;                // in camera space
varying vec3 vViewPosition;            // in camera space

void main() {

    vec3 normal = normalize( vNormal );

    vec4 lPosition[ NVERTS ];
    vec3 lVector[ NVERTS ];

    // stub in some ambient reflectance
    vec3 ambient = color * vec3( 0.2 );

    // direction vectors from point to area light corners
    for( int i = 0; i < NVERTS; i ++ ) {
        lPosition[ i ] = viewMatrix * lightMatrixWorld * vec4( lightverts[ i ], 1.0 ); // in camera space
        lVector[ i ] = normalize( lPosition[ i ].xyz + vViewPosition.xyz ); // dir from vertex to areaLight
    }

    // bail if the point is on the wrong side of the light... there must be a better way...
    float tmp = dot( lVector[ 0 ], cross( ( lPosition[ 2 ] - lPosition[ 0 ] ).xyz, ( lPosition[ 1 ] - lPosition[ 0 ] ).xyz ) );

    /* uncomment to see if it passes the test

    if( tmp > 0 ){
        gl_FragColor = vec4( 1.0, 1.0, 1.0, 1.0 );
    } else{
        gl_FragColor = vec4( 0.0, 0.0, 0.0, 1.0 );
    }
    
    return;
    
    */    

    if ( tmp > 0.0 ) {

        gl_FragColor = vec4( ambient, 1.0 );
        return;
    }

    // vector irradiance at point
    vec3 lightVec = vec3( 0.0 );

    for( int i = 0; i < NVERTS; i ++ ) {

        vec3 v0 = lVector[ i ];
        vec3 v1 = lVector[ int( mod( float( i + 1 ), float( NVERTS ) ) ) ]; // ugh...

        lightVec += acos( dot( v0, v1 ) ) * normalize( cross( v0, v1 ) );
    }

    // irradiance factor at point
    float factor = max( dot( lightVec, normal ), 0.0 ) / ( 2.0 * 3.14159265 );

    // frag color
    vec3 diffuse = color * lightColor * lightIntensity * factor;

    gl_FragColor = vec4( ambient + diffuse, 1.0 );
}

and the draw() call:

void ofApp::draw(){
    
    ofDisableAlphaBlending();
    ofEnableLighting();
    ofEnableDepthTest();
    
    ofBackground(0);
    
    // mesh surface (to cast light on)
    ofMesh surface;
    surface.setMode(OF_PRIMITIVE_TRIANGLE_STRIP);
    surface.addVertex(ofVec2f(-ofGetWidth()/2, -ofGetHeight()/2));
    surface.addVertex(ofVec2f(ofGetWidth()/2, -ofGetHeight()/2));
    surface.addVertex(ofVec2f(-ofGetWidth()/2, ofGetHeight()/2));
    surface.addVertex(ofVec2f(ofGetWidth()/2, ofGetHeight()/2));
    surface.addNormal((ofVec3f(0, 0, 1)));
    surface.addNormal((ofVec3f(0, 0, 1)));
    surface.addNormal((ofVec3f(0, 0, 1)));
    surface.addNormal((ofVec3f(0, 0, 1)));
    
    // light transformations
    ofNode areaLightNode;
    areaLightNode.setPosition(position);
    areaLightNode.rotate(ofQuaternion(cos(ofGetElapsedTimef()*0.25)*180,ofVec3f(1, 0, 0)));
    
    // light mesh
    ofMesh lightMesh;
    lightMesh.setMode(OF_PRIMITIVE_TRIANGLE_STRIP);
    lightMesh.addVertex(ofVec3f(-dimensions->x/2, -dimensions->y/2, 0));
    lightMesh.addVertex(ofVec3f(dimensions->x/2, -dimensions->y/2, 0));
    lightMesh.addVertex(ofVec3f(-dimensions->x/2, dimensions->y/2, 0));
    lightMesh.addVertex(ofVec3f(dimensions->x/2, dimensions->y/2, 0));
    
    vector<ofVec3f> & listPts = lightMesh.getVertices();
    
    // draw the scene

    ofCamera cam;
    cam.setPosition(ofVec3f(0, 0, 800));
    cam.lookAt( ofVec3f(0, 0, 0), ofVec3f(0, 1, 0));
    cam.setNearClip(0.1);
    cam.setFarClip(1000);
    cam.setFov(60.0);
    cam.begin();
    
    // view matrix
    ofMatrix4x4 viewMat = ofGetCurrentViewMatrix();
    
    // light matrix
    ofMatrix4x4 areaLightMat = areaLightNode.getGlobalTransformMatrix();
    
    lightShader.begin();
    lightShader.setUniform3f("color", matAmbientColor->r, matAmbientColor->g, matAmbientColor->b);
    lightShader.setUniform3f("lightColor", lightColor->r, lightColor->g, lightColor->b);
    lightShader.setUniform1f("lightIntensity", lightIntensity);
    lightShader.setUniformMatrix4f("viewMatrix", viewMat);
    lightShader.setUniformMatrix4f("lightMatrixWorld", areaLightMat);
    
    // note: points should be in clockwise order
    lightShader.setUniform3f("lightverts[0]", listPts[0].x, listPts[0].y, listPts[0].z);
    lightShader.setUniform3f("lightverts[1]", listPts[1].x, listPts[1].y, listPts[1].z);
    lightShader.setUniform3f("lightverts[2]", listPts[3].x, listPts[3].y, listPts[3].z);
    lightShader.setUniform3f("lightverts[3]", listPts[2].x, listPts[2].y, listPts[2].z);
    
    surface.draw();
    
    lightShader.end();
    
    ofDisableLighting();
    
    // light debug-vew
    
    ofPushMatrix();
    
    ofMultMatrix(areaLightMat);
    
    ofSetColor(ofColor::white);
    lightMesh.drawWireframe();

    ofPopMatrix();
    
    cam.end();
    
    ofDisableDepthTest();
    
    gui.draw();
}

Hi,

I haven’t tried it out myself, but there area lights are implemented in at least OF 0.92 and above, there is a new light type and then the shaders are at the bottom of ofMaterial.cpp

thanks for pointing that out, @hahakid.

there’s even an area light example that ships with 0.92!

i’ll take a look at what’s happening under the hood.