eos  0.7.1
render.hpp
1 /*
2  * Eos - A 3D Morphable Model fitting library written in modern C++11/14.
3  *
4  * File: include/eos/render/render.hpp
5  *
6  * Copyright 2014, 2015 Patrik Huber
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  */
20 #pragma once
21 
22 #ifndef RENDER_HPP_
23 #define RENDER_HPP_
24 
25 #include "eos/render/detail/render_detail.hpp"
26 #include "eos/render/utils.hpp"
27 
28 #include "opencv2/core/core.hpp"
29 
30 #include "boost/optional.hpp"
31 
32 #include <array>
33 #include <vector>
34 #include <memory>
35 
36 namespace eos {
37  namespace render {
38 
125 std::pair<cv::Mat, cv::Mat> render(Mesh mesh, cv::Mat model_view_matrix, cv::Mat projection_matrix, int viewport_width, int viewport_height, const boost::optional<Texture>& texture = boost::none, bool enable_backface_culling = false, bool enable_near_clipping = true, bool enable_far_clipping = true)
126 {
127  // Some internal documentation / old todos or notes:
128  // maybe change and pass depthBuffer as an optional arg (&?), because usually we never need it outside the renderer. Or maybe even a getDepthBuffer().
129  // modelViewMatrix goes to eye-space (camera space), projection does ortho or perspective proj.
130  // bool enable_texturing = false; Maybe re-add later, not sure
131  // take a cv::Mat texture instead and convert to Texture internally? no, we don't want to recreate mipmap levels on each render() call.
132 
133  assert(mesh.vertices.size() == mesh.colors.size() || mesh.colors.empty()); // The number of vertices has to be equal for both shape and colour, or, alternatively, it has to be a shape-only model.
134  assert(mesh.vertices.size() == mesh.texcoords.size() || mesh.texcoords.empty()); // same for the texcoords
135  // another assert: If cv::Mat texture != empty, then we need texcoords?
136 
137  using cv::Mat;
138  using std::vector;
139 
140  Mat colourbuffer = Mat::zeros(viewport_height, viewport_width, CV_8UC4); // make sure it's CV_8UC4?
141  Mat depthbuffer = std::numeric_limits<float>::max() * Mat::ones(viewport_height, viewport_width, CV_64FC1);
142 
143  // Vertex shader:
144  //processedVertex = shade(Vertex); // processedVertex : pos, col, tex, texweight
145  // Assemble the vertices, project to clip space, and store as detail::Vertex (the internal representation):
146  vector<detail::Vertex> clipspace_vertices;
147  clipspace_vertices.reserve(mesh.vertices.size());
148  for (int i = 0; i < mesh.vertices.size(); ++i) { // "previously": mesh.vertex
149  Mat clipspace_coords = projection_matrix * model_view_matrix * Mat(mesh.vertices[i]);
150  cv::Vec3f vertex_colour;
151  if (mesh.colors.empty()) {
152  vertex_colour = cv::Vec3f(0.5f, 0.5f, 0.5f);
153  }
154  else {
155  vertex_colour = mesh.colors[i];
156  }
157  clipspace_vertices.push_back(detail::Vertex(clipspace_coords, vertex_colour, mesh.texcoords[i]));
158  }
159 
160  // All vertices are in clip-space now.
161  // Prepare the rasterisation stage.
162  // For every vertex/tri:
163  vector<detail::TriangleToRasterize> triangles_to_raster;
164  for (const auto& tri_indices : mesh.tvi) {
165  // Todo: Split this whole stuff up. Make a "clip" function, ... rename "processProspective..".. what is "process"... get rid of "continue;"-stuff by moving stuff inside process...
166  // classify vertices visibility with respect to the planes of the view frustum
167  // we're in clip-coords (NDC), so just check if outside [-1, 1] x ...
168  // Actually we're in clip-coords and it's not the same as NDC. We're only in NDC after the division by w.
169  // We should do the clipping in clip-coords though. See http://www.songho.ca/opengl/gl_projectionmatrix.html for more details.
170  // However, when comparing against w_c below, we might run into the trouble of the sign again in the affine case.
171  // 'w' is always positive, as it is -z_camspace, and all z_camspace are negative.
172  unsigned char visibility_bits[3];
173  for (unsigned char k = 0; k < 3; k++)
174  {
175  visibility_bits[k] = 0;
176  float x_cc = clipspace_vertices[tri_indices[k]].position[0];
177  float y_cc = clipspace_vertices[tri_indices[k]].position[1];
178  float z_cc = clipspace_vertices[tri_indices[k]].position[2];
179  float w_cc = clipspace_vertices[tri_indices[k]].position[3];
180  if (x_cc < -w_cc) // true if outside of view frustum. False if on or inside the plane.
181  visibility_bits[k] |= 1; // set bit if outside of frustum
182  if (x_cc > w_cc)
183  visibility_bits[k] |= 2;
184  if (y_cc < -w_cc)
185  visibility_bits[k] |= 4;
186  if (y_cc > w_cc)
187  visibility_bits[k] |= 8;
188  if (enable_near_clipping && z_cc < -w_cc) // near plane frustum clipping
189  visibility_bits[k] |= 16;
190  if (enable_far_clipping && z_cc > w_cc) // far plane frustum clipping
191  visibility_bits[k] |= 32;
192  } // if all bits are 0, then it's inside the frustum
193  // all vertices are not visible - reject the triangle.
194  if ((visibility_bits[0] & visibility_bits[1] & visibility_bits[2]) > 0)
195  {
196  continue;
197  }
198  // all vertices are visible - pass the whole triangle to the rasterizer. = All bits of all 3 triangles are 0.
199  if ((visibility_bits[0] | visibility_bits[1] | visibility_bits[2]) == 0)
200  {
201  boost::optional<detail::TriangleToRasterize> t = detail::process_prospective_tri(clipspace_vertices[tri_indices[0]], clipspace_vertices[tri_indices[1]], clipspace_vertices[tri_indices[2]], viewport_width, viewport_height, enable_backface_culling);
202  if (t) {
203  triangles_to_raster.push_back(*t);
204  }
205  continue;
206  }
207  // at this moment the triangle is known to be intersecting one of the view frustum's planes
208  std::vector<detail::Vertex> vertices;
209  vertices.push_back(clipspace_vertices[tri_indices[0]]);
210  vertices.push_back(clipspace_vertices[tri_indices[1]]);
211  vertices.push_back(clipspace_vertices[tri_indices[2]]);
212  // split the triangle if it intersects the near plane:
213  if (enable_near_clipping)
214  {
215  vertices = detail::clip_polygon_to_plane_in_4d(vertices, cv::Vec4f(0.0f, 0.0f, -1.0f, -1.0f)); // "Normal" (or "4D hyperplane") of the near-plane. I tested it and it works like this but I'm a little bit unsure because Songho says the normal of the near-plane is (0,0,-1,1) (maybe I have to switch around the < 0 checks in the function?)
216  }
217 
218  // triangulation of the polygon formed of vertices array
219  if (vertices.size() >= 3)
220  {
221  for (unsigned char k = 0; k < vertices.size() - 2; k++)
222  {
223  boost::optional<detail::TriangleToRasterize> t = detail::process_prospective_tri(vertices[0], vertices[1 + k], vertices[2 + k], viewport_width, viewport_height, enable_backface_culling);
224  if (t) {
225  triangles_to_raster.push_back(*t);
226  }
227  }
228  }
229  }
230 
231  // Fragment/pixel shader: Colour the pixel values
232  for (const auto& tri : triangles_to_raster) {
233  detail::raster_triangle(tri, colourbuffer, depthbuffer, texture, enable_far_clipping);
234  }
235  return std::make_pair(colourbuffer, depthbuffer);
236 };
237 
238  } /* namespace render */
239 } /* namespace eos */
240 
241 #endif /* RENDER_HPP_ */
std::pair< cv::Mat, cv::Mat > render(Mesh mesh, cv::Mat model_view_matrix, cv::Mat projection_matrix, int viewport_width, int viewport_height, const boost::optional< Texture > &texture=boost::none, bool enable_backface_culling=false, bool enable_near_clipping=true, bool enable_far_clipping=true)
Definition: render.hpp:125
Namespace containing all of eos&#39;s 3D model fitting functionality.
std::vector< cv::Vec4f > vertices
3D vertex positions.
Definition: Mesh.hpp:47
std::vector< cv::Vec3f > colors
Colour information for each vertex. Expected to be in RGB order.
Definition: Mesh.hpp:48
This class represents a 3D mesh consisting of vertices, vertex colour information and texture coordin...
Definition: Mesh.hpp:45
std::vector< std::array< int, 3 > > tvi
Triangle vertex indices.
Definition: Mesh.hpp:51
std::vector< cv::Vec2f > texcoords
Texture coordinates for each vertex.
Definition: Mesh.hpp:49