OpenGL仕様の最新バージョンでは、などの組み込みユニフォームがgl_LightSource
非推奨としてマークされているため、現在、カスタムユニフォーム変数を介してすべての光とマテリアルの情報を受け取る基本的な照明システム(現在はポイントライト)を実装しています。
ポイントライトのライト減衰とスペキュラハイライトを実装しましたが、位置の不具合を除けば、うまく機能しているようです。ライトを手動で移動し、X軸に沿って位置を変更しています。ただし、光源(その下の正方形の平面に当たる光から判断すると)はX軸に沿って移動しているようには見えませんが、X軸とZ軸の両方で斜めに移動しているように見えます(おそらくYでもありますが、そうではありません)。完全にポジショニングのバグ)。
歪みがどのように見えるかのスクリーンショットを次に示します(ライトは-35、5、0にあり、スザンヌは0、2、0にあります ::
ライトが0、5、0にあるときは、問題ないように見えます。
OpenGL仕様によると、すべてのデフォルトの光の計算は目の座標で行われます。これは、ここでエミュレートしようとしているものです(したがって、光の位置とvMatrixの乗算)。レンダリングされている頂点バッチのモデル変換をライトに適用することは実際には意味がないため、ビューマトリックスのみを使用しています。
重要な場合は、すべての平面の法線が真上を向いています-0、1、0。
(注:msellとmyAcesのおかげで、この問題を修正しました。次のスニペットは修正されたバージョンです。ライトにスポットライトパラメーターを追加するオプションもあります(d3dスタイルのもの))
頂点シェーダーで使用しているコードは次のとおりです。
#version 330
uniform mat4 mvpMatrix;
uniform mat4 mvMatrix;
uniform mat4 vMatrix;
uniform mat3 normalMatrix;
uniform vec3 vLightPosition;
uniform vec3 spotDirection;
uniform bool useTexture;
uniform bool fogEnabled;
uniform float minFogDistance;
uniform float maxFogDistance;
in vec4 vVertex;
in vec3 vNormal;
in vec2 vTexCoord;
smooth out vec3 vVaryingNormal;
smooth out vec3 vVaryingLightDir;
smooth out vec2 vVaryingTexCoords;
smooth out float fogFactor;
smooth out vec4 vertPos_ec;
smooth out vec4 lightPos_ec;
smooth out vec3 spotDirection_ec;
void main() {
// Surface normal in eye coords
vVaryingNormal = normalMatrix * vNormal;
vec4 vPosition4 = mvMatrix * vVertex;
vec3 vPosition3 = vPosition4.xyz / vPosition4.w;
vec4 tLightPos4 = vMatrix * vec4(vLightPosition, 1.0);
vec3 tLightPos = tLightPos4.xyz / tLightPos4.w;
// Diffuse light
// Vector to light source (do NOT normalize this!)
vVaryingLightDir = tLightPos - vPosition3;
if(useTexture) {
vVaryingTexCoords = vTexCoord;
}
lightPos_ec = vec4(tLightPos, 1.0f);
vertPos_ec = vec4(vPosition3, 1.0f);
// Transform the light direction (for spotlights)
vec4 spotDirection_ec4 = vec4(spotDirection, 1.0f);
spotDirection_ec = spotDirection_ec4.xyz / spotDirection_ec4.w;
spotDirection_ec = normalMatrix * spotDirection;
// Projected vertex
gl_Position = mvpMatrix * vVertex;
// Fog factor
if(fogEnabled) {
float len = length(gl_Position);
fogFactor = (len - minFogDistance) / (maxFogDistance - minFogDistance);
fogFactor = clamp(fogFactor, 0, 1);
}
}
そして、これは私がフラグメントシェーダーで使用しているコードです:
#version 330
uniform vec4 globalAmbient;
// ADS shading model
uniform vec4 lightDiffuse;
uniform vec4 lightSpecular;
uniform float lightTheta;
uniform float lightPhi;
uniform float lightExponent;
uniform int shininess;
uniform vec4 matAmbient;
uniform vec4 matDiffuse;
uniform vec4 matSpecular;
// Cubic attenuation parameters
uniform float constantAt;
uniform float linearAt;
uniform float quadraticAt;
uniform float cubicAt;
// Texture stuff
uniform bool useTexture;
uniform sampler2D colorMap;
// Fog
uniform bool fogEnabled;
uniform vec4 fogColor;
smooth in vec3 vVaryingNormal;
smooth in vec3 vVaryingLightDir;
smooth in vec2 vVaryingTexCoords;
smooth in float fogFactor;
smooth in vec4 vertPos_ec;
smooth in vec4 lightPos_ec;
smooth in vec3 spotDirection_ec;
out vec4 vFragColor;
// Cubic attenuation function
float att(float d) {
float den = constantAt + d * linearAt + d * d * quadraticAt + d * d * d * cubicAt;
if(den == 0.0f) {
return 1.0f;
}
return min(1.0f, 1.0f / den);
}
float computeIntensity(in vec3 nNormal, in vec3 nLightDir) {
float intensity = max(0.0f, dot(nNormal, nLightDir));
float cos_outer_cone = lightTheta;
float cos_inner_cone = lightPhi;
float cos_inner_minus_outer = cos_inner_cone - cos_outer_cone;
// If we are a point light
if(lightTheta > 0.0f) {
float cos_cur = dot(normalize(spotDirection_ec), -nLightDir);
// d3d style smooth edge
float spotEffect = clamp((cos_cur - cos_outer_cone) /
cos_inner_minus_outer, 0.0, 1.0);
spotEffect = pow(spotEffect, lightExponent);
intensity *= spotEffect;
}
float attenuation = att( length(lightPos_ec - vertPos_ec) );
intensity *= attenuation;
return intensity;
}
/**
* Phong per-pixel lighting shading model.
* Implements basic texture mapping and fog.
*/
void main() {
vec3 ct, cf;
vec4 texel;
float at, af;
if(useTexture) {
texel = texture2D(colorMap, vVaryingTexCoords);
} else {
texel = vec4(1.0f);
}
ct = texel.rgb;
at = texel.a;
vec3 nNormal = normalize(vVaryingNormal);
vec3 nLightDir = normalize(vVaryingLightDir);
float intensity = computeIntensity(nNormal, nLightDir);
cf = matAmbient.rgb * globalAmbient.rgb + intensity * lightDiffuse.rgb * matDiffuse.rgb;
af = matAmbient.a * globalAmbient.a + lightDiffuse.a * matDiffuse.a;
if(intensity > 0.0f) {
// Specular light
// - added *after* the texture color is multiplied so that
// we get a truly shiny result
vec3 vReflection = normalize(reflect(-nLightDir, nNormal));
float spec = max(0.0, dot(nNormal, vReflection));
float fSpec = pow(spec, shininess) * lightSpecular.a;
cf += intensity * vec3(fSpec) * lightSpecular.rgb * matSpecular.rgb;
}
// Color modulation
vFragColor = vec4(ct * cf, at * af);
// Add the fog to the mix
if(fogEnabled) {
vFragColor = mix(vFragColor, fogColor, fogFactor);
}
}
どのような数学のバグがこの歪みを引き起こしている可能性がありますか?
編集1:
シェーダーコードを更新しました。減衰はフラグメントシェーダーで計算されています。これは、ずっと続いているはずです。ただし、現在は無効になっています。このバグは減衰とは何の関係もありません。ライトの減衰係数のみをレンダリングする場合(フラグメントシェーダーの最後の数行を参照)、減衰は正しく計算されます。これは、ライトの位置が目の座標に正しく変換されていることを意味するため、バグの原因にはなりません。
フラグメントシェーダーの最後の数行は、一部の(少しハック的ですが、それでも洞察に満ちた)デバッグに使用できます。理由はわかりませんが、光の強度がフラグメントごとに正しく計算されていないようです。
興味深いのは、このバグが画像の床のような(非常に)大きなクワッドでのみ目立つことです。小型モデルでは目立ちません。
編集2:
シェーダーコードを動作するバージョンに更新しました。gl_LightSource[i].*
これですべてがうまくいき、将来のユーザーがこれを読むのに役立つことを願っています。今日の時点で、固定機能や秘密の暗黙の変換(および目の空間への暗黙の変換など)をまったく使用せずにライトを実装するglslチュートリアルはまだ見ていません。)。
私のコードはBSD2条項ライセンスの下でライセンスされており、GitHubで見つけることができます!