https://github.com/lingqi/WaveOpticsBrdf
Raw File
Tip revision: 6cab40424e6067bad443ed0a57e51094db898800 authored by Milos Hasan on 14 March 2019, 18:14:50 UTC
fix linux makefile
Tip revision: 6cab404
waveNdf.cpp
#include <Eigen/Dense>
#include <unsupported/Eigen/FFT>
#include <vector>
#include <iostream>
#include "spectrum.h"
#include "waveNdf.h"

using namespace std;
using namespace Eigen;


void WaveNDF::fft2()
{
    FFT<float> fft;
	int n = img.rows();
	tmp.resize(n, n);
	a.resize(n); b.resize(n);

	for (int i = 0; i < n; i++)
	{
		a = img.row(i);
		fft.fwd(b, a);
		tmp.row(i) = b;
	}

	for (int i = 0; i < n; i++)
	{
		a = tmp.col(i);
		fft.fwd(b, a);
		img.col(i) = b;
	}
}


void WaveNDF::fftshift()
{
	int n = img.rows();
	tmp.resize(n, n);
	int nh = n / 2;

	tmp.rightCols(nh) = img.leftCols(nh);
	tmp.leftCols(nh) = img.rightCols(nh);
	img.topRows(nh) = tmp.bottomRows(nh);
	img.bottomRows(nh) = tmp.topRows(nh);
}


void WaveNDF::mkCrop()
{
	cropped.resize(crop, crop);
	int i = (resolution - crop) / 2;
	cropped = rgb.block(i, i, crop, crop);
}


void WaveNDF::generate(const Query& query, const char* outputFilename)
{
	if (query.lambda <= 0) { generateSpectral(query, outputFilename); return; }

	int n = resolution;
	int nh = n / 2;
	img.resize(n, n);
	out.resize(n, n);

	// normalizing constants
	float Ac = M_PI * query.sigma_p * query.sigma_p; // integral of *squared* footprint
	float C = 1 / (Ac * query.lambda * query.lambda); // see Wave NDF doc
	C *= 4; // this approximates the (psi . n)^2 term for small angles
	float T = 2.0f * k * query.sigma_p / n; // sample step in primal domain

	for (int i = 0; i < n; i++)
		for (int j = 0; j < n; j++)
		{
			// map window to [-k, k]^2
			float u = (i + 0.5f - nh) / nh;
			float v = (j + 0.5f - nh) / nh;
			u *= k; v *= k;
			
			// unit Gaussian window
			Float g = std::exp(-0.5f * (u*u + v*v));

			// map to query footprint [-k sigma, k sigma]^2
			u *= query.sigma_p; v *= query.sigma_p;
			u += query.mu_p[0]; v += query.mu_p[1];
			
			// lookup heightfield and compute R^*(u)
			float h = hf.eval(u, v);
			img(i, j) = g * cis(-4 * float(M_PI) * h / query.lambda);
		}

	if (visRs)
	{
		rgb.resize(n, n);
		for (int i = 0; i < n; i++)
			for (int j = 0; j < n; j++)
				rgb(i, j).setConstant(img(i, j).real());
		EXRImage::writeImage((float*) &rgb(0,0), "Rs.exr", n, n);
	}

	fftshift();
	fft2();
	fftshift();
	img *= T * T; // area of one cell

	for (int i = 0; i < n; i++)
		for (int j = 0; j < n; j++)
			out(i, j) = norm(img(i, j)); // "norm" is squared, per C++ complex docs

	// normalize
	out *= C;

	if (outputFilename != nullptr)
	{
		rgb.resize(n, n);
		for (int i = 0; i < n; i++)
			for (int j = 0; j < n; j++)
				rgb(i, j).setConstant(out(i, j));

		float* p = (float*) &rgb(0,0);
		if (crop > 0) { mkCrop(); p = (float*) &cropped(0,0); n = crop; }
		EXRImage::writeImage(p, outputFilename, n, n);
	}
}


void WaveNDF::generateSpectral(const Query& query, const char* outputFilename)
{
	const int s = SPECTRUM_SAMPLES;
	lambdas.resize(s);
	colors.resize(s);
	channels.resize(s);

	for (int i = 0; i < s; i++)
		lambdas[i] = (i + 0.5) / s * (0.7 - 0.3) + 0.3;

	for (int i = 0; i < s; i++)
	{
		Query q = query;
		q.lambda = lambdas[i];
		q.sigma_p *= lambdas[i] / 0.5;
		generate(q, nullptr);
		channels[i] = out;
	}

	float r, g, b;
	int n = resolution;
	rgb.resize(n, n);

	for (int i = 0; i < n; i++)
		for (int j = 0; j < n; j++)
		{
			for (int k = 0; k < s; k++) colors[k] = channels[k](i, j);
			SpectrumToRGB(colors, r, g, b);
			rgb(i, j) = Color(r, g, b);
		}

	float* p = (float*) &rgb(0,0);
	if (crop > 0) { mkCrop(); p = (float*) &cropped(0,0); n = crop; }
	EXRImage::writeImage(p, outputFilename, n, n);
}
back to top