eos 1.4.0
Loading...
Searching...
No Matches
texture_extraction.hpp
1/*
2 * eos - A 3D Morphable Model fitting library written in modern C++11/14.
3 *
4 * File: include/eos/render/texture_extraction.hpp
5 *
6 * Copyright 2014-2020, 2023 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 EOS_TEXTURE_EXTRACTION_HPP
23#define EOS_TEXTURE_EXTRACTION_HPP
24
25#include "eos/core/Image.hpp"
26#include "eos/core/Mesh.hpp"
27#include "eos/render/ProjectionType.hpp"
28#include "eos/render/detail/RayDirection.hpp"
29#include "eos/render/vertex_visibility.hpp"
30#include "eos/render/transforms.hpp"
31#include "eos/render/Rasterizer.hpp"
32#include "eos/render/FragmentShader.hpp"
33#include "eos/render/ray_triangle_intersect.hpp"
34#include "eos/render/detail/utils.hpp" // for detail::divide_by_w()
35
36#include <cassert>
37#include <cstddef>
38#include <vector>
39#include <algorithm>
40
41namespace eos {
42namespace render {
43
70inline eos::core::Image4u extract_texture(const core::Mesh& mesh, Eigen::Matrix4f view_model_matrix,
71 Eigen::Matrix4f projection_matrix, ProjectionType projection_type,
72 const eos::core::Image4u& image, int texturemap_resolution = 512)
73{
74 // Assert that either there are texture coordinates given for each vertex (in which case the texture map
75 // doesn't contain any seams), or that a separate list of texture triangle indices is given (i.e. mesh.tti
76 // is not empty):
77 assert(mesh.vertices.size() == mesh.texcoords.size() || !mesh.tti.empty());
78 // Sanity check on the texture triangle indices: They should be either empty or equal to tvi.size():
79 assert(mesh.tti.empty() || mesh.tti.size() == mesh.tvi.size());
80
81 using detail::divide_by_w;
82 using detail::RayDirection;
83 using Eigen::Vector2f;
84 using Eigen::Vector2d;
85 using Eigen::Vector3f;
86 using Eigen::Vector3d;
87 using Eigen::Vector4f;
88 using Eigen::Vector4d;
89 using std::vector;
90 // We only need a rasteriser to remap the texture, not the complete SoftwareRenderer:
91 Rasterizer<ExtractionFragmentShader> extraction_rasterizer(texturemap_resolution, texturemap_resolution);
92 Texture image_to_extract_from_as_tex = create_mipmapped_texture(image, 1);
93 extraction_rasterizer.enable_depth_test = false; // We don't need to depth-test in the rendered image (which is the texture map).
94 extraction_rasterizer.perspective_correct_barycentric_weights = false; // We want the uncorrected lambda be passed to our shader
95
96 // For the per-vertex view angle, and the self-occlusion tests, we have to know the projection type, and
97 // then use different vector directions depending on the projection type:
98 RayDirection ray_direction_type;
99 if (projection_type == ProjectionType::Orthographic)
100 {
101 ray_direction_type = RayDirection::Parallel;
102 } else
103 {
104 ray_direction_type = RayDirection::TowardsOrigin;
105 }
106
107 // Test for self-occlusion, i.e. whether each vertex is visible from
108 // the camera origin:
109 const vector<bool> per_vertex_visibility = compute_per_vertex_self_occlusion(
110 mesh.vertices, mesh.tvi, view_model_matrix, ray_direction_type);
111
112 vector<Vector4f> wnd_coords; // will contain [x_wnd, y_wnd, z_ndc, 1/w_clip]
113 for (auto&& vtx : mesh.vertices)
114 {
115 Vector4f clip_coords = projection_matrix * view_model_matrix * vtx.homogeneous();
116 clip_coords = divide_by_w(clip_coords);
117 // Note: We could make use of a new `viewport` parameter here, to allow any viewport transformations.
118 const Vector2f screen_coords =
119 clip_to_screen_space(clip_coords.x(), clip_coords.y(), image.width(), image.height());
120 clip_coords.x() = screen_coords.x(); // Todo: Can simplify this...?
121 clip_coords.y() = screen_coords.y();
122 wnd_coords.push_back(clip_coords);
123 }
124
125 // Go on with extracting: This only needs the rasteriser/FS, not the whole Renderer.
126 const int tex_width = texturemap_resolution;
127 const int tex_height =
128 texturemap_resolution; // keeping this in case we need non-square texture maps at some point
129
130 // Use Mesh::tti as texture triangle indices if present, tvi otherwise:
131 const auto& mesh_tti = mesh.tti.empty() ? mesh.tvi : mesh.tti;
132
133 for (std::size_t triangle_index = 0; triangle_index < mesh.tvi.size(); ++triangle_index)
134 {
135 // Select the three indices for the current triangle:
136 const auto& tvi = mesh.tvi[triangle_index];
137 const auto& tti = mesh_tti[triangle_index];
138
139 // Check if all three vertices of the current triangle are visible, and use the triangle if so:
140 if (per_vertex_visibility[tvi[0]] && per_vertex_visibility[tvi[1]] &&
141 per_vertex_visibility[tvi[2]]) // can also try using ||, but...
142 {
143 // The model's texcoords become the locations to extract to in the framebuffer (which is the
144 // texture map we're extracting to). The wnd_coords are the coordinates we're extracting from (the
145 // original image), which from the perspective of the rasteriser, is the texture map, and thus
146 // from the rasteriser's perspective they're the texture coords.
147 //
148 // (Note: A test with a rendered & re-extracted texture showed that we're off by a pixel or more,
149 // definitely need to correct this. Probably here. It looks like it is 1-2 pixels off. Definitely
150 // a bit more than 1.)
151 detail::Vertex<double> pa{
152 Vector4d(mesh.texcoords[tti[0]][0] * tex_width,
153 mesh.texcoords[tti[0]][1] * tex_height,
154 wnd_coords[tvi[0]].z(), // z_ndc
155 wnd_coords[tvi[0]].w()), // 1/w_clip
156 Vector3d(), // empty
157 Vector2d(wnd_coords[tvi[0]].x() / image.width(),
158 wnd_coords[tvi[0]].y() /
159 image.height() // (maybe '1 - wndcoords...'?) wndcoords of the projected/rendered
160 // model triangle (in the input img). Normalised to 0,1.
161 )};
162 detail::Vertex<double> pb{
163 Vector4d(mesh.texcoords[tti[1]][0] * tex_width,
164 mesh.texcoords[tti[1]][1] * tex_height,
165 wnd_coords[tvi[1]].z(), // z_ndc
166 wnd_coords[tvi[1]].w()), // 1/w_clip
167 Vector3d(), // empty
168 Vector2d(wnd_coords[tvi[1]].x() / image.width(),
169 wnd_coords[tvi[1]].y() /
170 image.height() // (maybe '1 - wndcoords...'?) wndcoords of the projected/rendered
171 // model triangle (in the input img). Normalised to 0,1.
172 )};
173 detail::Vertex<double> pc{
174 Vector4d(mesh.texcoords[tti[2]][0] * tex_width,
175 mesh.texcoords[tti[2]][1] * tex_height,
176 wnd_coords[tvi[2]].z(), // z_ndc
177 wnd_coords[tvi[2]].w()), // 1/w_clip
178 Vector3d(), // empty
179 Vector2d(wnd_coords[tvi[2]].x() / image.width(),
180 wnd_coords[tvi[2]].y() /
181 image.height() // (maybe '1 - wndcoords...'?) wndcoords of the projected/rendered
182 // model triangle (in the input img). Normalised to 0,1.
183 )};
184 // The wnd_coords (now p[a|b|c].texcoords) can actually be outside the image, if the head is
185 // outside the image. Just skip the whole triangle if that is the case:
186 if (pa.texcoords.x() < 0 || pa.texcoords.x() > 1 || pa.texcoords.y() < 0 || pa.texcoords.y() > 1 ||
187 pb.texcoords.x() < 0 || pb.texcoords.x() > 1 || pb.texcoords.y() < 0 || pb.texcoords.y() > 1 ||
188 pc.texcoords.x() < 0 || pc.texcoords.x() > 1 || pc.texcoords.y() < 0 || pc.texcoords.y() > 1)
189 {
190 continue;
191 }
192 extraction_rasterizer.raster_triangle(pa, pb, pc, image_to_extract_from_as_tex);
193 }
194 }
195
196 return extraction_rasterizer.colorbuffer;
197};
198
199} /* namespace render */
200} /* namespace eos */
201
202#endif /* EOS_TEXTURE_EXTRACTION_HPP */
Class to represent images.
Definition: Image.hpp:44
Todo.
Definition: Rasterizer.hpp:50
void raster_triangle(const detail::Vertex< T > &point_a, const detail::Vertex< T > &point_b, const detail::Vertex< T > &point_c, const cpp17::optional< Texture > &texture)
Todo.
Definition: Rasterizer.hpp:67
Represents a texture for rendering.
Definition: Texture.hpp:64
eos::core::Image4u extract_texture(const core::Mesh &mesh, Eigen::Matrix4f view_model_matrix, Eigen::Matrix4f projection_matrix, ProjectionType projection_type, const eos::core::Image4u &image, int texturemap_resolution=512)
Extracts the texture from the given image and returns a texture map.
Definition: texture_extraction.hpp:70
Eigen::Vector2f clip_to_screen_space(const Eigen::Vector2f &clip_coordinates, int screen_width, int screen_height)
Definition: transforms.hpp:47
std::vector< bool > compute_per_vertex_self_occlusion(const std::vector< Eigen::Vector3f > &viewspace_vertices, const std::vector< std::array< int, 3 > > &triangle_vertex_indices, detail::RayDirection ray_direction_type)
Definition: vertex_visibility.hpp:119
ProjectionType
Definition: ProjectionType.hpp:31
core::Image4u render(const core::Mesh &mesh, const Eigen::Matrix4f &model_view_matrix, const Eigen::Matrix4f &projection_matrix, int viewport_width, int viewport_height, bool enable_backface_culling=false, bool enable_near_clipping=true, bool enable_far_clipping=true)
Definition: render.hpp:52
Namespace containing all of eos's 3D model fitting functionality.
This class represents a 3D mesh consisting of vertices, vertex colour information and texture coordin...
Definition: Mesh.hpp:45
std::vector< Eigen::Vector3f > vertices
3D vertex positions.
Definition: Mesh.hpp:46
std::vector< std::array< int, 3 > > tvi
Triangle vertex indices.
Definition: Mesh.hpp:50
std::vector< std::array< int, 3 > > tti
Triangle texture indices.
Definition: Mesh.hpp:52
std::vector< Eigen::Vector2f > texcoords
Texture coordinates.
Definition: Mesh.hpp:48