Initial vertex lighting support.

This commit is contained in:
Lasse Öörni 2011-11-21 00:17:52 +00:00
parent 67a0650cdb
commit a51afb0631
28 changed files with 480 additions and 142 deletions

View File

@ -11,6 +11,7 @@ Scene@ testScene;
Node@ cameraNode;
Camera@ camera;
Node@ cameraLightNode;
Light@ cameraLight;
float yaw = 0.0;
float pitch = 0.0;
float objectangle = 0.0;
@ -303,7 +304,7 @@ void CreateCamera()
cameraNode.position = Vector3(-50, 2, -50);
cameraLightNode = cameraNode.CreateChild("CameraLight");
Light@ cameraLight = cameraLightNode.CreateComponent("Light");
cameraLight = cameraLightNode.CreateComponent("Light");
cameraLight.lightType = LIGHT_SPOT;
cameraLight.range = 50;
cameraLight.color = Color(2, 2, 2);
@ -416,6 +417,9 @@ void HandleUpdate(StringHash eventType, VariantMap& eventData)
cameraLightNode.parent = testScene;
}
}
if (input.keyPress['V'])
cameraLight.perVertex = !cameraLight.perVertex;
if (input.keyPress['C'])
camera.orthographic = !camera.orthographic;

View File

@ -1664,6 +1664,7 @@ Properties:<br>
- uint maxLights
- BoundingBox& worldBoundingBox (readonly)
- LightType lightType
- bool perVertex
- Color& color
- float specularIntensity
- float range

View File

@ -467,6 +467,8 @@ static void RegisterLight(asIScriptEngine* engine)
RegisterDrawable<Light>(engine, "Light");
engine->RegisterObjectMethod("Light", "void set_lightType(LightType)", asMETHOD(Light, SetLightType), asCALL_THISCALL);
engine->RegisterObjectMethod("Light", "LightType get_lightType() const", asMETHOD(Light, GetLightType), asCALL_THISCALL);
engine->RegisterObjectMethod("Light", "void set_perVertex(bool)", asMETHOD(Light, SetPerVertex), asCALL_THISCALL);
engine->RegisterObjectMethod("Light", "bool get_perVertex() const", asMETHOD(Light, GetPerVertex), asCALL_THISCALL);
engine->RegisterObjectMethod("Light", "void set_color(const Color&in)", asMETHOD(Light, SetColor), asCALL_THISCALL);
engine->RegisterObjectMethod("Light", "const Color& get_color() const", asMETHOD(Light, GetColor), asCALL_THISCALL);
engine->RegisterObjectMethod("Light", "void set_specularIntensity(float)", asMETHOD(Light, SetSpecularIntensity), asCALL_THISCALL);

View File

@ -172,7 +172,7 @@ void Batch::Prepare(Graphics* graphics, Renderer* renderer, bool setModelTransfo
if (graphics->NeedParameterUpdate(PSP_AMBIENTSTARTCOLOR, zone_))
graphics->SetShaderParameter(PSP_AMBIENTSTARTCOLOR, zone_->GetAmbientStartColor().ToVector4());
if (graphics->NeedParameterUpdate(PSP_AMBIENTENDCOLOR, zone_))
graphics->SetShaderParameter(PSP_AMBIENTENDCOLOR, zone_->GetAmbientEndColor().ToVector4());
graphics->SetShaderParameter(PSP_AMBIENTENDCOLOR, zone_->GetAmbientEndColor().ToVector4() - zone_->GetAmbientStartColor().ToVector4());
// If the pass is additive, override fog color to black so that shaders do not need a separate additive path
BlendMode blend = pass_->GetBlendMode();
@ -246,6 +246,56 @@ void Batch::Prepare(Graphics* graphics, Renderer* renderer, bool setModelTransfo
graphics->SetShaderParameter(VSP_SPOTPROJ, texAdjust * spotProj * spotView.Inverse());
}
if (graphics->NeedParameterUpdate(VSP_VERTEXLIGHTS, lightQueue_))
{
Vector4 vertexLights[MAX_VERTEX_LIGHTS * 3];
const PODVector<Light*>& lights = lightQueue_->vertexLights_;
for (unsigned i = 0; i < lights.Size(); ++i)
{
Light* vertexLight = lights[i];
LightType type = vertexLight->GetLightType();
// Attenuation
float invRange, cutoff, invCutoff;
if (type == LIGHT_DIRECTIONAL)
invRange = 0.0f;
else
invRange = 1.0f / max(vertexLight->GetRange(), M_EPSILON);
if (type == LIGHT_SPOT)
{
cutoff = cosf(vertexLight->GetFov() * 0.5f * M_DEGTORAD);
invCutoff = 1.0f / (1.0f - cutoff);
}
else
{
cutoff = -1.0f;
invCutoff = 1.0f;
}
// Color
float fade = 1.0f;
float fadeEnd = vertexLight->GetDrawDistance();
float fadeStart = vertexLight->GetFadeDistance();
// Do fade calculation for light if both fade & draw distance defined
if (vertexLight->GetLightType() != LIGHT_DIRECTIONAL && fadeEnd > 0.0f && fadeStart > 0.0f && fadeStart < fadeEnd)
fade = Min(1.0f - (vertexLight->GetDistance() - fadeStart) / (fadeEnd - fadeStart), 1.0f);
Color color = vertexLight->GetColor() * fade;
vertexLights[i * 3] = Vector4(color.r_, color.g_, color.b_, invRange);
// Direction
vertexLights[i * 3 + 1] = Vector4(-(vertexLight->GetNode()->GetWorldDirection()), cutoff);
// Position
vertexLights[i * 3 + 2] = Vector4(vertexLight->GetWorldPosition(), invCutoff);
}
if (lights.Size())
graphics->SetShaderParameter(VSP_VERTEXLIGHTS, vertexLights[0].GetData(), lights.Size() * 3 * 4);
}
if (graphics->NeedParameterUpdate(PSP_LIGHTCOLOR, light))
{
float fade = 1.0f;

View File

@ -290,7 +290,7 @@ struct ShadowBatchQueue
/// Queue for light related draw calls
struct LightBatchQueue
{
/// Light drawable.
/// Per-pixel light.
Light* light_;
/// Lit geometry draw calls.
BatchQueue litBatches_;
@ -298,4 +298,6 @@ struct LightBatchQueue
Texture2D* shadowMap_;
/// Shadow map split queues.
Vector<ShadowBatchQueue> shadowSplits_;
/// Per-vertex lights.
PODVector<Light*> vertexLights_;
};

View File

@ -40,6 +40,9 @@ OBJECTTYPESTATIC(Drawable);
Drawable::Drawable(Context* context) :
Component(context),
octant_(0),
viewFrame_(0),
viewCamera_(0),
firstLight_(0),
drawDistance_(0.0f),
shadowDistance_(0.0f),
lodBias_(1.0f),
@ -52,9 +55,6 @@ Drawable::Drawable(Context* context) :
lodDistance_(0.0f),
sortValue_(0.0f),
viewFrameNumber_(0),
viewFrame_(0),
viewCamera_(0),
firstLight_(0),
drawableFlags_(0),
visible_(true),
castShadows_(false),
@ -219,6 +219,7 @@ void Drawable::ClearLights()
basePassFlags_[i] = 0;
firstLight_ = 0;
lights_.Clear();
vertexLights_.Clear();
}
void Drawable::AddLight(Light* light)
@ -228,6 +229,11 @@ void Drawable::AddLight(Light* light)
lights_.Push(light);
}
void Drawable::AddVertexLight(Light* light)
{
vertexLights_.Push(light);
}
void Drawable::LimitLights()
{
// Maximum lights value 0 means unlimited
@ -244,6 +250,17 @@ void Drawable::LimitLights()
lights_.Resize(maxLights_);
}
void Drawable::LimitVertexLights()
{
for (unsigned i = 0; i < vertexLights_.Size(); ++i)
vertexLights_[i]->SetIntensitySortValue(GetWorldBoundingBox());
Sort(vertexLights_.Begin(), vertexLights_.End(), CompareDrawables);
if (vertexLights_.Size() > MAX_VERTEX_LIGHTS)
vertexLights_.Resize(MAX_VERTEX_LIGHTS);
}
void Drawable::SetBasePass(unsigned batchIndex)
{
unsigned index = batchIndex >> 5;

View File

@ -37,6 +37,7 @@ static const unsigned DEFAULT_LIGHTMASK = M_MAX_UNSIGNED;
static const unsigned DEFAULT_SHADOWMASK = M_MAX_UNSIGNED;
static const unsigned DEFAULT_ZONEMASK = M_MAX_UNSIGNED;
static const int DRAWABLES_PER_WORK_ITEM = 16;
static const int MAX_VERTEX_LIGHTS = 4;
struct Batch;
class Camera;
@ -170,10 +171,14 @@ public:
void MarkInView(const FrameInfo& frame, bool mainView = true);
/// Clear lights and base pass flags for a new frame.
void ClearLights();
/// Add a light.
/// Add a per-pixel light.
void AddLight(Light* light);
/// Sort and limit lights to maximum allowed.
/// Add a per-vertex light.
void AddVertexLight(Light* light);
/// Sort and limit per-pixel lights to maximum allowed.
void LimitLights();
/// Sort and limit per-vertex lights to maximum allowed.
void LimitVertexLights();
/// %Set base pass flag for a batch.
void SetBasePass(unsigned batchIndex);
/// Return octree octant.
@ -194,9 +199,11 @@ public:
bool IsInView(const FrameInfo& frame, bool mainView = true) const { return viewFrameNumber_ == frame.frameNumber_ && viewFrame_ == &frame && (!mainView || viewCamera_ == frame.camera_); }
/// Return whether has a base pass.
bool HasBasePass(unsigned batchIndex) const;
/// Return lights.
/// Return per-pixel lights.
const PODVector<Light*>& GetLights() const { return lights_; }
/// Return the first added light.
/// Return per-vertex lights.
const PODVector<Light*>& GetVertexLights() const { return vertexLights_; }
/// Return the first added per-pixel light.
Light* GetFirstLight() const { return firstLight_; }
protected:
@ -217,6 +224,22 @@ protected:
Octant* octant_;
/// World bounding box.
BoundingBox worldBoundingBox_;
/// Base pass flags per batch index.
PODVector<unsigned> basePassFlags_;
/// Last view's frameinfo. Not safe to dereference.
const FrameInfo* viewFrame_;
/// Last view's camera. Not safe to dereference.
Camera* viewCamera_;
/// Per-pixel lights affecting this drawable.
PODVector<Light*> lights_;
/// Per-vertex lights affecting this drawable.
PODVector<Light*> vertexLights_;
/// First per-pixel light added this frame.
Light* firstLight_;
/// Current zone.
WeakPtr<Zone> zone_;
/// Previous zone.
WeakPtr<Zone> lastZone_;
/// Draw distance.
float drawDistance_;
/// Shadow distance.
@ -241,20 +264,6 @@ protected:
float sortValue_;
/// Last visible frame number.
unsigned viewFrameNumber_;
/// Base pass flags per batch index.
PODVector<unsigned> basePassFlags_;
/// Last view's frameinfo. Not safe to dereference.
const FrameInfo* viewFrame_;
/// Last view's camera. Not safe to dereference.
Camera* viewCamera_;
/// Lights affecting this drawable.
PODVector<Light*> lights_;
/// First light added this frame.
Light* firstLight_;
/// Current zone.
WeakPtr<Zone> zone_;
/// Previous zone.
WeakPtr<Zone> lastZone_;
/// Drawable flags.
unsigned char drawableFlags_;
/// Visible flag.

View File

@ -43,6 +43,7 @@ StringHash VSP_VIEWRIGHTVECTOR("ViewRightVector");
StringHash VSP_VIEWUPVECTOR("ViewUpVector");
StringHash VSP_ZONE("Zone");
StringHash VSP_SKINMATRICES("SkinMatrices");
StringHash VSP_VERTEXLIGHTS("VertexLights");
StringHash PSP_AMBIENTSTARTCOLOR("AmbientStartColor");
StringHash PSP_AMBIENTENDCOLOR("AmbientEndColor");
StringHash PSP_FOGCOLOR("FogColor");

View File

@ -211,6 +211,7 @@ extern StringHash VSP_VIEWRIGHTVECTOR;
extern StringHash VSP_VIEWUPVECTOR;
extern StringHash VSP_ZONE;
extern StringHash VSP_SKINMATRICES;
extern StringHash VSP_VERTEXLIGHTS;
extern StringHash PSP_AMBIENTSTARTCOLOR;
extern StringHash PSP_AMBIENTENDCOLOR;
extern StringHash PSP_FOGCOLOR;

View File

@ -82,6 +82,7 @@ OBJECTTYPESTATIC(Light);
Light::Light(Context* context) :
Drawable(context),
lightType_(DEFAULT_LIGHTTYPE),
perVertex_(false),
specularIntensity_(0.0f),
range_(DEFAULT_RANGE),
fov_(DEFAULT_FOV),
@ -115,6 +116,7 @@ void Light::RegisterObject(Context* context)
ACCESSOR_ATTRIBUTE(Light, VAR_RESOURCEREF, "Attenuation Texture", GetRampTextureAttr, SetRampTextureAttr, ResourceRef, ResourceRef(Texture2D::GetTypeStatic()), AM_DEFAULT);
ACCESSOR_ATTRIBUTE(Light, VAR_RESOURCEREF, "Light Shape Texture", GetShapeTextureAttr, SetShapeTextureAttr, ResourceRef, ResourceRef(Texture2D::GetTypeStatic()), AM_DEFAULT);
ATTRIBUTE(Light, VAR_BOOL, "Is Visible", visible_, true, AM_DEFAULT);
ATTRIBUTE(Light, VAR_BOOL, "Per Vertex", perVertex_, false, AM_DEFAULT);
ATTRIBUTE(Light, VAR_BOOL, "Cast Shadows", castShadows_, false, AM_DEFAULT);
ACCESSOR_ATTRIBUTE(Light, VAR_FLOAT, "Draw Distance", GetDrawDistance, SetDrawDistance, float, 0.0f, AM_DEFAULT);
ACCESSOR_ATTRIBUTE(Light, VAR_FLOAT, "Fade Distance", GetFadeDistance, SetFadeDistance, float, 0.0f, AM_DEFAULT);
@ -261,6 +263,11 @@ void Light::SetLightType(LightType type)
OnMarkedDirty(node_);
}
void Light::SetPerVertex(bool enable)
{
perVertex_ = enable;
}
void Light::SetColor(const Color& color)
{
color_ = color;
@ -426,6 +433,10 @@ void Light::SetIntensitySortValue(float distance)
sortValue_ = Max(distance, M_MIN_NEARCLIP) / (color_.Intensity() + M_EPSILON);
else
sortValue_ = M_EPSILON / (color_.Intensity() + M_EPSILON);
// Additionally, give priority to vertex lights so that vertex light base passes can be determined before per pixel lights
if (perVertex_)
sortValue_ -= M_LARGE_VALUE;
}
void Light::SetIntensitySortValue(const BoundingBox& box)

View File

@ -167,6 +167,8 @@ public:
/// %Set light type.
void SetLightType(LightType type);
/// %Set vertex lighting mode.
void SetPerVertex(bool enable);
/// %Set color.
void SetColor(const Color& color);
/// %Set specular intensity.
@ -200,6 +202,8 @@ public:
/// Return light type.
LightType GetLightType() const { return lightType_; }
/// Return vertex lighting mode.
bool GetPerVertex() const { return perVertex_; }
/// Return color.
const Color& GetColor() const { return color_; }
/// Return specular intensity.
@ -212,7 +216,7 @@ public:
float GetAspectRatio() const { return aspectRatio_; }
/// Return fade start distance.
float GetFadeDistance() const { return fadeDistance_; }
/// Return shadow fade start distance.
/// Return shadow fade start distance.
float GetShadowFadeDistance() const { return shadowFadeDistance_; }
/// Return shadow depth bias parameters.
const BiasParameters& GetShadowBias() const { return shadowBias_; }
@ -256,6 +260,8 @@ protected:
private:
/// Light type.
LightType lightType_;
/// Per-vertex lighting flag.
bool perVertex_;
/// Color.
Color color_;
/// Specular intensity.

View File

@ -87,7 +87,13 @@ bool ShaderVariation::Create()
// Prepend the defines to the shader code
String shaderCode;
for (unsigned i = 0; i < defines_.Size(); ++i)
shaderCode += "#define " + defines_[i] + "\n";
{
Vector<String> nameAndValue = defines_[i].Split('=');
if (nameAndValue.Size() < 2)
shaderCode += "#define " + defines_[i] + "\n";
else
shaderCode += "#define " + nameAndValue[0] + " " + nameAndValue[1] + "\n";
}
if (!defines_.Empty())
shaderCode += "\n";
shaderCode += String(sourceCode_.Get(), sourceCodeLength_);

View File

@ -201,6 +201,15 @@ static const String lightVSVariations[] =
"PointSpecShadow"
};
static const String vertexLightVSVariations[] =
{
"",
"1VL",
"2VL",
"3VL",
"4VL"
};
static const String lightPSVariations[] =
{
"Dir",
@ -980,8 +989,27 @@ void Renderer::SetBatchShaders(Batch& batch, Technique* technique, Pass* pass, b
}
else
{
unsigned vsi = geomType;
batch.vertexShader_ = vertexShaders[vsi];
if (type == PASS_BASE)
{
unsigned numVertexLights = 0;
if (batch.lightQueue_)
numVertexLights = batch.lightQueue_->vertexLights_.Size();
unsigned vsi = geomType * MAX_VERTEXLIGHT_VS_VARIATIONS + numVertexLights;
batch.vertexShader_ = vertexShaders[vsi];
// If vertex lights variations do not exist, try without them
if (!batch.vertexShader_)
{
unsigned vsi = geomType * MAX_VERTEXLIGHT_VS_VARIATIONS;
batch.vertexShader_ = vertexShaders[vsi];
}
}
else
{
unsigned vsi = geomType;
batch.vertexShader_ = vertexShaders[vsi];
}
batch.pixelShader_ = pixelShaders[0];
}
}
@ -1184,10 +1212,24 @@ void Renderer::LoadPassShaders(Technique* technique, PassType type, bool allowSh
}
else
{
vertexShaders.Resize(MAX_GEOMETRYTYPES);
if (type == PASS_BASE)
{
vertexShaders.Resize(MAX_VERTEXLIGHT_VS_VARIATIONS * MAX_GEOMETRYTYPES);
for (unsigned j = 0; j < MAX_GEOMETRYTYPES * MAX_VERTEXLIGHT_VS_VARIATIONS; ++j)
{
unsigned g = j / MAX_VERTEXLIGHT_VS_VARIATIONS;
unsigned l = j % MAX_VERTEXLIGHT_VS_VARIATIONS;
vertexShaders[j] = GetVertexShader(vertexShaderName + vertexLightVSVariations[l] + geometryVSVariations[g], g != 0 || l != 0);
}
}
else
{
vertexShaders.Resize(MAX_GEOMETRYTYPES);
for (unsigned j = 0; j < MAX_GEOMETRYTYPES; ++j)
vertexShaders[j] = GetVertexShader(vertexShaderName + geometryVSVariations[j], j != 0);
}
pixelShaders.Resize(1);
for (unsigned j = 0; j < MAX_GEOMETRYTYPES; ++j)
vertexShaders[j] = GetVertexShader(vertexShaderName + geometryVSVariations[j], j != 0);
pixelShaders[0] = GetPixelShader(pixelShaderName);
}

View File

@ -71,6 +71,17 @@ enum LightVSVariation
MAX_LIGHT_VS_VARIATIONS
};
/// Per-vertex light vertex shader variations.
enum VertexLightVSVariation
{
VLVS_NOLIGHTS = 0,
VLVS_1LIGHT,
VLVS_2LIGHTS,
VLVS_3LIGHTS,
VLVS_4LIGHTS,
MAX_VERTEXLIGHT_VS_VARIATIONS
};
/// Light pixel shader variations.
enum LightPSVariation
{

View File

@ -242,6 +242,7 @@ void View::Update(const FrameInfo& frame)
alphaQueue_.Clear();
postAlphaQueue_.Clear();
lightQueues_.Clear();
vertexLightQueues_.Clear();
// Do not update if camera projection is illegal
// (there is a possibility of crash if occlusion is used and it can not clip properly)
@ -520,93 +521,108 @@ void View::GetBatches()
PROFILE(GetLightBatches);
Light* light = query.light_;
unsigned shadowSplits = query.numSplits_;
// Initialize light queue. Store light-to-queue mapping so that the queue can be found later
lightQueues_.Resize(lightQueues_.Size() + 1);
LightBatchQueue& lightQueue = lightQueues_.Back();
lightQueueMapping_[light] = &lightQueue;
lightQueue.light_ = light;
lightQueue.litBatches_.Clear();
// Allocate shadow map now
lightQueue.shadowMap_ = 0;
if (shadowSplits > 0)
// Per-pixel light
if (!light->GetPerVertex())
{
lightQueue.shadowMap_ = renderer_->GetShadowMap(light, camera_, width_, height_);
// If did not manage to get a shadow map, convert the light to unshadowed
if (!lightQueue.shadowMap_)
shadowSplits = 0;
}
// Setup shadow batch queues
lightQueue.shadowSplits_.Resize(shadowSplits);
for (unsigned j = 0; j < shadowSplits; ++j)
{
ShadowBatchQueue& shadowQueue = lightQueue.shadowSplits_[j];
Camera* shadowCamera = query.shadowCameras_[j];
shadowQueue.shadowCamera_ = shadowCamera;
shadowQueue.nearSplit_ = query.shadowNearSplits_[j];
shadowQueue.farSplit_ = query.shadowFarSplits_[j];
unsigned shadowSplits = query.numSplits_;
// Setup the shadow split viewport and finalize shadow camera parameters
shadowQueue.shadowViewport_ = GetShadowMapViewport(light, j, lightQueue.shadowMap_);
FinalizeShadowCamera(shadowCamera, light, shadowQueue.shadowViewport_, query.shadowCasterBox_[j]);
// Initialize light queue. Store light-to-queue mapping so that the queue can be found later
lightQueues_.Resize(lightQueues_.Size() + 1);
LightBatchQueue& lightQueue = lightQueues_.Back();
lightQueueMapping_[light] = &lightQueue;
lightQueue.light_ = light;
lightQueue.litBatches_.Clear();
// Loop through shadow casters
for (PODVector<Drawable*>::ConstIterator k = query.shadowCasters_.Begin() + query.shadowCasterBegin_[j];
k < query.shadowCasters_.Begin() + query.shadowCasterEnd_[j]; ++k)
// Allocate shadow map now
lightQueue.shadowMap_ = 0;
if (shadowSplits > 0)
{
Drawable* drawable = *k;
if (!drawable->IsInView(frame_, false))
{
drawable->MarkInView(frame_, false);
allGeometries_.Push(drawable);
}
lightQueue.shadowMap_ = renderer_->GetShadowMap(light, camera_, width_, height_);
// If did not manage to get a shadow map, convert the light to unshadowed
if (!lightQueue.shadowMap_)
shadowSplits = 0;
}
// Setup shadow batch queues
lightQueue.shadowSplits_.Resize(shadowSplits);
for (unsigned j = 0; j < shadowSplits; ++j)
{
ShadowBatchQueue& shadowQueue = lightQueue.shadowSplits_[j];
Camera* shadowCamera = query.shadowCameras_[j];
shadowQueue.shadowCamera_ = shadowCamera;
shadowQueue.nearSplit_ = query.shadowNearSplits_[j];
shadowQueue.farSplit_ = query.shadowFarSplits_[j];
unsigned numBatches = drawable->GetNumBatches();
// Setup the shadow split viewport and finalize shadow camera parameters
shadowQueue.shadowViewport_ = GetShadowMapViewport(light, j, lightQueue.shadowMap_);
FinalizeShadowCamera(shadowCamera, light, shadowQueue.shadowViewport_, query.shadowCasterBox_[j]);
for (unsigned l = 0; l < numBatches; ++l)
// Loop through shadow casters
for (PODVector<Drawable*>::ConstIterator k = query.shadowCasters_.Begin() + query.shadowCasterBegin_[j];
k < query.shadowCasters_.Begin() + query.shadowCasterEnd_[j]; ++k)
{
Batch shadowBatch;
drawable->GetBatch(shadowBatch, frame_, l);
Drawable* drawable = *k;
if (!drawable->IsInView(frame_, false))
{
drawable->MarkInView(frame_, false);
allGeometries_.Push(drawable);
}
Technique* tech = GetTechnique(drawable, shadowBatch.material_);
if (!shadowBatch.geometry_ || !tech)
continue;
unsigned numBatches = drawable->GetNumBatches();
Pass* pass = tech->GetPass(PASS_SHADOW);
// Skip if material has no shadow pass
if (!pass)
continue;
// Fill the rest of the batch
shadowBatch.camera_ = shadowCamera;
shadowBatch.zone_ = GetZone(drawable);
shadowBatch.lightQueue_ = &lightQueue;
FinalizeBatch(shadowBatch, tech, pass);
shadowQueue.shadowBatches_.AddBatch(shadowBatch);
for (unsigned l = 0; l < numBatches; ++l)
{
Batch shadowBatch;
drawable->GetBatch(shadowBatch, frame_, l);
Technique* tech = GetTechnique(drawable, shadowBatch.material_);
if (!shadowBatch.geometry_ || !tech)
continue;
Pass* pass = tech->GetPass(PASS_SHADOW);
// Skip if material has no shadow pass
if (!pass)
continue;
// Fill the rest of the batch
shadowBatch.camera_ = shadowCamera;
shadowBatch.zone_ = GetZone(drawable);
shadowBatch.lightQueue_ = &lightQueue;
FinalizeBatch(shadowBatch, tech, pass);
shadowQueue.shadowBatches_.AddBatch(shadowBatch);
}
}
}
}
// Loop through lit geometries
for (PODVector<Drawable*>::ConstIterator j = query.litGeometries_.Begin(); j != query.litGeometries_.End(); ++j)
{
Drawable* drawable = *j;
drawable->AddLight(light);
// If drawable limits maximum lights, only record the light, and check maximum count / build batches later
if (!drawable->GetMaxLights())
GetLitBatches(drawable, lightQueue);
else
maxLightsDrawables_.Insert(drawable);
// Loop through lit geometries
for (PODVector<Drawable*>::ConstIterator j = query.litGeometries_.Begin(); j != query.litGeometries_.End(); ++j)
{
Drawable* drawable = *j;
drawable->AddLight(light);
// If drawable limits maximum lights, only record the light, and check maximum count / build batches later
if (!drawable->GetMaxLights())
GetLitBatches(drawable, lightQueue);
else
maxLightsDrawables_.Insert(drawable);
}
}
// Per-vertex light
else
{
// Loop through lit geometries
for (PODVector<Drawable*>::ConstIterator j = query.litGeometries_.Begin(); j != query.litGeometries_.End(); ++j)
{
Drawable* drawable = *j;
drawable->AddVertexLight(light);
}
}
}
}
// Process drawables with limited light count
// Process drawables with limited per-pixel light count
if (maxLightsDrawables_.Size())
{
PROFILE(GetMaxLightsBatches);
@ -650,7 +666,7 @@ void View::GetBatches()
if (!renderTarget_ && baseBatch.material_ && baseBatch.material_->GetAuxViewFrameNumber() != frame_.frameNumber_)
CheckMaterialForAuxView(baseBatch.material_);
// If object already has a lit base pass, can skip the unlit base pass
// If object already has a pixel lit base pass, can skip the unlit / vertex lit base pass
if (drawable->HasBasePass(j))
continue;
@ -661,10 +677,28 @@ void View::GetBatches()
Pass* pass = 0;
// Check for unlit base pass
// Check for unlit or vertex lit base pass
pass = tech->GetPass(PASS_BASE);
if (pass)
{
// Check for vertex lights now
const PODVector<Light*>& vertexLights = drawable->GetVertexLights();
if (!vertexLights.Empty())
{
drawable->LimitVertexLights();
// Find a vertex light queue. If not found, create new
unsigned hash = GetVertexLightQueueHash(vertexLights);
HashMap<unsigned, LightBatchQueue>::Iterator i = vertexLightQueues_.Find(hash);
if (i == vertexLightQueues_.End())
{
vertexLightQueues_[hash].vertexLights_ = vertexLights;
i = vertexLightQueues_.Find(hash);
}
baseBatch.lightQueue_ = &(i->second_);
}
if (pass->GetBlendMode() == BLEND_REPLACE)
{
FinalizeBatch(baseBatch, tech, pass);
@ -795,7 +829,7 @@ void View::GetLitBatches(Drawable* drawable, LightBatchQueue& lightQueue)
Pass* pass = 0;
// Check for lit base pass. Because it uses the replace blend mode, it must be ensured to be the first light
if (light == firstLight && !drawable->HasBasePass(i))
if (light == firstLight && drawable->GetVertexLights().Empty() && !drawable->HasBasePass(i))
{
pass = tech->GetPass(PASS_LITBASE);
if (pass)
@ -991,7 +1025,7 @@ void View::ProcessLight(LightQueryResult& query, unsigned threadIndex)
LightType type = light->GetLightType();
// Check if light should be shadowed
bool isShadowed = drawShadows_ && light->GetCastShadows() && light->GetShadowIntensity() < 1.0f;
bool isShadowed = drawShadows_ && light->GetCastShadows() && !light->GetPerVertex() && light->GetShadowIntensity() < 1.0f;
// If shadow distance non-zero, check it
if (isShadowed && light->GetShadowDistance() > 0.0f && light->GetDistance() > light->GetShadowDistance())
isShadowed = false;
@ -1644,6 +1678,14 @@ unsigned View::GetShadowMask(Drawable* drawable)
return drawable->GetShadowMask() & GetZone(drawable)->GetShadowMask();
}
unsigned View::GetVertexLightQueueHash(const PODVector<Light*>& vertexLights)
{
unsigned hash = 0;
for (PODVector<Light*>::ConstIterator i = vertexLights.Begin(); i != vertexLights.End(); ++i)
hash += (unsigned)(*i);
return hash;
}
Technique* View::GetTechnique(Drawable* drawable, Material*& material)
{
if (!material)

View File

@ -121,7 +121,7 @@ private:
void GetBatches();
/// Update geometries and sort batches.
void UpdateGeometries();
/// Get lit batches for a certain light and drawable.
/// Get pixel lit batches for a certain light and drawable.
void GetLitBatches(Drawable* drawable, LightBatchQueue& lightQueue);
/// Render batches.
void RenderBatches();
@ -161,6 +161,8 @@ private:
unsigned GetLightMask(Drawable* drawable);
/// Return the drawable's shadow mask, considering also its zone.
unsigned GetShadowMask(Drawable* drawable);
/// Return hash code for a vertex light queue.
unsigned GetVertexLightQueueHash(const PODVector<Light*>& vertexLights);
/// Return material technique, considering the drawable's LOD distance.
Technique* GetTechnique(Drawable* drawable, Material*& material);
/// Check if material should render an auxiliary view (if it has a camera attached.)
@ -256,8 +258,10 @@ private:
BatchQueue postAlphaQueue_;
/// Intermediate light processing results.
Vector<LightQueryResult> lightQueryResults_;
/// Light queues.
/// Per-pixel light queues.
List<LightBatchQueue> lightQueues_;
/// Per-vertex light queues.
HashMap<unsigned, LightBatchQueue> vertexLightQueues_;
/// Current stencil value for light optimization.
unsigned char lightStencilValue_;
/// Camera zone's override flag.

View File

@ -3,16 +3,16 @@
#include "Fog.frag"
#include "Lighting.frag"
varying vec2 vTexCoord;
varying vec4 vTexCoord;
#ifdef VERTEXCOLOR
varying vec4 vColor;
#endif
varying vec2 vZonePosDepth;
varying vec3 vVertexLighting;
void main()
{
#ifdef DIFFMAP
vec4 diffColor = cMatDiffColor * texture2D(sDiffMap, vTexCoord);
vec4 diffColor = cMatDiffColor * texture2D(sDiffMap, vTexCoord.xy);
#else
vec4 diffColor = cMatDiffColor;
#endif
@ -21,5 +21,7 @@ void main()
diffColor *= vColor;
#endif
gl_FragColor = vec4(GetFog(GetAmbient(vZonePosDepth.x) * diffColor.rgb, vZonePosDepth.y), diffColor.a);
vec3 finalColor = (GetAmbient(vTexCoord.z) + vVertexLighting) * diffColor.rgb;
gl_FragColor = vec4(GetFog(finalColor, vTexCoord.w), diffColor.a);
}

View File

@ -1,19 +1,26 @@
#include "Uniforms.vert"
#include "Transform.vert"
#include "Lighting.vert"
varying vec2 vTexCoord;
varying vec4 vTexCoord;
#ifdef VERTEXCOLOR
varying vec4 vColor;
#endif
varying vec2 vZonePosDepth;
varying vec3 vVertexLighting;
void main()
{
mat4 modelMatrix = iModelMatrix;
vec3 worldPos = GetWorldPos(modelMatrix);
gl_Position = GetClipPos(worldPos);
vTexCoord = GetTexCoord(iTexCoord);
vZonePosDepth = vec2(GetZonePos(worldPos), GetDepth(gl_Position));
vTexCoord = vec4(GetTexCoord(iTexCoord), GetZonePos(worldPos), GetDepth(gl_Position));
vVertexLighting = vec3(0.0, 0.0, 0.0);
#ifdef NUMVERTEXLIGHTS
vec3 normal = GetWorldNormal(modelMatrix);
for (int i = 0; i < NUMVERTEXLIGHTS; ++i)
vVertexLighting += GetVertexLight(i, worldPos, normal) * cVertexLights[i * 3].rgb;
#endif
#ifdef VERTEXCOLOR
vColor = iColor;

View File

@ -2,8 +2,14 @@
<shader name="Ambient" type="vs">
<option name="VCol" define="VERTEXCOLOR" />
<variation name="" />
<variation name="1VL" define="NUMVERTEXLIGHTS=1" />
<variation name="2VL" define="NUMVERTEXLIGHTS=2" />
<variation name="3VL" define="NUMVERTEXLIGHTS=3" />
<variation name="4VL" define="NUMVERTEXLIGHTS=4" />
<option name="" /> <!-- Dummy option to separate the two variation groups -->
<variation name="" />
<variation name="Skinned" define="SKINNED" />
<variation name="Instanced" define="INSTANCED" />
<variation name="Instanced" define="INSTANCED" require="SM3" />
<variation name="Billboard" define="BILLBOARD" />
</shader>
<shader name="Ambient" type="ps">

View File

@ -29,7 +29,7 @@ float GetSpecular(vec3 normal, vec3 eyeVec, vec3 lightDir, float specularPower)
vec3 GetAmbient(float zonePos)
{
return mix(cAmbientStartColor, cAmbientEndColor, zonePos);
return cAmbientStartColor + zonePos * cAmbientEndColor;
}
float GetShadow(vec4 shadowPos)

View File

@ -0,0 +1,27 @@
float GetVertexLight(int index, vec3 worldPos, vec3 normal)
{
vec3 lightDir = cVertexLights[index * 3 + 1].xyz;
vec3 lightPos = cVertexLights[index * 3 + 2].xyz;
float invRange = cVertexLights[index * 3].w;
float cutoff = cVertexLights[index * 3 + 1].w;
float invCutoff = cVertexLights[index * 3 + 2].w;
// Directional light
if (invRange == 0.0)
{
float NdotL = max(dot(normal, lightDir), 0.0);
return NdotL;
}
// Point/spot light
else
{
vec3 lightVec = (lightPos - worldPos) * invRange;
float lightDist = length(lightVec);
vec3 localDir = lightVec / lightDist;
float NdotL = max(dot(normal, localDir), 0.0);
float atten = clamp(1.0 - lightDist * lightDist, 0.0, 1.0);
float spotEffect = dot(localDir, lightDir);
float spotAtten = clamp((spotEffect - cutoff) * invCutoff, 0.0, 1.0);
return NdotL * atten * spotAtten;
}
}

View File

@ -15,3 +15,4 @@ uniform vec3 cViewUpVector;
uniform mat4 cZone;
uniform mat4 cShadowProj[4];
uniform vec4 cSkinMatrices[64*3];
uniform vec4 cVertexLights[12];

View File

@ -5,6 +5,9 @@
#include "Fog.hlsl"
void VS(float4 iPos : POSITION,
#ifdef NUMVERTEXLIGHTS
float3 iNormal : NORMAL,
#endif
float2 iTexCoord : TEXCOORD0,
#ifdef VERTEXCOLOR
float4 iColor : COLOR0,
@ -19,8 +22,8 @@ void VS(float4 iPos : POSITION,
#ifdef BILLBOARD
float2 iSize : TEXCOORD1,
#endif
out float2 oTexCoord : TEXCOORD0,
out float2 oZonePosDepth : TEXCOORD1,
out float4 oTexCoord : TEXCOORD0,
out float3 oVertexLighting : TEXCOORD1,
#ifdef VERTEXCOLOR
out float4 oColor : COLOR0,
#endif
@ -29,23 +32,29 @@ void VS(float4 iPos : POSITION,
float4x3 modelMatrix = iModelMatrix;
float3 worldPos = GetWorldPos(modelMatrix);
oPos = GetClipPos(worldPos);
oTexCoord = GetTexCoord(iTexCoord);
oZonePosDepth = float2(GetZonePos(worldPos), GetDepth(oPos));
oTexCoord = float4(GetTexCoord(iTexCoord), GetZonePos(worldPos), GetDepth(oPos));
oVertexLighting = 0.0;
#ifdef NUMVERTEXLIGHTS
float3 normal = GetWorldNormal(modelMatrix);
for (int i = 0; i < NUMVERTEXLIGHTS; ++i)
oVertexLighting += GetVertexLight(i, worldPos, normal) * cVertexLights[i * 3].rgb;
#endif
#ifdef VERTEXCOLOR
oColor = iColor;
#endif
}
void PS(float2 iTexCoord : TEXCOORD0,
float2 iZonePosDepth : TEXCOORD1,
void PS(float4 iTexCoord : TEXCOORD0,
float3 iVertexLighting : TEXCOORD1,
#ifdef VERTEXCOLOR
float4 iColor : COLOR0,
#endif
out float4 oColor : COLOR0)
{
#ifdef DIFFMAP
float4 diffColor = cMatDiffColor * tex2D(sDiffMap, iTexCoord);
float4 diffColor = cMatDiffColor * tex2D(sDiffMap, iTexCoord.xy);
#else
float4 diffColor = cMatDiffColor;
#endif
@ -54,5 +63,7 @@ void PS(float2 iTexCoord : TEXCOORD0,
diffColor *= iColor;
#endif
oColor = float4(GetFog(GetAmbient(iZonePosDepth.x) * diffColor.rgb, iZonePosDepth.y), diffColor.a);
float3 finalColor = (GetAmbient(iTexCoord.z) + iVertexLighting) * diffColor.rgb;
oColor = float4(GetFog(finalColor, iTexCoord.w), diffColor.a);
}

View File

@ -2,6 +2,12 @@
<shader name="Ambient" type="vs">
<option name="VCol" define="VERTEXCOLOR" />
<variation name="" />
<variation name="1VL" define="NUMVERTEXLIGHTS=1" />
<variation name="2VL" define="NUMVERTEXLIGHTS=2" />
<variation name="3VL" define="NUMVERTEXLIGHTS=3" />
<variation name="4VL" define="NUMVERTEXLIGHTS=4" />
<option name="" /> <!-- Dummy option to separate the two variation groups -->
<variation name="" />
<variation name="Skinned" define="SKINNED" />
<variation name="Instanced" define="INSTANCED" require="SM3" />
<variation name="Billboard" define="BILLBOARD" />

View File

@ -146,11 +146,9 @@ void PS(float4 iTexCoord : TEXCOORD0,
float4 diffColor = cMatDiffColor;
#endif
/*
#ifdef VERTEXCOLOR
diffColor *= iColor;
#endif
*/
#ifdef NORMALMAP
float3 normal = DecodeNormal(tex2D(sNormalMap, iTexCoord.xy));

View File

@ -29,9 +29,37 @@ float GetSpecular(float3 normal, float3 eyeVec, float3 lightDir, float specularP
return pow(dot(normal, halfVec), specularPower);
}
float GetVertexLight(int index, float3 worldPos, float3 normal)
{
float3 lightDir = cVertexLights[index * 3 + 1].xyz;
float3 lightPos = cVertexLights[index * 3 + 2].xyz;
float invRange = cVertexLights[index * 3].w;
float cutoff = cVertexLights[index * 3 + 1].w;
float invCutoff = cVertexLights[index * 3 + 2].w;
// Directional light
if (invRange == 0.0)
{
float NdotL = max(dot(normal, lightDir), 0.0);
return NdotL;
}
// Point/spot light
else
{
float3 lightVec = (lightPos - worldPos) * invRange;
float lightDist = length(lightVec);
float3 localDir = lightVec / lightDist;
float NdotL = max(dot(normal, localDir), 0.0);
float atten = saturate(1.0 - lightDist * lightDist);
float spotEffect = dot(localDir, lightDir);
float spotAtten = saturate((spotEffect - cutoff) * invCutoff);
return NdotL * atten * spotAtten;
}
}
float3 GetAmbient(float zonePos)
{
return lerp(cAmbientStartColor, cAmbientEndColor, zonePos);
return cAmbientStartColor + zonePos * cAmbientEndColor;
}
float GetShadow(float4 shadowPos)

View File

@ -16,6 +16,7 @@ uniform float3 cViewUpVector : register(C25);
uniform float4x3 cZone : register(C26);
uniform float4x4 cShadowProj[4] : register(C29);
uniform float4x3 cSkinMatrices[64] : register(C45);
uniform float4 cVertexLights[12] : register(C237);
// Pixel shader parameters
uniform float3 cAmbientStartColor : register(C0);

View File

@ -103,6 +103,7 @@ struct Variation
String name_;
Vector<String> defines_;
Vector<String> defineValues_;
Vector<String> excludes_;
Vector<String> includes_;
Vector<String> requires_;
@ -114,6 +115,7 @@ struct CompiledVariation
ShaderType type_;
String name_;
Vector<String> defines_;
Vector<String> defineValues_;
PODVector<unsigned char> byteCode_;
unsigned byteCodeOffset_;
Set<Parameter> constants_;
@ -141,6 +143,7 @@ String inDir_;
String inFile_;
String outDir_;
Vector<String> defines_;
Vector<String> defineValues_;
bool useSM3_ = false;
volatile bool compileFailed_ = false;
@ -171,10 +174,6 @@ public:
{
workItem = workList_.Front();
workList_.Erase(workList_.Begin());
if (!workItem->name_.Empty())
PrintLine("Compiling shader variation " + workItem->name_);
else
PrintLine("Compiling base shader variation");
}
}
if (!workItem)
@ -263,7 +262,17 @@ void Run(const Vector<String>& arguments)
else if (arg == "SM2")
useSM3_ = false;
defines_.Push(arg);
Vector<String> nameAndValue = arg.Split('=');
if (nameAndValue.Size() == 2)
{
defines_.Push(nameAndValue[0]);
defineValues_.Push(nameAndValue[1]);
}
else
{
defines_.Push(arg);
defineValues_.Push("1");
}
}
XMLFile doc(context_);
@ -304,8 +313,20 @@ void Run(const Vector<String>& arguments)
String simpleDefine = variation.GetString("define");
if (!simpleDefine.Empty())
newVar.defines_.Push(simpleDefine);
{
Vector<String> nameAndValue = simpleDefine.Split('=');
if (nameAndValue.Size() == 2)
{
newVar.defines_.Push(nameAndValue[0]);
newVar.defineValues_.Push(nameAndValue[1]);
}
else
{
newVar.defines_.Push(simpleDefine);
newVar.defineValues_.Push("1");
}
}
String simpleExclude = variation.GetString("exclude");
if (!simpleExclude.Empty())
newVar.excludes_.Push(simpleExclude);
@ -321,7 +342,18 @@ void Run(const Vector<String>& arguments)
XMLElement define = variation.GetChild("define");
while (define)
{
newVar.defines_.Push(define.GetString("name"));
String defineName = define.GetString("name");
Vector<String> nameAndValue = defineName.Split('=');
if (nameAndValue.Size() == 2)
{
newVar.defines_.Push(nameAndValue[0]);
newVar.defineValues_.Push(nameAndValue[1]);
}
else
{
newVar.defines_.Push(defineName);
newVar.defineValues_.Push("1");
}
define = define.GetNext("define");
}
@ -499,9 +531,10 @@ void CompileVariations(const Shader& baseShader, XMLElement& shaders)
if (usedCombinations.Contains(active))
continue;
// Build shader variation name & defines active variations
// Build shader variation name & defines for active variations
String outName;
Vector<String> defines;
Vector<String> defineValues;
for (unsigned j = 0; j < variations.Size(); ++j)
{
if (active & (1 << j))
@ -509,7 +542,10 @@ void CompileVariations(const Shader& baseShader, XMLElement& shaders)
if (variations[j].name_.Length())
outName += variations[j].name_;
for (unsigned k = 0; k < variations[j].defines_.Size(); ++k)
{
defines.Push(variations[j].defines_[k]);
defineValues.Push(variations[j].defineValues_[k]);
}
}
}
@ -517,6 +553,7 @@ void CompileVariations(const Shader& baseShader, XMLElement& shaders)
compile.type_ = baseShader.type_;
compile.name_ = outName;
compile.defines_ = defines;
compile.defineValues_ = defineValues;
compiledVariations.Push(compile);
usedCombinations.Insert(active);
@ -609,14 +646,14 @@ void Compile(CompiledVariation* variation)
{
D3DXMACRO macro;
macro.Name = variation->defines_[i].CString();
macro.Definition = "1";
macro.Definition = variation->defineValues_[i].CString();
macros.Push(macro);
}
for (unsigned i = 0; i < defines_.Size(); ++i)
{
D3DXMACRO macro;
macro.Name = defines_[i].CString();
macro.Definition = "1";
macro.Definition = defineValues_[i].CString();
macros.Push(macro);
}
@ -664,15 +701,20 @@ void Compile(CompiledVariation* variation)
}
else
{
CopyStrippedCode(variation->byteCode_, shaderCode->GetBufferPointer(), shaderCode->GetBufferSize());
if (!variation->name_.Empty())
PrintLine("Compiled shader variation " + variation->name_ + ", code size " + String(variation->byteCode_.Size()));
else
PrintLine("Compiled base shader variation, code size " + String(variation->byteCode_.Size()));
// Print warnings if any
if (errorMsgs && errorMsgs->GetBufferSize())
{
String warning((const char*)errorMsgs->GetBufferPointer(), errorMsgs->GetBufferSize());
printf("WARNING: %s\n", warning.CString());
PrintLine("WARNING: " + warning);
}
CopyStrippedCode(variation->byteCode_, shaderCode->GetBufferPointer(), shaderCode->GetBufferSize());
// Parse the constant table for constants and texture units
D3DXCONSTANTTABLE_DESC desc;
constantTable->GetDesc(&desc);