YAL update

Blitz3D Forums/Blitz3D Programming/YAL update

sswift(Posted 2003) [#1]
http://www.blitzbasic.com/codearcs/codearcs.php?code=794

This version has all the graphical glitch fixes and uses my new ray intersect functions instead of entityvisible to determine illumination.


Beaker(Posted 2003) [#2]
Nice one. You got plans brewing? :)


R0B0T0(Posted 2003) [#3]
Thanks SSwift. I was in the process of implementing YAL into my own work, so this is indeed helpful.


sswift(Posted 2003) [#4]
"Nice one. You got plans brewing? :)"

No, not really. Someone just asked on the bug forum about getting shadows, when they really meant lightmaps, and I suggested YAL to them, and they said it was hard to use, and I decided to finally check out what YAL could do for myself, and when I saw the dark edge problems in it I remembered someone else who made a radiosity lightmap generator telling me they were impossible to fix without getting hard shadows, and so I decided to look at the code to see what it was doing and I ended up playing around with it and got to fixing it up, and here I am.

Of course I do have long term plans to do some kind of game that would use lightmapping, but that's way out there.

The next thing that's caught my interest is light sources that have area to them instead of just being points... That would allow one to render shadows which get softer the farther the shadow falls from the object that cast it.

Though I wonder if that is worthwhile when one could simply do radiosity and basically achieve a similar effect. I suppose it would make one's radiosity renderer more realistic looking though.


Ruz(Posted 2003) [#5]
Hi sswift, I managed to get my lightmap made usng YAL. Looks very nice too, and I am pleased wiht the resulting effect.
Now could you explain how I apply the lightmap I produced to my own level
See, what i did was to replace the cube in the example function with my level mesh to get the map produced.
So I now have a 1024x1024 flattened out lightmap. I tried using entity texture on layer 1, but it just applied it without the uvs, got the map on every face he he.
( bit of a framerate killer)


Shambler(Posted 2003) [#6]
An interesting observation I had today regarding shadows.

I was walking to catch the bus from work 'as you do' and I noticed I had a very long shadow.

What I saw in the shadow was that the further away it got, the more blurred it was, but it wasn't softer i.e. less dark.

I think I read a post asking for a shadow which got lighter the further away from the shadowcaster it got, but in this instance that doesn't seem to be the case.

Nice work on YAL swift, much appreciated.


sswift(Posted 2003) [#7]
" I tried using entity texture on layer 1, but it just applied it without the uvs, got the map on every face he he. "

There are functions in the YAL lib to load the UV file it generates for a specific mesh and to load the lightmaps. I don't have time right now to check how to use them though cause I'm going out to eat.


One thing to note. If you make levels which have correct geometry, it looks like you should set the lumel offset constant at the start of the code that I made to be 0. Otherwise you'll get light bleeding again. I tried this on the maplet map that maplet has and got that effect.

I am going to continue to work on this and try to fix it up some more.


Ruz(Posted 2003) [#8]
ok, NP, if you get time in the next few days could you give me a few more specifics.
cheers


DH(Posted 2003) [#9]
bug found!!!!!

in the included demo:

change line 1926 :"tex = LightMapMesh(ent, 0.25, 1024, 1, "Lightmapping " + EntityName(ent))"

to read as follows
tex = LightMapMesh(ent, 0.5, 128, 1, "Lightmapping " + EntityName(ent))


or
tex = LightMapMesh(ent, 0.1, 512, 1, "Lightmapping " + EntityName(ent))


you should see the following:


Other then that, kudos on the new YAL sswift!!!!!!. Yal was such a pain to use before, and never worked correctly (never could get the shadows to work). Your a genius!


TheMan(Posted 2003) [#10]
What make the lightmapping proccess in maplet faster than the YAL? ... and what we can do to make YAL faster?


sswift(Posted 2003) [#11]
Dark Half:

That's an issue with how YAL was written. It puts all lightmaps into a single texture.

See these values?
0.1, 512

The 0.1 is the lumel size, in world coordinates. Ie, for every 1/10th of a world coordinate, there is one lumel.

512 is the lightmap size. This is the size of the texture for the ENTIRE LEVEL.

Your lumel size of 0.1 is means that the lightmaps for each surface are a certain size, and in this case they're too large to all be packed into the 512x512 texture.



A more advanced lightmapping system would generate a lightmap as large as it needs and split it up and create multiple surfaces if it exceeds the lightmap size you specify, but YAL does not, it assumes you want one surface and one texture.

I don't intend to muck about with this and try and fix it right now because it would be too time consuming to fix. The biggest problem with YAL right now is that it's damn slow at generating detailed lightmaps.


Hyderman:

For one, Maplet is written in C. Blitz is very fast, but C, the language Blitz is written in, is much faster.

For another, I think maplet is using a different method to solve the bleeding issues. The method I used here requires 4x as much processing as normal lightmapping.

I realised last night though that you could speed the function up 4X and still use the same method by calculating a lightmap which is just one pixel wider on the top and left.

Right now I cast a ray to the corner of each lumel to determine if it is visible to the light.

However I realised that each corner is shared by 4 lumels. Why should I cast the same ray 4 times when I only need to cast it once?

Unfortunately implementing a solution isn't super easy because YAL calls a function for each seperate lumel so no two lumels know what the other is doing.


sswift(Posted 2003) [#12]
Ruz:
I looked at YAL. Applying the lightmap looks really simple.

After you create the lightmap you will have a lightmap image, and a file containing the UV coordinates for that mesh. You need both of these. The UV coordinate file describes the UV coordinates used to apply the lightmap texture to the mesh, which are seperate from the UV coordinates you change when you apply a texture to the mesh.

To load a mesh and lightmap it just call this function:
LoadLightMap(mesh, imgfile$, luvfile$, layer=4)

That's it. That loads the LUV file, applies the UV coordinates to the second UV channel, and loads the lightmap texture and applies it to the 4th texture channel.

I would suggest that if you're not using the second texture channel on any surfaces in your level that you specify layer 2 instead, because some video cards only have two texture units, so you will gain a lot of framerate.


Ruz(Posted 2003) [#13]
cheers Sswift,
I did try that function peviously( well,hacked similar from YAL, but probably did something really dumb to not make it work.
I wll try it again now.


Ruz(Posted 2003) [#14]
You confused me sswift
the function you refer to is this

Function LoadLightMap(mesh, imgfile$, luvfile$, layer = 1)
Unweld(mesh)

If FileType(luvfile$)
LoadLUVs(mesh, luvfile$)
EndIf

tex = LoadTexture(imgfile$)
If tex
EntityFX(mesh, 1)
TextureCoords(tex, 1)
EntityTexture(mesh, tex, 0, layer)
FreeTexture tex
EndIf
End Function
What about Load LUV function and Unweld mesh?
I understand how this works overall ie the lighmap is loaded on to texture layer one and the uv co ords are handled by the luv file,
just confused as to the right syntax etc.


sswift(Posted 2003) [#15]
I don't understand your question.

What about them? Those are called by the loadlightmap function. You don't need to call them seperately.

Just do this:

LoadLightMap("level.b3d", "level_lm.bmp", "level.luv")

That's it. Your level should be lightmapped as long as both those files and the level mesh is in the current directory.


Marcelo(Posted 2003) [#16]
Hello,

This is exactly what I was expecting from the community when I posted YAL, improvements and bug fixes! Thanks sswift.

Ispired by your post I was checking if there's any math bug that causes this black borders and I found a huge one. I've created some cones where the lumels should stay, and I found that the DT# black border reduction was messing the lumels position.

I've posted temporally the fixes for the math here:
http://raintree.hpg.com.br/lmap2.bb

One good optimization to your ray checking lib is to first check a ray-sphere or ray-box before checking the poly's, if the ray doesn't intersect the bounding volume it won't intersect any polys inside it.


Ruz(Posted 2003) [#17]
sswift,sorry for the confusion.
I was hoping to paste the relevant bits of code from your lightmapper in to 'my' level code file to load my light map.
Something like entity texture, but that includes the luv info
I don't need to have your fully functioning lightmapper as part of my code file, just need to load the lightmap on to my level mesh.
If were to make a lightmap in max5 for expample how would I load that in blitz 3d.


Marcelo(Posted 2003) [#18]
New version, the inversion of LumelPivot and LightPivot on check produced some artifacts on the bottom of the objects, so back to the old move away and back workaround stuff.

http://raintree.hpg.com.br/lmap2.bb




sswift(Posted 2003) [#19]
"New version, the inversion of LumelPivot and LightPivot on check produced some artifacts on the bottom of the objects, so back to the old move away and back workaround stuff."

I don't understand what you mean, could you rephrase that?

Also could you post that room you have lit above, with the code to load it and lightmap it? It would be helpful to have a more complex level to test with.


sswift(Posted 2003) [#20]
"One good optimization to your ray checking lib is to first check a ray-sphere or ray-box before checking the poly's, if the ray doesn't intersect the bounding volume it won't intersect any polys inside it."

Yeah, I was thinking about something like that the other day but I forgot about it. Maybe I'll implement that in my functions. I really didn't think about it much because most levels I assumed would be a single object. But I suppose people might want detail objects within the levels to cast shadows too.


sswift(Posted 2003) [#21]
"If were to make a lightmap in max5 for expample how would I load that in blitz 3d."

To make a lightmap in Max you'd need to create non-overlapping texture coordinates. Weren't you the one asking about lightmapping in the features forum and I told you to take a look at YAL?

If you want to do your lighting in Max I can't help you much. The functions contained in YAL are not suitable for that.

All I can tell you is you'd have to use two texture UV channels in Max, using the first for the texture and the second for the lightmap texture and then find a Max exporter which will export both those channels.

Then you load the texture and apply it to the model normally, and load the lightmap texture and use the TextureCoords command to set it to use UV set 1 which is the second set of UV coordinates. Then you apply that texture to the model normally, but you set the "index" parameter of EntityTexture to 1 instead of the default of 0.

And that's it.


Ruz(Posted 2003) [#22]
ok, cheers.and wasn't saying I was going to use max 5, I was just using that as an example. to say 'me have lightmap,how do i load it'
Bu thanks for the info anyway


Ice9(Posted 2003) [#23]
Nice work Marcelo and Swift and the rest.


Marcelo(Posted 2003) [#24]
"I don't understand what you mean, could you rephrase that?"

Sorry for my english. :)

The raycasting works better if done from the light to the lumel instead of the inverse (EntityVisible). Using your method is surely good and less workaroundish, but some light appears behind the objects.

What I do is to move the current surface poly's to far away, so they aren't checked in EntityVisible call.

The lit room above is created in a world editor that I've made for the blitz, it requires a lot of code dependency (libs, etc), but I'll try to pack it to you.


sswift(Posted 2003) [#25]
"What I do is to move the current surface poly's to far away, so they aren't checked in EntityVisible call."


I don't think you understand why I did what I did.

You move the surface to avoid the surface self shadowing itself.

But that is not why I reversed the lumel and the light in the entityvisible call.

In fact, it has NOTHING to do with avoiding self shadowing on surfaces. But I suppose that may be a side effect.

Even if you reversed the entityvisible call in my version of YAL back to the way you have it, you would find that there is no self shadowing of the surfaces.

This is because you don't need to move the polygons to keep the surface from self shadowing.

What I did in my code to fix another problem, (the dark edges around the cube sitting flush with the floor) was I took the lumel pivot and I moved it a tiny bit on it's Y axis.

This moves it away from the surface a bit in the direction of the surface normal.

...And this prevents the surface from self shadowing and is probably faster than moving every single polygon in the level as you lightmap it.



Now, that you understand why I did that and that the side effect is removing the need to move the surfaces, you're probably wondering why I also flipped the light pivot and the lumel pivot around in the entityvisible call.

The reason I did that was to get rid of the dark edges on all the walls.

The way my code accomplishes this is that when you reverse the two, any part of the lightmap which is inside the wall thinks it can see the light source, because all the backfaces of the object are culled. So the lightmap inside the walls is LIT, not shadowed.

The result is that there's no dark edges peeking out from under the walls.

But as you saw, doing that alone creates a graphical glitch. However, this was better than the dark walls.

Imagine you have a 2D rectangle sitting on the floor like so:

__||__

Now imagine the light is on the left, and the wall's right side is in shadow.

Because we have reversed the entityvisible call, the parts of the lightmap between the wall are illuminated... not in shadow.

However if you look on the right side at the bottom of the wall, you might have a lumel from inside the wall peeking halfway out there. And you will see that light.

That's the glitch you saw.

So what I did to correct this glitch was I cast a ray from all four corners of each lumel. And if ANY of them was in shadow then that lumel was in shadow.

And this fixes that lighting bug, for the most part.

...

Anyhow... I see in the current version that you have done a great job fixing the lumel alignement. So the problem of dark edges on this appears to be eradicated on the test room.

However, the battle is not won. Imagine if you have a diagonal wall where the center of each lumel is inside the wall. You will still get a dark edge. You can't see this in your test though because you don't have any walls at 45 degree angles.

Now, I see that you have a multi-pass algorithm in there which is on by default which would do an average of the brightness of each corner of the lumel.

However, doing an average will not solve the problem.

If a lumel is supposed to be illuminated, then making it only 50% dark because a 45 degree wall passes through the center of it and two corners are inside the wall does not look much better than making it 100% dark. It looks a little better but it still looks wrong.

So what you need to do is get rid of the whole averaging concept, and do the opposite of what I did.

If any corner of a lumel is in light, then make the whole lumel illuminated. Because lumels are in shadow when inside walls in your setup, this will fix the problem with shadow bleeding under walls when part of the lumel is exposed, and any lumels which are in shadow halfway out of a wall will also still be in shadow on the half inside the wall and it will all work out just like it did when I did with reverse with light.


Okay, so to wrap this up...

Now that you have fixed the lumel offset the issue with doing it your way is much less. However, there is one way in which my method is still superior.

If you do it your way, then if you only sample the center of the pixels, on every wall that is at an angle you will get a dark edge.

If you do it my way, then you will get some light bleeding under walls but any wall which has no light on the opposite side will have no problem and as most areas in a game are lit you won't notice the problem so much.

So my method will look somewhat better if you only sample the center of eac lumel.

But if you do that, then you will not be fixing all the glitches. So that's not really helpful.

So doing entityvisible your way is fine now that you have fixed the lumel offset bug, and because we're doing 4x the sampling.



And now, here's something cool you should take note of...

You know how I said you should light a lumel 100% if any corner was in light rather than doing an average like you are now?

Well, right now you'd be sampling each lumel 4 times to accomplish that.

But you don't need to!

If you rewrite your code a bit, you can cut the time to light a level down to 1/4th what it is now.

Imagine I want to know if a lumel is in light...

A-B-E-F
C-D-G-H

Here we have three lumels in a row. ABCD are the four corners of one lumel.

We only need to cast a ray at each point there ONCE. But right now, you cast to ABCD, then to BEDG, then to EFGH.

So, instead of what you're doing now, cast a ray at A. If A is in light, then illuminate lumel 1 100%.

Then move onto B. If B is in light, then illuminate lumel 2, and if we did not illuminate lumel 1 in the previous step, illuminate it now.

This is not exactly how you should code it though. I'd have to think about it more carefully. I think you can do it in a very efficient manner with very little memory storage.


Well, that's all I have to say about it for now. :-) You don't need to send me that test room I'll just add some more privitives to the test level like a rotated cube and a cone and a sphere.


sswift(Posted 2003) [#26]
Oh crap I forgot one thing.


How come in your code where you have the 4 passes you have:

MoveEntity(LumelPivot, -lumelsize/3, 0, -lumelsize/3)


And then immediately after it on the second corner:

MoveEntity(LumelPivot, lumelsize/2.5, 0, 0)


Maybe I'm just confused but if you move something by lumelsize/3 that moves it 1/3 of the lumel width to the left. If you assume that the lumelpivot is at the center of the lumel to start, then 1/3 of the way to the left is not far enough. You want to move 1/2 of the lumel width to the left.

And then you move it 1/2.5 to the right. You you've moved it 1/3 to the left which puts it at 1/2 - 1/3 which is at 0.17 if the lumel is one unit wide. Then you move it right by 1/2.5 which is 0.4, so that puts it at 0.57.

The lumel pivot should be at 0,0 0,1 1,0 and 1,1 not at 0.17,0.17 0.17,0.57 0.57,0.17 and 0.57,0.57.


So you should move it by -Lumelwidth / 2 on both the X and the Z the first time, and that puts it at the first corner, and then you should move it by lumelwidth on the X axis, and then -lumelwidth on the Z axis, and then by -lumelwidth on the X axis one lest time, to get all four corners in a clockwise direction. I think.

Of course it's easier to be sure you're moving it right if you just reposition it at LumX, LumY, LumZ each time before you move it again.


Marcelo(Posted 2003) [#27]
Hello again,

One image speak as a thousand words :)


In the first shot I do a EntityVisible between the ground and the light position, you can see that it produces this artifacts on the ground.

So we got two options, or move the ground away while checking visibility (so the light won't pick it), or we make all the objects double sided, as can be seen in the second screenshot.

I've tested it with complex levels and doesn't seem to be producing any visible artifact, check these screens:





I understood it correctly, the problem you mentioned of a lumel in a corner wall, doesn't exists. Because the lightmapper will split the wall into two distinct surfaces.


Marcelo(Posted 2003) [#28]
About the MoveEntity stuff:

Sorry, I've typed it wrong (should be 1.5), the correct is:
MoveEntity(LumelPivot, -lumelsize/3, 0, -lumelsize/3)
MoveEntity(LumelPivot, lumelsize/1.5, 0, 0)
MoveEntity(LumelPivot, 0, 0, lumelsize/1.5)
MoveEntity(LumelPivot, -lumelsize/1.5, 0, 0)


Suppose that lumelsize = 1.0, it will move (-0.33,-0.33) (going near the top left border of the lumel, but not touching it), then it will move (+0.66,0) (near top right), (0, +0.66) (bottom right) and (-0.66,0) (bottom left), in a clockwise direction.

First I tried to move to the "vertices" of the lumel quad as you mentioned, but keeping the rays inside the lumel showed better results. And as you said, firing rays to the corners is unnecessary, it will have the same effect as a blur effect in the image.

One thing that needs to be done in the lightmapper is to somehow use the vertex normal data to generate smooth curved surfaces, try to apply a lightmap to a sphere or cylinder to see how ugly it will look. :)


sswift(Posted 2003) [#29]
Marcello, we seem to be having some difficulty communicating. You're clearly still confused about some of the thing I've said. :-)


For example:

"First I tried to move to the "vertices" of the lumel quad as you mentioned, but keeping the rays inside the lumel showed better results. And as you said, firing rays to the corners is unnecessary, it will have the same effect as a blur effect in the image."


That has nothing to do with what I was talking about. :-)


What I said was:

"Do not fire 4 rays and then average the result."

"Instead, fire 4 rays at each corner of the lumel, and if any of them are illuminated, illuminate that pixel."


I did NOT say that firing them at the CORNERS was the problem. Firing 4 rays and then averaging them is the problem. Firing 4 rays at the corners and then illuminating the lumel if any of the 4 are in light is perfectly fine.


Also, I told you a way to speed up the lightmap rendering 4x. But if you cast your rays to areas that are NEAR the corners, rather than AT the corners then you will not be able to use that optimization.

There is NO benefit to casting the rays NEAR the corners instead of ON the corners.




"One thing that needs to be done in the lightmapper is to somehow use the vertex normal data to generate smooth curved surfaces, try to apply a lightmap to a sphere or cylinder to see how ugly it will look. :)"

Yes I was thinking about this myself.

This is a big problem, because if you break the sphere up so that you can use multiple lightmaps, then the lumels will probably not line up properly. I am not sure of this, but I suspect it is true.

If that is NOT a problem, then all you need to do is interpolate the vertex normals across the triangles like you would do with phong shading. That is kind of an expensive operation to do, but since you don't need to do it in realtime it should be fast enough to be tolerable.





I do not see any dark edges on the corner wall there, but that does not mean there aren't any.

Could you render a close up of that angled wall in the distance, and when you do so, could you disable all ambient light, and increase the brightness of the nearby light source?

Your 4x lumel averaging thing you've been doing might be hiding the dark edges, but I do not see anything in your code which would get rid of them completely, so I suspect they are stiull there and I just can't see them at this distance with this lighting.

I suppose I will do a test myself as well now. I will fix your moveentity commands while I'm at it just so we're doing the same thing.