Single pass multi-light + bump map shader implementation

Started by
7 comments, last by Nedelman 17 years, 2 months ago
I've got my single-pass multi-light shader working using phong illumination model. I also have another working lighting shader that does bump mapping, but this one only uses one light source at the moment. I've recently tried to blend the two together in order to get a single-pass multi-light shader that supports bump-mapping. However, Im running into difficulties when it comes to passing the interpolants required for each light source to the fragment shader. It was easy with the 1-light source version - Here's the structure that gets outputed by the vertex shader and received as input by the fragment shader (HLSL): struct vertexOutput { float4 hpos : POSITION; float2 baseCoords : TEXCOORD0; float2 lightmapCoords : TEXCOORD1; float3 viewDirTS : TEXCOORD2; // dir = Vertex to Camera float3 lightDirTS : TEXCOORD3; // dir = Vertex to Light }; TS stands for Tangent Space. Obviously, the only light-dependant interpolant is lightDirTS. Now the tricky part is modifying this to support more than one light. Ideally, instead of passing a single lightDirTS to the fragment shader, I'd like to pass an array of lightDirTS, with each entry corresponding to a different light source. Is there any clean way of doing this, or do I have to go lightDirTS1, lightDirTS2, lightDirTS3, etc..? How is this done in state-of-the-art engines? Also, Ive got another question related to multi-pass/single-pass lighting shaders. I'm planning on adding support for shadow mapping to my engine soon, and someone in another thread suggested that in this case it's better to have single-pass approach to lighting (as in, one light per shader pass). I remember the guy knew what he was talking about, but he didnt give a more detailed explanation. Can anyone provide more insights on this topic please? And as anyone doing graphics programming loves to show off their work, here are some of the most recent shots of my humble D3D engine that supports several quake formats: mastok_md5_4_HQ.jpg mastok_md5_5_HQ.jpg Thanks in advance for your help! [Edited by - Spk on January 31, 2007 9:30:44 AM]
Advertisement
I havn't done multi-light shaders yet so I hada question for you. You are using 3 texture coords already which leaves 5 texture coords for your vertex-to-light vectors. So you can never have more than 5 lights unless you do some float packing/compressing?

I have done a shadowmap shader, and for a single light I had to output:
struct VS_OUTPUT {
float4 position : POSITION;
float2 tex : TEXCOORD0;
float4 posFromLight : TEXCOORD1; // vertex postion relative to light
float3 normal : TEXCOORD2;
float4 pos : TEXCOORD3; // untransformed vertex position
};
I don't see why you couldn't do multiple shadowmap lookups in the pixel shader. Just being limited to 8 texture coordinates would limit you to 2 lights or so. Maybe there is something I'm missing. I'm just figuring this out myself :p
There's indeed a limit on the number of interpolants you can provide to the fragment shader. The exact number must depend on the shader model or the hardware itself. I guess this is also why most single-pass multi-light shaders don't seem to be able to support more than 3-4 lights at the same time, depending on the extra techniques supported. The multi pass approach sounds a lot more flexible to me, but Im curious about how people get it working in a single pass.
A thought just occurred to me: you could probably encode the light data into a texture and pass that in (assuming the lights are static, otherwise modifying the texture could be messy)
This won't work here, the light sources Im using are dynamic, and the liighting parameters required by the bump map change on a per-pixel basis, so they definately need to go the "interpolant" way.
Can't you store the light paramaters as pixel program constants? You only need to interpolate the vertex positions per pixel, then extend the lighting equation inside the pixel program. This means you don't need anything per light in the vertex program, you move it all down to the pixel program.

struct vertexOutput {float4 hpos : POSITION;float2 baseCoords : TEXCOORD0;float2 lightmapCoords : TEXCOORD1;float3 vertexpos : TEXCOORD2; // vertex position (world space probably)};


Then in the pixel program, that vertexpos will have been interpolated per pixel, and you can use it as the current world space position of that pixel. Plug that into your full lighting equation and viola, you get how ever many light paramaters there as you can store in the pixel program constants.
As xycsoscyx said, but you will need to TBN matrix inside the pixel shader, pass that to the pixel shader using 3 float4 TEXTURE fields and reassemble inside the ps. Have fun !!

Single-pass multi-light shader with bump map that working fine, but TEXCOORD be limited no more than 8, you have to pass lightDir to PS. And other limit is arithmetic instruction slots, max.64 allowed by the PS2.0, so that you has no enough slots to do attenuation, Parallax ...etc.
Hello Spk. I've done something similar to what you're doing. I created a 3DS Viewer application that does multiple lights, bump mapping, and other things in a single pass.

The light data (positions, directions, colors, etc) are stored as effect parameters. Doing lighting in a single pass like this, on a per pixel basis, requires very long pixel shader instruction sequences, so the rendering only looks nice on shader model 3.0 hardware, though I do have a fallback for shader model 2.0 hardware that uses vertex lighting. The fallback looks pretty bad, as you might imagine.

I've posted the application with the full source code, so you may want to take a look at it. I'd be glad to answer any questions you have about the approach that I took for other aspects of the program.
www.gameprojects.com - Share Your Games and Other Projects

This topic is closed to new replies.

Advertisement