Lightstyle friendly alpha-masked textures (example shader)


(WolfWings) #1

Nothing much, but this is a follow-up to a previous post I made in a thread, where someone seemed interested in uses for non-standard blend and alphaFunc settings.

So, I thought I’d post the shader I worked up for alpha-masked textures, that places the lightmap first, and still uses nearly full alpha-blending so the edges almost always look better than ‘harsh’ alphaFunc masking, assuming you have a properly-alpha-channeled texture to begin with.


textures/whatever
{
    surfaceparm trans
    surfaceparm alphaShadow
    { // Use the mask from the 'grate' texture
        map textures/whatever.tga
        blendFunc GL_ZERO GL_ONE // Technically optional
        alphaFunc GE128
        depthWrite
    }
    { // Base lighting layer
        map $lightmap
        blendFunc GL_ONE GL_ZERO // Technically optional
        depthFunc equal
    }
    q3map_styleMarker2
    { // Add the actual 'grate' texture
        map textures/whatever.tga
        blendFunc filter
        depthFunc equal
    }
    { // Add the 'faint edges' of the mask texture
        detail
        map textures/whatever.tga
        blendFunc GL_DST_COLOR GL_ONE_MINUS_SRC_ALPHA
        alphaFunc LT128
    }
}

The first pass just writes out the masking element of the grate texture to the depth buffer, leaving the color buffer untouched.

The second pass adds the alpha-masked lightmap, and has the marker so the alpha-masked lightstyles can be added as well.

The third pass is the actual texture of the grate.

The fourth pass is a ‘trim’ pass, which is why I marked it as ‘detail’ in this case. It evens out the edges of the grate texture, and allows it to continue to be seen even if very sparse at a distance, such as chainlink, instead of simply ‘vanishing’ as plain alpha-masking does. While not ‘technically’ or ‘truly mathematically’ correct, it works quite well none-the-less.

Theoretically, a ‘mathematically correct’ shader could be assembled, but it would involve even more passes, and 4 (7 with 3 lightstyles on it) is already quite a few passes in a single shader for something with no animation or anything else. :slight_smile:

And for those wanting to see what a difference that last stage of the shader makes, along with proof (though subtle) that it works with dynamic lighting:

That image is two screenshots taken from the same place, without moving the viewpoint or anything else, only toggling r_detailtextures to enable/disable that last ‘edging’ pass on the alpha-masked texture.

I didn’t try to perfectly match the colour cycle (it’s a “rotating” white light in a green-lit room shining up at the pillars, a cheap test map I built) but it’s also handy to prove it handles dynamic lighting well.


(MadJack) #2

Hey no screenies? :stuck_out_tongue:


(WolfWings) #3

I’ll make some later, I posted that after working out the math at work. =^.6=


(WolfWings) #4

Oh, and just so folks know… the frame with the ‘white spots’ in the middle is the frame with the edging. The upper-right pillar is the most obvious example of the improvement the edging pass gives. Most of the pillar’s “framework” vanishes entirely without the edging pass. :slight_smile:


(MadJack) #5

I think I get the general idea but I’m wondering about practicality? In what circumstances could that be used? I’m not too sure how it can be used except for areas where dlights are present but what are the added benefits of it?

Also, we all know that dlights can be harsh on FPS, that blending with 4 passes might pull it down even more or am I wrong?


(ydnar) #6

The detail stage isn’t necessary, so if you wanted to simplify the shader that could be removed.


(WolfWings) #7

The above shader can be used anywhere you’d be using alpha-masked textures with styled lights. It’s really that simple.

If you don’t need styled-light support, the following shader would work just as well, and remove one stage entirely:


textures/whatever
{
    surfaceparm trans
    surfaceparm alphaShadow
    { // Add the actual 'grate' texture
        map textures/whatever.tga
        alphaFunc GE128
        depthWrite
    }
    { // Lighting layer
        map $lightmap
        blendFunc filter
        depthFunc equal
    }
    { // Add the 'faint edges' of the mask texture
        detail
        map textures/whatever.tga
        blendFunc GL_DST_COLOR GL_ONE_MINUS_SRC_ALPHA
        alphaFunc LT128
    }
}

And yes, the detail stage is a good portion of the reason for posting this shader. Leaving it in, but marked detail, allows anyone that wants to simply turn off that stage of the shader from the console with /r_detailtextures 0 easilly enough.

It prevents ‘thin’ grate-work from vanishing in the distance too soon, letting it blend off smoothly at the edges instead of the usual harsh jaggies that alpha-masked textures usually have, while still supporting lightmapping. The screenshot-pair I posted as the small animation should show the difference it makes.


(obsidian) #8

You could try doing something like this to solve the problem of mipmapped fineline alpha:

Cardigan
posted 02-09-2003 04:44 PM

Just discovered this by accident but its quite useful to know, so thought I’d share it…
If you have a texture with alpha transparency and more of the texture is transparent than not, you may find the texture becoming completely seethru at a distance as the detail in the alpha channel is mipmapped out of existence. In my particular case it was a chain texture, but I’ve seen the same effect on a couple of evil_lairs more ornate grate textures too.

Anyway, provided the shader uses an alphafunc GE128 rather than a blendfunc (most do for depth sorting purposes) you can fix this prob by simply changing the seethru areas in the alphachannel from black to dark grey (about 30% grey or something like that). Then when the texture is mipmapped, the average value will be lighter and the texture retains its integrity. Because anything under 50% grey is made seethru when using alphafunc GE128, the texture still looks how it should.

Note if you are using alphashadow on the texture you’ll need to have two versions, otherwise this trick will cause a dark patch around the texture’s shadow. Use a normal (black) version for compiling and then simply switch in the grey one at runtime.


(ydnar) #9

No need to swap textures in/out. Just use q3map_lightImage to specify the original texture for light/alphashadow purposes.

y


(WolfWings) #10

Actually, as I’ve said a few times, the reason I like this extra pass is because it provides a smooth blend, instead of a harshly aliased edge, on the edges of the masked textures more than anything else, akin to a super-sampled FSAA pass over the alpha-masked texture. That it still looks mostly correct with lightmapping is a nice bonus, really. :slight_smile: