Problem with normal mapping?

Blitz3D Forums/Blitz3D Programming/Problem with normal mapping?

Who was John Galt?(Posted 2003) [#1]
Taking a look at Fredborg's demo , I may have noticed a problem.

If you replace the head mesh with a cube and keep the light position fixed, it becomes apparent that the normal mapping isn't putting shadows in the right direction- it just looks like a fixed texture. Is this....
(1) A problem with Blitz normal mapping
(2) A problem with the demo [looks OK 2 me]
(3) A known limitation of cubemapping
(4) Something I have done wrong in my (minimal) tweaking
(5) None of the above

Any ideas?


Rob(Posted 2003) [#2]
Well you need a normal texture don't you? And the cube needs texture coordinates of the right kind. I don't use this method personally.


Who was John Galt?(Posted 2003) [#3]
I'm using the normal texture as per Fredborg's demo. I don't THINK the texture coordinates come into it any more than they do with a standard texture although I may b wrong.


sswift(Posted 2003) [#4]
Blitz is currently limited in what you can do with normal mapping. At least, nobody has figured out any ways to use it exaclty how Doom 3 does.

You can simulate a "directional" point light casting light onto a non-vertex-animated object with a normal map in object space, by adjusting the object's entitycolor according to where the light source is in object space as the entity moves and rotates.

This however is useless for animated characters unless said characters are segmented 3D models, or have a different model for each frame of animation along with a different texture.

What you really need to do to do Doom 3 lighting is have normal maps which are in tangent space... Ie, relative to the polygon face. When you see a normal map which is mostly pale pinks and yellows, that is an indicator that it is in tangent space. When a large part of the model is blue, that indicates global space.

By having the maps in tangent space, the maps need not change as the model changes shape. They are always correct. However, you need to dot3 them by a light that is also in tangent space, and the color you need to use will be different for each polygon in the model. Games like Doom use vertex and pixel shaders to do that sort of thing.

The best I think you can do in Blitz right now is to use a cubic environment map to get those tangent space colors onto the model fast. Ie, the cubic map is a smoother spherical gradient which at each location has the global normal of the light realtive to a polygon is facing in that direction. Then the polygons pick up that color when they face that direction and that is dot3 blended with the tangent space normal map.

But the problem with using that, though it works with vertex animated characters, is that your light source is then fixed in position in global space relative to the character. Ie, it's like a directional light. You can't "walk past it". You can though "walk away from it", because you can adjust the entitycolor of the surface the cubic map is on before the cubic map is dot 3 blended with the model.

The only way to simulate point lights with this method so that you can cimulate things like torches which you can walk around, would be to update the cubic map every frame. This would require placing a camera inside an appropriately textured fullbright sphere, and rendering six views. This may be feasable with small enough cubic maps and few enough characters visible at once.

These still are not TRUE point lights though, because they can't pass under a monster's arm and light the model properly from there. They're point-directional, a term I just invented. :-)


Who was John Galt?(Posted 2003) [#5]
swift- thanks very much for that detailed response.

I'm intrigued by the idea of using a cubic environment map to set the required vertex colours (tangents). Is a cubic environment map just a cube (6 sided) map? Could you please try to explain this to me one more time as I'm not sure I wholly understand it. Why is a cubic map needed?


fredborg(Posted 2003) [#6]
When applying the dot3 texture to the cube, it is probably turned upside down or flipped horizontally. That's why the light comes from the wrong side. Generally a dot3 texture is only good for the model it's made for.


Who was John Galt?(Posted 2003) [#7]
No problem with the way the dot3 texture goes on the cube, and I can understand a dot3 texture may not look quite right on models other than that for which it was made, but it looks vaguely like a face when on a flat surface. The problem with your demo (it's an excellent demo btw- taught me a thing or 2), is that it doesn't account for the rotation of vertices about their normal. Say I have a flat poly with a normal map for a face on it (designed for use on a flat poly). Now say I rotate the poly 180 degrees about its normal- the normal, hence the vertex colours have not changed, but a rotation of the values in the vertex colour are required to make the shadows on the normal map face the right way.Perhaps this isn't an issue if wrapping onto a proper 3D model, but it may be. Try this bodged version. Lose the decals and hit 3 a couple of times to see what I'm on about..........

; Dot3 Lighting example
;
; By Mikkel Fredborg
; Model and texture from http://www.soclab.bth.se/practices/orb.html

;RUINED BY LEE

Type Dot3Light
	Field ent
	Field mul#
	Field typ
End Type

Graphics3D 640,480,0,2

decl=LoadTexture( "derhead_decal.jpg",1+8)
bump=LoadTexture( "derhead_local.tga",1+8)
cube=CreateQuad();LoadMesh("head.x")
PositionEntity cube,0,0,-1

EntityFX cube,1+2
TextureBlend bump,4
EntityTexture cube,bump,0,0
EntityTexture cube,decl,0,1

UpdateNormals cube

light = Dot3_CreateLight(2)
lighticon = CreateSphere(8,light)
EntityFX lighticon,1
ScaleEntity lighticon,0.1,0.1,0.1
Dot3_LightRange light,8

camera=CreateCamera()
CameraClsColor camera,100,120,130
PositionEntity camera,0,0,-1.6
CameraRange camera,0.1,100

a# = 0.


AmbientLight 0,0,0

declon = True
bumpon = True

rotatemode=0

While Not KeyHit(1)
	
	a = a + 0.5
	If a = 360.0 Then a = 0
	
	
	If rotatemode>0
		PositionEntity light,Cos(0)*5,Sin(0)*5,-9
		RotateEntity cube,0,0,a
	Else
		PositionEntity light,Cos(a)*5,Sin(a)*5,-9
		RotateEntity cube,0,0,0
	EndIf
	 


	If KeyHit(2)
		If declon
			TextureBlend decl,0
		Else
			TextureBlend decl,2
		End If
		declon = Not declon
	End If
	If KeyHit(3)
		If bumpon
			TextureBlend bump,0
			EntityFX cube,0
		Else
			TextureBlend bump,4
			EntityFX cube,1+2
		End If
		bumpon = Not bumpon
	End If

	If KeyHit(4) rotatemode=rotatemode+1
	If rotatemode=4 Then rotatemode=0
		
	If KeyDown(30) TranslateEntity camera,0,0,0.01
	If KeyDown(44) TranslateEntity camera,0,0,-0.01
	
	If bumpon
		If rotatemode=2
			UpdateBumpNormals(cube,light,a)
		Else
			updatebumpnormals(cube,light,0)
	
		EndIf
	End If
		
	UpdateWorld
	RenderWorld
	
	Text GraphicsWidth()/2,0,"1 - Toggle Texture | 2 - Toggle Bumpmap | 3 - Toggle rotmode",True,False
	Flip
Wend

Function UpdateBumpNormals(mesh,light,ang#, lighttype=0)
	
	n_surf = CountSurfaces(mesh)
	For s = 1 To n_surf
		surf = GetSurface(mesh,s)
		n_vert = CountVertices(surf)-1
		For v = 0 To n_vert
			red2# = 0.0
			grn2# = 0.0
			blu2# = 0.0	
			For d3l.Dot3Light = Each Dot3Light
				If d3l\typ = 1 ; Directional light
					TFormVector 0,0,1,d3l\ent,0
					nx# = TFormedX()
					ny# = TFormedY()
					nz# = TFormedZ()
					TFormNormal VertexNX(surf,v),VertexNY(surf,v),VertexNZ(surf,v),mesh,0
					red# = TFormedX()
					grn# = TFormedY()
					blu# = TFormedZ()
				ElseIf d3l\typ = 2 ; Point light
					TFormNormal VertexNX(surf,v),VertexNY(surf,v),VertexNZ(surf,v),mesh,0
					nx# = -TFormedX()
					ny# = -TFormedY()
					nz# = -TFormedZ()
					TFormPoint VertexX(surf,v),VertexY(surf,v),VertexZ(surf,v),mesh,0
					red# = TFormedX()-EntityX(d3l\ent,True)
					grn# = TFormedY()-EntityY(d3l\ent,True)
					blu# = TFormedZ()-EntityZ(d3l\ent,True)
					d# = Sqr(red*red + grn*grn + blu*blu)
					red = red/d
					grn = grn/d
					blu = blu/d
				Else
					RuntimeError "Do it yourself, will ya?"
				End If

				;UGLY HAK
				red=red*Cos(ang)-grn*Sin(ang)
				grn=grn*Cos(ang)+red*Sin(ang)
				;UGLY HAK
				
				dot# = (red*nx + grn*ny + blu*nz)
				If dot<0.0 Then dot = 0.0	

				red# = ((1.0+(red*dot))*127.5)*d3l\mul
				grn# = ((1.0+(grn*dot))*127.5)*d3l\mul
				blu# = ((1.0+(blu*dot))*127.5)*d3l\mul
				If red<0 Then red = 0
				If grn<0 Then grn = 0
				If blu<0 Then blu = 0				
				red2# = red2+red
				grn2# = grn2+grn
				blu2# = blu2+blu	
			Next
			
			VertexColor surf,v,red2,grn2,blu2
		Next
	Next

End Function

Function Dot3_CreateLight(typ=1,parent=0,real=True)
	
	d3l.Dot3Light = New Dot3Light
	
	If real
		d3l\ent = CreateLight(typ,parent)
	Else
		d3l\ent = CreatePivot(parent)
	End If

	d3l\typ = typ
	d3l\mul = 1.0
	
	Return d3l\ent

End Function

Function Dot3_LightRange(ent,range#)

	For d3l.dot3light = Each dot3light
		If d3l\ent = ent
			If Lower$(EntityClass(d3l\ent))="light"
				LightRange d3l\ent,range
				Return
			End If
		End If
	Next
	
End Function

Function Dot3_LightIntensity(ent,intens#)
	For d3l.dot3light = Each dot3light
		If d3l\ent = ent
			d3l\mul = intens
			If Lower$(EntityClass(d3l\ent))="light"
				LightColor d3l\ent,d3l\mul*255.0,d3l\mul*255.0,d3l\mul*255.0
			End If			
			Return
		End If
	Next
End Function

Function createquad(parent=0)
	quad=CreateMesh(parent)
	surf=CreateSurface(quad)
	AddVertex surf,-1,1,0,0,0
	AddVertex surf,1,1,0,1,0
	AddVertex surf,1,-1,0,1,1
	AddVertex surf,-1,-1,0,0,1
	AddTriangle surf,0,1,2
	AddTriangle surf,0,2,3

	Return quad
End Function






sswift(Posted 2003) [#8]
"Is a cubic environment map just a cube (6 sided) map? Could you please try to explain this to me one more time as I'm not sure I wholly understand it. Why is a cubic map needed?"

A cubic environment map, which is six textures that form a cube is needed because with the method I mentioned, you need to multiply the tangent space normal map on each face with a light normal which is realtive to that face. That means the light normal is different for every face becuase it has to be converted from world space to face space.

Now with a little leap of logic, you'll realise that if the direction the face is facing is what determines how the light normal is transformed, then that means that if there is a way to set the color of the face that varies with the direction it faces, then we have a solution to the problem. And a cubic map does just that. A face which is facing upwards will be textured by the top texture, and a face which is facing right will be textured with the right side of the cube. It's just as if the cubic environment map is a skybox, and your texture normal is your camera pointing in a particular direction. Whatever the face sees is what gets pasted onto it.

Now, ideally, if the normal map is in face space, then you'd have one solid color per face, but we can't do that with the cubic map... you'll see a section of the cubic map there, so you just have to put up with a little error in the light
direction across the surface of the face. This error should be invisible though.


Who was John Galt?(Posted 2003) [#9]
Yeah makes sense now cheers.