Deferred レンダラーも実装した後、修士論文プロジェクト用に OpenGL で Pre-Pass Lighting アルゴリズムを実装しています。Deferred レンダラーは完全に機能し、PPL の実装はこれに基づいています。アルゴリズムのライティング パスの後、非常に奇妙なアーティファクトが発生しました。シーン内のライトの影響を蓄積する L バッファに含まれるデータは正しいのですが、結果はジオメトリに関してわずかにずれているため、マテリアルパスのシーンに適用すると、結果がはっきりと見えます! (ここに画像を投稿することはできませんが、ここにそれを見るためのリンクがありますhttp://postimage.org/image/kxhlbnl9v/)
ライト マップ キューブは、ジオメトリからのオフセット (軸ごとに異なる) を使用して何らかの方法で計算されているようです。シェーダーと C++ コードを何度も確認しましたが、この問題の原因がわかりません。アイデアが不足しています。以下は、順番に呼び出されるアルゴリズムの 3 つのパスのコードです。コードは今のところ実験的なものなので、この段階では適切に設計されていないことはわかっています。また、すべての段階で使用するシェーダーを追加して、G バッファー、L バッファー、フレームバッファーに順番に書き込みます。
C++ コード:
// Draw geometry to g buffer
void GLPrePassLightingRendererV2::GeometryStage()
{
// Set GL states
glFrontFace(GL_CCW);
glCullFace(GL_BACK);
glEnable(GL_CULL_FACE);
glDepthFunc(GL_LEQUAL);
glDisable(GL_BLEND);
glEnable(GL_DEPTH_TEST);
glDepthMask(GL_TRUE);
// Bind G-Buffer for geometry pass
mGBuffer->BindForWriting();
// Bind geometry stage shaders
mTargetRenderSystem->BindShader(mGeometryStageVS);
mTargetRenderSystem->BindShader(mGeometryStageFS);
// Clear the framebuffer
mTargetRenderSystem->ClearFrameBuffer(FBT_COLOUR | FBT_DEPTH);
// Iterate over all the Renderables in the previously built RenderQueue
RenderableList* visibles = mSceneManager->GetRenderQueue()->GetRenderables();
// Set shader params here
//[...]
// Get the transformation info from the node the renderable is attached to
for (RenderableList::iterator it = visibles->begin(); it != visibles->end(); ++it)
{
Renderable* renderable = *it;
Material* mat = renderable->GetMaterial();
mGeometryStageVS->Update();
mGeometryStageFS->Update();
// Render the object
RenderOperation rop;
renderable->GetRenderOperation(rop);
mTargetRenderSystem->Render(rop);
}
// Only the geometry pass will write to the depth buffer
glDepthMask(GL_FALSE);
glDisable(GL_DEPTH_TEST);
}
// Accumulate lights contribs in L-buffer using G-buffer
void GLPrePassLightingRendererV2::LightingStage()
{
// Enable additive blending for lights
glEnable(GL_BLEND);
glBlendEquation(GL_FUNC_ADD);
glBlendFunc(GL_ONE, GL_ONE);
//glCullFace(GL_FRONT);
// Bind shader for light stage
mTargetRenderSystem->BindShader(mLightStageVS);
mTargetRenderSystem->BindShader(mLightStageFS);
// Bind G-Buffer for reading and L-Buffer for writing for lighting pass
mGBuffer->BindForReading();
mLBuffer->BindForWriting();
mTargetRenderSystem->ClearFrameBuffer(FBT_COLOUR);
// Set shader params
// [...]
// Get all the lights in frustum, not by renderable
const LightList& lights = mSceneManager->GetLightsInFrustum();
// For each light in the frustum
LightList::const_iterator front_light_it;
for (LightList::const_iterator lit = lights.begin(); lit != lights.end(); ++lit)
{
// Send per light parameters to the shader
Light* l = (*lit);
SetLight(*l);
// Calculate bounding sphere for light and scale accordingly to instensity
float lightSphereScale = GetPointLightSphereScale(l->GetColor(), l->GetDiffuseIntensity());
// TODO: Render a sphere for each point light, a full screen quad for each directional
worldMtx.Identity();
worldMtx.SetScale(lightSphereScale, lightSphereScale, lightSphereScale);
worldMtx.SetTranslation(l->GetPosition());
mLightStageVS->SetParameterValue("gWorldMtx", (float*)&worldMtx);
mLightStageVS->Update();
mLightStageFS->Update();
static MeshInstance* sphere = mSceneManager->CreateMeshInstance("LightSphere", MBT_LIGHT_SPHERE);
RenderOperation rop;
sphere->GetSubMeshInstance(0)->GetRenderOperation(rop);
mTargetRenderSystem->Render(rop);
}
// Disable additive blending
glDisable(GL_BLEND);
}
// Combine L-buffer and material information per object
void GLPrePassLightingRendererV2::MaterialStage()
{
// Set some GL states
glDepthMask(GL_TRUE);
glEnable(GL_DEPTH_TEST);
//glCullFace(GL_BACK);
// Bind material stage shaders (TODO: actually every object will bind its own matarial, if not a default one is used)
mTargetRenderSystem->BindShader(mMaterialStageVS);
mTargetRenderSystem->BindShader(mMaterialStageFS);
// Bind L-Buffer for reading
mLBuffer->BindForReading();
mTargetRenderSystem->ClearFrameBuffer(FBT_COLOUR | FBT_DEPTH, Math::ColourValue::WHITE);
// Iterate over all the Renderables in the previously built RenderQueue
RenderableList* visibles = mSceneManager->GetRenderQueue()->GetRenderables();
// Set shader params here
// [...]
// Get the transformation info from the node the renderable is attached to
for (RenderableList::iterator it = visibles->begin(); it != visibles->end(); ++it)
{
Renderable* renderable = *it;
Material* mat = renderable->GetMaterial();
// Set texture units
if (mat)
{
for (unsigned short i = 0; i < mat->GetTextureUnitCount(); ++i)
{
const TextureUnit* unit = mat->GetTextureUnit(i);
GLTexture* t = static_cast<GLTexture*>(unit->GetTexture());
glActiveTexture(GL_TEXTURE1); // This is needed because the first texture map slot is hold by the LBuffer!
glBindTexture(GL_TEXTURE_2D, t->GetGLId());
}
}
mMaterialStageVS->Update();
mMaterialStageFS->Update();
// Render the object
RenderOperation rop;
renderable->GetRenderOperation(rop);
mTargetRenderSystem->Render(rop);
}
}
NVIDIA CG シェーダー:
// Vertex shader for Deferred Rendering geometry stage.
float4x4 gWorldMtx;
float4x4 gViewMtx;
float4x4 gProjectionMtx;
struct a2v
{
float3 position : POSITION;
float3 normal : NORMAL;
float2 texCoord : TEXCOORD0;
};
struct v2f
{
float4 position : POSITION;
float3 normal : TEXCOORD0;
float3 wPosition : TEXCOORD1;
float2 texCoord : TEXCOORD2;
};
v2f PPL_geometry_stage_vs(a2v IN)
{
v2f OUT;
// Transform to world space
OUT.wPosition = mul(gWorldMtx, float4(IN.position, 1.0f)).xyz;
OUT.normal = mul(gWorldMtx, float4(IN.normal, 0.0f)).xyz;
// Transform to homogeneous clip space
OUT.position = mul(gViewMtx, float4(OUT.wPosition, 1.0f));
OUT.position = mul(gProjectionMtx, OUT.position);
OUT.texCoord = IN.texCoord;
return OUT;
}
// Fragment shader for Pre-pass Lighing geometry stage.
struct f2a
{
float4 position : COLOR0;
float4 normal : COLOR1;
};
f2a PPL_geometry_stage_fs(v2f IN)
{
f2a OUT;
OUT.position = float4(IN.wPosition, 1.0f);
OUT.normal = float4(normalize(IN.normal), 1.0f);
return OUT;
}
// Vertex shader for Pre-pass lighing light stage.
float4x4 gWorldMtx;
float4x4 gViewMtx;
float4x4 gProjectionMtx;
struct a2v
{
float3 position : POSITION;
};
struct v2f
{
float4 position : POSITION;
float4 lightPos : TEXCOORD0;
};
v2f PPL_light_stage_vs(a2v IN)
{
v2f OUT;
float4x4 wv = mul(gWorldMtx, gViewMtx);
float4x4 wvp = mul(gViewMtx, gProjectionMtx);
wvp = mul(wvp, gWorldMtx);
// Only transforms position to world space
OUT.position = mul(wvp, float4(IN.position, 1.0f));
// Copy light position to calculate fragment coordinate
OUT.lightPos = OUT.position;
return OUT;
}
// Fragment shader for Pre-pass lighing light stage.
// Light structures
struct BaseLight
{
float3 color;
float ambientIntensity;
float diffuseIntensity;
};
struct DirectionalLight
{
struct BaseLight base;
float3 direction;
};
struct Attenuation
{
float constant;
float linearr;
float quadratic;
};
struct PointLight
{
struct BaseLight base;
float3 position;
Attenuation atten;
};
struct SpotLight
{
struct PointLight base;
float3 direction;
float cutoff;
};
// G-Buffer textures
sampler2D gPositionMap : TEXUNIT0;
sampler2D gNormalMap : TEXUNIT1;
// Light variables
float3 gEyePosition;
DirectionalLight gDirectionalLight;
PointLight gPointLight;
SpotLight gSpotLight;
int gLightType;
float gSpecularPower;
float4 PPL_light_stage_point_light_fs(v2f IN) : COLOR0
{
// Get fragment coordinate, from NDC space [-1, 1] to [0, 1].
float2 fragcoord = ((IN.lightPos.xy / IN.lightPos.w) + 1.0f) / 2.0f;
// Calculate lighting with G-Buffer textures
float3 position = tex2D(gPositionMap, fragcoord).xyz;
float3 normal = tex2D(gNormalMap, fragcoord).xyz;
normal = normalize(normal);
// Attenuation
float3 lightDirection = position - gPointLight.position;
float dist = length(lightDirection);
float att = gPointLight.atten.constant + gPointLight.atten.linearr * dist + gPointLight.atten.quadratic * dist * dist;
// NL
lightDirection = normalize(lightDirection);
float NL = dot(normal, -lightDirection);
// Specular (Blinn-Phong)
float specular = 0.0f;
//if (NL > 0)
//{
// float3 vertexToEye = normalize(gEyePosition - position);
// float3 lightReflect = normalize(reflect(lightDirection, normal));
// specular = pow(saturate(dot(vertexToEye, lightReflect)), gSpecularPower);
//}
// Apply attenuation to NL
NL = NL / min(1.0, att);
float3 lightColor = gPointLight.base.color * gPointLight.base.diffuseIntensity;
return float4(lightColor.r, lightColor.g, lightColor.b, 1.0f) * NL;
}
// Vertex shader for Pre-pass lighing material stage.
float4x4 gWorldMtx;
float4x4 gViewMtx;
float4x4 gProjectionMtx;
struct a2v
{
float3 position : POSITION;
float3 normal : NORMAL;
float2 texcoord : TEXCOORD0;
};
struct v2f
{
float4 position : POSITION;
float2 texcoord : TEXCOORD0;
float3 normal : TEXCOORD1;
float4 projPos : TEXCOORD2;
};
v2f PPL_material_stage_vs(a2v IN)
{
v2f OUT;
float4x4 wv = mul(gWorldMtx, gViewMtx);
float4x4 wvp = mul(gViewMtx, gProjectionMtx);
wvp = mul(wvp, gWorldMtx);
// Only transforms position to world space
OUT.position = mul(wvp, float4(IN.position, 1.0f));
// Normal (It's not necessary, but i have to see if it influences the execution)
OUT.normal = mul(gWorldMtx, float4(IN.normal, 0.0f)).xyz;
// Copy texture coordinates
OUT.texcoord = IN.texcoord;
// Copy projected position to get the fragment coordinate
OUT.projPos = OUT.position;
return OUT;
}
// Fragment shader for Pre-pass lighing material stage.
// L-buffer texture
sampler2D gLightMap : TEXUNIT0;
// Object's material specific textures
sampler2D gColorMap : TEXUNIT1;
float4 PPL_material_stage_fs(v2f IN) : COLOR0
{
float2 fragcoord = ((IN.projPos.xy / IN.projPos.w) + 1.0f) / 2.0f;
// Get all light contributions for this pixel
float4 light = tex2D(gLightMap, fragcoord);
float3 combined = saturate(light.rgb);// + light.aaa);
// Get material albedo from texture map
float4 diffuse = tex2D(gColorMap, IN.texcoord);
return float4(combined, 1.0f) * diffuse;
}
助言がありますか?