Filtering optimizations.
This commit is contained in:
parent
18b3f8025e
commit
149a50a26f
@ -228,42 +228,35 @@ static float solidAngleTerm(uint x, uint y, float inverseEdgeLength) {
|
||||
|
||||
|
||||
// Small solid angle table that takes into account cube map symmetry.
|
||||
struct SolidAngleTable {
|
||||
SolidAngleTable::SolidAngleTable(uint edgeLength) : size(edgeLength/2) {
|
||||
// Allocate table.
|
||||
data.resize(size * size);
|
||||
|
||||
SolidAngleTable(uint edgeLength) : size(edgeLength/2) {
|
||||
// Allocate table.
|
||||
data.resize(size * size);
|
||||
// Init table.
|
||||
const float inverseEdgeLength = 1.0f / edgeLength;
|
||||
|
||||
// Init table.
|
||||
const float inverseEdgeLength = 1.0f / edgeLength;
|
||||
|
||||
for (uint y = 0; y < size; y++) {
|
||||
for (uint x = 0; x < size; x++) {
|
||||
data[y * size + x] = solidAngleTerm(128+x, 128+y, inverseEdgeLength);
|
||||
}
|
||||
for (uint y = 0; y < size; y++) {
|
||||
for (uint x = 0; x < size; x++) {
|
||||
data[y * size + x] = solidAngleTerm(128+x, 128+y, inverseEdgeLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float lookup(uint x, uint y) const {
|
||||
if (x >= size) x -= size;
|
||||
else if (x < size) x = size - x - 1;
|
||||
if (y >= size) y -= size;
|
||||
else if (y < size) y = size - y - 1;
|
||||
float SolidAngleTable::lookup(uint x, uint y) const {
|
||||
if (x >= size) x -= size;
|
||||
else if (x < size) x = size - x - 1;
|
||||
if (y >= size) y -= size;
|
||||
else if (y < size) y = size - y - 1;
|
||||
|
||||
return data[y * size + x];
|
||||
}
|
||||
|
||||
uint size;
|
||||
nv::Array<float> data;
|
||||
};
|
||||
return data[y * size + x];
|
||||
}
|
||||
|
||||
|
||||
// ilen = inverse edge length.
|
||||
static Vector3 texelDirection(uint face, uint x, uint y, float ilen)
|
||||
static Vector3 texelDirection(uint face, uint x, uint y, float inverseEdgeLength)
|
||||
{
|
||||
// Transform x,y to [-1, 1] range, offset by 0.5 to point to texel center.
|
||||
float u = (float(x) + 0.5f) * (2 * ilen) - 1.0f;
|
||||
float v = (float(y) + 0.5f) * (2 * ilen) - 1.0f;
|
||||
float u = (float(x) + 0.5f) * (2 * inverseEdgeLength) - 1.0f;
|
||||
float v = (float(y) + 0.5f) * (2 * inverseEdgeLength) - 1.0f;
|
||||
nvDebugCheck(u >= -1.0f && u <= 1.0f);
|
||||
nvDebugCheck(v >= -1.0f && v <= 1.0f);
|
||||
|
||||
@ -305,29 +298,105 @@ static Vector3 texelDirection(uint face, uint x, uint y, float ilen)
|
||||
return normalizeFast(n);
|
||||
}
|
||||
|
||||
struct VectorTable {
|
||||
VectorTable(uint edgeLength) : size(edgeLength) {
|
||||
float invEdgeLength = 1.0f / edgeLength;
|
||||
|
||||
data.resize(size*size*6);
|
||||
VectorTable::VectorTable(uint edgeLength) : size(edgeLength) {
|
||||
float invEdgeLength = 1.0f / edgeLength;
|
||||
|
||||
for (uint f = 0; f < 6; f++) {
|
||||
for (uint y = 0; y < size; y++) {
|
||||
for (uint x = 0; x < size; x++) {
|
||||
data[(f * size + y) * size + x] = texelDirection(f, x, y, invEdgeLength);
|
||||
data.resize(size*size*6);
|
||||
|
||||
for (uint f = 0; f < 6; f++) {
|
||||
for (uint y = 0; y < size; y++) {
|
||||
for (uint x = 0; x < size; x++) {
|
||||
data[(f * size + y) * size + x] = texelDirection(f, x, y, invEdgeLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const Vector3 & VectorTable::lookup(uint f, uint x, uint y) const {
|
||||
nvDebugCheck(f < 6 && x < size && y < size);
|
||||
return data[(f * size + y) * size + x];
|
||||
}
|
||||
|
||||
|
||||
|
||||
// We want to find the alpha such that:
|
||||
// cos(alpha)^cosinePower = epsilon
|
||||
// That's: acos(epsilon^(1/cosinePower))
|
||||
|
||||
// We can cull texels in two different ways:
|
||||
// - culling faces that do not touch the cone.
|
||||
// - computing one rectangle per face, find intersection between cone and face.
|
||||
// -
|
||||
|
||||
// Other speedups:
|
||||
// - parallelize.
|
||||
// - use ISPC?
|
||||
|
||||
static Vector3 faceNormals[6] = {
|
||||
Vector3(1, 0, 0),
|
||||
Vector3(-1, 0, 0),
|
||||
Vector3(0, 1, 0),
|
||||
Vector3(0, -1, 0),
|
||||
Vector3(0, 0, 1),
|
||||
Vector3(0, 0, -1),
|
||||
};
|
||||
|
||||
|
||||
// Convolve filter against this cube.
|
||||
Vector3 CubeSurface::Private::applyCosinePowerFilter(const Vector3 & filterDir, float cosineConeAngle, float cosinePower)
|
||||
{
|
||||
const float coneAngle = acos(cosineConeAngle);
|
||||
|
||||
Vector3 color(0);
|
||||
float sum = 0;
|
||||
|
||||
// For each texel of the input cube.
|
||||
for (uint f = 0; f < 6; f++) {
|
||||
|
||||
// Test face cone agains filter cone.
|
||||
float cosineFaceAngle = dot(filterDir, faceNormals[f]);
|
||||
|
||||
if (cosineFaceAngle > cos(coneAngle + atan(sqrt(2)))) { // @@ Simplify this with cos(a+b) = cos(a)cos(b) - sin(a)sin(b) formula?
|
||||
// Skip face.
|
||||
continue;
|
||||
}
|
||||
|
||||
// @@ We could do a less conservative test and test the face frustum against the cone...
|
||||
|
||||
// @@ Compute bounding box of cone intersection against face.
|
||||
// The intersection of the cone with the face is an elipse, we want the extents of that elipse.
|
||||
// Hmm... we could even rasterize an elipse! Sounds like FUN!
|
||||
uint x0 = 0, x1 = edgeLength-1;
|
||||
uint y0 = 0, y1 = edgeLength-1;
|
||||
|
||||
const Surface & inputFace = face[f];
|
||||
const FloatImage * inputImage = inputFace.m->image;
|
||||
|
||||
for (uint y = y0; y <= y1; y++) {
|
||||
for (uint x = x0; x <= x1; x++) {
|
||||
|
||||
Vector3 dir = vectorTable->lookup(f, x, y);
|
||||
float cosineAngle = dot(dir, filterDir);
|
||||
|
||||
if (cosineAngle > cosineConeAngle) {
|
||||
float solidAngle = solidAngleTable->lookup(x, y);
|
||||
float scale = powf(saturate(cosineAngle), cosinePower);
|
||||
float contribution = solidAngle * scale;
|
||||
|
||||
sum += contribution;
|
||||
color.x += contribution * inputImage->pixel(0, x, y, 0);
|
||||
color.y += contribution * inputImage->pixel(1, x, y, 0);
|
||||
color.z += contribution * inputImage->pixel(2, x, y, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const Vector3 & lookup(uint f, uint x, uint y) {
|
||||
nvDebugCheck(f < 6 && x < size && y < size);
|
||||
return data[(f * size + y) * size + x];
|
||||
}
|
||||
color *= (1.0f / sum);
|
||||
|
||||
uint size;
|
||||
nv::Array<Vector3> data;
|
||||
};
|
||||
return color;
|
||||
}
|
||||
|
||||
|
||||
CubeSurface CubeSurface::cosinePowerFilter(int size, float cosinePower) const
|
||||
@ -338,10 +407,18 @@ CubeSurface CubeSurface::cosinePowerFilter(int size, float cosinePower) const
|
||||
CubeSurface filteredCube;
|
||||
filteredCube.m->allocate(size);
|
||||
|
||||
SolidAngleTable solidAngleTable(edgeLength);
|
||||
VectorTable vectorTable(edgeLength);
|
||||
// Store these tables along with the surface. Compute them only once!
|
||||
if (m->solidAngleTable == NULL) {
|
||||
m->solidAngleTable = new SolidAngleTable(edgeLength);
|
||||
}
|
||||
if (m->vectorTable == NULL) {
|
||||
m->vectorTable = new VectorTable(edgeLength);
|
||||
}
|
||||
|
||||
const float threshold = 0.0001f;
|
||||
const float cosineConeAngle = pow(threshold, 1/cosinePower);
|
||||
//const float coneAngle = acos(cosineConeAngle);
|
||||
|
||||
|
||||
#if 0
|
||||
// Scatter approach.
|
||||
@ -421,41 +498,8 @@ CubeSurface CubeSurface::cosinePowerFilter(int size, float cosinePower) const
|
||||
|
||||
const Vector3 filterDir = texelDirection(f, x, y, 1.0f / size);
|
||||
|
||||
Vector3 color(0);
|
||||
float sum = 0;
|
||||
|
||||
// For each texel of the input cube.
|
||||
for (uint ff = 0; ff < 6; ff++) {
|
||||
const Surface & inputFace = m->face[ff];
|
||||
const FloatImage * inputImage = inputFace.m->image;
|
||||
|
||||
for (uint yy = 0; yy < edgeLength; yy++) {
|
||||
for (uint xx = 0; xx < edgeLength; xx++) {
|
||||
|
||||
// @@ We should probably store solid angle and direction together.
|
||||
Vector3 inputDir = vectorTable.lookup(ff, xx, yy);
|
||||
|
||||
float scale = powf(saturate(dot(inputDir, filterDir)), cosinePower);
|
||||
|
||||
if (scale > threshold) {
|
||||
float solidAngle = solidAngleTable.lookup(xx, yy);
|
||||
float contribution = solidAngle * scale;
|
||||
|
||||
sum += contribution;
|
||||
|
||||
float r = inputImage->pixel(0, xx, yy, 0);
|
||||
float g = inputImage->pixel(1, xx, yy, 0);
|
||||
float b = inputImage->pixel(2, xx, yy, 0);
|
||||
|
||||
color.x += r * contribution;
|
||||
color.y += g * contribution;
|
||||
color.z += b * contribution;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
color *= (1.0f / sum);
|
||||
// Convolve filter against cube.
|
||||
Vector3 color = m->applyCosinePowerFilter(filterDir, cosineConeAngle, cosinePower);
|
||||
|
||||
filteredImage->pixel(0, x, y, 0) = color.x;
|
||||
filteredImage->pixel(1, x, y, 0) = color.y;
|
||||
@ -464,6 +508,26 @@ CubeSurface CubeSurface::cosinePowerFilter(int size, float cosinePower) const
|
||||
}
|
||||
}
|
||||
|
||||
/*int jobCount = 6 * size * size;
|
||||
for (int i = 0; i < jobCount; i++) {
|
||||
int f = i / (size * size);
|
||||
int idx = i % (size * size);
|
||||
int y = idx / size;
|
||||
int x = idx % size;
|
||||
|
||||
nvtt::Surface filteredFace = filteredCube.m->face[f];
|
||||
FloatImage * filteredImage = filteredFace.m->image;
|
||||
|
||||
const Vector3 filterDir = texelDirection(f, x, y, 1.0f / size);
|
||||
|
||||
// Convolve filter against cube.
|
||||
Vector3 color = m->applyCosinePowerFilter(filterDir, coneAngle, cosinePower);
|
||||
|
||||
filteredImage->pixel(0, idx) = color.x;
|
||||
filteredImage->pixel(1, idx) = color.y;
|
||||
filteredImage->pixel(2, idx) = color.z;
|
||||
}*/
|
||||
|
||||
#endif
|
||||
|
||||
return filteredCube;
|
||||
|
@ -29,12 +29,32 @@
|
||||
|
||||
#include "nvimage/FloatImage.h"
|
||||
|
||||
#include "nvmath/Vector.h"
|
||||
|
||||
#include "nvcore/RefCounted.h"
|
||||
#include "nvcore/Ptr.h"
|
||||
#include "nvcore/Array.h"
|
||||
|
||||
|
||||
namespace nvtt
|
||||
{
|
||||
struct SolidAngleTable {
|
||||
SolidAngleTable(uint edgeLength);
|
||||
float lookup(uint x, uint y) const;
|
||||
|
||||
uint size;
|
||||
nv::Array<float> data;
|
||||
|
||||
};
|
||||
|
||||
struct VectorTable {
|
||||
VectorTable(uint edgeLength);
|
||||
const nv::Vector3 & lookup(uint f, uint x, uint y) const;
|
||||
|
||||
uint size;
|
||||
nv::Array<nv::Vector3> data;
|
||||
};
|
||||
|
||||
|
||||
struct CubeSurface::Private : public nv::RefCounted
|
||||
{
|
||||
@ -45,6 +65,8 @@ namespace nvtt
|
||||
nvDebugCheck( refCount() == 0 );
|
||||
|
||||
edgeLength = 0;
|
||||
solidAngleTable = NULL;
|
||||
vectorTable = NULL;
|
||||
}
|
||||
Private(const Private & p) : RefCounted() // Copy ctor. inits refcount to 0.
|
||||
{
|
||||
@ -54,9 +76,13 @@ namespace nvtt
|
||||
for (uint i = 0; i < 6; i++) {
|
||||
face[i] = p.face[6];
|
||||
}
|
||||
solidAngleTable = NULL; // @@ Transfer tables. Needs refcounting?
|
||||
vectorTable = NULL;
|
||||
}
|
||||
~Private()
|
||||
{
|
||||
delete solidAngleTable;
|
||||
delete vectorTable;
|
||||
}
|
||||
|
||||
void allocate(uint edgeLength)
|
||||
@ -69,8 +95,13 @@ namespace nvtt
|
||||
}
|
||||
}
|
||||
|
||||
// Filtering helpers:
|
||||
nv::Vector3 applyCosinePowerFilter(const nv::Vector3 & dir, float coneAngle, float cosinePower);
|
||||
|
||||
uint edgeLength;
|
||||
Surface face[6];
|
||||
SolidAngleTable * solidAngleTable;
|
||||
VectorTable * vectorTable;
|
||||
};
|
||||
|
||||
} // nvtt namespace
|
||||
|
@ -568,6 +568,7 @@ namespace nvtt
|
||||
NVTT_API CubeSurface irradianceFilter(int size) const;
|
||||
NVTT_API CubeSurface cosinePowerFilter(int size, float cosinePower) const;
|
||||
|
||||
|
||||
/*
|
||||
NVTT_API void resize(int w, int h, ResizeFilter filter);
|
||||
NVTT_API void resize(int w, int h, ResizeFilter filter, float filterWidth, const float * params = 0);
|
||||
|
Loading…
Reference in New Issue
Block a user