PolyFEM
Loading...
Searching...
No Matches
raster.cpp
Go to the documentation of this file.
1#include "raster.hpp"
2
3#include <igl/per_vertex_normals.h>
4#include <iostream>
5
6namespace polyfem
7{
8 namespace renderer
9 {
10 namespace
11 {
12 void rasterize_triangle(const Program &program, const UniformAttributes &uniform, const VertexAttributes &v1, const VertexAttributes &v2, const VertexAttributes &v3, Eigen::Matrix<FrameBufferAttributes, Eigen::Dynamic, Eigen::Dynamic> &frameBuffer)
13 {
14 Eigen::Matrix<double, 3, 4> p;
15 p.row(0) = v1.position.array() / v1.position[3];
16 p.row(1) = v2.position.array() / v2.position[3];
17 p.row(2) = v3.position.array() / v3.position[3];
18
19 p.col(0) = ((p.col(0).array() + 1.0) / 2.0) * frameBuffer.rows();
20 p.col(1) = ((p.col(1).array() + 1.0) / 2.0) * frameBuffer.cols();
21
22 int lx = std::floor(p.col(0).minCoeff());
23 int ly = std::floor(p.col(1).minCoeff());
24 int ux = std::ceil(p.col(0).maxCoeff());
25 int uy = std::ceil(p.col(1).maxCoeff());
26
27 lx = std::min(std::max(lx, int(0)), int(frameBuffer.rows() - 1));
28 ly = std::min(std::max(ly, int(0)), int(frameBuffer.cols() - 1));
29 ux = std::max(std::min(ux, int(frameBuffer.rows() - 1)), int(0));
30 uy = std::max(std::min(uy, int(frameBuffer.cols() - 1)), int(0));
31
32 Eigen::Matrix3d A;
33 A.col(0) = p.row(0).segment(0, 3);
34 A.col(1) = p.row(1).segment(0, 3);
35 A.col(2) = p.row(2).segment(0, 3);
36 A.row(2) << 1.0, 1.0, 1.0;
37
38 Eigen::Matrix3d Ai = A.inverse();
39
40 for (unsigned i = lx; i <= ux; i++)
41 {
42 for (unsigned j = ly; j <= uy; j++)
43 {
44
45 Eigen::Vector3d pixel(i + 0.5, j + 0.5, 1);
46 Eigen::Vector3d b = Ai * pixel;
47 if (b.minCoeff() >= 0)
48 {
49 VertexAttributes va = VertexAttributes::interpolate(v1, v2, v3, b[0], b[1], b[2]);
50
51 if (va.position[2] >= -1 && va.position[2] <= 1)
52 {
53 FragmentAttributes frag = program.FragmentShader(va, uniform);
54 frameBuffer(i, j) = program.BlendingShader(frag, frameBuffer(i, j));
55 }
56 }
57 }
58 }
59 }
60
61 void rasterize_triangles(const Program &program, const UniformAttributes &uniform, const std::vector<VertexAttributes> &vertices, Eigen::Matrix<FrameBufferAttributes, Eigen::Dynamic, Eigen::Dynamic> &frameBuffer)
62 {
63 std::vector<VertexAttributes> v(vertices.size());
64 for (unsigned i = 0; i < vertices.size(); i++)
65 v[i] = program.VertexShader(vertices[i], uniform);
66
67 for (unsigned i = 0; i < vertices.size() / 3; i++)
68 rasterize_triangle(program, uniform, v[i * 3 + 0], v[i * 3 + 1], v[i * 3 + 2], frameBuffer);
69 }
70
71 void rasterize_line(const Program &program, const UniformAttributes &uniform, const VertexAttributes &v1, const VertexAttributes &v2, double line_thickness, Eigen::Matrix<FrameBufferAttributes, Eigen::Dynamic, Eigen::Dynamic> &frameBuffer)
72 {
73 Eigen::Matrix<double, 2, 4> p;
74 p.row(0) = v1.position.array() / v1.position[3];
75 p.row(1) = v2.position.array() / v2.position[3];
76
77 p.col(0) = ((p.col(0).array() + 1.0) / 2.0) * frameBuffer.rows();
78 p.col(1) = ((p.col(1).array() + 1.0) / 2.0) * frameBuffer.cols();
79
80 int lx = std::floor(p.col(0).minCoeff() - line_thickness);
81 int ly = std::floor(p.col(1).minCoeff() - line_thickness);
82 int ux = std::ceil(p.col(0).maxCoeff() + line_thickness);
83 int uy = std::ceil(p.col(1).maxCoeff() + line_thickness);
84
85 lx = std::min(std::max(lx, int(0)), int(frameBuffer.rows() - 1));
86 ly = std::min(std::max(ly, int(0)), int(frameBuffer.cols() - 1));
87 ux = std::max(std::min(ux, int(frameBuffer.rows() - 1)), int(0));
88 uy = std::max(std::min(uy, int(frameBuffer.cols() - 1)), int(0));
89
90 Eigen::Vector2f l1(p(0, 0), p(0, 1));
91 Eigen::Vector2f l2(p(1, 0), p(1, 1));
92
93 double t = -1;
94 double ll = (l1 - l2).squaredNorm();
95
96 for (unsigned i = lx; i <= ux; i++)
97 {
98 for (unsigned j = ly; j <= uy; j++)
99 {
100
101 Eigen::Vector2f pixel(i + 0.5, j + 0.5);
102
103 if (ll == 0.0)
104 t = 0;
105 else
106 {
107 t = (pixel - l1).dot(l2 - l1) / ll;
108 t = std::fmax(0, std::fmin(1, t));
109 }
110
111 Eigen::Vector2f pixel_p = l1 + t * (l2 - l1);
112
113 if ((pixel - pixel_p).squaredNorm() < (line_thickness * line_thickness))
114 {
115 VertexAttributes va = VertexAttributes::interpolate(v1, v2, v1, 1 - t, t, 0);
116 FragmentAttributes frag = program.FragmentShader(va, uniform);
117 frameBuffer(i, j) = program.BlendingShader(frag, frameBuffer(i, j));
118 }
119 }
120 }
121 }
122
123 void rasterize_lines(const Program &program, const UniformAttributes &uniform, const std::vector<VertexAttributes> &vertices, double line_thickness, Eigen::Matrix<FrameBufferAttributes, Eigen::Dynamic, Eigen::Dynamic> &frameBuffer)
124 {
125 std::vector<VertexAttributes> v(vertices.size());
126 for (unsigned i = 0; i < vertices.size(); i++)
127 v[i] = program.VertexShader(vertices[i], uniform);
128
129 for (unsigned i = 0; i < vertices.size() / 2; i++)
130 rasterize_line(program, uniform, v[i * 2 + 0], v[i * 2 + 1], line_thickness, frameBuffer);
131 }
132
133 void framebuffer_to_uint8(const Eigen::Matrix<FrameBufferAttributes, Eigen::Dynamic, Eigen::Dynamic> &frameBuffer, std::vector<uint8_t> &image)
134 {
135 const int w = frameBuffer.rows();
136 const int h = frameBuffer.cols();
137 const int comp = 4;
138 const int stride_in_bytes = w * comp;
139 image.resize(w * h * comp, 0);
140
141 for (unsigned wi = 0; wi < w; ++wi)
142 {
143 for (unsigned hi = 0; hi < h; ++hi)
144 {
145 unsigned hif = h - 1 - hi;
146 image[(hi * w * 4) + (wi * 4) + 0] = frameBuffer(wi, hif).color[0];
147 image[(hi * w * 4) + (wi * 4) + 1] = frameBuffer(wi, hif).color[1];
148 image[(hi * w * 4) + (wi * 4) + 2] = frameBuffer(wi, hif).color[2];
149 image[(hi * w * 4) + (wi * 4) + 3] = frameBuffer(wi, hif).color[3];
150 }
151 }
152 }
153 } // namespace
154
155 std::vector<uint8_t> render(const Eigen::MatrixXd &vertices, const Eigen::MatrixXi &faces, const Eigen::MatrixXi &faces_id,
156 int width, int height,
157 const Eigen::Vector3d &camera_position, const double camera_fov, const double camera_near, const double camera_far, const bool is_perspective, const Eigen::Vector3d &lookat, const Eigen::Vector3d &up,
158 const Eigen::Vector3d &ambient_light, const std::vector<std::pair<Eigen::MatrixXd, Eigen::MatrixXd>> &lights,
159 std::vector<Material> &materials)
160 {
161 using namespace renderer;
162 using namespace Eigen;
163
164 Eigen::Matrix<FrameBufferAttributes, Eigen::Dynamic, Eigen::Dynamic> frameBuffer(width, height);
165 UniformAttributes uniform;
166
167 const Vector3d gaze = lookat - camera_position;
168 const Vector3d w = -gaze.normalized();
169 const Vector3d u = up.cross(w).normalized();
170 const Vector3d v = w.cross(u);
171
172 Matrix4d M_cam_inv;
173 M_cam_inv << u(0), v(0), w(0), camera_position(0),
174 u(1), v(1), w(1), camera_position(1),
175 u(2), v(2), w(2), camera_position(2),
176 0, 0, 0, 1;
177
178 uniform.M_cam = M_cam_inv.inverse();
179
180 {
181 const double camera_ar = double(width) / height;
182 const double tan_angle = tan(camera_fov / 2);
183 const double n = -camera_near;
184 const double f = -camera_far;
185 const double t = tan_angle * n;
186 const double b = -t;
187 const double r = t * camera_ar;
188 const double l = -r;
189
190 uniform.M_orth << 2 / (r - l), 0, 0, -(r + l) / (r - l),
191 0, 2 / (t - b), 0, -(t + b) / (t - b),
192 0, 0, 2 / (n - f), -(n + f) / (n - f),
193 0, 0, 0, 1;
194 Matrix4d P;
195 if (is_perspective)
196 {
197 P << n, 0, 0, 0,
198 0, n, 0, 0,
199 0, 0, n + f, -f * n,
200 0, 0, 1, 0;
201 }
202 else
203 {
204 P << 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1;
205 }
206
207 uniform.M = uniform.M_orth * P * uniform.M_cam;
208 }
209
210 Program program;
211 program.VertexShader = [&](const VertexAttributes &va, const UniformAttributes &uniform) {
213 out.position = uniform.M * va.position;
214 Vector3d color = ambient_light;
215
216 Vector3d hit(va.position(0), va.position(1), va.position(2));
217 for (const auto &l : lights)
218 {
219 Vector3d Li = (l.first - hit).normalized();
220 Vector3d N = va.normal;
221 Vector3d diffuse = va.material.diffuse_color * std::max(Li.dot(N), 0.0);
222 Vector3d H;
223 if (is_perspective)
224 {
225 H = (Li + (camera_position - hit).normalized()).normalized();
226 }
227 else
228 {
229 H = (Li - gaze.normalized()).normalized();
230 }
231 const Vector3d specular = va.material.specular_color * std::pow(std::max(N.dot(H), 0.0), va.material.specular_exponent);
232 const Vector3d D = l.first - hit;
233 color += (diffuse + specular).cwiseProduct(l.second) / D.squaredNorm();
234 }
235 out.color = color;
236 return out;
237 };
238
239 program.FragmentShader = [](const VertexAttributes &va, const UniformAttributes &uniform) {
240 FragmentAttributes out(va.color(0), va.color(1), va.color(2));
241 out.depth = -va.position(2);
242 return out;
243 };
244
245 program.BlendingShader = [](const FragmentAttributes &fa, const FrameBufferAttributes &previous) {
246 if (fa.depth < previous.depth)
247 {
248 FrameBufferAttributes out(fa.color[0] * 255, fa.color[1] * 255, fa.color[2] * 255, fa.color[3] * 255);
249 out.depth = fa.depth;
250 return out;
251 }
252 else
253 {
254 return previous;
255 }
256 };
257
258 Eigen::MatrixXd vnormals;
259 igl::per_vertex_normals(vertices, faces, vnormals);
260 const Material material = materials.front();
261
262 std::vector<VertexAttributes> vertex_attributes;
263 for (int i = 0; i < faces.rows(); ++i)
264 {
265 const auto &mat = faces_id.size() <= 0 ? material : materials[faces_id(i)];
266 for (int j = 0; j < 3; j++)
267 {
268 int vid = faces(i, j);
269 VertexAttributes va(vertices(vid, 0), vertices(vid, 1), vertices(vid, 2));
270 va.material = mat;
271 va.normal = vnormals.row(vid).normalized();
272 vertex_attributes.push_back(va);
273 }
274 }
275
276 rasterize_triangles(program, uniform, vertex_attributes, frameBuffer);
277
278 std::vector<uint8_t> image;
279 framebuffer_to_uint8(frameBuffer, image);
280
281 return image;
282 }
283 } // namespace renderer
284} // namespace polyfem
std::vector< Eigen::VectorXi > faces
std::function< FragmentAttributes(const VertexAttributes &, const UniformAttributes &)> FragmentShader
Definition raster.hpp:83
std::function< VertexAttributes(const VertexAttributes &, const UniformAttributes &)> VertexShader
Definition raster.hpp:82
std::function< FrameBufferAttributes(const FragmentAttributes &, const FrameBufferAttributes &)> BlendingShader
Definition raster.hpp:84
static VertexAttributes interpolate(const VertexAttributes &a, const VertexAttributes &b, const VertexAttributes &c, const double alpha, const double beta, const double gamma)
Definition raster.hpp:27
std::vector< uint8_t > render(const Eigen::MatrixXd &vertices, const Eigen::MatrixXi &faces, const Eigen::MatrixXi &faces_id, int width, int height, const Eigen::Vector3d &camera_position, const double camera_fov, const double camera_near, const double camera_far, const bool is_perspective, const Eigen::Vector3d &lookat, const Eigen::Vector3d &up, const Eigen::Vector3d &ambient_light, const std::vector< std::pair< Eigen::MatrixXd, Eigen::MatrixXd > > &lights, std::vector< Material > &materials)
Definition raster.cpp:155
Eigen::Vector3d specular_color
Definition raster.hpp:16
Eigen::Vector3d diffuse_color
Definition raster.hpp:15