Has anyone done a simple demo about normal mapping with OpenGL? All it would need to do is draw a cube, using texture and normalmap texture, as a bonus even a specular map texture.
It would be nice if it's possible without getting into pixel-shaders, but i know there are some demos using GL_ARB_multitexture. I have looked up some C sources but just can't get it working. Must be missing some detail. Best i have come up with is like in attached screenshot (you can see that it's actually just drawing the normal map, instead of using it for lighting, which in itself goes wrong).
Also, i am aware there are 2 different kinds of normal maps. One where it multiplies the normalmap with models own face normals, and one where normalmap is the only thing used for lighting calculation. I'm actually more interested in the latter, because it would mean less memory use if i don't need vector arrays for normals.
Well, i can give some converted code which has propably ton of bugs (it's fit for nxPascal engine):
Code:
model: TGLModel;
texture, normalMap, cubemap: cardinal;
dl: TDisplayList;
tangentSpaceLight: array of TVector;
sTangent, tTangent, normal: array of TVector;
Code:
procedure GenerateNormalisationCubeMap;
var size: integer; offset, halfSize: single; data: array of byte;
procedure Generate(arb: byte);
var i, j, dp: integer; tempVector: TVector;
begin
dp:=0;
for j:=0 to size-1 do
for i:=0 to size-1 do begin
case arb of
0: begin // +X
tempVector.x:=halfSize;
tempVector.y:=-(j+offset-halfSize);
tempVector.z:=-(i+offset-halfSize);
end;
1: begin // -X
tempVector.x:=-halfSize;
tempVector.y:=-(j+offset-halfSize);
tempVector.z:=(i+offset-halfSize);
end;
2: begin // +Y
tempVector.x:=(i+offset-halfSize);
tempVector.y:=halfSize;
tempVector.z:=(j+offset-halfSize);
end;
3: begin // -Y
tempVector.x:=(i+offset-halfSize);
tempVector.y:=-halfSize;
tempVector.z:=-(j+offset-halfSize);
end;
4: begin // +Z
tempVector.x:=(i+offset-halfSize);
tempVector.y:=-(j+offset-halfSize);
tempVector.z:=halfSize;
end;
5: begin // -Z
tempVector.x:=-(i+offset-halfSize);
tempVector.y:=-(j+offset-halfSize);
tempVector.z:=-halfSize;
end;
end;
tempVector:=Norm2(tempVector);
tempVector.x:=0.5*tempVector.x+0.5;
tempVector.y:=0.5*tempVector.y+0.5;
tempVector.z:=0.5*tempVector.z+0.5;
data[dp]:=round(tempVector.x*255);
data[dp+1]:=round(tempVector.y*255);
data[dp+2]:=round(tempVector.z*255);
inc(dp,3);
end;
arb:=GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB+arb;
glTexImage2D(arb, 0, GL_RGBA8, size, size, 0, GL_RGB,
GL_UNSIGNED_BYTE, @data[0]);
end;
var i: integer;
begin
size:=32; offset:=0.5; halfSize:=size/2;
setlength(data, size*size*3);
for i:=0 to 5 do Generate(i);
end;
Code:
procedure TForm1.FormCreate(Sender: TObject);
var bmp: TBitmap; i: integer;
begin
clientwidth:=800; clientheight:=600;
nx.CreateGlWindow(self);
nx.Perspective(false); nx.DefaultLights;
if not nx.GLInfo('GL_ARB_multitexture') then begin
showmessage('GL_ARB_multitexture not supported!'); exit;
end;
tex.Options:=tex.Options+[toKeepData];
model:=TGLModel.Create;
model.LoadFromW3D('data\donut.w3d');
model.LoadTextures('data');
texture:=model.mat[0].texIndex;
model.MakeDisplayList(dl);
setlength(tangentSpaceLight, model.vCount);
setlength(sTangent, model.fCount);
setlength(tTangent, model.fCount);
setlength(normal, model.fCount);
for i:=0 to model.fCount-1 do CalculateTangentSpace(i);
bmp:=TBitmap.Create;
MakeBumpTexture(0, bmp);
normalMap:=tex.AddTexture('bump','');
tex.LoadBMPData(normalMap, bmp);
tex.Restore(normalMap);
normalMap:=tex.texture[normalMap].index;
model.mat[0].texIndex:=normalMap;
bmp.Free;
cubemap:=tex.AddTexture('_cubemap_','');
cubemap:=tex.texture[cubemap].index;
glBindTexture(GL_TEXTURE_CUBE_MAP_ARB, cubemap);
GenerateNormalisationCubeMap;
glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
...
end;
Code:
procedure TForm1.Timer1Timer(Sender: TObject);
var i, j: integer; objectLightPosition, lightVector: TVector;
begin
...
objectLightPosition:=vector(0, 5, -10);
for i:=0 to model.fCount-1 do
for j:=0 to 2 do begin
lightVector:=VectorSub2(objectLightPosition, model.va[model.fa[i, j]]);
tangentSpaceLight[model.fa[i, j]].x:=Dot(sTangent[i], lightVector);
tangentSpaceLight[model.fa[i, j]].y:=Dot(tTangent[i], lightVector);
tangentSpaceLight[model.fa[i, j]].z:=Dot(normal[i], lightVector);
end;
//Bind normal map to texture unit 0
glBindTexture(GL_TEXTURE_2D, normalMap);
glEnable(GL_TEXTURE_2D);
//Bind normalisation cube map to texture unit 1
glActiveTextureARB(GL_TEXTURE1_ARB);
glBindTexture(GL_TEXTURE_CUBE_MAP_ARB, CubeMap);
glEnable(GL_TEXTURE_CUBE_MAP_ARB);
glActiveTextureARB(GL_TEXTURE0_ARB);
glVertexPointer(3, GL_FLOAT, 0, @model.va[0]);
glEnableClientState(GL_VERTEX_ARRAY);
glTexCoordPointer(2, GL_FLOAT, 0, @model.ta[0]);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glClientActiveTextureARB(GL_TEXTURE1_ARB);
glTexCoordPointer(3, GL_FLOAT, 0, @tangentSpaceLight[0]);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glClientActiveTextureARB(GL_TEXTURE0_ARB);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE);
glActiveTextureARB(GL_TEXTURE1_ARB);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_DOT3_RGB_ARB);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PREVIOUS_ARB);
glActiveTextureARB(GL_TEXTURE0_ARB);
// Render Bumps
glDrawElements(GL_TRIANGLES, model.fCount*3, GL_UNSIGNED_SHORT, @model.fa[0]);
glDisable(GL_TEXTURE_2D);
glActiveTextureARB(GL_TEXTURE1_ARB);
glDisable(GL_TEXTURE_CUBE_MAP_ARB);
glActiveTextureARB(GL_TEXTURE0_ARB);
//disable vertex arrays
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glClientActiveTextureARB(GL_TEXTURE1_ARB);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glClientActiveTextureARB(GL_TEXTURE0_ARB);
//Return to standard modulate texenv
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
//Enable multiplicative blending
glBlendFunc(GL_DST_COLOR, GL_ZERO);
glEnable(GL_BLEND);
glBindTexture(GL_TEXTURE_2D, Texture);
glEnable(GL_TEXTURE_2D);
glVertexPointer(3, GL_FLOAT, 0, @model.va[0]);
glEnableClientState(GL_VERTEX_ARRAY);
glNormalPointer(GL_FLOAT, 0, @model.va[0]);
glEnableClientState(GL_NORMAL_ARRAY);
glTexCoordPointer(2, GL_FLOAT, 0, @model.ta[0]);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
// Render Object
glDrawElements(GL_TRIANGLES, model.fCount*3, GL_UNSIGNED_SHORT, @model.fa[0]);
//Disable texture
glDisable(GL_TEXTURE_2D);
//disable vertex arrays
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
//Disable blending if it is enabled
glDisable(GL_BLEND);
nx.Flip;
But of course, if this would be simpler or more efficient to do with pixel-shaders, then i will of course try that instead. I just don't know about this topic much.
Bookmarks