Specular mapping

hey everybody,

i’m trying to create some specular lighting on a texture, without calculating the lighting via actual lights/geometry. that means i’m having a ofPlane and i am mapping a texture onto it and am displaying it, all without cameras/lights/displayment of geometry.

imagine the texture i want to add specular lighting onto looks like this:

then i have a specular map, which is greyscale and supposed to determine where on the texture i am having reflections. that map could looks like this:

here’s an example illustrating what i am trying to achieve: http://learningwebgl.com/lessons/lesson15/index.html
a slightly more visual pleasing version here from mio: https://www.instagram.com/p/BBpuUpPkk0n/

so imagine the gray noise texture would displace the colored texture and calculate the lighting according to that geometry. unfortunately this is rather slow performance wise… :confused:

my trouble now is to adopt it to modern opengl, as i am using opengl 4.4 for other aspects of the project and because i would like to not use a camera, as it should always stay 2d. in case somebody has some directions, im very thankful already.

best,
marcel

Ok, let me keep this as a notebook for myself & others:

First one needs to calculate the normal map from a grayscale image like it’s described here: http://stackoverflow.com/questions/5281261/generating-a-normal-map-from-a-height-map

Eventually I managed to elevate a plane’s vertices on the vertex shader depending on the vertex shader and computing per pixel lighting in the fragment shader.

With this heightmap:

And this texture (doesn’t really matter in this case, though):

I am arriving with this:

Though I have per pixel lighting, I want the lighting to be phong-like, not like in the image above. I’ve been reading that one simply needs to interpolate normals for each fragment, though I haven’t gotten to that.

Here’s the code

normalmap.vert:

#version 330

uniform sampler2DRect grayTex;
uniform mat4 modelViewProjectionMatrix;
uniform mat4 modelViewMatrix;

in vec2 texcoord;
in vec4 position;
in vec3 normal;

out vec3 vpos;
out vec3 vertexnormal;
out vec2 vertexTexCoord;

void main() {
	vec4 color = texture(grayTex, texcoord);
	vec4 pos = modelViewProjectionMatrix * vec4(position.x, position.y, color.x * 50.0, position.w);
	vpos = pos.xyz;
	vertexTexCoord = texcoord;
	// two options
	//vertexnormal = (modelViewMatrix * pos).xyz;
	vertexnormal = (pos).xyz;
	gl_Position = pos;
}

normalmap.frag:

// adopted from https://bitbucket.org/volumesoffun/polyvox/wiki/Computing%20normals%20in%20a%20fragment%20shader

#version 330

in vec2 vertexTexCoord;
in vec3 vpos;
in vec3 vertexnormal;
uniform sampler2DRect colorTex;
uniform vec3 lightpos;
uniform float specularness;
out vec4 outputColor;
void main( ) {
    vec3 N = normalize(cross(dFdy(vpos), dFdx(vpos)));
    // N is the world normal
	vec3 V = normalize(vertexnormal);
    vec3 R = reflect(V, N);
    vec3 L = normalize(vec3(lightpos.x, lightpos.y, lightpos.y));

    vec4 texture = texture( colorTex, vertexTexCoord ) * 0.7;
    vec4 ambient = vec4(0.2, 0.2, 0.2, 0.0);
    vec4 diffuse = vec4(0.2, 0.2, 0.2, 0.5) * max(dot(L, N), 0.0);
    vec4 specular = vec4(1.0, 1.0, 1.0, 0.8) * pow(max(dot(R, L), 0.0), specularness);
    outputColor = texture + ambient + diffuse + specular;
}

cpp/of:

#include "ofMain.h"

class NormalMapGenerationExample : public ofBaseApp {
public:
	ofEasyCam cam;

	ofShader shader;
	ofPlanePrimitive plane;

	ofImage heightmap, textureImage;

	void setup() {
		ofEnableDepthTest();
		ofSetVerticalSync( false );
		ofSetFrameRate( 500 );
		shader.load( "shaders\\normalmap");
		heightmap.load("perlin_noise.png");
		textureImage.load("colormap_result.png");

		int planeWidth = heightmap.getWidth();
		int planeHeight = heightmap.getHeight();
		int planeGridSize = 1;
		int planeColumns = planeWidth / planeGridSize;
		int planeRows = planeHeight / planeGridSize;

		plane.set( planeWidth, planeHeight, planeColumns, planeRows, OF_PRIMITIVE_TRIANGLES );
		plane.mapTexCoords( 0, 0, heightmap.getWidth(), heightmap.getHeight() );
	}

	void update() {
		ofSetWindowTitle( ofToString(ofGetFrameRate()) );
	}

	void draw() {
		ofBackground( 0 );
		cam.begin();
		shader.begin();
		shader.setUniformTexture( "grayTex", heightmap.getTextureReference(), 0 );
		shader.setUniformTexture( "colorTex", textureImage.getTextureReference(), 1 );

		float x = ofMap(ofGetMouseX(), 0, ofGetWidth(), 0, 1 );
		float y = ofMap(ofGetMouseY(), 0, ofGetHeight(), 0, 1 );
		shader.setUniform3f( "lightpos", ofVec3f(x, y, 0.0) );
		shader.setUniform1f( "specularness", 40.0 );
		plane.draw();
		shader.end();
		cam.end();
	}
};

int main() {
	ofSetupOpenGL( 1280, 720, OF_WINDOW );
	ofRunApp( new NormalMapGenerationExample () );
}
3 Likes