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;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.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;


Reply With Quote
Those examples were just too good. I just updated nxPascal SVN and here is source code, if someone wants to try it.
I have tried modifying the code myself a bit too, but the math behind them is still complicated. Basically neither shader should not include word "gl_Normal", in my opinion. The normal in pixel position is exactly that of normalMap texture in those coords. Every shader that i have found is using gl_Normal.

