You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
369 lines
11 KiB
369 lines
11 KiB
#ifndef RENDER_H
|
|
#define RENDER_H
|
|
|
|
#include <cmath>
|
|
#include <algorithm>
|
|
|
|
#include "co_types.h"
|
|
#include "common.h"
|
|
#include "geometry.h"
|
|
|
|
|
|
template <typename T>
|
|
struct Camera {
|
|
const T fx;
|
|
const T fy;
|
|
const T px;
|
|
const T py;
|
|
const T R0, R1, R2, R3, R4, R5, R6, R7, R8;
|
|
const T t0, t1, t2;
|
|
const T C0, C1, C2;
|
|
const int height;
|
|
const int width;
|
|
|
|
Camera(const T fx, const T fy, const T px, const T py, const T* R, const T* t, int width, int height) :
|
|
fx(fx), fy(fy), px(px), py(py),
|
|
R0(R[0]), R1(R[1]), R2(R[2]), R3(R[3]), R4(R[4]), R5(R[5]), R6(R[6]), R7(R[7]), R8(R[8]),
|
|
t0(t[0]), t1(t[1]), t2(t[2]),
|
|
C0(-(R[0] * t[0] + R[3] * t[1] + R[6] * t[2])),
|
|
C1(-(R[1] * t[0] + R[4] * t[1] + R[7] * t[2])),
|
|
C2(-(R[2] * t[0] + R[5] * t[1] + R[8] * t[2])),
|
|
height(height), width(width)
|
|
{
|
|
}
|
|
|
|
CPU_GPU_FUNCTION
|
|
inline void to_cam(const T* x, T* y) const {
|
|
y[0] = R0 * x[0] + R1 * x[1] + R2 * x[2] + t0;
|
|
y[1] = R3 * x[0] + R4 * x[1] + R5 * x[2] + t1;
|
|
y[2] = R6 * x[0] + R7 * x[1] + R8 * x[2] + t2;
|
|
}
|
|
|
|
CPU_GPU_FUNCTION
|
|
inline void to_world(const T* x, T* y) const {
|
|
y[0] = R0 * (x[0] - t0) + R3 * (x[1] - t1) + R6 * (x[2] - t2);
|
|
y[1] = R1 * (x[0] - t0) + R4 * (x[1] - t1) + R7 * (x[2] - t2);
|
|
y[2] = R2 * (x[0] - t0) + R5 * (x[1] - t1) + R8 * (x[2] - t2);
|
|
}
|
|
|
|
CPU_GPU_FUNCTION
|
|
inline void to_ray(const int h, const int w, T* dir) const {
|
|
T uhat[2];
|
|
uhat[0] = (w - px) / fx;
|
|
uhat[1] = (h - py) / fy;
|
|
dir[0] = R0 * (uhat[0]) + R3 * (uhat[1]) + R6;
|
|
dir[1] = R1 * (uhat[0]) + R4 * (uhat[1]) + R7;
|
|
dir[2] = R2 * (uhat[0]) + R5 * (uhat[1]) + R8;
|
|
}
|
|
|
|
CPU_GPU_FUNCTION
|
|
inline void to_2d(const T* xyz, T* u, T* v, T* d) const {
|
|
T xyz_t[3];
|
|
to_cam(xyz, xyz_t);
|
|
*u = fx * xyz_t[0] + px * xyz_t[2];
|
|
*v = fy * xyz_t[1] + py * xyz_t[2];
|
|
*d = xyz_t[2];
|
|
*u /= *d;
|
|
*v /= *d;
|
|
}
|
|
|
|
CPU_GPU_FUNCTION
|
|
inline void get_C(T* C) const {
|
|
C[0] = C0;
|
|
C[1] = C1;
|
|
C[2] = C2;
|
|
}
|
|
|
|
CPU_GPU_FUNCTION
|
|
inline int num_pixel() const {
|
|
return height * width;
|
|
}
|
|
};
|
|
|
|
|
|
template <typename T>
|
|
struct RenderInput {
|
|
T* verts;
|
|
T* colors;
|
|
T* normals;
|
|
int n_verts;
|
|
int* faces;
|
|
int n_faces;
|
|
|
|
RenderInput() : verts(nullptr), colors(nullptr), normals(nullptr), n_verts(0), faces(nullptr), n_faces(0) {}
|
|
};
|
|
|
|
template <typename T>
|
|
struct Buffer {
|
|
T* depth;
|
|
T* color;
|
|
T* normal;
|
|
|
|
Buffer() : depth(nullptr), color(nullptr), normal(nullptr) {}
|
|
};
|
|
|
|
template <typename T>
|
|
struct Shader {
|
|
const T ka;
|
|
const T kd;
|
|
const T ks;
|
|
const T alpha;
|
|
|
|
Shader(T ka, T kd, T ks, T alpha) : ka(ka), kd(kd), ks(ks), alpha(alpha) {}
|
|
|
|
CPU_GPU_FUNCTION
|
|
T operator()(const T* orig, const T* sp, const T* lp, const T* norm) const {
|
|
return reflectance_phong(orig, sp, lp, norm, ka, kd, ks, alpha);
|
|
}
|
|
};
|
|
|
|
|
|
|
|
template <typename T>
|
|
class BaseRenderer {
|
|
public:
|
|
const Camera<T> cam;
|
|
const Shader<T> shader;
|
|
Buffer<T> buffer;
|
|
|
|
BaseRenderer(const Camera<T> cam, const Shader<T> shader, Buffer<T> buffer) : cam(cam), shader(shader), buffer(buffer) {
|
|
}
|
|
|
|
virtual ~BaseRenderer() {}
|
|
|
|
virtual void render_mesh(const RenderInput<T> input) = 0;
|
|
virtual void render_mesh_proj(const RenderInput<T> input, const Camera<T> proj, const float* pattern, float d_alpha, float d_beta) = 0;
|
|
};
|
|
|
|
|
|
|
|
template <typename T>
|
|
struct RenderFunctor {
|
|
const Camera<T> cam;
|
|
const Shader<T> shader;
|
|
Buffer<T> buffer;
|
|
|
|
RenderFunctor(const Camera<T> cam, const Shader<T> shader, Buffer<T> buffer) : cam(cam), shader(shader), buffer(buffer) {}
|
|
};
|
|
|
|
|
|
template <typename T>
|
|
struct RenderMeshFunctor : public RenderFunctor<T> {
|
|
const RenderInput<T> input;
|
|
|
|
RenderMeshFunctor(const RenderInput<T> input, const Shader<T> shader, const Camera<T> cam, Buffer<T> buffer) : RenderFunctor<T>(cam, shader,buffer), input(input) {
|
|
}
|
|
|
|
CPU_GPU_FUNCTION void operator()(const int idx) {
|
|
int h = idx / this->cam.width;
|
|
int w = idx % this->cam.width;
|
|
|
|
T orig[3];
|
|
this->cam.get_C(orig);
|
|
T dir[3];
|
|
this->cam.to_ray(h, w, dir);
|
|
|
|
int face_idx;
|
|
T t, tu, tv;
|
|
bool valid = ray_triangle_mesh_intersect_3d(orig, dir, this->input.faces, this->input.n_faces, this->input.verts, &face_idx, &t, &tu, &tv);
|
|
|
|
if(this->buffer.depth != nullptr) {
|
|
this->buffer.depth[idx] = valid ? t : -1;
|
|
}
|
|
|
|
if(!valid) {
|
|
if(this->buffer.color != nullptr) {
|
|
this->buffer.color[idx * 3 + 0] = 0;
|
|
this->buffer.color[idx * 3 + 1] = 0;
|
|
this->buffer.color[idx * 3 + 2] = 0;
|
|
}
|
|
if(this->buffer.normal != nullptr) {
|
|
this->buffer.normal[idx * 3 + 0] = 0;
|
|
this->buffer.normal[idx * 3 + 1] = 0;
|
|
this->buffer.normal[idx * 3 + 2] = 0;
|
|
}
|
|
}
|
|
else if(this->buffer.normal != nullptr || this->buffer.color != nullptr) {
|
|
const int* face = input.faces + face_idx * 3;
|
|
T tw = 1 - tu - tv;
|
|
|
|
T norm[3];
|
|
vec_fill(norm, 0.f);
|
|
vec_add(1.f, norm, tu, this->input.normals + face[0] * 3, norm);
|
|
vec_add(1.f, norm, tv, this->input.normals + face[1] * 3, norm);
|
|
vec_add(1.f, norm, tw, this->input.normals + face[2] * 3, norm);
|
|
if(vec_dot(norm, dir) > 0) {
|
|
vec_mul_scalar(norm, -1.f, norm);
|
|
}
|
|
|
|
if(this->buffer.normal != nullptr) {
|
|
this->buffer.normal[idx * 3 + 0] = norm[0];
|
|
this->buffer.normal[idx * 3 + 1] = norm[1];
|
|
this->buffer.normal[idx * 3 + 2] = norm[2];
|
|
}
|
|
|
|
if(this->buffer.color != nullptr) {
|
|
T color[3];
|
|
vec_fill(color, 0.f);
|
|
vec_add(1.f, color, tu, this->input.colors + face[0] * 3, color);
|
|
vec_add(1.f, color, tv, this->input.colors + face[1] * 3, color);
|
|
vec_add(1.f, color, tw, this->input.colors + face[2] * 3, color);
|
|
|
|
T sp[3];
|
|
vec_add(1.f, orig, t, dir, sp);
|
|
T reflectance = this->shader(orig, sp, orig, norm);
|
|
|
|
this->buffer.color[idx * 3 + 0] = mmin(1.f, mmax(0.f, reflectance * color[0]));
|
|
this->buffer.color[idx * 3 + 1] = mmin(1.f, mmax(0.f, reflectance * color[1]));
|
|
this->buffer.color[idx * 3 + 2] = mmin(1.f, mmax(0.f, reflectance * color[2]));
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
template <typename T, int n=3>
|
|
CPU_GPU_FUNCTION
|
|
inline void interpolate_linear(const T* im, T x, T y, int height, int width, T* out_vec) {
|
|
int x1 = int(x);
|
|
int y1 = int(y);
|
|
int x2 = x1 + 1;
|
|
int y2 = y1 + 1;
|
|
|
|
T denom = (x2 - x1) * (y2 - y1);
|
|
T t11 = (x2 - x) * (y2 - y);
|
|
T t21 = (x - x1) * (y2 - y);
|
|
T t12 = (x2 - x) * (y - y1);
|
|
T t22 = (x - x1) * (y - y1);
|
|
|
|
x1 = mmin(mmax(x1, int(0)), width-1);
|
|
x2 = mmin(mmax(x2, int(0)), width-1);
|
|
y1 = mmin(mmax(y1, int(0)), height-1);
|
|
y2 = mmin(mmax(y2, int(0)), height-1);
|
|
|
|
for(int idx = 0; idx < n; ++idx) {
|
|
out_vec[idx] = (im[(y1 * width + x1) * 3 + idx] * t11 +
|
|
im[(y2 * width + x1) * 3 + idx] * t12 +
|
|
im[(y1 * width + x2) * 3 + idx] * t21 +
|
|
im[(y2 * width + x2) * 3 + idx] * t22) / denom;
|
|
}
|
|
}
|
|
|
|
template <typename T>
|
|
struct RenderProjectorFunctor : public RenderFunctor<T> {
|
|
const RenderInput<T> input;
|
|
const Camera<T> proj;
|
|
const float* pattern;
|
|
const float d_alpha;
|
|
const float d_beta;
|
|
|
|
RenderProjectorFunctor(const RenderInput<T> input, const Shader<T> shader, const Camera<T> cam, const Camera<T> proj, const float* pattern, float d_alpha, float d_beta, Buffer<T> buffer) : RenderFunctor<T>(cam, shader, buffer), input(input), proj(proj), pattern(pattern), d_alpha(d_alpha), d_beta(d_beta) {
|
|
}
|
|
|
|
CPU_GPU_FUNCTION void operator()(const int idx) {
|
|
int h = idx / this->cam.width;
|
|
int w = idx % this->cam.width;
|
|
|
|
T orig[3];
|
|
this->cam.get_C(orig);
|
|
T dir[3];
|
|
this->cam.to_ray(h, w, dir);
|
|
|
|
int face_idx;
|
|
T t, tu, tv;
|
|
bool valid = ray_triangle_mesh_intersect_3d(orig, dir, this->input.faces, this->input.n_faces, this->input.verts, &face_idx, &t, &tu, &tv);
|
|
if(this->buffer.depth != nullptr) {
|
|
this->buffer.depth[idx] = valid ? t : -1;
|
|
}
|
|
|
|
this->buffer.color[idx * 3 + 0] = 0;
|
|
this->buffer.color[idx * 3 + 1] = 0;
|
|
this->buffer.color[idx * 3 + 2] = 0;
|
|
|
|
if(valid) {
|
|
if(this->buffer.normal != nullptr) {
|
|
const int* face = input.faces + face_idx * 3;
|
|
T tw = 1 - tu - tv;
|
|
|
|
T norm[3];
|
|
vertex_normal_3d(
|
|
this->input.verts + face[0] * 3,
|
|
this->input.verts + face[1] * 3,
|
|
this->input.verts + face[2] * 3,
|
|
norm);
|
|
vec_normalize(norm, norm);
|
|
|
|
if(vec_dot(norm, dir) > 0) {
|
|
vec_mul_scalar(norm, -1.f, norm);
|
|
}
|
|
|
|
T color[3];
|
|
vec_fill(color, 0.f);
|
|
vec_add(1.f, color, tu, this->input.colors + face[0] * 3, color);
|
|
vec_add(1.f, color, tv, this->input.colors + face[1] * 3, color);
|
|
vec_add(1.f, color, tw, this->input.colors + face[2] * 3, color);
|
|
|
|
T sp[3];
|
|
vec_add(1.f, orig, t, dir, sp);
|
|
T reflectance = this->shader(orig, sp, orig, norm);
|
|
|
|
this->buffer.normal[idx * 3 + 0] = mmin(1.f, mmax(0.f, reflectance * color[0]));
|
|
this->buffer.normal[idx * 3 + 1] = mmin(1.f, mmax(0.f, reflectance * color[1]));
|
|
this->buffer.normal[idx * 3 + 2] = mmin(1.f, mmax(0.f, reflectance * color[2]));
|
|
}
|
|
|
|
// get 3D point
|
|
T pt[3];
|
|
vec_mul_scalar(dir, t, pt);
|
|
vec_add(orig, pt, pt);
|
|
|
|
// get dir from proj
|
|
T proj_orig[3];
|
|
proj.get_C(proj_orig);
|
|
T proj_dir[3];
|
|
vec_sub(pt, proj_orig, proj_dir);
|
|
vec_div_scalar(proj_dir, proj_dir[2], proj_dir);
|
|
|
|
// check if it hit same tria
|
|
int p_face_idx;
|
|
T p_t, p_tu, p_tv;
|
|
valid = ray_triangle_mesh_intersect_3d(proj_orig, proj_dir, this->input.faces, this->input.n_faces, this->input.verts, &p_face_idx, &p_t, &p_tu, &p_tv);
|
|
// if(!valid || p_face_idx != face_idx) {
|
|
// return;
|
|
// }
|
|
|
|
T p_pt[3];
|
|
vec_mul_scalar(proj_dir, p_t, p_pt);
|
|
vec_add(proj_orig, p_pt, p_pt);
|
|
T diff[3];
|
|
vec_sub(p_pt, pt, diff);
|
|
if(!valid || vec_norm(diff) > 1e-5) {
|
|
return;
|
|
}
|
|
|
|
// get uv in proj
|
|
T u,v,d;
|
|
proj.to_2d(p_pt, &u,&v,&d);
|
|
|
|
// if valid u,v than use it to inpaint
|
|
if(u >= 0 && v >= 0 && u < this->proj.width && v < this->proj.height) {
|
|
// int pattern_idx = ((int(v) * this->proj.width) + int(u)) * 3;
|
|
// this->buffer.color[idx * 3 + 0] = pattern[pattern_idx + 0];
|
|
// this->buffer.color[idx * 3 + 1] = pattern[pattern_idx + 1];
|
|
// this->buffer.color[idx * 3 + 2] = pattern[pattern_idx + 2];
|
|
interpolate_linear(pattern, u, v, this->proj.height, this->proj.width, this->buffer.color + idx * 3);
|
|
|
|
// decay based on distance
|
|
T decay = d_alpha + d_beta * d;
|
|
decay *= decay;
|
|
decay = mmax(decay, T(1));
|
|
vec_div_scalar(this->buffer.color + idx * 3, decay, this->buffer.color + idx * 3);
|
|
}
|
|
}
|
|
|
|
}
|
|
};
|
|
|
|
|
|
|
|
|
|
#endif
|
|
|