Enable new atmospheres to work for irregular objects.

Moved parameter setup for atmospheres, lighting, and shadows into the CelestiaGLProgram class.
ver1_5_1
Chris Laurel 2006-08-29 06:13:41 +00:00
parent ffcb3299cf
commit 1cc0b38fde
9 changed files with 484 additions and 408 deletions

View File

@ -13,6 +13,7 @@
#include <celutil/reshandle.h>
#include <celutil/color.h>
#include <celmath/vecmath.h>
#include <celengine/multitexture.h>
class Atmosphere
@ -54,5 +55,13 @@ class Atmosphere
Vec3f absorptionCoeff;
};
// Atmosphere density is modeled with a exp(-y/H) falloff, where
// H is the scale height of the atmosphere. Thus atmospheres have
// infinite extent, but we still need to choose some finite sphere
// to render. The radius of the sphere is the height at which the
// density of the atmosphere falls to the extinction threshold, i.e.
// -H * ln(extinctionThreshold)
extern const double AtmosphereExtinctionThreshold;
#endif // _ATMOSPHERE_H_

View File

@ -0,0 +1,63 @@
// lightenv.h
//
// Structures that describe the lighting environment for rendering objects
// in Celestia.
//
// Copyright (C) 2006, Chris Laurel <claurel@shatters.net>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
#ifndef _CELENGINE_LIGHTENV_H_
#define _CELENGINE_LIGHTENV_H_
#include <celutil/color.h>
#include <celmath/vecmath.h>
#include <vector>
static const unsigned int MaxLights = 8;
class DirectionalLight
{
public:
Color color;
float irradiance;
Vec3f direction_eye;
Vec3f direction_obj;
// Required for eclipse shadows only--may be able to use
// distance instead of position.
Point3d position;
float apparentSize;
};
class EclipseShadow
{
public:
Point3f origin;
Vec3f direction;
float penumbraRadius;
float umbraRadius;
};
class LightingState
{
public:
LightingState() : nLights(0),
eyeDir_obj(0.0f, 0.0f, -1.0f),
eyePos_obj(0.0f, 0.0f, -1.0f)
{ shadows[0] = NULL; };
unsigned int nLights;
DirectionalLight lights[MaxLights];
std::vector<EclipseShadow>* shadows[MaxLights];
Vec3f eyeDir_obj;
Point3f eyePos_obj;
Vec3f ambientColor;
};
#endif // _CELENGINE_LIGHTENV_H_

View File

@ -360,6 +360,7 @@ setExtendedVertexArrays(const Mesh::VertexDescription& desc,
GLSL_RenderContext::GLSL_RenderContext(const LightingState& ls, float _objRadius, const Mat4f& _xform) :
lightingState(ls),
atmosphere(NULL),
blendOn(false),
objRadius(_objRadius),
xform(_xform),
@ -402,6 +403,7 @@ GLSL_RenderContext::initLightingEnvironment()
}
// TODO: eliminate this and use CelestiaGLProgram::setLightingParameters instead
void
GLSL_RenderContext::setLightingParameters(CelestiaGLProgram& prog, Color materialDiffuse, Color materialSpecular)
{
@ -462,6 +464,7 @@ GLSL_RenderContext::setLightingParameters(CelestiaGLProgram& prog, Color materia
}
// TODO: eliminate this and use CelestiaGLProgram::setShadowParameters instead
void
GLSL_RenderContext::setShadowParameters(CelestiaGLProgram& prog)
{
@ -584,6 +587,13 @@ GLSL_RenderContext::makeCurrent(const Mesh::Material& m)
}
}
if (atmosphere != NULL)
{
// Only use new atmosphere code in OpenGL 2.0 path when new style parameters are defined.
if (atmosphere->mieScaleHeight > 0.0f)
shaderProps.texUsage |= ShaderProperties::Scattering;
}
// Get a shader for the current rendering configuration
CelestiaGLProgram* prog = GetShaderManager().getShader(shaderProps);
if (prog == NULL)
@ -613,6 +623,11 @@ GLSL_RenderContext::makeCurrent(const Mesh::Material& m)
{
prog->nightTexMin = 1.0f;
}
if (shaderProps.hasScattering())
{
prog->setAtmosphereParameters(*atmosphere, objRadius, objRadius);
}
bool blendOnNow = false;
if (m.opacity != 1.0f || (baseTex != NULL && baseTex->hasAlpha()))
@ -645,6 +660,12 @@ GLSL_RenderContext::setVertexArrays(const Mesh::VertexDescription& desc,
}
void
GLSL_RenderContext::setAtmosphere(const Atmosphere* _atmosphere)
{
atmosphere = _atmosphere;
}
// Extended material properties -- currently just lunarLambert term
void
GLSL_RenderContext::setLunarLambert(float l)

View File

@ -1,6 +1,6 @@
// rendcontext.h
//
// Copyright (C) 2004, Chris Laurel <claurel@shatters.net>
// Copyright (C) 2004-2006, Chris Laurel <claurel@shatters.net>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
@ -14,47 +14,6 @@
#include "shadermanager.h"
static const unsigned int MaxLights = 8;
struct DirectionalLight
{
Color color;
float irradiance;
Vec3f direction_eye;
Vec3f direction_obj;
// Required for eclipse shadows only--may be able to use
// distance instead of position.
Point3d position;
float apparentSize;
};
struct EclipseShadow
{
Point3f origin;
Vec3f direction;
float penumbraRadius;
float umbraRadius;
};
struct LightingState
{
LightingState() : nLights(0),
eyeDir_obj(0.0f, 0.0f, -1.0f),
eyePos_obj(0.0f, 0.0f, -1.0f)
{ shadows[0] = NULL; };
unsigned int nLights;
DirectionalLight lights[MaxLights];
std::vector<EclipseShadow>* shadows[MaxLights];
Vec3f eyeDir_obj;
Point3f eyePos_obj;
Vec3f ambientColor;
};
class RenderContext
{
public:
@ -138,7 +97,8 @@ class GLSL_RenderContext : public RenderContext
virtual void setVertexArrays(const Mesh::VertexDescription& desc,
void* vertexData);
virtual void setLunarLambert(float);
virtual void setLunarLambert(float);
virtual void setAtmosphere(const Atmosphere*);
private:
void initLightingEnvironment();
@ -147,6 +107,7 @@ class GLSL_RenderContext : public RenderContext
private:
const LightingState& lightingState;
const Atmosphere* atmosphere;
bool blendOn;
float objRadius;
Mat4f xform;

View File

@ -4971,7 +4971,7 @@ void Renderer::renderObject(Point3f pos,
if (context->getRenderPath() == GLContext::GLPath_GLSL && lit)
{
ResourceHandle texOverride = obj.surface->baseTexture.tex[textureResolution];
renderModel_GLSL(model, ri, texOverride, ls, obj.radius, planetMat);
renderModel_GLSL(model, ri, texOverride, ls, obj.atmosphere, obj.radius, renderFlags, planetMat);
for (unsigned int i = 1; i < 8;/*context->getMaxTextures();*/ i++)
{
glx::glActiveTextureARB(GL_TEXTURE0_ARB + i);

View File

@ -50,172 +50,7 @@ using namespace std;
const double AtmosphereExtinctionThreshold = 0.05;
static void setLightParameters_GLSL(CelestiaGLProgram& prog,
const ShaderProperties& shadprop,
const LightingState& ls,
Color materialDiffuse,
Color materialSpecular)
{
unsigned int nLights = min(MaxShaderLights, ls.nLights);
Vec3f diffuseColor(materialDiffuse.red(),
materialDiffuse.green(),
materialDiffuse.blue());
Vec3f specularColor(materialSpecular.red(),
materialSpecular.green(),
materialSpecular.blue());
for (unsigned int i = 0; i < nLights; i++)
{
const DirectionalLight& light = ls.lights[i];
Vec3f lightColor = Vec3f(light.color.red(),
light.color.green(),
light.color.blue()) * light.irradiance;
prog.lights[i].direction = light.direction_obj;
if (shadprop.usesShadows() ||
shadprop.usesFragmentLighting() ||
shadprop.lightModel == ShaderProperties::RingIllumModel)
{
prog.fragLightColor[i] = Vec3f(lightColor.x * diffuseColor.x,
lightColor.y * diffuseColor.y,
lightColor.z * diffuseColor.z);
if (shadprop.hasSpecular())
{
prog.fragLightSpecColor[i] = Vec3f(lightColor.x * specularColor.x,
lightColor.y * specularColor.y,
lightColor.z * specularColor.z);
}
}
else
{
prog.lights[i].diffuse = Vec3f(lightColor.x * diffuseColor.x,
lightColor.y * diffuseColor.y,
lightColor.z * diffuseColor.z);
}
prog.lights[i].specular = Vec3f(lightColor.x * specularColor.x,
lightColor.y * specularColor.y,
lightColor.z * specularColor.z);
Vec3f halfAngle_obj = ls.eyeDir_obj + light.direction_obj;
if (halfAngle_obj.length() != 0.0f)
halfAngle_obj.normalize();
prog.lights[i].halfVector = halfAngle_obj;
}
prog.ambientColor = ls.ambientColor;
prog.opacity = materialDiffuse.alpha();
}
// Set GLSL shader constants for shadows from ellipsoid occluders; shadows from
// irregular objects are not handled yet.
static void
setEclipseShadowShaderConstants(const LightingState& ls,
float planetRadius,
const Mat4f& planetMat,
CelestiaGLProgram& prog)
{
for (unsigned int li = 0;
li < min(ls.nLights, MaxShaderLights);
li++)
{
vector<EclipseShadow>* shadows = ls.shadows[li];
if (shadows != NULL)
{
unsigned int nShadows = min((size_t) MaxShaderShadows,
shadows->size());
for (unsigned int i = 0; i < nShadows; i++)
{
EclipseShadow& shadow = shadows->at(i);
CelestiaGLProgramShadow& shadowParams = prog.shadows[li][i];
float R2 = 0.25f;
float umbra = shadow.umbraRadius / shadow.penumbraRadius;
umbra = umbra * umbra;
if (umbra < 0.0001f)
umbra = 0.0001f;
else if (umbra > 0.99f)
umbra = 0.99f;
float umbraRadius = R2 * umbra;
float penumbraRadius = R2;
float shadowBias = 1.0f / (1.0f - penumbraRadius / umbraRadius);
shadowParams.bias = shadowBias;
shadowParams.scale = -shadowBias / umbraRadius;
// Compute the transformation to use for generating texture
// coordinates from the object vertices.
Point3f origin = shadow.origin * planetMat;
Vec3f dir = shadow.direction * planetMat;
float scale = planetRadius / shadow.penumbraRadius;
Vec3f axis = Vec3f(0, 1, 0) ^ dir;
float angle = (float) acos(Vec3f(0, 1, 0) * dir);
axis.normalize();
Mat4f mat = Mat4f::rotation(axis, -angle);
Vec3f sAxis = Vec3f(0.5f * scale, 0, 0) * mat;
Vec3f tAxis = Vec3f(0, 0, 0.5f * scale) * mat;
float sw = (Point3f(0, 0, 0) - origin) * sAxis / planetRadius + 0.5f;
float tw = (Point3f(0, 0, 0) - origin) * tAxis / planetRadius + 0.5f;
shadowParams.texGenS = Vec4f(sAxis.x, sAxis.y, sAxis.z, sw);
shadowParams.texGenT = Vec4f(tAxis.x, tAxis.y, tAxis.z, tw);
}
}
}
}
// Set the scattering and absoroption shader parameters for atmosphere simulation.
// They are from standard units to the normalized system used by the shaders.
// atmObjRadius - the radius in km of the planet with the atmosphere
// objRadius - the radius in km of the object we're rendering
static void setAtmosphereParameters_GLSL(CelestiaGLProgram& prog,
const Atmosphere& atmosphere,
float atmPlanetRadius,
float objRadius)
{
// Compute the radius of the atmosphere to render; the density falls off
// exponentially with height above the planet's surface, so the actual
// radius is infinite. That's a bit impractical, so well just render the
// portion out to the point where the density is 1/1000 of the surface
// density.
float atmosphereRadius = atmPlanetRadius + -atmosphere.mieScaleHeight * (float) log(AtmosphereExtinctionThreshold);
float mieCoeff = atmosphere.mieCoeff * objRadius;
Vec3f rayleighCoeff = atmosphere.rayleighCoeff * objRadius;
Vec3f absorptionCoeff = atmosphere.absorptionCoeff * objRadius;
float r = atmosphereRadius / objRadius;
prog.atmosphereRadius = Vec3f(r, r * r, atmPlanetRadius / objRadius);
prog.mieCoeff = mieCoeff;
prog.mieScaleHeight = objRadius / atmosphere.mieScaleHeight;
// The scattering shaders use the Schlick approximation to the
// Henyey-Greenstein phase function because it's slightly faster
// to compute. Convert the HG asymmetry parameter to the Schlick
// parameter.
float g = atmosphere.miePhaseAsymmetry;
prog.miePhaseAsymmetry = 1.55f * g - 0.55f * g * g * g;
prog.rayleighCoeff = rayleighCoeff;
prog.rayleighScaleHeight = 0.0f; // TODO
// Precompute sum and inverse sum of scattering coefficients to save work
// in the vertex shader.
Vec3f scatterCoeffSum = Vec3f(rayleighCoeff.x + mieCoeff,
rayleighCoeff.y + mieCoeff,
rayleighCoeff.z + mieCoeff);
prog.scatterCoeffSum = scatterCoeffSum;
prog.invScatterCoeffSum = Vec3f(1.0f / scatterCoeffSum.x, 1.0f / scatterCoeffSum.y, 1.0f / scatterCoeffSum.z);
prog.extinctionCoeff = scatterCoeffSum + absorptionCoeff;
}
// Render a planet sphere with GLSL shaders
void renderSphere_GLSL(const RenderInfo& ri,
const LightingState& ls,
RingSystem* rings,
@ -348,8 +183,7 @@ void renderSphere_GLSL(const RenderInfo& ri,
prog->use();
setLightParameters_GLSL(*prog, shadprop, ls,
ri.color, ri.specularColor);
prog->setLightParameters(ls, ri.color, ri.specularColor);
prog->eyePosition = ls.eyePos_obj;
prog->shininess = ri.specularPower;
@ -376,11 +210,11 @@ void renderSphere_GLSL(const RenderInfo& ri,
if (shadprop.hasScattering())
{
setAtmosphereParameters_GLSL(*prog, *atmosphere, radius, radius);
prog->setAtmosphereParameters(*atmosphere, radius, radius);
}
if (shadprop.shadowCounts != 0)
setEclipseShadowShaderConstants(ls, radius, planetMat, *prog);
prog->setEclipseShadowParameters(ls, radius, planetMat);
glColor(ri.color);
@ -396,17 +230,25 @@ void renderSphere_GLSL(const RenderInfo& ri,
}
// Render a mesh object
void renderModel_GLSL(Model* model,
const RenderInfo& ri,
ResourceHandle texOverride,
const LightingState& ls,
const Atmosphere* atmosphere,
float radius,
int renderFlags,
const Mat4f& planetMat)
{
glDisable(GL_LIGHTING);
GLSL_RenderContext rc(ls, radius, planetMat);
if (renderFlags & Renderer::ShowAtmospheres)
{
rc.setAtmosphere(atmosphere);
}
// Handle extended material attributes (per model only, not per submesh)
rc.setLunarLambert(ri.lunarLambert);
@ -427,6 +269,7 @@ void renderModel_GLSL(Model* model,
}
// Render the cloud sphere for a world a cloud layer defined
void renderClouds_GLSL(const RenderInfo& ri,
const LightingState& ls,
Atmosphere* atmosphere,
@ -507,8 +350,7 @@ void renderClouds_GLSL(const RenderInfo& ri,
prog->use();
setLightParameters_GLSL(*prog, shadprop, ls,
ri.color, ri.specularColor);
prog->setLightParameters(ls, ri.color, ri.specularColor);
prog->eyePosition = ls.eyePos_obj;
prog->ambientColor = Vec3f(ri.ambientColor.red(), ri.ambientColor.green(),
ri.ambientColor.blue());
@ -518,7 +360,7 @@ void renderClouds_GLSL(const RenderInfo& ri,
if (shadprop.hasScattering())
{
setAtmosphereParameters_GLSL(*prog, *atmosphere, radius, cloudRadius);
prog->setAtmosphereParameters(*atmosphere, radius, cloudRadius);
}
if (shadprop.texUsage & ShaderProperties::RingShadowTexture)
@ -529,7 +371,7 @@ void renderClouds_GLSL(const RenderInfo& ri,
}
if (shadprop.shadowCounts != 0)
setEclipseShadowShaderConstants(ls, cloudRadius, planetMat, *prog);
prog->setEclipseShadowParameters(ls, cloudRadius, planetMat);
unsigned int attributes = LODSphereMesh::Normals;
g_lodSphere->render(context,
@ -543,6 +385,7 @@ void renderClouds_GLSL(const RenderInfo& ri,
}
// Render the sky sphere for a world with an atmosphere
void
renderAtmosphere_GLSL(const RenderInfo& ri,
const LightingState& ls,
@ -569,19 +412,19 @@ renderAtmosphere_GLSL(const RenderInfo& ri,
prog->use();
setLightParameters_GLSL(*prog, shadprop, ls,
ri.color, ri.specularColor);
prog->setLightParameters(ls, ri.color, ri.specularColor);
prog->ambientColor = Vec3f(0.0f, 0.0f, 0.0f);
float atmosphereRadius = radius + -atmosphere->mieScaleHeight * (float) log(AtmosphereExtinctionThreshold);
float atmScale = atmosphereRadius / radius;
prog->eyePosition = Point3f(ls.eyePos_obj.x / atmScale, ls.eyePos_obj.y / atmScale, ls.eyePos_obj.z / atmScale);
setAtmosphereParameters_GLSL(*prog, *atmosphere, radius, atmosphereRadius);
prog->setAtmosphereParameters(*atmosphere, radius, atmosphereRadius);
#if 0
#if 0
// Currently eclipse shadows are ignored when rendering atmospheres
if (shadprop.shadowCounts != 0)
setEclipseShadowShaderConstants(ls, radius, planetMat, *prog);
prog->setEclipseShadowParameters(ls, radius, planetMat);
#endif
glPushMatrix();
@ -634,6 +477,7 @@ static void renderRingSystem(float innerRadius,
}
// Render a planetary ring system
void renderRings_GLSL(RingSystem& rings,
RenderInfo& ri,
const LightingState& ls,
@ -675,8 +519,7 @@ void renderRings_GLSL(RingSystem& rings,
prog->eyePosition = ls.eyePos_obj;
prog->ambientColor = Vec3f(ri.ambientColor.red(), ri.ambientColor.green(),
ri.ambientColor.blue());
setLightParameters_GLSL(*prog, shadprop, ls,
ri.color, ri.specularColor);
prog->setLightParameters(ls, ri.color, ri.specularColor);
for (unsigned int li = 0; li < ls.nLights; li++)
{

View File

@ -9,13 +9,10 @@
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
// Atmosphere density is modeled with a exp(-y/H) falloff, where
// H is the scale height of the atmosphere. Thus atmospheres have
// infinite extent, but we still need to choose some finite sphere
// to render. The radius of the sphere is the height at which the
// density of the atmosphere falls to the extinction threshold, i.e.
// -H * ln(extinctionThreshold)
extern const double AtmosphereExtinctionThreshold;
#ifndef _CELENGINE_RENDERGLSL_H_
#define _CELENGINE_RENDERGLSL_H_
#include <celengine/lightenv.h>
void renderSphere_GLSL(const RenderInfo& ri,
const LightingState& ls,
@ -33,7 +30,9 @@ void renderModel_GLSL(Model* model,
const RenderInfo& ri,
ResourceHandle texOverride,
const LightingState& ls,
const Atmosphere* atmosphere,
float radius,
int renderFlags,
const Mat4f& planetMat);
void renderClouds_GLSL(const RenderInfo& ri,
@ -66,6 +65,7 @@ void renderRings_GLSL(RingSystem& rings,
bool renderShadow,
unsigned int nSections);
#endif // _CELENGINE_RENDERGLSL_H_

View File

@ -34,6 +34,7 @@ static const char* errorFragmentShaderSource =
"}\n";
ShaderManager&
GetShaderManager()
{
@ -205,42 +206,6 @@ ShaderManager::getShader(const ShaderProperties& props)
}
CelestiaGLProgram::CelestiaGLProgram(GLProgram& _program,
const ShaderProperties& props) :
program(&_program)
{
initParameters(props);
initSamplers(props);
};
CelestiaGLProgram::~CelestiaGLProgram()
{
delete program;
}
FloatShaderParameter
CelestiaGLProgram::floatParam(const string& paramName)
{
return FloatShaderParameter(program->getID(), paramName.c_str());
}
Vec3ShaderParameter
CelestiaGLProgram::vec3Param(const string& paramName)
{
return Vec3ShaderParameter(program->getID(), paramName.c_str());
}
Vec4ShaderParameter
CelestiaGLProgram::vec4Param(const string& paramName)
{
return Vec4ShaderParameter(program->getID(), paramName.c_str());
}
static string
LightProperty(unsigned int i, char* property)
{
@ -313,142 +278,6 @@ VarScatterInFS()
}
void
CelestiaGLProgram::initParameters(const ShaderProperties& props)
{
for (unsigned int i = 0; i < props.nLights; i++)
{
lights[i].direction = vec3Param(LightProperty(i, "direction"));
lights[i].diffuse = vec3Param(LightProperty(i, "diffuse"));
lights[i].specular = vec3Param(LightProperty(i, "specular"));
lights[i].halfVector = vec3Param(LightProperty(i, "halfVector"));
fragLightColor[i] = vec3Param(FragLightProperty(i, "color"));
fragLightSpecColor[i] = vec3Param(FragLightProperty(i, "specColor"));
for (unsigned int j = 0; j < props.getShadowCountForLight(i); j++)
{
shadows[i][j].texGenS =
vec4Param(IndexedParameter("shadowTexGenS", i, j));
shadows[i][j].texGenT =
vec4Param(IndexedParameter("shadowTexGenT", i, j));
shadows[i][j].scale =
floatParam(IndexedParameter("shadowScale", i, j));
shadows[i][j].bias =
floatParam(IndexedParameter("shadowBias", i, j));
}
}
if (props.hasSpecular())
{
shininess = floatParam("shininess");
}
if (props.isViewDependent() || props.hasScattering())
{
eyePosition = vec3Param("eyePosition");
}
opacity = floatParam("opacity");
ambientColor = vec3Param("ambientColor");
if (props.texUsage & ShaderProperties::RingShadowTexture)
{
ringWidth = floatParam("ringWidth");
ringRadius = floatParam("ringRadius");
}
textureOffset = floatParam("textureOffset");
if (props.texUsage & ShaderProperties::CloudShadowTexture)
{
cloudHeight = floatParam("cloudHeight");
shadowTextureOffset = floatParam("cloudShadowTexOffset");
}
if ((props.texUsage & ShaderProperties::NightTexture) != 0)
{
nightTexMin = floatParam("nightTexMin");
}
if (props.hasScattering())
{
mieCoeff = floatParam("mieCoeff");
mieScaleHeight = floatParam("mieH");
miePhaseAsymmetry = floatParam("mieK");
rayleighCoeff = vec3Param("rayleighCoeff");
rayleighScaleHeight = floatParam("rayleighH");
atmosphereRadius = vec3Param("atmosphereRadius");
scatterCoeffSum = vec3Param("scatterCoeffSum");
invScatterCoeffSum = vec3Param("invScatterCoeffSum");
extinctionCoeff = vec3Param("extinctionCoeff");
}
if (props.lightModel == ShaderProperties::LunarLambertModel)
{
lunarLambert = floatParam("lunarLambert");
}
}
void
CelestiaGLProgram::initSamplers(const ShaderProperties& props)
{
program->use();
unsigned int nSamplers = 0;
if (props.texUsage & ShaderProperties::DiffuseTexture)
{
int slot = glx::glGetUniformLocationARB(program->getID(), "diffTex");
if (slot != -1)
glx::glUniform1iARB(slot, nSamplers++);
}
if (props.texUsage & ShaderProperties::NormalTexture)
{
int slot = glx::glGetUniformLocationARB(program->getID(), "normTex");
if (slot != -1)
glx::glUniform1iARB(slot, nSamplers++);
}
if (props.texUsage & ShaderProperties::SpecularTexture)
{
int slot = glx::glGetUniformLocationARB(program->getID(), "specTex");
if (slot != -1)
glx::glUniform1iARB(slot, nSamplers++);
}
if (props.texUsage & ShaderProperties::NightTexture)
{
int slot = glx::glGetUniformLocationARB(program->getID(), "nightTex");
if (slot != -1)
glx::glUniform1iARB(slot, nSamplers++);
}
if (props.texUsage & ShaderProperties::RingShadowTexture)
{
int slot = glx::glGetUniformLocationARB(program->getID(), "ringTex");
if (slot != -1)
glx::glUniform1iARB(slot, nSamplers++);
}
if (props.texUsage & ShaderProperties::OverlayTexture)
{
int slot = glx::glGetUniformLocationARB(program->getID(), "overlayTex");
if (slot != -1)
glx::glUniform1iARB(slot, nSamplers++);
}
if (props.texUsage & ShaderProperties::CloudShadowTexture)
{
int slot = glx::glGetUniformLocationARB(program->getID(), "cloudShadowTex");
if (slot != -1)
glx::glUniform1iARB(slot, nSamplers++);
}
}
static void
DumpShaderSource(ostream& out, const std::string& source)
{
@ -615,7 +444,7 @@ AssignDiffuse(unsigned int lightIndex, const ShaderProperties& props)
// NH - dot product of light and half vectors
static string
DirectionalLight(unsigned int i, const ShaderProperties& props)
AddDirectionalLightContrib(unsigned int i, const ShaderProperties& props)
{
string source;
@ -1185,7 +1014,7 @@ ShaderManager::buildVertexShader(const ShaderProperties& props)
for (unsigned int i = 0; i < props.nLights; i++)
{
source += DirectionalLight(i, props);
source += AddDirectionalLightContrib(i, props);
}
if (props.texUsage & ShaderProperties::NightTexture)
@ -1991,3 +1820,340 @@ ShaderManager::buildProgram(const ShaderProperties& props)
else
return new CelestiaGLProgram(*prog, props);
}
CelestiaGLProgram::CelestiaGLProgram(GLProgram& _program,
const ShaderProperties& _props) :
program(&_program),
props(_props)
{
initParameters();
initSamplers();
};
CelestiaGLProgram::~CelestiaGLProgram()
{
delete program;
}
FloatShaderParameter
CelestiaGLProgram::floatParam(const string& paramName)
{
return FloatShaderParameter(program->getID(), paramName.c_str());
}
Vec3ShaderParameter
CelestiaGLProgram::vec3Param(const string& paramName)
{
return Vec3ShaderParameter(program->getID(), paramName.c_str());
}
Vec4ShaderParameter
CelestiaGLProgram::vec4Param(const string& paramName)
{
return Vec4ShaderParameter(program->getID(), paramName.c_str());
}
void
CelestiaGLProgram::initParameters()
{
for (unsigned int i = 0; i < props.nLights; i++)
{
lights[i].direction = vec3Param(LightProperty(i, "direction"));
lights[i].diffuse = vec3Param(LightProperty(i, "diffuse"));
lights[i].specular = vec3Param(LightProperty(i, "specular"));
lights[i].halfVector = vec3Param(LightProperty(i, "halfVector"));
fragLightColor[i] = vec3Param(FragLightProperty(i, "color"));
fragLightSpecColor[i] = vec3Param(FragLightProperty(i, "specColor"));
for (unsigned int j = 0; j < props.getShadowCountForLight(i); j++)
{
shadows[i][j].texGenS =
vec4Param(IndexedParameter("shadowTexGenS", i, j));
shadows[i][j].texGenT =
vec4Param(IndexedParameter("shadowTexGenT", i, j));
shadows[i][j].scale =
floatParam(IndexedParameter("shadowScale", i, j));
shadows[i][j].bias =
floatParam(IndexedParameter("shadowBias", i, j));
}
}
if (props.hasSpecular())
{
shininess = floatParam("shininess");
}
if (props.isViewDependent() || props.hasScattering())
{
eyePosition = vec3Param("eyePosition");
}
opacity = floatParam("opacity");
ambientColor = vec3Param("ambientColor");
if (props.texUsage & ShaderProperties::RingShadowTexture)
{
ringWidth = floatParam("ringWidth");
ringRadius = floatParam("ringRadius");
}
textureOffset = floatParam("textureOffset");
if (props.texUsage & ShaderProperties::CloudShadowTexture)
{
cloudHeight = floatParam("cloudHeight");
shadowTextureOffset = floatParam("cloudShadowTexOffset");
}
if ((props.texUsage & ShaderProperties::NightTexture) != 0)
{
nightTexMin = floatParam("nightTexMin");
}
if (props.hasScattering())
{
mieCoeff = floatParam("mieCoeff");
mieScaleHeight = floatParam("mieH");
miePhaseAsymmetry = floatParam("mieK");
rayleighCoeff = vec3Param("rayleighCoeff");
rayleighScaleHeight = floatParam("rayleighH");
atmosphereRadius = vec3Param("atmosphereRadius");
scatterCoeffSum = vec3Param("scatterCoeffSum");
invScatterCoeffSum = vec3Param("invScatterCoeffSum");
extinctionCoeff = vec3Param("extinctionCoeff");
}
if (props.lightModel == ShaderProperties::LunarLambertModel)
{
lunarLambert = floatParam("lunarLambert");
}
}
void
CelestiaGLProgram::initSamplers()
{
program->use();
unsigned int nSamplers = 0;
if (props.texUsage & ShaderProperties::DiffuseTexture)
{
int slot = glx::glGetUniformLocationARB(program->getID(), "diffTex");
if (slot != -1)
glx::glUniform1iARB(slot, nSamplers++);
}
if (props.texUsage & ShaderProperties::NormalTexture)
{
int slot = glx::glGetUniformLocationARB(program->getID(), "normTex");
if (slot != -1)
glx::glUniform1iARB(slot, nSamplers++);
}
if (props.texUsage & ShaderProperties::SpecularTexture)
{
int slot = glx::glGetUniformLocationARB(program->getID(), "specTex");
if (slot != -1)
glx::glUniform1iARB(slot, nSamplers++);
}
if (props.texUsage & ShaderProperties::NightTexture)
{
int slot = glx::glGetUniformLocationARB(program->getID(), "nightTex");
if (slot != -1)
glx::glUniform1iARB(slot, nSamplers++);
}
if (props.texUsage & ShaderProperties::RingShadowTexture)
{
int slot = glx::glGetUniformLocationARB(program->getID(), "ringTex");
if (slot != -1)
glx::glUniform1iARB(slot, nSamplers++);
}
if (props.texUsage & ShaderProperties::OverlayTexture)
{
int slot = glx::glGetUniformLocationARB(program->getID(), "overlayTex");
if (slot != -1)
glx::glUniform1iARB(slot, nSamplers++);
}
if (props.texUsage & ShaderProperties::CloudShadowTexture)
{
int slot = glx::glGetUniformLocationARB(program->getID(), "cloudShadowTex");
if (slot != -1)
glx::glUniform1iARB(slot, nSamplers++);
}
}
void
CelestiaGLProgram::setLightParameters(const LightingState& ls,
Color materialDiffuse,
Color materialSpecular)
{
unsigned int nLights = min(MaxShaderLights, ls.nLights);
Vec3f diffuseColor(materialDiffuse.red(),
materialDiffuse.green(),
materialDiffuse.blue());
Vec3f specularColor(materialSpecular.red(),
materialSpecular.green(),
materialSpecular.blue());
for (unsigned int i = 0; i < nLights; i++)
{
const DirectionalLight& light = ls.lights[i];
Vec3f lightColor = Vec3f(light.color.red(),
light.color.green(),
light.color.blue()) * light.irradiance;
lights[i].direction = light.direction_obj;
if (props.usesShadows() ||
props.usesFragmentLighting() ||
props.lightModel == ShaderProperties::RingIllumModel)
{
fragLightColor[i] = Vec3f(lightColor.x * diffuseColor.x,
lightColor.y * diffuseColor.y,
lightColor.z * diffuseColor.z);
if (props.hasSpecular())
{
fragLightSpecColor[i] = Vec3f(lightColor.x * specularColor.x,
lightColor.y * specularColor.y,
lightColor.z * specularColor.z);
}
}
else
{
lights[i].diffuse = Vec3f(lightColor.x * diffuseColor.x,
lightColor.y * diffuseColor.y,
lightColor.z * diffuseColor.z);
}
lights[i].specular = Vec3f(lightColor.x * specularColor.x,
lightColor.y * specularColor.y,
lightColor.z * specularColor.z);
Vec3f halfAngle_obj = ls.eyeDir_obj + light.direction_obj;
if (halfAngle_obj.length() != 0.0f)
halfAngle_obj.normalize();
lights[i].halfVector = halfAngle_obj;
}
eyePosition = ls.eyePos_obj;
ambientColor = Vec3f(ls.ambientColor.x * diffuseColor.x,
ls.ambientColor.y * diffuseColor.y,
ls.ambientColor.z * diffuseColor.z);
opacity = materialDiffuse.alpha();
}
// Set GLSL shader constants for shadows from ellipsoid occluders; shadows from
// irregular objects are not handled yet.
void
CelestiaGLProgram::setEclipseShadowParameters(const LightingState& ls,
float planetRadius,
const Mat4f& planetMat)
{
for (unsigned int li = 0;
li < min(ls.nLights, MaxShaderLights);
li++)
{
if (shadows != NULL)
{
unsigned int nShadows = min((size_t) MaxShaderShadows, ls.shadows[li]->size());
for (unsigned int i = 0; i < nShadows; i++)
{
EclipseShadow& shadow = ls.shadows[li]->at(i);
CelestiaGLProgramShadow& shadowParams = shadows[li][i];
float R2 = 0.25f;
float umbra = shadow.umbraRadius / shadow.penumbraRadius;
umbra = umbra * umbra;
if (umbra < 0.0001f)
umbra = 0.0001f;
else if (umbra > 0.99f)
umbra = 0.99f;
float umbraRadius = R2 * umbra;
float penumbraRadius = R2;
float shadowBias = 1.0f / (1.0f - penumbraRadius / umbraRadius);
shadowParams.bias = shadowBias;
shadowParams.scale = -shadowBias / umbraRadius;
// Compute the transformation to use for generating texture
// coordinates from the object vertices.
Point3f origin = shadow.origin * planetMat;
Vec3f dir = shadow.direction * planetMat;
float scale = planetRadius / shadow.penumbraRadius;
Vec3f axis = Vec3f(0, 1, 0) ^ dir;
float angle = (float) acos(Vec3f(0, 1, 0) * dir);
axis.normalize();
Mat4f mat = Mat4f::rotation(axis, -angle);
Vec3f sAxis = Vec3f(0.5f * scale, 0, 0) * mat;
Vec3f tAxis = Vec3f(0, 0, 0.5f * scale) * mat;
float sw = (Point3f(0, 0, 0) - origin) * sAxis / planetRadius + 0.5f;
float tw = (Point3f(0, 0, 0) - origin) * tAxis / planetRadius + 0.5f;
shadowParams.texGenS = Vec4f(sAxis.x, sAxis.y, sAxis.z, sw);
shadowParams.texGenT = Vec4f(tAxis.x, tAxis.y, tAxis.z, tw);
}
}
}
}
// Set the scattering and absoroption shader parameters for atmosphere simulation.
// They are from standard units to the normalized system used by the shaders.
// atmPlanetRadius - the radius in km of the planet with the atmosphere
// objRadius - the radius in km of the object we're rendering
void
CelestiaGLProgram::setAtmosphereParameters(const Atmosphere& atmosphere,
float atmPlanetRadius,
float objRadius)
{
// Compute the radius of the sky sphere to render; the density of the atmosphere
// fallse off exponentially with height above the planet's surface, so the actual
// radius is infinite. That's a bit impractical, so well just render the portion
// out to the point where the density is some fraction of the surface density.
float skySphereRadius = atmPlanetRadius + -atmosphere.mieScaleHeight * (float) log(AtmosphereExtinctionThreshold);
float tMieCoeff = atmosphere.mieCoeff * objRadius;
Vec3f tRayleighCoeff = atmosphere.rayleighCoeff * objRadius;
Vec3f tAbsorptionCoeff = atmosphere.absorptionCoeff * objRadius;
float r = skySphereRadius / objRadius;
atmosphereRadius = Vec3f(r, r * r, atmPlanetRadius / objRadius);
mieCoeff = tMieCoeff;
mieScaleHeight = objRadius / atmosphere.mieScaleHeight;
// The scattering shaders use the Schlick approximation to the
// Henyey-Greenstein phase function because it's slightly faster
// to compute. Convert the HG asymmetry parameter to the Schlick
// parameter.
float g = atmosphere.miePhaseAsymmetry;
miePhaseAsymmetry = 1.55f * g - 0.55f * g * g * g;
rayleighCoeff = tRayleighCoeff;
rayleighScaleHeight = 0.0f; // TODO
// Precompute sum and inverse sum of scattering coefficients to save work
// in the vertex shader.
Vec3f tScatterCoeffSum = Vec3f(tRayleighCoeff.x + tMieCoeff,
tRayleighCoeff.y + tMieCoeff,
tRayleighCoeff.z + tMieCoeff);
scatterCoeffSum = tScatterCoeffSum;
invScatterCoeffSum = Vec3f(1.0f / tScatterCoeffSum.x, 1.0f / tScatterCoeffSum.y, 1.0f / tScatterCoeffSum.z);
extinctionCoeff = tScatterCoeffSum + tAbsorptionCoeff;
}

View File

@ -13,6 +13,8 @@
#include <map>
#include <iostream>
#include <celengine/glshader.h>
#include <celengine/lightenv.h>
#include <celengine/atmosphere.h>
class ShaderProperties
{
@ -91,6 +93,16 @@ class CelestiaGLProgram
void use() const { program->use(); }
void setLightParameters(const LightingState& ls,
Color materialDiffuse,
Color materialSpecular);
void setEclipseShadowParameters(const LightingState& ls,
float planetRadius,
const Mat4f& planetMat);
void setAtmosphereParameters(const Atmosphere& atmosphere,
float atmPlanetRadius,
float objRadius);
public:
CelestiaGLProgramLight lights[MaxShaderLights];
Vec3ShaderParameter fragLightColor[MaxShaderLights];
@ -152,14 +164,15 @@ class CelestiaGLProgram
CelestiaGLProgramShadow shadows[MaxShaderLights][MaxShaderShadows];
private:
void initParameters(const ShaderProperties&);
void initSamplers(const ShaderProperties&);
void initParameters();
void initSamplers();
FloatShaderParameter floatParam(const std::string&);
Vec3ShaderParameter vec3Param(const std::string&);
Vec4ShaderParameter vec4Param(const std::string&);
GLProgram* program;
const ShaderProperties props;
};