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 () );
}