Lights: improve bandwidth usage

It should improve performance a lot, but it handles at most 32 simultaneous lights on screen.

git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/trunk@14800 178a84e3-b1eb-0310-8ba1-8eac791a3b58
This commit is contained in:
vincentlj 2013-12-26 22:55:15 +00:00
parent 508d866a2b
commit 4d0b9236b2
7 changed files with 105 additions and 91 deletions

View File

@ -1,12 +1,13 @@
uniform sampler2D ntex;
uniform vec3 center;
uniform vec3 col;
uniform float r;
uniform vec4 center[32];
uniform vec4 col[32];
uniform float energy[32];
uniform int lightcount;
uniform float spec;
uniform vec2 screen;
uniform mat4 invproj;
uniform float energy;
uniform mat4 viewm;
void main() {
vec2 texc = gl_FragCoord.xy / screen;
@ -16,21 +17,31 @@ void main() {
xpos = invproj * xpos;
xpos /= xpos.w;
float d = distance(center, xpos.xyz);
float att = energy * 200.0 / (4. * 3.14 * d * d);
vec3 diffuse = vec3(0.), specular = vec3(0.);
for (int i = 0; i < lightcount; ++i) {
vec4 pseudocenter = viewm * vec4(center[i].xyz, 1.0);
pseudocenter /= pseudocenter.w;
vec3 light_pos = pseudocenter.xyz;
vec3 light_col = col[i].xyz;
float d = distance(light_pos, xpos.xyz);
float att = energy[i] * 200. / (4. * 3.14 * d * d);
vec3 norm = texture2D(ntex, texc).xyz;
norm = (norm - 0.5) * 2.0;
// Light Direction
vec3 L = normalize(xpos.xyz - center);
vec3 L = normalize(xpos.xyz - light_pos);
float NdotL = max(0.0, dot(norm, -L));
diffuse += NdotL * light_col * att;
// Reflected light dir
vec3 R = reflect(-L, norm);
float RdotE = max(0.0, dot(R, normalize(xpos.xyz)));
float Specular = pow(RdotE, spec);
gl_FragData[0] = vec4(NdotL * col * att, 1.);
gl_FragData[1] = vec4(Specular * col * att, 1.);
specular += Specular * light_col * att;
}
gl_FragData[0] = vec4(diffuse, 1.);
gl_FragData[1] = vec4(specular , 1.);
}

View File

@ -356,13 +356,15 @@ void LightBlendProvider::OnSetConstants(IMaterialRendererServices *srv, int)
void PointLightProvider::OnSetConstants(IMaterialRendererServices *srv, int)
{
int lightcount = m_color.size() / 4;
srv->setVertexShaderConstant("screen", m_screen, 2);
srv->setVertexShaderConstant("spec", &m_specular, 1);
srv->setVertexShaderConstant("col", m_color, 3);
srv->setVertexShaderConstant("center", m_pos, 3);
srv->setVertexShaderConstant("r", &m_radius, 1);
srv->setVertexShaderConstant("invproj", m_invproj.pointer(), 16);
srv->setVertexShaderConstant("energy", &m_energy, 1);
srv->setVertexShaderConstant("energy[0]", m_energy.data(), m_energy.size());
srv->setVertexShaderConstant("col[0]", m_color.data(), m_color.size());
srv->setVertexShaderConstant("center[0]", m_pos.data(), m_pos.size());
srv->setVertexShaderConstant("viewm", m_view.pointer(), 16);
srv->setVertexShaderConstant("lightcount", &lightcount, 1);
if (!firstdone)
{

View File

@ -384,30 +384,15 @@ public:
virtual void OnSetConstants(video::IMaterialRendererServices *srv, int);
void setColor(float r, float g, float b)
void setColor(const std::vector<float> &col)
{
m_color[0] = r;
m_color[1] = g;
m_color[2] = b;
m_color = col;
}
void setPosition(float x, float y, float z)
void setPosition(const std::vector<float> &pos)
{
const video::IVideoDriver * const drv = irr_driver->getVideoDriver();
// get position in eye space coordinates
core::matrix4 m_view = drv->getTransform(video::ETS_VIEW);
float *mat = m_view.pointer();
float scale = mat[3] * x + mat[7] * y + mat[11] * z + mat[15];
m_pos[0] = (mat[0] * x + mat[4] * y + mat[8] * z + mat[12]) / scale;
m_pos[1] = (mat[1] * x + mat[5] * y + mat[9] * z + mat[13]) / scale;
m_pos[2] = (mat[2] * x + mat[6] * y + mat[10] * z + mat[14]) / scale;
}
void setRadius(float r)
{
m_radius = r;
m_pos = pos;
return;
}
void setSpecular(float s)
@ -415,27 +400,26 @@ public:
m_specular = s;
}
void setEnergy(float e) {
void setEnergy(const std::vector<float> &e) {
m_energy = e;
}
void updateIPVMatrix()
{
const video::IVideoDriver * const drv = irr_driver->getVideoDriver();
m_view = drv->getTransform(video::ETS_VIEW);
m_invproj = drv->getTransform(video::ETS_PROJECTION);
m_invproj.makeInverse();
}
private:
core::matrix4 m_invproj;
core::matrix4 m_invproj, m_view;
float m_color[3];
float m_pos[3];
std::vector<float> m_color;
std::vector<float> m_pos;
std::vector<float> m_energy;
float m_screen[2];
float m_radius;
float m_specular;
float m_energy;
};
//

View File

@ -31,15 +31,31 @@ using namespace video;
using namespace scene;
using namespace core;
IMesh * LightNode::sphere = NULL;
SMaterial LightNode::mat;
aabbox3df LightNode::box;
LightNode::LightNode(scene::ISceneManager* mgr, float radius, float e, float r, float g, float b):
ISceneNode(mgr->getRootSceneNode(), mgr, -1)
{
sq = new ScreenQuad(irr_driver->getVideoDriver());
energy = e;
m_color[0] = r;
m_color[1] = g;
m_color[2] = b;
}
LightNode::~LightNode()
{
}
void LightNode::render()
{
return;
}
void LightNode::renderLightSet(const std::vector<float> &positions, const std::vector<float> &colors, const std::vector<float> &energy)
{
assert (colors.size() == positions.size() && positions.size() == (energy.size() * 4));
ScreenQuad *sq = new ScreenQuad(irr_driver->getVideoDriver());
SMaterial &mat = sq->getMaterial();
mat.Lighting = false;
@ -59,28 +75,9 @@ LightNode::LightNode(scene::ISceneManager* mgr, float radius, float e, float r,
mat.MaterialTypeParam = pack_textureBlendFunc(EBF_ONE, EBF_ONE);
mat.BlendOperation = EBO_ADD;
sphere = mgr->getGeometryCreator()->createSphereMesh(1, 16, 16);
box = sphere->getBoundingBox();
setScale(vector3df(radius));
m_radius = radius;
energy = e;
m_color[0] = r;
m_color[1] = g;
m_color[2] = b;
}
LightNode::~LightNode()
{
}
void LightNode::render()
{
PointLightProvider * const cb = (PointLightProvider *) irr_driver->getCallback(ES_POINTLIGHT);
cb->setColor(m_color[0], m_color[1], m_color[2]);
cb->setPosition(getPosition().X, getPosition().Y, getPosition().Z);
cb->setRadius(m_radius);
cb->setColor(colors);
cb->setPosition(positions);
cb->setEnergy(energy);
// Irrlicht's ScreenQuad reset the matrixes, we need to keep them
IVideoDriver * const drv = irr_driver->getVideoDriver();

View File

@ -21,6 +21,7 @@
#include <ISceneNode.h>
#include <utils/cpp2011.h>
#include <vector>
using namespace irr;
@ -37,6 +38,7 @@ public:
virtual ~LightNode();
virtual void render() OVERRIDE;
static void renderLightSet(const std::vector<float> &positions, const std::vector<float> &colors, const std::vector<float> &energy);
virtual const core::aabbox3d<f32>& getBoundingBox() const OVERRIDE
{
@ -46,17 +48,15 @@ public:
virtual void OnRegisterSceneNode() OVERRIDE;
virtual u32 getMaterialCount() const OVERRIDE { return 1; }
virtual video::SMaterial& getMaterial(u32 i) OVERRIDE { return mat; }
virtual bool isCullable() { return true; }
virtual bool isPointLight() { return true; }
float getRadius() const { return m_radius; }
void getColor(float out[3]) const { memcpy(out, m_color, 3 * sizeof(float)); }
float getEnergy() const { return energy; }
core::vector3df getColor() const { return core::vector3df(m_color[0], m_color[1], m_color[2]); }
protected:
static video::SMaterial mat;
static core::aabbox3df box;
static scene::IMesh *sphere;
class ScreenQuad *sq;
float m_radius;

View File

@ -703,13 +703,22 @@ void IrrDriver::renderLights(const core::aabbox3df& cambox,
const core::vector3df &camdir = (camnode->getTarget() - camcenter).normalize();
float fov = camnode->getFOV() / 2.;
std::vector<float> accumulatedLightPos;
std::vector<float> accumulatedLightColor;
std::vector<float> accumulatedLightEnergy;
unsigned lightnum = 0;
for (unsigned int i = 0; i < lightcount; i++)
{
if (!m_lights[i]->isPointLight()) {
m_lights[i]->render();
continue;
}
if (lightnum >= 32)
continue;
// Light culling
const core::vector3df &lightpos = (m_lights[i]->getPosition() - camcenter);
float light_radius = m_lights[i]->getRadius();
float dotprod = camdir.dotProduct(lightpos);
if (m_lights[i]->isCullable()) {
if (dotprod > 0.) {
// Pixels in front of camera
// Are they too far ?
@ -721,9 +730,20 @@ void IrrDriver::renderLights(const core::aabbox3df& cambox,
continue;
} else if (lightpos.getLength() > light_radius)
continue;
}
m_lights[i]->render();
const core::vector3df &pos = m_lights[i]->getPosition();
accumulatedLightPos.push_back(pos.X);
accumulatedLightPos.push_back(pos.Y);
accumulatedLightPos.push_back(pos.Z);
accumulatedLightPos.push_back(0.);
const core::vector3df &col = m_lights[i]->getColor();
accumulatedLightColor.push_back(col.X);
accumulatedLightColor.push_back(col.Y);
accumulatedLightColor.push_back(col.Z);
accumulatedLightColor.push_back(0.);
accumulatedLightEnergy.push_back(m_lights[i]->getEnergy());
lightnum++;
} // for i in lights
LightNode::renderLightSet(accumulatedLightPos, accumulatedLightColor, accumulatedLightEnergy);
// Handle SSAO
SMaterial m_material;
GaussianBlurProvider * const gacb = (GaussianBlurProvider *) irr_driver->

View File

@ -32,7 +32,7 @@ public:
virtual ~SunNode();
virtual void render() OVERRIDE;
virtual bool isCullable() { return false; }
virtual bool isPointLight() OVERRIDE { return false; }
private:
ScreenQuad *sq;