1 // ----------------------------------------------------------------------------
2 // - Open3D: www.open3d.org -
3 // ----------------------------------------------------------------------------
4 // The MIT License (MIT)
5 //
6 // Copyright (c) 2018 www.open3d.org
7 //
8 // Permission is hereby granted, free of charge, to any person obtaining a copy
9 // of this software and associated documentation files (the "Software"), to deal
10 // in the Software without restriction, including without limitation the rights
11 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 // copies of the Software, and to permit persons to whom the Software is
13 // furnished to do so, subject to the following conditions:
14 //
15 // The above copyright notice and this permission notice shall be included in
16 // all copies or substantial portions of the Software.
17 //
18 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
24 // IN THE SOFTWARE.
25 // ----------------------------------------------------------------------------
26
27 #include "PhongShader.h"
28
29 #include <Core/Geometry/PointCloud.h>
30 #include <Core/Geometry/TriangleMesh.h>
31 #include <Visualization/Shader/Shader.h>
32 #include <Visualization/Utility/ColorMap.h>
33
34 namespace three{
35
36 namespace glsl {
37
Compile()38 bool PhongShader::Compile()
39 {
40 if (CompileShaders(PhongVertexShader, NULL, PhongFragmentShader) == false) {
41 PrintShaderWarning("Compiling shaders failed.");
42 return false;
43 }
44 vertex_position_ = glGetAttribLocation(program_, "vertex_position");
45 vertex_normal_ = glGetAttribLocation(program_, "vertex_normal");
46 vertex_color_ = glGetAttribLocation(program_, "vertex_color");
47 MVP_ = glGetUniformLocation(program_, "MVP");
48 V_ = glGetUniformLocation(program_, "V");
49 M_ = glGetUniformLocation(program_, "M");
50 light_position_world_ =
51 glGetUniformLocation(program_, "light_position_world_4");
52 light_color_ = glGetUniformLocation(program_, "light_color_4");
53 light_diffuse_power_ = glGetUniformLocation(program_,
54 "light_diffuse_power_4");
55 light_specular_power_ = glGetUniformLocation(program_,
56 "light_specular_power_4");
57 light_specular_shininess_ = glGetUniformLocation(program_,
58 "light_specular_shininess_4");
59 light_ambient_ = glGetUniformLocation(program_, "light_ambient");
60 return true;
61 }
62
Release()63 void PhongShader::Release()
64 {
65 UnbindGeometry();
66 ReleaseProgram();
67 }
68
BindGeometry(const Geometry & geometry,const RenderOption & option,const ViewControl & view)69 bool PhongShader::BindGeometry(const Geometry &geometry,
70 const RenderOption &option, const ViewControl &view)
71 {
72 // If there is already geometry, we first unbind it.
73 // We use GL_STATIC_DRAW. When geometry changes, we clear buffers and
74 // rebind the geometry. Note that this approach is slow. If the geometry is
75 // changing per frame, consider implementing a new ShaderWrapper using
76 // GL_STREAM_DRAW, and replace UnbindGeometry() with Buffer Object
77 // Streaming mechanisms.
78 UnbindGeometry();
79
80 // Prepare data to be passed to GPU
81 std::vector<Eigen::Vector3f> points;
82 std::vector<Eigen::Vector3f> normals;
83 std::vector<Eigen::Vector3f> colors;
84 if (PrepareBinding(geometry, option, view, points, normals, colors) ==
85 false) {
86 PrintShaderWarning("Binding failed when preparing data.");
87 return false;
88 }
89
90 // Create buffers and bind the geometry
91 glGenBuffers(1, &vertex_position_buffer_);
92 glBindBuffer(GL_ARRAY_BUFFER, vertex_position_buffer_);
93 glBufferData(GL_ARRAY_BUFFER, points.size() * sizeof(Eigen::Vector3f),
94 points.data(), GL_STATIC_DRAW);
95 glGenBuffers(1, &vertex_normal_buffer_);
96 glBindBuffer(GL_ARRAY_BUFFER, vertex_normal_buffer_);
97 glBufferData(GL_ARRAY_BUFFER, normals.size() * sizeof(Eigen::Vector3f),
98 normals.data(), GL_STATIC_DRAW);
99 glGenBuffers(1, &vertex_color_buffer_);
100 glBindBuffer(GL_ARRAY_BUFFER, vertex_color_buffer_);
101 glBufferData(GL_ARRAY_BUFFER, colors.size() * sizeof(Eigen::Vector3f),
102 colors.data(), GL_STATIC_DRAW);
103 bound_ = true;
104 return true;
105 }
106
RenderGeometry(const Geometry & geometry,const RenderOption & option,const ViewControl & view)107 bool PhongShader::RenderGeometry(const Geometry &geometry,
108 const RenderOption &option, const ViewControl &view)
109 {
110 if (PrepareRendering(geometry, option, view) == false) {
111 PrintShaderWarning("Rendering failed during preparation.");
112 return false;
113 }
114 glUseProgram(program_);
115 glUniformMatrix4fv(MVP_, 1, GL_FALSE, view.GetMVPMatrix().data());
116 glUniformMatrix4fv(V_, 1, GL_FALSE, view.GetViewMatrix().data());
117 glUniformMatrix4fv(M_, 1, GL_FALSE, view.GetModelMatrix().data());
118 glUniformMatrix4fv(light_position_world_, 1, GL_FALSE,
119 light_position_world_data_.data());
120 glUniformMatrix4fv(light_color_, 1, GL_FALSE, light_color_data_.data());
121 glUniform4fv(light_diffuse_power_, 1, light_diffuse_power_data_.data());
122 glUniform4fv(light_specular_power_, 1, light_specular_power_data_.data());
123 glUniform4fv(light_specular_shininess_, 1,
124 light_specular_shininess_data_.data());
125 glUniform4fv(light_ambient_, 1, light_ambient_data_.data());
126 glEnableVertexAttribArray(vertex_position_);
127 glBindBuffer(GL_ARRAY_BUFFER, vertex_position_buffer_);
128 glVertexAttribPointer(vertex_position_, 3, GL_FLOAT, GL_FALSE, 0, NULL);
129 glEnableVertexAttribArray(vertex_normal_);
130 glBindBuffer(GL_ARRAY_BUFFER, vertex_normal_buffer_);
131 glVertexAttribPointer(vertex_normal_, 3, GL_FLOAT, GL_FALSE, 0, NULL);
132 glEnableVertexAttribArray(vertex_color_);
133 glBindBuffer(GL_ARRAY_BUFFER, vertex_color_buffer_);
134 glVertexAttribPointer(vertex_color_, 3, GL_FLOAT, GL_FALSE, 0, NULL);
135 glDrawArrays(draw_arrays_mode_, 0, draw_arrays_size_);
136 glDisableVertexAttribArray(vertex_position_);
137 glDisableVertexAttribArray(vertex_normal_);
138 glDisableVertexAttribArray(vertex_color_);
139 return true;
140 }
141
UnbindGeometry()142 void PhongShader::UnbindGeometry()
143 {
144 if (bound_) {
145 glDeleteBuffers(1, &vertex_position_buffer_);
146 glDeleteBuffers(1, &vertex_normal_buffer_);
147 glDeleteBuffers(1, &vertex_color_buffer_);
148 bound_ = false;
149 }
150 }
151
SetLighting(const ViewControl & view,const RenderOption & option)152 void PhongShader::SetLighting(const ViewControl &view,
153 const RenderOption &option)
154 {
155 const auto &box = view.GetBoundingBox();
156 light_position_world_data_.setOnes();
157 light_color_data_.setOnes();
158 for (int i = 0; i < 4; i++) {
159 light_position_world_data_.block<3, 1>(0, i) =
160 box.GetCenter().cast<GLfloat>() + (float)box.GetSize() * (
161 (float)option.light_position_relative_[i](0) * view.GetRight() +
162 (float)option.light_position_relative_[i](1) * view.GetUp() +
163 (float)option.light_position_relative_[i](2) * view.GetFront());
164 light_color_data_.block<3, 1>(0, i) =
165 option.light_color_[i].cast<GLfloat>();
166 }
167 if (option.light_on_) {
168 light_diffuse_power_data_ = Eigen::Vector4d(
169 option.light_diffuse_power_).cast<GLfloat>();
170 light_specular_power_data_ = Eigen::Vector4d(
171 option.light_specular_power_).cast<GLfloat>();
172 light_specular_shininess_data_ = Eigen::Vector4d(
173 option.light_specular_shininess_).cast<GLfloat>();
174 light_ambient_data_.block<3, 1>(0, 0) =
175 option.light_ambient_color_.cast<GLfloat>();
176 light_ambient_data_(3) = 1.0f;
177 } else {
178 light_diffuse_power_data_ = GLHelper::GLVector4f::Zero();
179 light_specular_power_data_ = GLHelper::GLVector4f::Zero();
180 light_specular_shininess_data_ = GLHelper::GLVector4f::Ones();
181 light_ambient_data_ = GLHelper::GLVector4f(1.0f, 1.0f, 1.0f, 1.0f);
182 }
183 }
184
PrepareRendering(const Geometry & geometry,const RenderOption & option,const ViewControl & view)185 bool PhongShaderForPointCloud::PrepareRendering(const Geometry &geometry,
186 const RenderOption &option,const ViewControl &view)
187 {
188 if (geometry.GetGeometryType() !=
189 Geometry::GeometryType::PointCloud) {
190 PrintShaderWarning("Rendering type is not PointCloud.");
191 return false;
192 }
193 glEnable(GL_DEPTH_TEST);
194 glDepthFunc(GL_LESS);
195 glPointSize(GLfloat(option.point_size_));
196 SetLighting(view, option);
197 return true;
198 }
199
PrepareBinding(const Geometry & geometry,const RenderOption & option,const ViewControl & view,std::vector<Eigen::Vector3f> & points,std::vector<Eigen::Vector3f> & normals,std::vector<Eigen::Vector3f> & colors)200 bool PhongShaderForPointCloud::PrepareBinding(const Geometry &geometry,
201 const RenderOption &option, const ViewControl &view,
202 std::vector<Eigen::Vector3f> &points,
203 std::vector<Eigen::Vector3f> &normals,
204 std::vector<Eigen::Vector3f> &colors)
205 {
206 if (geometry.GetGeometryType() !=
207 Geometry::GeometryType::PointCloud) {
208 PrintShaderWarning("Rendering type is not PointCloud.");
209 return false;
210 }
211 const PointCloud &pointcloud = (const PointCloud &)geometry;
212 if (pointcloud.HasPoints() == false) {
213 PrintShaderWarning("Binding failed with empty pointcloud.");
214 return false;
215 }
216 if (pointcloud.HasNormals() == false) {
217 PrintShaderWarning("Binding failed with pointcloud with no normals.");
218 return false;
219 }
220 const ColorMap &global_color_map = *GetGlobalColorMap();
221 points.resize(pointcloud.points_.size());
222 normals.resize(pointcloud.points_.size());
223 colors.resize(pointcloud.points_.size());
224 for (size_t i = 0; i < pointcloud.points_.size(); i++) {
225 const auto &point = pointcloud.points_[i];
226 const auto &normal = pointcloud.normals_[i];
227 points[i] = point.cast<float>();
228 normals[i] = normal.cast<float>();
229 Eigen::Vector3d color;
230 switch (option.point_color_option_) {
231 case RenderOption::PointColorOption::XCoordinate:
232 color = global_color_map.GetColor(
233 view.GetBoundingBox().GetXPercentage(point(0)));
234 break;
235 case RenderOption::PointColorOption::YCoordinate:
236 color = global_color_map.GetColor(
237 view.GetBoundingBox().GetYPercentage(point(1)));
238 break;
239 case RenderOption::PointColorOption::ZCoordinate:
240 color = global_color_map.GetColor(
241 view.GetBoundingBox().GetZPercentage(point(2)));
242 break;
243 case RenderOption::PointColorOption::Color:
244 case RenderOption::PointColorOption::Default:
245 default:
246 if (pointcloud.HasColors()) {
247 color = pointcloud.colors_[i];
248 } else {
249 color = global_color_map.GetColor(
250 view.GetBoundingBox().GetZPercentage(point(2)));
251 }
252 break;
253 }
254 colors[i] = color.cast<float>();
255 }
256 draw_arrays_mode_ = GL_POINTS;
257 draw_arrays_size_ = GLsizei(points.size());
258 return true;
259 }
260
PrepareRendering(const Geometry & geometry,const RenderOption & option,const ViewControl & view)261 bool PhongShaderForTriangleMesh::PrepareRendering(const Geometry &geometry,
262 const RenderOption &option,const ViewControl &view)
263 {
264 if (geometry.GetGeometryType() !=
265 Geometry::GeometryType::TriangleMesh) {
266 PrintShaderWarning("Rendering type is not TriangleMesh.");
267 return false;
268 }
269 if (option.mesh_show_back_face_) {
270 glDisable(GL_CULL_FACE);
271 } else {
272 glEnable(GL_CULL_FACE);
273 }
274 glEnable(GL_DEPTH_TEST);
275 glDepthFunc(GL_LESS);
276 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
277 if (option.mesh_show_wireframe_) {
278 glEnable(GL_POLYGON_OFFSET_FILL);
279 glPolygonOffset(1.0, 1.0);
280 } else {
281 glDisable(GL_POLYGON_OFFSET_FILL);
282 }
283 SetLighting(view, option);
284 return true;
285 }
286
PrepareBinding(const Geometry & geometry,const RenderOption & option,const ViewControl & view,std::vector<Eigen::Vector3f> & points,std::vector<Eigen::Vector3f> & normals,std::vector<Eigen::Vector3f> & colors)287 bool PhongShaderForTriangleMesh::PrepareBinding(const Geometry &geometry,
288 const RenderOption &option, const ViewControl &view,
289 std::vector<Eigen::Vector3f> &points,
290 std::vector<Eigen::Vector3f> &normals,
291 std::vector<Eigen::Vector3f> &colors)
292 {
293 if (geometry.GetGeometryType() !=
294 Geometry::GeometryType::TriangleMesh) {
295 PrintShaderWarning("Rendering type is not TriangleMesh.");
296 return false;
297 }
298 const TriangleMesh &mesh = (const TriangleMesh &)geometry;
299 if (mesh.HasTriangles() == false) {
300 PrintShaderWarning("Binding failed with empty triangle mesh.");
301 return false;
302 }
303 if (mesh.HasTriangleNormals() == false || mesh.HasVertexNormals() == false)
304 {
305 PrintShaderWarning("Binding failed because mesh has no normals.");
306 PrintShaderWarning("Call ComputeVertexNormals() before binding.");
307 return false;
308 }
309 const ColorMap &global_color_map = *GetGlobalColorMap();
310 points.resize(mesh.triangles_.size() * 3);
311 normals.resize(mesh.triangles_.size() * 3);
312 colors.resize(mesh.triangles_.size() * 3);
313
314 for (size_t i = 0; i < mesh.triangles_.size(); i++) {
315 const auto &triangle = mesh.triangles_[i];
316 for (size_t j = 0; j < 3; j++) {
317 size_t idx = i * 3 + j;
318 size_t vi = triangle(j);
319 const auto &vertex = mesh.vertices_[vi];
320 points[idx] = vertex.cast<float>();
321
322 Eigen::Vector3d color;
323 switch (option.mesh_color_option_) {
324 case RenderOption::MeshColorOption::XCoordinate:
325 color = global_color_map.GetColor(
326 view.GetBoundingBox().GetXPercentage(vertex(0)));
327 break;
328 case RenderOption::MeshColorOption::YCoordinate:
329 color = global_color_map.GetColor(
330 view.GetBoundingBox().GetYPercentage(vertex(1)));
331 break;
332 case RenderOption::MeshColorOption::ZCoordinate:
333 color = global_color_map.GetColor(
334 view.GetBoundingBox().GetZPercentage(vertex(2)));
335 break;
336 case RenderOption::MeshColorOption::Color:
337 if (mesh.HasVertexColors()) {
338 color = mesh.vertex_colors_[vi];
339 break;
340 }
341 case RenderOption::MeshColorOption::Default:
342 default:
343 color = option.default_mesh_color_;
344 break;
345 }
346 colors[idx] = color.cast<float>();
347
348 if (option.mesh_shade_option_ ==
349 RenderOption::MeshShadeOption::FlatShade) {
350 normals[idx] = mesh.triangle_normals_[i].cast<float>();
351 } else {
352 normals[idx] = mesh.vertex_normals_[vi].cast<float>();
353 }
354 }
355 }
356 draw_arrays_mode_ = GL_TRIANGLES;
357 draw_arrays_size_ = GLsizei(points.size());
358 return true;
359 }
360
361 } // namespace glsl
362
363 } // namespace three
364