incorporate SpaceStation atmosphere lighting in ModelBody

master
Martin Bays 2013-03-16 21:11:27 -04:00
parent a0db3a0ba0
commit 4c0c5cede5
4 changed files with 105 additions and 124 deletions

View File

@ -12,6 +12,7 @@
#include "Space.h"
#include "WorldView.h"
#include "Camera.h"
#include "Planet.h"
#include "collider/collider.h"
#include "graphics/Renderer.h"
#include "scenegraph/SceneGraph.h"
@ -146,6 +147,102 @@ void ModelBody::SetFrame(Frame *f)
}
}
// Calculates the ambiently and directly lit portions of the lighting model taking into account the atmosphere and sun positions at a given location
// 1. Calculates the amount of direct illumination available taking into account
// * multiple suns
// * sun positions relative to up direction i.e. light is dimmed as suns set
// * Thickness of the atmosphere overhead i.e. as atmospheres get thicker light starts dimming earlier as sun sets, without atmosphere the light switches off at point of sunset
// 2. Calculates the split between ambient and directly lit portions taking into account
// * Atmosphere density (optical thickness) of the sky dome overhead
// as optical thickness increases the fraction of ambient light increases
// this takes altitude into account automatically
// * As suns set the split is biased towards ambient
void ModelBody::CalcLighting(double &ambient, double &direct, const Camera *camera)
{
// position relative to the rotating frame of the planet
Body *astro = GetFrame()->GetBody();
if (astro && astro->IsType(Object::PLANET)) {
Planet *planet = static_cast<Planet*>(astro);
vector3d upDir = GetPosition();
const double dist = upDir.Length();
upDir = upDir.Normalized();
double pressure, density;
planet->GetAtmosphericState(dist, &pressure, &density);
double surfaceDensity;
Color cl;
planet->GetSystemBody()->GetAtmosphereFlavor(&cl, &surfaceDensity);
// approximate optical thickness fraction as fraction of density remaining relative to earths
double opticalThicknessFraction = density/EARTH_ATMOSPHERE_SURFACE_DENSITY;
// tweak optical thickness curve - lower exponent ==> higher altitude before ambient level drops
opticalThicknessFraction = pow(std::max(0.00001,opticalThicknessFraction),0.15); //max needed to avoid 0^power
//step through all the lights and calculate contributions taking into account sun position
double light = 0.0;
double light_clamped = 0.0;
const std::vector<Camera::LightSource> &lightSources = camera->GetLightSources();
for(std::vector<Camera::LightSource>::const_iterator l = lightSources.begin();
l != lightSources.end(); ++l) {
double sunAngle;
// calculate the extent the sun is towards zenith
if (l->GetBody()){
// relative to the rotating frame of the planet
const vector3d lightDir = (l->GetBody()->GetInterpPositionRelTo(planet->GetFrame()).Normalized());
sunAngle = lightDir.Dot(upDir);
} else {
// light is the default light for systems without lights
sunAngle = 1.0;
}
//0 to 1 as sunangle goes from 0.0 to 1.0
double sunAngle2 = (Clamp(sunAngle, 0.0,1.0))/1.0;
//0 to 1 as sunAngle goes from endAngle to startAngle
// angle at which light begins to fade on Earth
const double startAngle = 0.3;
// angle at which sun set completes, which should be after sun has dipped below the horizon on Earth
const double endAngle = -0.18;
const double start = std::min((startAngle*opticalThicknessFraction),1.0);
const double end = std::max((endAngle*opticalThicknessFraction),-0.2);
sunAngle = (Clamp(sunAngle, end, start)-end)/(start-end);
light += sunAngle;
light_clamped += sunAngle2;
}
// brightness depends on optical depth and intensity of light from all the stars
direct = (Clamp((light),0.0,1.0));
// ambient light fraction
// alter ratio between directly and ambiently lit portions towards ambiently lit as sun sets
const double fraction = (0.1+0.8*(
1.0-light_clamped*(Clamp((opticalThicknessFraction),0.0,1.0))
)+0.1); //fraction goes from 0.6 to 1.0
// fraction of light left over to be lit directly
direct = (1.0-fraction)*direct;
// scale ambient by amount of light
ambient = fraction*(Clamp((light),0.0,1.0))*0.25;
const double minAmbient = std::min(1.0,density)*0.05;
ambient = std::max(minAmbient, ambient);
}
else {
// not in an atmosphere
ambient = 0.0;
direct = 1.0;
}
}
void ModelBody::RenderModel(Graphics::Renderer *r, const Camera *camera, const vector3d &viewCoords, const matrix4x4d &viewTransform)
{
matrix4x4d m2 = GetInterpOrient();
@ -162,13 +259,15 @@ void ModelBody::RenderModel(Graphics::Renderer *r, const Camera *camera, const v
// Set up lighting
std::vector<Graphics::Light> origLights, newLights;
double ambient, direct;
CalcLighting(ambient, direct, camera);
const std::vector<Camera::LightSource> &lightSources = camera->GetLightSources();
for(size_t i = 0; i < lightSources.size(); i++) {
Graphics::Light light(lightSources[i].GetLight());
origLights.push_back(light);
float intensity = camera->ShadowedIntensity(i, this);
float intensity = direct * camera->ShadowedIntensity(i, this);
Color c = light.GetDiffuse();
Color cs = light.GetSpecular();
@ -184,12 +283,15 @@ void ModelBody::RenderModel(Graphics::Renderer *r, const Camera *camera, const v
newLights.push_back(light);
}
const Color oldAmbient = r->GetAmbientColor();
r->SetAmbientColor(Color(ambient));
r->SetLights(newLights.size(), &newLights[0]);
m_model->Render(trans);
// restore old lights
r->SetLights(origLights.size(), &origLights[0]);
r->SetAmbientColor(oldAmbient);
glPopMatrix();
}

View File

@ -44,6 +44,8 @@ protected:
virtual void Load(Serializer::Reader &rd, Space *space);
private:
void CalcLighting(double &ambient, double &direct, const Camera *camera);
bool m_isStatic;
bool m_colliding;
RefCountedPtr<CollMesh> m_collMesh;

View File

@ -571,90 +571,6 @@ Sint64 SpaceStation::GetPrice(Equip::Type t) const {
return (mul * Sint64(Equip::types[t].basePrice)) / 100;
}
// Calculates the ambiently and directly lit portions of the lighting model taking into account the atmosphere and sun positions at a given location
// 1. Calculates the amount of direct illumination available taking into account
// * multiple suns
// * sun positions relative to up direction i.e. light is dimmed as suns set
// * Thickness of the atmosphere overhead i.e. as atmospheres get thicker light starts dimming earlier as sun sets, without atmosphere the light switches off at point of sunset
// 2. Calculates the split between ambient and directly lit portions taking into account
// * Atmosphere density (optical thickness) of the sky dome overhead
// as optical thickness increases the fraction of ambient light increases
// this takes altitude into account automatically
// * As suns set the split is biased towards ambient
void SpaceStation::CalcLighting(Planet *planet, double &ambient, double &intensity, const std::vector<Camera::LightSource> &lightSources)
{
// position relative to the rotating frame of the planet
vector3d upDir = GetPosition();
const double dist = upDir.Length();
upDir = upDir.Normalized();
double pressure, density;
planet->GetAtmosphericState(dist, &pressure, &density);
double surfaceDensity;
Color cl;
planet->GetSystemBody()->GetAtmosphereFlavor(&cl, &surfaceDensity);
// approximate optical thickness fraction as fraction of density remaining relative to earths
double opticalThicknessFraction = density/EARTH_ATMOSPHERE_SURFACE_DENSITY;
// tweak optical thickness curve - lower exponent ==> higher altitude before ambient level drops
opticalThicknessFraction = pow(std::max(0.00001,opticalThicknessFraction),0.15); //max needed to avoid 0^power
//step through all the lights and calculate contributions taking into account sun position
double light = 0.0;
double light_clamped = 0.0;
for(std::vector<Camera::LightSource>::const_iterator l = lightSources.begin();
l != lightSources.end(); ++l) {
double sunAngle;
// calculate the extent the sun is towards zenith
if (l->GetBody()){
// relative to the rotating frame of the planet
const vector3d lightDir = (l->GetBody()->GetInterpPositionRelTo(planet->GetFrame()).Normalized());
sunAngle = lightDir.Dot(upDir);
} else {
// light is the default light for systems without lights
sunAngle = 1.0;
}
//0 to 1 as sunangle goes from 0.0 to 1.0
double sunAngle2 = (Clamp(sunAngle, 0.0,1.0))/1.0;
//0 to 1 as sunAngle goes from endAngle to startAngle
// angle at which light begins to fade on Earth
const double startAngle = 0.3;
// angle at which sun set completes, which should be after sun has dipped below the horizon on Earth
const double endAngle = -0.18;
const double start = std::min((startAngle*opticalThicknessFraction),1.0);
const double end = std::max((endAngle*opticalThicknessFraction),-0.2);
sunAngle = (Clamp(sunAngle, end, start)-end)/(start-end);
light += sunAngle;
light_clamped += sunAngle2;
}
// brightness depends on optical depth and intensity of light from all the stars
intensity = (Clamp((light),0.0,1.0));
// ambient light fraction
// alter ratio between directly and ambiently lit portions towards ambiently lit as sun sets
const double fraction = (0.1+0.8*(
1.0-light_clamped*(Clamp((opticalThicknessFraction),0.0,1.0))
)+0.1); //fraction goes from 0.6 to 1.0
// fraction of light left over to be lit directly
intensity = (1.0-fraction)*intensity;
// scale ambient by amount of light
ambient = fraction*(Clamp((light),0.0,1.0))*0.25;
}
// Renders space station and adjacent city if applicable
// For orbital starports: renders as normal
// For surface starports:
@ -672,40 +588,6 @@ void SpaceStation::Render(Graphics::Renderer *r, const Camera *camera, const vec
else {
Planet *planet = static_cast<Planet*>(b);
// calculate lighting
// available light is calculated and split between directly (diffusely/specularly) lit and ambiently lit
const std::vector<Camera::LightSource> &lightSources = camera->GetLightSources();
double ambient, intensity;
CalcLighting(planet, ambient, intensity, lightSources);
ambient = std::max(0.05, ambient);
std::vector<Graphics::Light> origLights, newLights;
for(size_t i = 0; i < lightSources.size(); i++) {
Graphics::Light light(lightSources[i].GetLight());
origLights.push_back(light);
Color c = light.GetDiffuse();
Color cs = light.GetSpecular();
c.r*=float(intensity);
c.g*=float(intensity);
c.b*=float(intensity);
cs.r*=float(intensity);
cs.g*=float(intensity);
cs.b*=float(intensity);
light.SetDiffuse(c);
light.SetSpecular(cs);
newLights.push_back(light);
}
const Color oldAmbient = r->GetAmbientColor();
r->SetAmbientColor(Color(ambient));
r->SetLights(newLights.size(), &newLights[0]);
/* don't render city if too far away */
if (viewCoords.Length() < 1000000.0){
if (!m_adjacentCity) {
@ -715,10 +597,6 @@ void SpaceStation::Render(Graphics::Renderer *r, const Camera *camera, const vec
}
RenderModel(r, camera, viewCoords, viewTransform);
// restore old lights & ambient
r->SetLights(origLights.size(), &origLights[0]);
r->SetAmbientColor(oldAmbient);
}
}

View File

@ -119,7 +119,6 @@ private:
void DockingUpdate(const double timeStep);
void PositionDockedShip(Ship *ship, int port) const;
void DoLawAndOrder(const double timeStep);
void CalcLighting(Planet *planet, double &ambient, double &intensity, const std::vector<Camera::LightSource> &lightSources);
/* Stage 0 means docking port empty
* Stage 1 means docking clearance granted to ->ship