How are shadows done these days?

Blitz3D Forums/Blitz3D Programming/How are shadows done these days?

Robert Cummings(Posted 2010) [#1]
Looking for information or links on shadows!


Yasha(Posted 2010) [#2]
There are several methods, but what you want will depend on both the style of your game (i.e. how much "cheating" you can get away with that wouldn't necessarily look right in a different enironment), and how much performance you're willing to sacrifice for aesthetics. There are a few standard methods, however:

1) Copied, flattened mesh. Use a second copy of the mesh that has been flattened in Y, to multiply against the floor. Accuracy is good, but it requires a lot of crushing of vertices, which is processor-intensive, and there's no easy way to have a floor that's anything other than flat, or other objects in the way.

2) Projective shadows. Using a second, black-on-white, render from the perspective of the light, create a shadow texture. Project this onto the environment using:
a) a flat floor. Same disadvantages as above, but slightly better performance because you're usign a render;
b) a "towel" flexible quad mesh; just about OK for rough terrain but doesn't work for walls or other objects;
c) a dynamic shadow mesh reconstructed each frame. Pretty heavy on the processor but produces good results.
This method(s) can produce high-quality shadows but it's dependent on the shadow texture render quality. FastExt (which you should have anyway as it's very good in many ways) does this for whole scenes, and SSwift's shadow system does it for individual meshes (higher quality, at cost of performance). There is no good way for this method to provide self-shadows, or any shadows cast on animated meshes. This is easy to implement, but very hard to implement well.

Either of the above methods can easily be changed to use generic "blob" shadows if you don't want the associated cost of a shadow render (on most machines however, the render is probably the fastest part).

3) Stencil shadows. These use some multipass rendering tricks to apply shadows as a postprocess step to the whole screen (so the shadows can't be applied per-object, and rendering them has a constant performance cost). However, in order to set up the render you need to project shadow meshes from casters, which is extremely prohibitive using Blitz3D's internal mesh handling. There may be ways around this but as far as I know nobody has researched the matter. Also, no existing system is compatible with animated B3D meshes.

There are a few variations and combinations of these basic ideas, and a ton of code archive examples, but that's the main part. Other shadow alorithms, such as shadow mapping using the Z-buffer, haven't been successfully implemented in Blitz3D to the best of my knowledge (which is a pity, the Z-buffer method is easily the best).

TL;DR: Get FastExt for pre-packaged shadows. Otherwise, check the code archives, or ask again for help if you're having real difficulty and someone will share theirs.


Robert Cummings(Posted 2010) [#3]
I thought FastExt was bugged with latest version of blitz. Any ready to go solutions without using FastExt as I don't want to pay for yet another addon.


Rroff(Posted 2010) [#4]
Pre-baked lightmaps are still the best way to do shadows in Blitz3D. FastExt has some useful stuff you can use for limited dynamic shadows.

Shadowmaps + PSSM is badly needed :(

Last edited 2010


Robert Cummings(Posted 2010) [#5]
Just need dynamic ones for the players.


MikhailV(Posted 2010) [#6]
o_O FastExt was bugged with latest version of blitz? Are you sure?
You are mistaken. Check latest version 1.17

Last edited 2010


jfk EO-11110(Posted 2010) [#7]
For me shadows are also the real feature lack in Blitz3D. ODE works, and fancy postprocessing stuff isn't really needed imho when the Design is good. DC's stencil shadows unfort. only support a single light, might be ok for outdoor things, but I can't rely on that.

I know, Blitz3D wouldn't be the language someone should use to create the negt gen, end of all best shadow algorithm, but I can't stop thinking about it. Maybe I come up with something the sooner or later.

Other than that, as far as I know, there is only the ashadow lib for Blitz3D, but when you're browsing the 3d engine database on devmaster.net, there are many DLLs you could use instead, open source, ready to be wrapped for Blitz3D. There is for instance a Irrlicht Wrapper for Freebasic. Irrlicht might not be next gen, but it has shadows and much more.

There are more engines, and some of them really do offer a lot, including wrappable sourcecode. But for now I am searching for a powerful, simple shadow addon for good old Blitz3D. I think Shadow Volumes, Stencil Shadows are not the way to go. Ehrm... AFK to try something :) ...


Yasha(Posted 2010) [#8]
JFK: https://sites.google.com/site/nangdongseng/downloads/Shadow.zip?attredirects=0&d=1

Simple and high-res, if you don't mind the CPU load.


One of these days I still mean to go back and have another shot at implementing Z-buffer shadows... it must be possible in B3D; everyone else has them...


jfk EO-11110(Posted 2010) [#9]
Thanks Yasha, that's not bad, tho not perfect and as you said a framerate killer. I am currently experimenting with what I understand is Depth Light Mapping. It won't be fast, but maybe pretty flexible and sophisticated. There is one problem I got right now. I'd need a orthographic projection camera WITH working fog, to make a depth render from the point of view of the light.

Unfort. in Blitz Fog doesn't seem to work with the orthographic projection camera. Maybe somebody can help me, is it possible to combine CameraZoom and ScaleEntity in a way that gives me an (almost) orthographic projection with CamerProjMode 1, with about the same viewport as a standard camera using default values ?


Yasha(Posted 2010) [#10]
That's the same thing I'm talking about (I think). Unfortunately using fog is (in my opinion) a non-starter: you only have 8-bit depth precision, which is fine for a proof-of-concept demo but not good enough for real ingame use on complex, detailed meshes. I've been looking around to see if there's any other way to get the necessary data... nothing yet, but rest assured it will become public very quickly if I find out anything.

Last edited 2010


Rroff(Posted 2010) [#11]
Haven't looked at Yasha's link yet - but would it be possible if its CPU heavy to thread it with fastpointer?


jfk EO-11110(Posted 2010) [#12]
Hi again. To my surprise, emulating a zbuffer using fog was straight forward, but I expected to get at least 768 levels, I was wrong. Although it's really impressive how things are simplified by such a radical approach. I don't know much about 3D math, so I was cheating a lot, I used a lookuptable that would give the 3d location of a certain render x,y,fogcolor, in fact a perspective-un-correction, turning that data back into orthographic projection.

Sawtheeth / Stairs artefacts due to rounding errors in depth determination by fog is horrible with these 8 Bits. When watched in wireframes, the potential becomes visible. A Resource-Hugger, of course. A spotlights takes 64k Verts and 32kTris. But when I think of the hardware power we most likely will see in the next years, I think this could be useable (o course, not with fog).
What I mean is: why does everybody always talk about shadows where shadows don't exist in reality, only the absence of light. So why don't we work with the light right away? Because there is an empty hall with a little NPC in the middle? Well I don't want such a leveldesign anyway :)

I am currently trying to access the ZBuffer, but zbuffer.bb with zbuffer.decls and zbuffer.dll, that was released here some time ago (don't remember by who) simply refuses to work on my machine. It says userlib not found,but it's all there. I also tried to use DevilsChild's Shadowlib with a long list of wrapped DX7 functions in userlibs, but I couldn't make it work. Those commands, Lockbuffer, LockZWrites, ENabeZBuffer, DisableZWrites and so on are confusing me kind of, I don't know DirectX personally, I always sent Mark to them...

Any Ideas for Zbuffer access? Would be nice to have an individual CameraViewport for the ZBuffer, then use eg. ReadPixel on the backbuffer to get the depth of a certain pixel.With more than 8 Bit, of course.

Here's something VERY experimental: (Note, it will on the first run create ans save a lookup table named cache.z,this may take a while)

does expect kernell32.decls with at least this:
.lib "kernel32.dll"
RtlMoveMemory2%(Destination*,Source,Length) : "RtlMoveMemory"

It's a projected Towel Mesh. The smart (well, rather a quick hack) bit (IMO) is the determination of xyz, based on fog density/darkness and screen coordinate of a given pixel.

Yes, it's really ugly, but the projections are right and the system doesn't care at all what is light and what's not. Even shadows on the ceil are done without any special action. It would be pretty easy to use this, if the towel resolultion and hardware power is high enough. 64kVerts limits make it a but rough. Towels could however be assembled with multiple patches. Press W to see the vertex resolution, rather than the fog-Depth-Resolution.

Last edited 2010

Last edited 2010


jfk EO-11110(Posted 2010) [#13]
While it's fun to do some experiments, I do not really have the time and skils to develop something good by my own. Some refreshing from-scratch-coding is ok, but I'll concentrate on solutions that can be adopted.

This page is informative, not too scientific:
http://www.devmaster.net/articles/shadows/

I would tend to Perspective Depth Mapping, with some kind of multisample/softening method.

But what's really imporant IMO is: we can't just go and add shadows to our game that is running slowly already by the huge amount of Tris, Picks and so on. We can have shadows, yes, but a game must be designed with this significant perfomence hit in mind. In other words, it should run extremly fast without shadows. VIS occlusion, LOD and much more should be the base of it.


Yasha(Posted 2010) [#14]
Heh, we really have been looking at the same things...

The problem with depth mapping is that a lot of cards don't seem to actually store the Z-buffer in an accessible location; as far as I can tell this means that the old DLLs that copied the Z-buffer to the BackBuffer weren't guaranteed to work on a lot of hardware, especially modern hardware. In my own little experiments I wasn't able to get any such thing to happen, no matter what (on the other hand I really don't know what I'm doing with DirectX... try asking Mark or Mikhail instead).

All current games that have depth mapped shadows implement them with shaders. Partly this is because the depth information is then available in place, but it's also partly because the per-pixel lighting transformation is a massive operation and is only feasible for real-time use if done in hardware - you can't do it on a loop on the CPU. This means that even if someone finds a way to copy the Z-buffer, you'll still need to farm out the heavy processing to a DLL with access to pixel shaders for any kind of speed (involving a lot of copying images through memory, since you'd need to get it "out" of DX7 and "in" again when done - itself not a very fast way of doing things).

All told I think the only practical global illumination system for DX7 is stencil shadows. With a bit of prodding there are a few articles floating around that explain how to get soft-shadows out of them. Obviously you'd need to replace the B3D animation system to use with animated characters though.... although this isn't necessarily as hard a task as it sounds.


jfk EO-11110(Posted 2010) [#15]
But stencil shadows are slow too, no? The higher that number of vertices, the more complex shadow volumes will be. Working with a lowres ghost mesh for the volume creation is complicating things (unless your LOD allows it out of the box, so to say) and may be a bit inaccurate.

Even when the method I just tried seems silly, there is so few math in it, it can be really fast. I may try to fool the fog or something, or to hack the ZBuffer. Could we make a function that is seeking the ZBuffer on a computer, using characteristic depth relationships of a sequence of pixels/numbers?

Imagine pixelperfect alignement of 10 quads, each one covering a pixel in a sequence, each one with an individual Z-Offset to the camera, eg. the first 10 pixels in the first line of the screen. Each one has a knwon ratio to the first one, as a factor. Now simply search for a sequence of numbers (be it 32,24,16 bit) with the same ratios to oneantother. Shouldn't this work? It might be slow, then again it has to be done only once on a machine. I'd search for it around the backbuffer(), maybe. Maybe I'll try to implement this later tonight...


Yasha(Posted 2010) [#16]
I can think of three problems with that right off the bat:

1) (least important) A golden rule when dealing with memory on a low-level is: never assume that you've "found" something, because it could really be anything. Yeah, I know this one isn't really important... the chances of a random structure imitating your key numbers are very small, and since you only have to do it once, you could easily make it a hundred or a thousand depths for extra safety at no noticeable cost. But it's a valid point of principle.

2) The depth buffer stores floats of some kind. I don't know offhand how it works, but you might find floating-point accuracy issues make it impossible to get an exact match in this way (as in, the search values you expect to find aren't the same as the depths the Z-buffer computes, even though your maths is correct).

3) (most important) This idea will only work if the Z-buffer is located in accessible memory, and not hidden somewhere on the GPU. My earlier observation about being unable to copy it indicates this isn't true on all cards, or even most cards. It may simply not be there for you to find!

The annoying thing is that all of this would be unnecessary, if only Microsoft maintained older versions of DirectX documentation that would actually explain the details for us. As far as I can tell, this is not the case.

It also doesn't solve the problem of the massive amount of computation for actually rendering the shadowed scene... at the moment, I'm inclined to think that stencils, and the associated speed costs of volume meshes, are still faster than trying to use this method without a native shader capability.

Last edited 2010


jfk EO-11110(Posted 2010) [#17]
I guess you're right about that. Contrary to the XBox where the Zbuffer lies within regular adress space and sweet things like depth of field can be done easily and fast, on the PC there has always been a lack of proper Z-Buffer access, Only some hacks were possible, and they were all diffrent on diffrent hardware and DX versions, see also here:
http://aras-p.info/texts/D3D9GPUHacks.html

Anyway, I was thinking about to find an other solution to the problem. Just for the fun of it, I converted the fog to a custom made fog emulation made of a number of alphaed, black quads. Unfort. I seem to be unable to use more than 256 shades of alpha again (I mean, when I set alpha to less than 0.002 then it will be zero). So, again this 8 Bit limit, grrrr! But at least the alignement is now much better than with System fog 8 Bit - for some mysterious reason.

So if there's anybody who knows how to display an object alphablended with less then 0.002 opacity, let me know. If I can manage to get more than 256 levels of depth somehow, this would result in useful projected shadows. Currently it is smoothed and everything. Rendered to a srceen overlay quad, 4x sampling, giving me a kind of shadow and gloom at the same time...

I should get back to the topic, excuse me.

When I consider all pros and cons of what's best right now for Blitz3D Shadows, then my conclusion is:

It depends on the kind of game, really. Let's say we want indoor FPS with realistic projection, then Stencil Shadows may be an option, but let's be honest, when you want to do them right (eg. not casting on the backside of receivers etc.) and you want them smooth and fading out in the distance, and two shadows should not add their darkness if they're overlapping, this is all rather resources-binding. Therefor I'd say Sswifts old shadow system is still one of the best solutions around. All you have to do is divide your content budget by 2, use 25k Tris instead of 50, and 7 NPC instead of 14 etc. And yes, implement it in a way that allows to turn it off and use simple blobs instead.

I still have to see the Blitz3D stencil shadows with the features I just listed. Any links?

Last edited 2010


Yasha(Posted 2010) [#18]
...what examples are you thinking of? Because as for the issue of shadows multiplying when overlapping and appearing on the backside of receivers - those are problems with SSwift's system, that don't affect stencils at all! Take a look at Devil's Shadow System for a nice pluggable example.

I don't think there are any examples of smooth stencil shadows or autofading (you wouldn't use fading anyway most likely, as it's a different approach to it), but these can both be done - the real problem has always been the fact you can't use it with B3D animations.

Altogether though because of the animation issue I would still agree with you that SSwift-style projective texture shadows (FastExt uses the same method) are the best simple, pluggable solution.

Last edited 2010


Rroff(Posted 2010) [#19]
Would it be conceivable to do something along the lines of PSSM whereby rendering only a range within the precision of an 8-bit buffer for close up shadows and then render again everything beyond that range for another 8-bit resolve? as distant shadows loosing precision isn't such a big issue.


jfk EO-11110(Posted 2010) [#20]
This might work. Maybe slow, but it should work.


Rroff(Posted 2010) [#21]
Could be an issue tho if you have a large building at a distance that your in the shadow of tho I guess amongst other potential problems.


jfk EO-11110(Posted 2010) [#22]
Ok, I made some further tests. Simple Idea, great effect - in theory:

I will use VertexColor to color each vertex according to its world coordinate (within a certain, positive range), so xyz becomes rgb. This works nicely. Now the idea: take a render from both, eye and light point of view. The eye render tells us, what point in space can bee seen at all. The light render will tell us which of those the eye can see can also bee seen by the light. Only these points are considered "lit and relevant to the eye". They are then painted to a texture that can be used as screen overlay, or as perspective mapping. The point is, all the visibility checks, even the obscuring by geometry checks, are done by this ICU method with a rgb color space.

I then use a reverse lookup table in space, that's fast, but it requires 16 MB of ram, and I haven't got an idea how this could be solved with a higher preceision like 48 Bit colors onscreen, if we once had such RGB values onscreen.

There's a further problem with all these rendered zbuffer and color space substitutes: they are a little bit different on all graphics cards. They are dithered and the result will look different on various machines.

Additionally, the artefacts, as described in the code, are hard to fight. It might be an ideal way of shadow mapping if the sampling resolution would be high enough. But rendering and reading buffers in Blitz is limited to thumps, what is needed is a 1:1 screen resolution render for the color space info. With Pixelshaders this might be an option.

Anyway, here's the code:
to see it in plain vanilla ICU mode use these settings:

Global look_w=128
Global look_h=96
Global icu2rel=1
Global sshr=0
Global blurmode=0

it runs pretty fast this way, but most of the points are missed due to low sampling rate etc, as you'll see.







So, as a new conclusion here's my question: Is there any way to use Pixelshaders with Blitz3D ?

Thanks.

Last edited 2010


Yasha(Posted 2010) [#23]
Beautiful! Absolutely magnificent! Codearc this for posterity! That really is a wonderful effect, even with the artifacts.

Is there any way to use Pixelshaders with Blitz3D ?


...no.

At least, not without copying a buffer into memory (....sigh... once again not helping much with the Z-buffer) and over to a DLL written for DX8+/OpenGL, and copying it back again when done. While this does have the potential to be hepful for some postprocessing techniques, I doubt this is one of them.

The other option would be to go over to SoftPixelEngine and pretend as though Blitz3D has always supported DX9 and OpenGL (i.e. total 3D engine replacement). Best shot from a practical point of view, but it does feel like a bit of a cop-out from the challenge angle.


jfk EO-11110(Posted 2010) [#24]
Thank you. :) Replacement of the 3D engine is ok IMO. I was waiting a long time for a DX9isation of Blitz3D, then waited for MAX3D before I'd buy BMAX, but honestly, Blitz3D offered so much and there was so few missing, it wasn't completely unbearable. But now I guess it's about time to use the hardware to its limits. That also means access to Shaders. While I love BASIC languages, ASM that is used with shaders is also fascinating.

I couldn't run SoftPixelEngine. I am browsing Devmaster.net and there's a huge amount of engines out there, it's almost a pain to read about all of them. There are however a few candidates. But since this thread was opened by Rob and he is "just back", I am sure he wants to know how to do shaows in Blitz3D.

It would however be a good idea to have a (sticky?) thread to discuss kind of how-to DX9ize your Blitz3D, including ports and engine replacements.


Rroff(Posted 2010) [#25]
Thats a really nice effort.


Yue(Posted 2010) [#26]
I can say with certainty that a good shade can be done in Blitz3D, but requires more than the programmer, and lack of knowledge as is mine greatly appreciate Fastext.





jfk EO-11110(Posted 2010) [#27]
Indeed, the fastext shadow system may be one of the best full systems for Blitz.

Some more infos: I tested Freebasic and its Wrapper for Irrlicht. My old card doesn't support OpenGL 1.4 so most of the advanced examples didn't work, but the non-shader stencil shadows were extremly slow, pretty much useless that way.


jfk EO-11110(Posted 2010) [#28]
Yasha - I just revisited the SHADOWS demo of Kalisme, from the link given by you.

When I first tried it, it was flickering and so on, but then I spent some time and modded it to do a lot of things, and more and more I thought what a bril coder kalimse is. Since the ortho cam mode is kind of broken on my machine, I first modded it to use a standard cam. Camerazoom 100, then move 100 units away, range 100 to 200, that's almost like the real ortho cam. Then I added support for multiple lights. I thought It doesn't make sense to have more than 3 active lights (cpu load), so I wrote a system that activates the 3 closest lights. I added fading by distance and now it looks really nice when you move your TPS npc around in the scene full of lights. Adding support for animated NPCs was a bit confusing since HideEntity does reset the animation, but the caster clones must be hidden, so I am using SetAnimTime, this works, excluding for transitions.

I've added a Tris load (high detail flipped sphere) with two texture layers, and it still used to run pretty fast.

Then I modded it to use a simple map mesh, made with maplet, with about 40kVerts and 15kTris. I had to mod the code so it will work with multisurface meshes. The implementation was rather easy, just it didn't work :/, but not only were the projections wrong and shadows missing or misplayed, it was also extremly slow. When I used the simple 1 surface flat, flipped sphere that actually has more Tris than the level map, it was running smoothly, relative fast.

Why is it such a diffrence to parse 13 surfaces with 15kTris, compared to 1 surface
with maybe 40kTris?


Yasha(Posted 2010) [#29]
Who's Kalisme? (very confused... I only linked to my own shadow system..?)

(EDIT: Ah, I see he made a similar thing a while back)

Anyway surfaces represent each "block" of verts and polygons to be uploaded to the video card. 13 surfaces mean 13 uploads with associated open, copy, close of streams; one surface means only one upload. Modern computers are insanely fast at crunching polygons so the number of surfaces is usually a far bigger bottleneck, even when the number of verts and polys is lower.

Last edited 2010


jfk EO-11110(Posted 2010) [#30]
Oops, I confused things again. I think they call it Alz..ehrm..thingie :). So this was by you? Hats off, you've got my respect, that's some really nice code and it was a great pleasure to mod it. Just sent you a mail.


jfk EO-11110(Posted 2010) [#31]
Uh, I've got one more question: does that mean when I check vertexX etc. of the vertices of a surface, open, copy, close of streams is forced each time I get these informations? I though these things are done only when something is changed, or with renderworld() no?


Yasha(Posted 2010) [#32]
(Sorry I haven't responded to the email yet... haven't had time to analyse the problem - the shadow code is quite old) Replied.

No, it doesn't. Vertex information is taken from the reference copies of the meshes in the Blitz3D engine, which is entirely CPU/memory. These are uploaded to the GPU every time RenderWorld is called. For the same reason, changing a vertex property with VertexCoords, VertexTexCoords etc. should have no noticeable cost by itself (O(1) access time). This incidentally is also why you can't get vertex positions after bone deformation: the vertex position is calculated every frame in the same step as being passed to DirectX.

The other time things are changed are if you use the Blitz collision system; changing a vertex position will mark that the collision mesh needs to be updated, although it won't actually happen until the next time you use a Pick or UpdateWorld (which is why people recommend doing all your mesh stuff at once, then doing all your picks at once).

Last edited 2010


jfk EO-11110(Posted 2010) [#33]
Thanks a lot for these informations. I've just seen, when the map mesh is loaded then (tho the map has only about 15 kTris) the shadow system seems to create up to 120k additional Tris, so I think there must be something wrong. Of crouse, these dynamicly created shadow entity triangles must be uploaded for each render. So, no surprise it's so slow (besides projection doesn't work right). BTW I removed the cellshading part and most of the remed lines, so now it's really compact, even more than the original.

In reality the number of triangles per shadow should not be higher than about a dozen or two or so, as long as the receiver isn't a hi detail surface, like a shpere or so.

So when I got 3 Casters in the scene, there shouldn't be 120 kTris for shadows. Therefor I am optimistic that the system could be rather effective. Currently there seem to be a lot of Triangles that are way further away from the light than the light range is set to, this seems to be unnecessary. So a per-triangle-distance check should be added.

I also wonder if there are problems with ceils etc. in a building that has multiple floors. Will this project the shadow not only to the floor and maybe the ceil, but to all floors and all ceils? And if so, could this be turned off somehow?


Chroma(Posted 2010) [#34]
Yep FastExt is awesome. I seldom buy user-made addons but FastExt is a no-brainer. If you use Blitz3D these days, it's an absolute must.

Last edited 2010


Rroff(Posted 2010) [#35]
Bare with me as this isn't a topic I'm familiar with in any detail and what I'm suggesting may be mind numbling obvious and used all the time or even not feasible - hopefully its not exactly the method your doing above.

Wouldn't it be possible for single surface meshes, especially for object self shadowing, to generate realtime dynamic lightmaps by unwrapping the polygons to a (mipmap disabled) lightmap texture, assigning each value in the lightmap texture a unique color based on its x,y coords then rendering the object from the lights point of view, rebuilding the lightmap based on walking the render buffer using the unique rgb values to work out the x,y positions that can be seen in the light camera view and setting those as lit? this means you can vary the lightmap texture size and light camera viewpoint resolutions for speed without compromising too much on precision.

This also lets you render other objects in the scene with their entity color set to black to occlude pixels that would otherwise be lit and do extra lighting.

EDIT: Guess on objects too far from the light this might have issues with the precision of rendering from the light point of view. The more I think about it I guess the more limited it is tho it might have potential with blitz for certain things depending on how fast you can read the render buffer color values.

EDIT2: Tho even if its possible to get someting working IIRC readpixelfast is quite slow on windows 7 compared to XP :(

Last edited 2010


jfk EO-11110(Posted 2010) [#36]
It definitely has advantages to use a color texture instead of vertex colors, but there's one thing that I don't seem to understand: if you encode X/Y only, and not Z, wouldn't that mean the camera must be static? Or do you mean the information of what lightmap texel color code represents what screen XY coordinate is updated dynamilcy? Or maybe you meant the texels that can be seen on the lights render will be painted white (or be incremented in a multi light environment), anything else black (on a parallel "real" lightmap). This would surely work, but it may be very slow. You'd best draw to the backbuffer, then copyrect. Or maybe to a imagebuffer, not shure what's faster, well, trying is easy. It would however be a very fast Lightmapper. There's only one problem, it's the same with all these render tricks: because the light render may be taken from a completely diffrent angle than the players eye render, some pixels that are distant to the eye may be close to the light, resuliting in moire artefacts. There may be ways to reduce these artefacts. eg. when there are two pixels of color B and D, and the eye wants to know if the light can see color C, then the light may say "I can see B and D, so yes, it's likely that I would also see C, if they were not that far away".


Rroff(Posted 2010) [#37]
Its similiar to a lightmapper but a bit more limited.

Not really sure how to explain it concisely.

Like a lightmapper you unwrap the geometry you want shadowed and give each polygon its own space in a large "lightmap" texture.

You also need 2 textures, one will be cleared with the ambient light color and used to render the lightmap in the final render stage, the other has each pixel assigned to a color computed from that pixel's x/y coordinate in the texture. With 24 bit color this gives us plenty of unique colors to play with. This texture should also have mipmaps disabled to ensure as much precision as possible.

Render the geometry you want shadowed from the light view point with only the multicolored texture stage and not its usual diffuse, etc. stages, fog disabled and fullbright enabled to again ensure precision of the color data.

Walk through each pixel in the light view point buffer, decode the x/y coordinates from the color picked, set this x/y position in the final lightmap texture to white as a lit pixel, this automatically leaves occluded pixels in shadow.

Render the scene again as normal with the lightmap and diffuse textures.

This would give realtime lightmapping of quake 3 kinda quality obviously not ideal and has some distance precision issues but most point lights in normal useage have a low enough light radius that it won't be an issue and for directional lights i.e. sunlight you can render from a phantom position nearer the player so that the shadows around the player are high quality. (tho you'd also need some tricks to make sure distant pixels weren't all dark).

Last edited 2010


Rroff(Posted 2010) [#38]
Anti-aliasing might screw this up a bit if the user forces it on but probably something that can be worked around.

Think I should be able to have a rough version of this coded in a day or so, some of its a little beyond my present experience tho.

Last edited 2010


jfk EO-11110(Posted 2010) [#39]
I was also thinking about it. Your description is exactly what I thought. There are several points I got to mention, since all the depth buffer emulation tricks I tried recently do have the same problem: the precision of the system is dynamic and depends on the angle between user camera and light camera. If these angles are similar, the result is good. But if for exampe the light is in 180 deg. opposite to the player cam, then there will be massive sampling artefacts, resulting in almost useless data. B: the colors on the screen are not really as you tell them to be, they are dithered, resulting in slight inaccuracies, probably even patterns.

But let us assume it's possible to solve these problems somehow, then there is one thing you must keep in mind: This system allows lights, they may be dynamic. A dynamic lightmap is created, that's factastic. You don't need a single-surface Mesh, it may contain many surfaces, but they all need the lightmap texture and UV Set on the second texture layer. The only problem is: All entities must use the same lightmap. It may or may not be tricky to have NPCs useing the same lightmap as the Level Map.

Besides NPCs, this is really a simple thing if you let a Lightmapper UVmap your Map mesh for you, not really a need for UV-mapping in the code, just take something pre-wrapped, vertexcolor and dynamic texel size turned off etc.

Coloring the texels could be something like this:

rgb= (x shl 12) or y

and to read it:

x= (rgb shr 12)
y=rgb and %111111111111

then simply color every pixel in the texture, it doesn'T matter if it wil be in use or not. 12 Bit allow pixel adresses of 0 to 4095. (Tho, copyrect a 4096 texture may be a little slow :) probably try one of those 256ers, friendly offered by Maplet, first)

But as noted, inaccuracies with dithering or as you said antialiasing may cause problems, even overflows from Y to X... Turning off mipmapping is neccessary, but it also demonstrates the core problem: distant objects will show only a few of the colors that are actually on their surface.

Please don't take this for negative nay-saying. I see a very high potential in the idea. Just let's fix those problems somehow, especially the sampling rate problems.

How about this: let's say there are two colors recognized, 2 Pixels side by side, and they have a certain (rather small) color distance that suggests that it is not a geometry contour, but only a distant object , then those hypotetic colors between them may be considered "found" as well - something like that maybe.

PixelShaders would really be great for something like that. There you could simply copy the color to the adress and brigthen the texel on the target lightmap texture. No copybuffer, no readpixelfast etc., the whole thing within the gpu.

Last edited 2010


Rroff(Posted 2010) [#40]
Yeah similiar to my thinking, only a couple of issues I hadn't thought of yet heh.

I've got as far as generating the unique color texture, unwrapping any provided geometry and assigning the tris positions on the texture - having some issues with optimal layout of the triangles as so far its not very efficent leaving massive gaps - even so a 2048x2048 texture should be ample for your average sized indoor level with reasonable shadow resolution.

Aside from working around color precision I think I've got my head around the other stuff.

Not sure if I can make anything useful of this or not but gonna have a go.


_PJ_(Posted 2010) [#41]

But as noted, inaccuracies with dithering or as you said antialiasing may cause problems, ..... distant objects will show only a few of the colors that are actually on their surface.


Sorry if I've grossly misunderstood something here, but, isn't that the 'point' of antialising and such? there's no way an object with n pixels could ever be rendered at a distance with the same 'clarity' because, obviously, there'd be less pixels for the distant rendering...

I'm not trying to be a smartass or anything, I'm sure you're referring to something much more 'complex', I just read that and thought "hang on a minute!" :D


Rroff(Posted 2010) [#42]
Yeah thats the point of anti-aliasing - but it causes issues with doing shadows this way in that you are trying to fill your objects texels so each pixel in the light's viewpoint has a unique color that can be used to mark a certain point as lit or not, anti-aliasing will induce extra color values into the viewpoint that might correspond with a point thats actually occluded from the light's viewpoint and hence allow light on areas that are actually in shadow. However with the way I'm doing it I think it will 90+% of the time merely give a slight softshadowing effect in places without being so inaccurate that its noticeable its wrong.

With objects at a long distance from the camera view not all pixels will show unless you render very very high resolution, so adjacent pixels might be blended or not even show up, causing mottled shadows where there should be none, etc. however my contention here is that point lights are usually used with a low enough radius for it not to matter and if its possible to get this working people using it would just have to be more careful with their light placement/useage. For directional lights i.e. sunlight we can use tricks to position the light "source" nearer the player to ensure higher accuracy shadows around the player position. Large terrain gets complicated as you'd have to drop down to 1 sample size for quite a large area if you used just 1 global lightmap, however it would be possible to use a quadtree type approach and sub-divide the terrain into smaller sections and only use the shadow technique on the nearest sections and either use a low resolution or static map on the further away terrain.


Rroff(Posted 2010) [#43]
EDIT: Seems like the built in bi-linear filtering is killing this :( thought I had a work around but so far don't seem to be getting great results from the pixels read from the light camera view.

Not sure my color encoding/decoding is working properly either will have to test that too.

Last edited 2010


jfk EO-11110(Posted 2010) [#44]
Maybe try to turn off dithering in the card settings. Then again, it should work with most default settings to make sense at all.

The mottled shadows you mentioned: Of course, it would be fast to simply take the found colors, translate it to lightmap texel coords and write the light right into the lightmap. But the true bottleneck is the Readpixelfast action, We can easily do a little bit more math with the data, once read from the light camera render. For this purpose the render should be copied to an array in the first place, using readpixelfast.
In the array you can parse the data scanline for scanline. EG:
x1=rgb(x,y) shr 12
y1=rgb(x,y) and %111111111111
x2=rgb(x+1,y) shr 12

if abs(x2-x1)<10 then ; I think this would require 10 pixel gaps between the tris on the lightmap to work correctly, and it may still miss some pixels, depending on the distance.
for xx=x1 to x2-1 ; filling the missed colors
lightmap(xx,y1)=1
next
endif

tho, practicly the for loop may also have to use a negative step value.

Last edited 2010


Rroff(Posted 2010) [#45]
yeah readpixelfast straight out the backbuffer is very slow, not tried dumping the backbuffer into a normal buffer and reading from there to see if its any faster.

Also not sure if:

rgb= (x shl 12) or y

and to read it:

x= (rgb shr 12)
y=rgb and %111111111111


works correctly (with writepixelfast - works fine as raw maths) - will have to test with known values but I didn't seem to be getting anywhere at all until I padded out the full 32bit value including the 8bit alpha.

Last edited 2010


jfk EO-11110(Posted 2010) [#46]
You have to mask the alpha byte, it will give random values otherwise if I remember right. rgb= readpixelfast(x,y) and $FFFFFF. Unfort. the alpha byte cannot be used, as far as I see.


Rroff(Posted 2010) [#47]
Looking like the only way to realistically get this working at all is to cover the color "key" texture with large squares of the same color which massively reduces its resolution and how many triangles you can map.


EDIT: FastExtension v1.16.1+ has the ability to disable texture filtering which might produce better results... tho does depend on a 3rd party lib.

Last edited 2010


jfk EO-11110(Posted 2010) [#48]
Large squares? Why?


Rroff(Posted 2010) [#49]
The bilinear filtering is massively screwing with the accuracy - unless you have several pixels together of the same color you can't guarantee you even have the original pixel value present in the rendered view. However FastExt now has a function for disabling texture filtering which might solve this issue.

Last edited 2010


Rroff(Posted 2010) [#50]
BTW jfk EO-11110 how fast does your shadow code work for you? as copy and pasted it runs about 20fps for me but if I use copyrect to move the backbuffer to a texture (flags 1+512 might be useful, tried +256 but its slightly slower) and readpixelfast from there it runs over 5x faster.

Not sure if its an issue with my machine or backbuffer reads generally.

Last edited 2010


jfk EO-11110(Posted 2010) [#51]
5 times faster is always good. Were you talking about the experimantal thing I posted earlier in this thread? The Vertex Color thing? I really got to try that (do I get this right, you are copying to a texturebuffer to be able to ReadPixelFast etc. from there, which is faster than the backbuffer?) One problem with the 256 flag is, it is not always faster, not on every machine and setup. It also depends on if you use Copyrect or single pixel access. In fact we should use a function that determines the fastest texture flags combinations for such operations.

The blinear Filtering is indeed a problem. I've just tried to implement your idea. It was rather tricky with a Maplet mesh as long as I didn't realize that I cannot simply apply the color map to both texture layers, this might have something to do with the Blendmode of Maplet Mesh Textures, and I also remember that certain properties could not be alteres after loading, probably this was fixed in later Blitz versions.

The bilinear filtering is much more a problem with 12 Bits and will result in wild flickering artefacts while the camera and/or light is in motion.

With 8 Bit is much smoother, but it still has artefacts, caused by "linefeeds" within the texture. Most of the texture is now fading smoothly from one corner to the other, with a 256 x 256 Texture you can say Color 0,x,y. With 12 Bit you still get unique colors, but there are much more neighbour diffrences that will result in false detections dur to bilinear filtering, as you already expected. I really have to try that with FastExt asap.

Here's the code, excuse the globals etc., just a quick test. Note right now the mapsize should be 256, or 512, but with a bitres of 8, the max size for the texture is 256. I've noticed with 512 it is also rather slow.

Const mapsize=256
Const bitres=8
Const bitmask=(2^bitres)-1


Graphics3D 800,600,32,2
SetBuffer BackBuffer()

Collisions 1,2,2,2

player=CreatePivot()
PositionEntity player,0,3,0
EntityRadius player,0.75,1.5
EntityType player,1

Global camera=CreateCamera( player )
CameraRange camera,.1,200
TranslateEntity camera,0,1.5,0


;light=CreateLight()
;RotateEntity light,45,45,45

Global world=LoadMesh( "test_room1_"+mapsize+".b3d" )
EntityFX world,1
EntityType world,2

sp#=.5
ey#=EntityY(player)



;testlight=CreatePivot()
testlight=CreateSphere()
ScaleEntity testlight,0.1,0.1,0.1
Global lightcam=CreateCamera()
CameraProjMode lightcam,0
CameraViewport lightcam,0,0,mapsize,mapsize

Dim texwork(mapsize,mapsize)


Global texmap=LoadTexture("oldbric.bmp")
Global white=CreateTexture(16,16)
Color 255,255,255
Rect 0,0,16,16,1
CopyRect 0,0,16,16,0,0,BackBuffer(), TextureBuffer(white)



ClearTextureFilters()
Global colmap=CreateTexture(mapsize,mapsize,256)
Global litmap=CreateTexture(mapsize,mapsize,256)
TextureCoords colmap,1
TextureCoords litmap,1


For y=0 To mapsize-1
 For x=0 To mapsize-1
  rgb=(x Shl bitres) Or y
;:  Color 0,0,rgb
;  Plot x,y ; color tagging
  WritePixel x,y,rgb
 Next 
Next


CopyRect 0,0,mapsize,mapsize,0,0,BackBuffer(),TextureBuffer(colmap)
Flip
Delay 500


While Not KeyHit(1)
	a#=a+1
	If a>=360 Then a=0


	mxs#=-MouseXSpeed()/4.0
	mys#= MouseYSpeed()/4.0
	MoveMouse GraphicsWidth()/2,GraphicsWidth()/2

	If KeyHit(17)
		wire=1-wire
		WireFrame wire
	EndIf

	MoveEntity player,0,-.1,0

	If KeyHit(57) jump#=180
	
	If jump#>0
	 jump#=jump#-10
	 If jump#<0 Then jump#=0
	 MoveEntity player,0,-Cos(jump),0
	EndIf
	
    TurnEntity camera,mys,0,0
    TurnEntity player,0,mxs,0

	If KeyDown(200) MoveEntity player,0,0,sp
	If KeyDown(208) MoveEntity player,0,0,-sp

	If KeyDown(205) MoveEntity player,sp,0,0
	If KeyDown(203) MoveEntity player,-sp,0,0
	
	PositionEntity testlight, EntityX(camera,1), EntityY(camera,1), EntityZ(camera,1),1
	RotateEntity testlight, EntityPitch(camera,1), EntityYaw(camera,1), EntityRoll(camera,1),1
	MoveEntity testlight,Sin(a)*3.0,0,Cos(a)*3.0 ; just to see the shadows at all
	
	UpdateWorld
	
	
	UpdateLightMap(testlight)
	
	
	RenderWorld
	Text 0,0,TrisRendered()
	Flip 0
Wend

End







Function UpdateLightMap(li)
 Local x%,y%
 EntityTexture world, white,0,0
 EntityTexture world, colmap,0,1

 CameraProjMode camera,0
 CameraProjMode lightcam,1
 PositionEntity lightcam,EntityX(li,1),EntityY(li,1),EntityZ(li,1)
 RotateEntity lightcam,EntityPitch(li,1),EntityYaw(li,1),EntityRoll(li,1)

 RenderWorld()

 SetBuffer BackBuffer()
 LockBuffer BackBuffer()
  For y=0 To mapsize-1
  For x=0 To mapsize-1
   texwork(x,y)=ReadPixelFast(x,y) And $FFFFFF
  Next
  Next
 UnlockBuffer BackBuffer()

; Flip 0 ; visually debuggin...

 Color 40,40,40
 Rect 0,0,mapsize,mapsize,1
 CopyRect 0,0,mapsize,mapsize,0,0,BackBuffer(),TextureBuffer(litmap)

 SetBuffer TextureBuffer(litmap)
 LockBuffer TextureBuffer(litmap)
  For y=0 To mapsize-1
  For x=0 To mapsize-1
   xx=texwork(x,y) Sar bitres
   yy=texwork(x,y) And bitmask

   If(xx>=0) And (yy>=0) And (xx<mapsize) And(yy<mapsize)
    WritePixelFast xx,yy,$FFFFFF
   EndIf

  Next
  Next
 UnlockBuffer TextureBuffer(litmap)
 SetBuffer BackBuffer()

 EntityTexture world, texmap,0,0
 EntityTexture world, litmap,0,1
 CameraProjMode camera,1
 CameraProjMode lightcam,0
End Function


The Media used in this test can be found here:
http://www.melog.ch/dl/icu_mapping_test.zip

Btw this line:
yy=texwork(x,y) And (2^bitres)-1

should be optimized in the sourcecode that comes with the zipfile, it can be a const, as seen here.

Instead of writing directly to the lightmap for a certain color that was found, you may also add a value to an Array and repeat this with multiple lights, finally write the array to the lightmap. But at the moment I still have no idea how you could reduce the light by distance. This might be ok for sunlight, but pointlights for indoor situations really need this. So we're back at the need for depth buffer access :) .
A further optimation would be to WritePIxelFast only if you did not already writepixelfast to that texel, using a further array to store that information.


Rroff(Posted 2010) [#52]
Yeah was talking about your vertex coloring code... not sure if its my PCs (all recent nVidia cards with 260+ drivers) but directly reading the backbuffer is insanely slow - but by copyrect the backbuffer to a texture and then doing readpixelfast from that texture I get over 5x faster performance. Could just be an issue with newer nVidia setups that are more focused on DX9+ than DX7 tho.

I'm using this to unwrap arbitary geometry to test stuff - the unwrapping function is hugely inefficent but it works for testing purposes:


Global LMU# = 0; x position to place next tris
Global LMV# = 0; 

Global LMSCALE# = 1.0/16.0 ; based on using quake 3 scale where 32 units = 1 metre.

Const LMRES = 512
Const LMCLAMP# = 16 ; should be a multiple of LMRES

Const LMAMBR = 64
Const LMAMBG = 64
Const LMAMBB = 64

Const LMCAMRES = 128

Function LMadd(mesh) ; adds a mesh to be lit
	Local x0#, y0#, z0#
	Local x1#, y1#, z1#
	Local x2#, y2#, z2#
	
	Local u1#
	Local v1#
	
	Local dist#
	
	Local surf
	Local surfcount
	Local tris
	Local triscount
	Local vert1
	Local vert2
	Local vert3
	
	Local scale# = 1.0/LMRES
	Local blocksize# = LMCLAMP# * scale#
	
	Local oldbuffer
	
	mesh = LMunweld(mesh)
	
	Color 255,255,255

	oldbuffer = GraphicsBuffer()
	SetBuffer ImageBuffer(timg)

	For surfcount = 1 To CountSurfaces(mesh)
		surf = GetSurface(mesh,surfcount)
		triscount = CountTriangles(surf)
		For tris = 0 To triscount-1
			vert1 = TriangleVertex(surf,tris,0)
			x0# = VertexX#(surf,vert1)
			y0# = VertexY#(surf,vert1)
			z0# = VertexZ#(surf,vert1)
			
			vert2 = TriangleVertex(surf,tris,1)
			x1# = VertexX#(surf,vert2)
			y1# = VertexY#(surf,vert2)
			z1# = VertexZ#(surf,vert2)

			vert3 = TriangleVertex(surf,tris,2)
			x2# = VertexX#(surf,vert3)
			y2# = VertexY#(surf,vert3)
			z2# = VertexZ#(surf,vert3)

			u1# = Sqr#(((x1#-x0#)^2)+((y1#-y0#)^2)+((z1#-z0#)^2))
			v1# = Sqr#(((x2#-x0#)^2)+((y2#-y0#)^2)+((z2#-z0#)^2))
			
			u1# = u1# * LMSCALE#
			v1# = v1# * LMSCALE#
			
			If (u1# > LMCLAMP#) Then ; this isn't ideal but we don't want large polygons screwing it up
				u1# = LMCLAMP#
			EndIf
			
			If (v1# > LMCLAMP#) Then
				v1# = LMCLAMP#
			EndIf
			
			u1# = u1# * scale#
			v1# = v1# * scale#
			
			VertexTexCoords surf,vert1,LMU#,LMV#,0,1
			VertexTexCoords surf,vert2,LMU#+u1#,LMV#,0,1
			VertexTexCoords surf,vert3,LMU#,LMV#+v1#,0,1
			
			Line LMU#*LMRES,LMV#*LMRES,(LMU#+u1#)*LMRES,LMV#*LMRES
			Line LMU#*LMRES,LMV#*LMRES,LMU#*LMRES,(LMV#+v1#)*LMRES
			Line LMU#*LMRES,(LMV#+v1#)*LMRES,(LMU#+u1#)*LMRES,LMV#*LMRES
			
			LMU# = LMU# + blocksize#
			
			If (LMU# >= 1.0) Then
				LMU# = 0
				LMV# = LMV# + blocksize#
				If (LMV# >= 1.0) Then
					RuntimeError "Ooops ran out of space on the lightmap!"
				EndIf
			EndIf

		Next
	Next
	
	SetBuffer oldbuffer
	
	EntityTexture mesh,LMmap,0,0
	EntityTexture mesh,LMkey,0,7
End Function

Function LMunweld(mesh) ; *** This function is the work of David Dawkins(Starfox) ***
;Unweld a mesh, retaining all of its textures coords and textures
	For surfcount = 1 To CountSurfaces(mesh)
		surf = GetSurface(mesh,surfcount)

		count = CountTriangles(surf)
		bank = CreateBank((15*count)*4)
		For tricount = 0 To count-1
			off = (tricount*15)*4
			in = TriangleVertex(surf,tricount,0)
			x# = VertexX(surf,in):y#=VertexY(surf,in):z#=VertexZ(surf,in)
			u# = VertexU(surf,in):v#=VertexV(surf,in)
			PokeFloat(bank,off,x)
			PokeFloat(bank,off+4,y)
			PokeFloat(bank,off+8,z)
			PokeFloat(bank,off+12,u)
			PokeFloat(bank,off+16,v)

			in = TriangleVertex(surf,tricount,1)
			x# = VertexX(surf,in):y#=VertexY(surf,in):z#=VertexZ(surf,in)
			u# = VertexU(surf,in):v#=VertexV(surf,in)
			PokeFloat(bank,off+20,x)
			PokeFloat(bank,off+24,y)
			PokeFloat(bank,off+28,z)
			PokeFloat(bank,off+32,u)
			PokeFloat(bank,off+36,v)

			in = TriangleVertex(surf,tricount,2)
			x# = VertexX(surf,in):y#=VertexY(surf,in):z#=VertexZ(surf,in)
			u# = VertexU(surf,in):v#=VertexV(surf,in)
			PokeFloat(bank,off+40,x)
			PokeFloat(bank,off+44,y)
			PokeFloat(bank,off+48,z)
			PokeFloat(bank,off+52,u)
			PokeFloat(bank,off+56,v)

		Next

		ClearSurface(surf,True,True)

		For tricount = 0 To count-1
			off = (tricount*15)*4
			x# = PeekFloat(bank,off)
			y# = PeekFloat(bank,off+4)
			z# = PeekFloat(bank,off+8)
			u# = PeekFloat(bank,off+12)
			v# = PeekFloat(bank,off+16)
			a = AddVertex(surf,x,y,z,u,v)
			x# = PeekFloat(bank,off+20)
			y# = PeekFloat(bank,off+24)
			z# = PeekFloat(bank,off+28)
			u# = PeekFloat(bank,off+32)
			v# = PeekFloat(bank,off+36)
			b = AddVertex(surf,x,y,z,u,v)
			x# = PeekFloat(bank,off+40)
			y# = PeekFloat(bank,off+44)
			z# = PeekFloat(bank,off+48)
			u# = PeekFloat(bank,off+52)
			v# = PeekFloat(bank,off+56)
			c = AddVertex(surf,x,y,z,u,v)
			AddTriangle(surf,a,b,c)
		Next
		FreeBank bank
	Next

	UpdateNormals mesh
	Return mesh
End Function


the unweld function isn't my work (the LMadd one is), theres also some code in there from where I was building an image of the UV unwrapping for debugging purposes hence the line code. Excuse the habit of localing every variable I use its a habit I find useful for keeping track of stuff.

EDIT: Also finding the TextureAnisotropy 0/-2 command from fastext hugely useful I don't think its possible with native b3d to get great results without it.

Last edited 2010


jfk EO-11110(Posted 2010) [#53]
That's a handy, compact function, thanks. BTW I was trying to find the FastExt function to turn off bilinear filtering, but I could only find a sample about Anisotropy and LODBias - did you mean this? (edit, seems like that, but it doesn't make a diffrence for some reason, guess I am doing something wrong there).

Last edited 2010


Rroff(Posted 2010) [#54]
I'm using TextureAnisotropy -2 before rendering the light camera(s) and TextureAnisotropy 0 again to reset it before rendering the scene, seems to help preserve the integrity of color values in the light camera.

My plan if I got it anything like useable was to cap point lights to a range thats acceptable precision wise - indoor with good map design and light useage it should be possible to keep point light radius low enough and maybe try and find a way to only update distant lights to the player at a less regular rate. With sunlight moving the light "source" position along a vector from the player so that you get good precision around the player position.


jfk EO-11110(Posted 2010) [#55]
FastExt wasn't useful to fight the problems caused by Bilinear Filtering, but it comes in handy for rendering to a texture, that might make sense at a certain point.

But I have found an other way to turn off bilinear mapping: you can use DevisChild Shadow System, it contains a DX7 extension DLL with many useful commands, including SetTextureStageState, that allows to turn off the biliniear filter.

As a preventive Anti-404 Approach I uploaded the complete DCs Shadow Lib and added a sample for the Biliniear FIlter thing here (DC, hope you don't mind, otherwise let me know):
www.melog.ch/dl/bb_bilinear_off.zip

I've also made a test with it, trying the bespoken Realtime Lightmapping, and it works nicely, at least much better than with bilin.filters. There is still the speed problem with a 512 Texture. I tried to use a texturebuffer as you suggested, but it didn't help on my machine. I still hope somebody is going to hack a bridge in between DX7 and a parallel DX8 mini-surface that acts only as a programmable shader. Meanwhile I'll try to make it faster "somehow" :)


Rroff(Posted 2010) [#56]
Include "include\FastExt.bb"

Graphics3D 800,600,32,2

InitExt
SetBuffer BackBuffer()


;light=CreateLight()
;RotateEntity light,45,45,0
AmbientLight 255,255,255 ; preserve color data
cube=CreateCube()


w=256
tex1=CreateTexture(w,w)
For i=0 To 100000
 Color Rand(255),Rand(255),Rand(255)
 Plot Rand(0,255),Rand(0,255)
Next
EntityTexture cube,tex1


CopyRect 0,0,256,256,0,0,BackBuffer(), TextureBuffer(tex1)


camera=CreateCamera()
CameraRange camera,0.001,50
TranslateEntity camera,0,0,-1.5

; FastExt filtering

TextureAnisotropy -2


While KeyHit(1)=0
 TurnEntity cube,0,.1,0
 RenderWorld()
 Flip
 Delay 10
Wend

End


Does exactly the same thing for me using fastext which is much nicer than using DSS.

I was intending to try and use the lightmap as a mask (for shadowing) eventually using the normal lights to light stuff rather than purely using the lightmap for lighting as otherwise you need to also render the z-buffer to do light falloff, etc.

I'll try and get some code up and working with the method I use for reading from the backbuffer as on my PC it makes the difference between <20fps and >70fps which makes a world of difference in useability.

EDIT: Updated to include AmbientLight 255,255,255 so as to preserve the integrity of color samples.

Last edited 2010


jfk EO-11110(Posted 2010) [#57]
Note TextureAnisotropy -2 didn't turn off bilinear filtering on a Ati radeon, that
's a fact. Anisotropy is an other feature that you can access seperately in DCs DX7 dll. Are you really sure we're talking about the same thing? There is LOD Bias and Anisotropy in FastExt, but no SetTextureStageState%(0,D3DTSS_MAGFILTER, D3DTFG_POINT).

While I was happy to get it turned of at all, I realized that there is one problem that I cannot solve, even after hours of hacking: Whenever I want to access a rendered Pixel it must be downloaded from VRam to Ram somehow, and no matter what Method I try, they are all horribly slow. Some of the methods I tried:

-Copy from backbuffer to imagebuffer, readpixel there
-copy from 256-texture (where the scene was rendered to, using Fastext) to 0-texture
-copy from backbuffer to 0-texture
-using external copybuffer from DX7 dll to copy from 256-tex to 0-tex
-copy from backbuffer to image, then use RTLMoveMEmory to move en bloc to a bank, completely skipping readpixel, using ReadInt on the bank.

No matter what, as soon as the rendered Pixels are read, there is a massive slowdown: on backbuffer, on a texturebuffer with flag 256, on an image, and even with RtlMoveMemory from an image to Ram. Sample: Readpixelfast on a 256-texture is 36 times slower than Readpixelfast on a texture with no 256-flag. But I can render only to a texture when the 256 flag is set. With copyrect it's even worse, copying a 256-tex to a 0-tex is 75 times slower than copying 0-tex to 0-tex.
(note 0-tex means a texture with flags 0...).
Using texture flags 512+1 and copyrect the backbuffer to it is lightning fast, 20 times faster than anything else, there's only one little problem: noting is copied at all (one more of those "I MADE IT!... not" moments).

So, for now I seem to be blocked by the hardware. Kinda frustrating. Now, just imagine: the same thing could be done with shaders easily, but then everything would happen within the GPU, not only the CPU would not have to do the calculations, there would also be no need to download anything:

-Do a color-tagged render in Blitz to a texture buffer
-Run a pixel shader that takes the color render and sets the lumels in the lightmap
-if possible it may use the depthbuffer for light fallof, not sure if this is supported
by the blitz render (maybe when it's rendered to a texture with FastExt, DCs lib however seems to allow to apply a Depthbuffer to a Texturebuffer).


Rroff(Posted 2010) [#58]
Oh didn't know TextureAnisotropy -2 didn't work on ATI - running all nVidia hardware here :S it definitely does exactly the same thing here - disables filtering as the code using DC's dll.

Looks like theres some issues on different types of hardware - doing something like:

	tempbuffer = CreateTexture(512,512,1+512)
	
	CopyRect 0,0,512,512,0,0,BackBuffer(),TextureBuffer(tempbuffer)
	
	SaveBuffer TextureBuffer(tempbuffer),"c:\buffer.bmp"


is extremely fast on all my PCs - using savebuffer I can see its copying data - and I can read pixels from that tempbuffer as fast as normal reads.

Last edited 2010


Rroff(Posted 2010) [#59]
This is the modified version of your ICU code as I run it on my setup - does ~100fps on an E6600 w/ GTX260 and well over 100 on my gaming PC (Q9550 @ 4gig w/ GTX470 SLI) - SLI not used in b3d tho.

(I also dropped the light range to simulate a smaller point light, it works fine albiet poor shadow resolution/precision on my nVidia hardware - latest version of fastext is needed for the filtering stuff but runs fine with that commented out)

Include "include\FastExt.bb"

Const mapsize=256
Const bitres=8
Const bitmask=(2^bitres)-1

Global scratch


Graphics3D 800,600,32,2
InitExt
SetBuffer BackBuffer()

Collisions 1,2,2,2

player=CreatePivot()
PositionEntity player,0,3,0
EntityRadius player,0.75,1.5
EntityType player,1

Global camera=CreateCamera( player )
CameraRange camera,.1,200
TranslateEntity camera,0,1.5,0


;light=CreateLight()
;RotateEntity light,45,45,45

Global world=LoadMesh( "test_room1_"+mapsize+".b3d" )
EntityFX world,1
EntityType world,2

sp#=.5
ey#=EntityY(player)



;testlight=CreatePivot()
testlight=CreateSphere()
ScaleEntity testlight,0.1,0.1,0.1
Global lightcam=CreateCamera()
CameraProjMode lightcam,0
CameraViewport lightcam,0,0,mapsize,mapsize

Dim texwork(mapsize,mapsize)


Global texmap=LoadTexture("oldbric.bmp")
Global white=CreateTexture(16,16)
Color 255,255,255
Rect 0,0,16,16,1
CopyRect 0,0,16,16,0,0,BackBuffer(), TextureBuffer(white)



ClearTextureFilters()
Global colmap=CreateTexture(mapsize,mapsize,256)
Global litmap=CreateTexture(mapsize,mapsize,256)
TextureCoords colmap,1
TextureCoords litmap,1


For y=0 To mapsize-1
 For x=0 To mapsize-1
  rgb=(x Shl bitres) Or y
;:  Color 0,0,rgb
;  Plot x,y ; color tagging
  WritePixel x,y,rgb
 Next 
Next


CopyRect 0,0,mapsize,mapsize,0,0,BackBuffer(),TextureBuffer(colmap)
Flip
Delay 500

scratch = CreateTexture(mapsize,mapsize,1+512)


While Not KeyHit(1)
	a#=a+1
	If a>=360 Then a=0


	mxs#=-MouseXSpeed()/4.0
	mys#= MouseYSpeed()/4.0
	MoveMouse GraphicsWidth()/2,GraphicsWidth()/2

	If KeyHit(17)
		wire=1-wire
		WireFrame wire
	EndIf

	MoveEntity player,0,-.1,0

	If KeyHit(57) jump#=180
	
	If jump#>0
	 jump#=jump#-10
	 If jump#<0 Then jump#=0
	 MoveEntity player,0,-Cos(jump),0
	EndIf
	
    TurnEntity camera,mys,0,0
    TurnEntity player,0,mxs,0

	If KeyDown(200) MoveEntity player,0,0,sp
	If KeyDown(208) MoveEntity player,0,0,-sp

	If KeyDown(205) MoveEntity player,sp,0,0
	If KeyDown(203) MoveEntity player,-sp,0,0
	
	PositionEntity testlight, EntityX(camera,1), EntityY(camera,1), EntityZ(camera,1),1
	RotateEntity testlight, EntityPitch(camera,1), EntityYaw(camera,1), EntityRoll(camera,1),1
	MoveEntity testlight,Sin(a)*3.0,0,Cos(a)*3.0 ; just to see the shadows at all
	
	UpdateWorld
	
	
	UpdateLightMap(testlight)
	
	TextureAnisotropy 0
	
	RenderWorld()
	Text 0,0,TrisRendered()
	Flip 0
Wend

End







Function UpdateLightMap(li)
 Local x%,y%
Local oldbuffer = GraphicsBuffer()
 EntityTexture world, white,0,0
 EntityTexture world, colmap,0,1

 CameraProjMode camera,0
 CameraProjMode lightcam,1
 PositionEntity lightcam,EntityX(li,1),EntityY(li,1),EntityZ(li,1)
 RotateEntity lightcam,EntityPitch(li,1),EntityYaw(li,1),EntityRoll(li,1)
 CameraRange lightcam,1,8

TextureAnisotropy -2

 RenderWorld()

 CopyRect 0,0,mapsize,mapsize,0,0,BackBuffer(),TextureBuffer(scratch)


 SetBuffer TextureBuffer(scratch) ;BackBuffer()
 LockBuffer TextureBuffer(scratch) ;BackBuffer()
  For y=0 To mapsize-1
  For x=0 To mapsize-1
   texwork(x,y)=ReadPixelFast(x,y) And $FFFFFF
  Next
  Next
 UnlockBuffer TextureBuffer(scratch); BackBuffer()

SetBuffer oldbuffer ; backbuffer?

 ;Flip 0 ; visually debuggin...

 Color 40,40,40
 Rect 0,0,mapsize,mapsize,1
 CopyRect 0,0,mapsize,mapsize,0,0,BackBuffer(),TextureBuffer(litmap)

 SetBuffer TextureBuffer(litmap)
 LockBuffer TextureBuffer(litmap)
  For y=0 To mapsize-1
  For x=0 To mapsize-1
   xx=texwork(x,y) Sar bitres
   yy=texwork(x,y) And bitmask

   If(xx>=0) And (yy>=0) And (xx<mapsize) And(yy<mapsize)
    WritePixelFast xx,yy,$FFFFFF
   EndIf

  Next
  Next
 UnlockBuffer TextureBuffer(litmap)
 SetBuffer BackBuffer()

 EntityTexture world, texmap,0,0
 EntityTexture world, litmap,0,1
 CameraProjMode camera,1
 CameraProjMode lightcam,0
End Function


Last edited 2010

Last edited 2010


jfk EO-11110(Posted 2010) [#60]
If this runs with 100 fps, you should try:

Const mapsize=512
Const bitres=12

This looks not so blocky. If bilinear filtering is really turned off, then it should look ok. There is still the sampling problem of distant locations (player/light angle problem), but the rest is looking nice. If this system is used with a small mesh, eg. a single room in a big building, then the texels may be really small and fine. So it isn't entirely unpractical. With an additional 8 Bit depthmap (created with one of the methods mentioned earlier) you could even realize the light falloff. Unfortunately Renderworld has a little overhead, so it wont be an option to render the depth in slices to gather Depth information.

Using an animated NPC in this scene may also be less complicated than I guessed in the first place: Simply take the lightmap of the room (let us assume it uses the texture like Maplet, leaving the bottom right corner free, depending on how much of the texture is used), and UVmap the NPCs 2nd texture layer on this free corner. Atually you will have to simply scale it down to the corner in a Tool like UU3D or so.

Then, when your player is walking trough the level and a portal system is used that switches the lightmaps for the realtime lights, simply switch the NPC(s) lightmap as well. Again, if speed in't an issue then you can use up to a 4kx4k Texture with 12 Bit encoding. I think you can do a lot with a texture like that, for a single portal cell only. Any UVmapping is static, does not consume CPU power, even for animated Meshes.

Too bad speed IS an issue :/ . Good to hear it works better on NVidia. Mabye it is rather the AGP Bus that I still have. I think these things work diffrent with PCIe.

While copyrect from backbuffer (or a 256 texture) to a 1+512 texture is not copying anything and savebuffer saves a black, empty rect, (and we may discuss of this should be considered a feature or a bug), it seems I do have some asymetric Bus capabilities: upload works nicely, that's also what is required normally by a game, fast upload, resulting in liquid graphic changes. But as soon as something must be copied from Vram back to Ram, there is a strong brake (in the best case 35 times slower than the upload). I guess it has something to do with muliplexing data.

AGP Vram is addressed in the logical Ram adress space, somewhere at the top of the 4 gigas. Now this is only for AGP (I guess) and it might be not very interesting, I however was thinking about the usage of the "AGP Aperture" to speed thing up. AGP Aperture (Size can be set in the BIOS) is the amount of RAM the system will provide to the graphics card if it is running out of true VRAM. So this wil be Vram that lies within the conventional Ram. Shure, it wouldn't be a good idea to fill up VRam with unused textures, just to be able to have something in the Aperture Vram, but maybe there's a way to use it directly, maybe with some wrapped DX7 calls, probably something for a new FastExt version.

In theory reading from a texture Buffer that lies within the "Aperture Vram" should be much faster than reading from true Vram. Especially when a hack would be used, eg. with RtlMoveMemory. Too bad this Aperture Vram

"is spread in a non-contiguous form throughout the physical memory. Accessing these pages directly would hinder performance because of scattering/gathering requiring extra logic. To get around this limitation the GART (Graphics Address Remapping Table) which is implemented in hardware in the Northbridge's Memory Controller Hub provides an automatic physical-to-physical mapping between the scattered pages and the AGP Aperture. ... The actual usable amount of this 'virtual' AGP memory is less than half the AGP Aperture size set in the BIOS. This is because the Aperture is divided into two areas. One uncached half and another write-combined area."

Well, thats AGP. Maybe better placed in the museum. Hacking the impossible is still fun for some reason.

EDIT: I just tried to wipe out all Vram and create the lightmap texture in the Aperture Ram. Using CreateImage to use all the Vram works nicely, and the program proceeds, but in the render the textures created after there was nomore Vram will remain black, additionally, the speed isn't any higher.

Last edited 2010


Rroff(Posted 2010) [#61]
Thats one of the things I tried - unfortunatly brings it down to 20-30fps tho the shadows do start to look much more like shadows.

I'm assuming the issue with copyrect to a 1+512 texture is a bug - possibly related to AGP - try the code with just flags 1?

My function (with some adaption especially for efficency) can map any mesh into the lightmap, tho not sure how well it would turn out unwelding an NPC - by using a dynamic scale you could map higher resolution meshes into a smaller space which should still provide decent enough detail for an NPC model.

I can see some potential applications for this system tho i.e. realtime shadowing astroid fields with only the one distant light source (moved along a vector for precision around the player), etc.

Last edited 2010


Rroff(Posted 2010) [#62]
Oooh just disabled debug mode and its doing 86fps with 512/12 :D and actually looks quite good the odd speckle/incorrect pixel aside.

http://www.youtube.com/watch?v=ydJCmXEHLrY - was doing ~86fps on an E6600 w/ GTX260.

Baring in mind that the lightmap can easily be 2048x2048 without any affect on performance if you use my system of clsing the texture with ambient color and only writepixel based on the decoded x/y value and 4096x4096 on high end hardware it would be possible to get fairly decent res shadows assuming precision with a 512x512 or less light cam is possible, tho for point lights you'd really need to render twice with high field of view back to back :S

Last edited 2010


jfk EO-11110(Posted 2010) [#63]
I tried 1 alone and in combination with other flags. As soon as it works, it also becomes as slow as described (eg. 1+2). Yes, 512 may be 25 fps, since it is 4x bigger than 256. I think even on PCIe boards, download from Vram is still not really fast. Since modern shaders are more and more used to calculate non-graphical data as well (doing the work of the CPU using the GPU), we maybe can expect the hardware development will support a better Download in the future.

Not only asteroid fields, but basicly any sunlight sytem. Note: you can have an almost orthogonal camera (without to use the true Ortho Cam of Blitz, that suffers of Z-Sorting problems, at least here) when you set the Zoom to 100, then move the camera back 100 units and set the range to 100 to 200 (or so). So this render will provide a directional light source, like the sun.


Rroff(Posted 2010) [#64]
See my updated post above, atleast on newer nVidia hardware its possible to get it working very fast. Unfortunatly I don't have anything else to test it on other than core 2 CPUs and GTX260 or newer.


Rroff(Posted 2010) [#65]
256/8 with debug disabled and fullscreen does over 300fps! need to get someone on a 4xxx or higher ATI card to try with newer drivers too. Bit of a let down if it only works on recent PCI-e cards decently tho.


jfk EO-11110(Posted 2010) [#66]
Wow, 300 fps is more than I ever expected. Well, most things work only on PCI-e these days :) I think you should try a 1024 lightmap now :) this should still be about 70 fps. you may simply scale the texture up, the UVs remain the same.

EDIT oh, a misunderstanding, thought 256/8 refers to the flags. Well, still pretty fast. How much do you get with 512 texels ? (this coder language is really confusing, are we alking about 256 or about 256... actualy when it comes to binary there are "10" types of people: those who understand it and those who don't.

Last edited 2010


Rroff(Posted 2010) [#67]
For clarity - with debug disabled, fullscreen and using 1+512 flags on the texture I'm dumping the back buffer in:

800x600 with 256x light cam/map 8bit precision = 315fps
800x600 with 512x light cam/map 12bit precision = 120fps
1280x1024 with 1024x light cam/map 12bit precision = 25fps
1280x1024 with 512x light cam, 1024x map with 12bit precision = 100fps

1024x1024 is a little slow if I use that for light cam size and redrawing the whole texture (25fps). But if I keep the light cam size to 512 and only draw to the lightmap texture based on the x/y coords its still a good 100fps.

EDIT: With 1+256 (don't really need 512 just using it to make sure precision is always as high as possible incase of driver opptimisations, etc.) I can get to 335fps at 800x600 w/ 256 sized texture tho it seems a bit slower with the 256 texture flag versus just 1 or 1+512 at higher texture resolutions.

Last edited 2010


jfk EO-11110(Posted 2010) [#68]
That's really fast. When you think about the lot of indoor games that are using an individual lightmap vor every room or even for every light (so you can EG. turn the lights on and off (swapping two lightmap variations), using a light switch item in the game), then this is really promising. I still wonder if there's an easy way to gather depth information for light falloff.


Rroff(Posted 2010) [#69]
It would be better if possible to try and use the lightmap texture as a mask somehow (to shadow areas) and use the standard dx lights for actually lighting.


jfk EO-11110(Posted 2010) [#70]
As a mask? Not sure if I understand what you mean. Isn't it the same with a mask except for the sharp edges? You could set the alpha byte instead of $FFFFFF in the lightmap, and use the flag 4 (does 4 work together with flag 256 at all?)


Rroff(Posted 2010) [#71]
Not sure quite how to do it off the top of my head without playing (the first method that comes to mind would mean some parts were darker than they should be)

Essentially apply the lightmap texture with a blend so that the "lit" pixels in the lightmap texture allow the original DX lights to light the geometry, rather themselves lighting it, and the dark parts of the lightmap texture are shadows, cancelling out the DX lights where applicable.

Don't think I'm doing a great job of explaining it lol.


EDIT: So rather than a traditional lightmap that has both light and shadow info its more like a shadow map.

Last edited 2010


jfk EO-11110(Posted 2010) [#72]
Wouldn't cancelling out DX light mean the masked parts must be fully opaque?


jfk EO-11110(Posted 2010) [#73]
I just tried it on an other machine with PCI-e Bus. Although still a low end onboard chip, it's really fast now and the Flags 1+512 also worked. The sooner or later i can plug in a fullfledged PCIe Card there. I also used to write my first pixel shader, in GLSL, I love it... AFK to do some more tests.


Rroff(Posted 2011) [#74]
I haven't forgotten about this btw, just been busy with work with the new year and everything, should have a couple of weeks off coming up when I'm planning to put some more effort into it.


MikhailV(Posted 2011) [#75]
Excuse me if not in a theme, but...


jfk EO-11110:
Note TextureAnisotropy -2 didn't turn off bilinear filtering on a Ati radeon, that
's a fact.



Fact? Whence you it took? I have checked up - it works perfectly, fullscreen mode too. See below:


click for zoom


And...

... There is LOD Bias and Anisotropy in FastExt, but no SetTextureStageState%(0,D3DTSS_MAGFILTER, D3DTFG_POINT).



Filtration switch-off is implemented more correctly in FastExtension library, I am assured. See a part of source code of TextureAnisotropy function from FastExt.dll:
...
*Ext_D3DDev7\SetTextureStageState(layer, #D3DTSS_MAGFILTER, #D3DTFG_POINT)
*Ext_D3DDev7\SetTextureStageState(layer, #D3DTSS_MINFILTER, #D3DTFN_POINT)
*Ext_D3DDev7\SetTextureStageState(layer, #D3DTSS_MIPFILTER, mode)		; mode = -2 (#D3DTFP_NONE), -1 (#D3DTFP_POINT)
...


Last edited 2011


jfk EO-11110(Posted 2011) [#76]
Hi, sorry MikhailV, it didn't work for some reason here, but maybe I was only too silly. It think It was because I checked the include file with the "new blitz3d functions", but not the decls.

BTW what happened to the other (extremly fast) shadow system that was beta-implemented and then was removed from FastExt? Any chance to see this continued in a seperate project?

Hey Rroff, I decided to try it with pixel shaders, the sooner or later. But no matter what, Vram isn't really designed to be read, while writing to it is highly optimized in any way. That's why eg. perspective rendered shadows are rather quick, because you never need to examine a rendered pixel with the cpu. Pixel shaders seem to be the solution because they are designed to read and write from/to textures (and direct rendering to texture is used for sampling). Furthermore there is the nonlinear WBuffer (contrary to Zbuffer that is linear) that may help to reduce the named sampling problems.


MikhailV(Posted 2011) [#77]

BTW what happened to the other (extremly fast) shadow system that was beta-implemented and then was removed from FastExt? Any chance to see this continued in a seperate project?


There are problems which is actually unreal to solve:
— Low quality of shadows, low accuracy + artefacts (access denied to a dx7 depth buffer)
— High complexity of integration of such shadows in real projects. :(


Rroff(Posted 2011) [#78]
Was that the fake PSSM version? shame its not feasible.


jfk EO-11110(Posted 2011) [#79]
:-( Thanks for the info.


Rroff(Posted 2011) [#80]
Well I've been working on various approaches to ICU dynamic lightmapping over the last few week and unfortunatly so far not been able to find anything that gives a good blend of quality shadows and performance :( good performance versions have way too many artifacts in the shadow map to be useable and the filtering systems/sample resolution, etc. needed to get clean accurate shadows produce sub-realtime speeds.

Unfortunatly as JFK said it seems the only real approach to getting this to work would be via pixelshaders and theres better ways to do shadows if you have that capability.

EDIT: Thinking out loud somewhat but I wonder if its possible to use a combination of ICU with the approach with the way fastext does shadows - using a system where each polygon is mapped to a unique color (similiar to the lightmap key system) and when the results of the light cam is projected from the player eye perspective the color differences between the key map and the projected texture can cancel out areas that are lit (same color from each) and provide a map of the areas in shade (different colors)?

Last edited 2011


jfk EO-11110(Posted 2011) [#81]
Uh, I kind of don't get that. Maybe I got to read it again later :)


Rroff(Posted 2011) [#82]
Well I might be talking rubbish as I'm not entirely sure how fastext works tho I understand the basics.

Unwrap mesh and give each tris unique coords in a "lightmap" texture but instead of different colors for each texel use a different color for each polygon (also means we don't need to give each polygon much space in the texture).

Render from the light view like fastext does but with each polygon showing a unique color.

Project the light cam view back over the geometry still in unique polygon color mode so that where the colors are the same in each area thats "lit" but where the colors are different between the 2 camera it would be "unlit" with clever blending it should be possible to use this to generate a shadow mask.

Rerender with proper textures and the shadow mask.

Depending on how the shadow map is projected in fastext it might or might not work - this should work around the artifact issues of per texel mapping, performance issues of manually comparing each pixel and allow self shadowing and z correct shadows, tho it would need some kinda AND or OR blend mode.

EDIT: Or it might not be possible with the projection method to do self shadowing or z correct shadows in which case it would just be a more complicated way to do shadows the same way fastext works lol.

Last edited 2011


Yasha(Posted 2011) [#83]
Project the light cam view back over the geometry


What does this mean? (I'm assuming you mean render one camera as light position, then apply a perspective-mapped texture to the scene but somehow actually render it from the perspective of the other, normal camera?)

If so, I think you're on to something. But can that move be done? (Answer: probably not in the current version of FastExt, but that doesn't necessarily rule it out as a future addition if we ask Mikhail nicely.)


Rroff(Posted 2011) [#84]
I'm not sure how fastext projects its shadow map, probably not in a method thats applicable to this use tho :S (would still have issues with the Z coord).


Yasha(Posted 2011) [#85]
I wasn't referring to the shadow system, only to the ability to use a vertex's screen coordinates as the UV coordinates for perspective texturing. Somehow I had the idea this was important to your suggestion... now I'm not sure. I think I misunderstood.


Rroff(Posted 2011) [#86]
Oh thats a different application to what I was meaning, could also have potential tho very complex.


jfk EO-11110(Posted 2011) [#87]
I tried hard, still no luck +_+. I am esp. having problems with "Project the light cam view back over the geometry".


Rroff(Posted 2011) [#88]
I think I'm probably overestimating the method fastext uses for shadows.

Not easy to explain will have to get some code up and running if its at all possible which will take a bit.


Rroff(Posted 2011) [#89]
Well I had a go at my idea with fastext - a bit complicated to explain - the long and short of it tho due to the difference in camera/perspective resolution between the player camera and light cam there are unfixable artifacts along the edges of polygons... so back to square one.


The only avenue I've now not explored is using the original lightmap idea but tagging the original points in realtime 3D space (probably a bit slow) so as to get visibility information from a sparse octree in terms of which point on the LM is in shadow.


Yasha(Posted 2011) [#90]
tagging the original points in realtime 3D space (probably a bit slow) so as to get visibility information from a sparse octree in terms of which point on the LM is in shadow


As used by Quake and Quake 2 (sadly I can't find the link at the moment).

The basic idea, as I understand it, is that the lightmap is made without any overlap; each pixel is also linked to a reference to its (only) 3D position, and light sources pick against nearby pixel points then update the render copy of the lightmap as appropriate.

It can definitely be made very fast (Quake 2 gives hundreds of FPS even in software rendering mode), but the resolution isn't that high unless you have a lot of separate lightmaps (obviously increased resolution will reduce performance). Also won't be usable for animated or movable objects of any sort.

Definitely a good choice for games with a retro aesthetic though. Quake 2 is one of the best-looking games ever.


Rroff(Posted 2011) [#91]
Quake/Quake 2 only uses a static baked lightmap (non-overlap) for lighting. The game does build a light grid from this - but this is used to test a position to get the light level in realtime rather than to update the lightmap*. The light grid is used to correctly shade moving entities like players but only in a course manner (no shadow projection) - its also used in the AI - a player thats position is in a darker area in the light grid is harder for the AI to detect and react to than someone in the middle of a brightly lit area.

I'm talking about extending it beyond a static light grid so as to use a voxel style method to update the non-overalp light map in real time.

Another advantage of using this if its possible to do fast enough would be realtime global illumination which would be perfectly possible from this method.

* It is used for switchable lights to update the lightmap i.e. lights turning on/off, changing colors or flickering patterns, etc. to simulate say candle light, but it doesn't update the shadow projection only the brightness of a spot on the lightmap with no additional spot occlusion testing against light sources.

Last edited 2011