必要なのは、光源から照らされているフラグメントまでの距離 (または、平方距離を保存するために平方距離) です。次に、最も単純なケースでは、ライト マップと光源の寄与の間を線形に補間できます。
距離は、頂点シェーダーの頂点ごとに実行できる単純な計算です。
in vec4 VertexPosition; // let's assume world space for simplicity
uniform vec4 LightPosisiton; // world-space - might also be part of a uniform block etc.
out float LightDistance; // pass the distance to the fragment shader
// other stuff you need here ....
void main()
{
// do stuff
LightDistance = length(VertexPosition - LightPosisiton);
}
フラグメント シェーダーでは、距離を使用して、光源とライトマップの寄与の間の補間係数を計算します。
in float LightDistance;
const float MAX_DISTANCE = 10.0;
uniform sampler2D LightMap;
// other stuff ...
out vec4 FragColor;
void main()
{
vec4 LightContribution;
// calculate illumination (including shadow map evaluation) here
// store in LightContribution
vec4 LightMapConstribution = texture(LightMap, /* tex coords here */);
// The following DistanceFactor will map distances in the range [0, MAX_DISTANCE] to
// [0,1]. The idea is that at LightDistance >= MAX_DISTANCE, the light source
// doesn't contribute anymore.
float DistanceFactor = min(1.0, LightDistance / MAX_DISTANCE);
// linearly interpolat between LightContribution and LightMapConstribution
vec4 FinalContribution = mix(LightContribution, LightMapConstribution, DistanceFactor);
FragColor = WhatEverColor * vec4(FinalContribution.xyz, 1.0);
}
HTH。
編集: Nicol Bolas の発言を考慮に入れるために、LightMap は RGB カラーとしてエンコードされた寄与を格納し、各チャネルの寄与を格納すると仮定します。実際に単色の寄与のみを格納する単一チャネルのライトマップがある場合は、表面の色を使用するか、光源の色を使用するか、光源の寄与を単一のチャネルに減らす必要があります。
EDIT2 : これは数学的には機能しますが、物理的に健全ではありません。少なくとも物理的にもっともらしいものにするために、最終的な寄与を修正する必要があるかもしれません。効果のみを目的とする場合は、結果に満足するまで補正係数をいじることができます。