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