// ---------------------------------------------------------------------------- // - Open3D: www.open3d.org - // ---------------------------------------------------------------------------- // The MIT License (MIT) // // Copyright (c) 2018 www.open3d.org // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. // ---------------------------------------------------------------------------- #include "Open3D/Visualization/Shader/PhongShader.h" #include "Open3D/Geometry/PointCloud.h" #include "Open3D/Geometry/TriangleMesh.h" #include "Open3D/Visualization/Shader/Shader.h" #include "Open3D/Visualization/Utility/ColorMap.h" namespace open3d { namespace visualization { namespace glsl { bool PhongShader::Compile() { if (CompileShaders(PhongVertexShader, NULL, PhongFragmentShader) == false) { PrintShaderWarning("Compiling shaders failed."); return false; } vertex_position_ = glGetAttribLocation(program_, "vertex_position"); vertex_normal_ = glGetAttribLocation(program_, "vertex_normal"); vertex_color_ = glGetAttribLocation(program_, "vertex_color"); MVP_ = glGetUniformLocation(program_, "MVP"); V_ = glGetUniformLocation(program_, "V"); M_ = glGetUniformLocation(program_, "M"); light_position_world_ = glGetUniformLocation(program_, "light_position_world_4"); light_color_ = glGetUniformLocation(program_, "light_color_4"); light_diffuse_power_ = glGetUniformLocation(program_, "light_diffuse_power_4"); light_specular_power_ = glGetUniformLocation(program_, "light_specular_power_4"); light_specular_shininess_ = glGetUniformLocation(program_, "light_specular_shininess_4"); light_ambient_ = glGetUniformLocation(program_, "light_ambient"); return true; } void PhongShader::Release() { UnbindGeometry(); ReleaseProgram(); } bool PhongShader::BindGeometry(const geometry::Geometry &geometry, const RenderOption &option, const ViewControl &view) { // If there is already geometry, we first unbind it. // We use GL_STATIC_DRAW. When geometry changes, we clear buffers and // rebind the geometry. Note that this approach is slow. If the geometry is // changing per frame, consider implementing a new ShaderWrapper using // GL_STREAM_DRAW, and replace UnbindGeometry() with Buffer Object // Streaming mechanisms. UnbindGeometry(); // Prepare data to be passed to GPU std::vector points; std::vector normals; std::vector colors; if (PrepareBinding(geometry, option, view, points, normals, colors) == false) { PrintShaderWarning("Binding failed when preparing data."); return false; } // Create buffers and bind the geometry glGenBuffers(1, &vertex_position_buffer_); glBindBuffer(GL_ARRAY_BUFFER, vertex_position_buffer_); glBufferData(GL_ARRAY_BUFFER, points.size() * sizeof(Eigen::Vector3f), points.data(), GL_STATIC_DRAW); glGenBuffers(1, &vertex_normal_buffer_); glBindBuffer(GL_ARRAY_BUFFER, vertex_normal_buffer_); glBufferData(GL_ARRAY_BUFFER, normals.size() * sizeof(Eigen::Vector3f), normals.data(), GL_STATIC_DRAW); glGenBuffers(1, &vertex_color_buffer_); glBindBuffer(GL_ARRAY_BUFFER, vertex_color_buffer_); glBufferData(GL_ARRAY_BUFFER, colors.size() * sizeof(Eigen::Vector3f), colors.data(), GL_STATIC_DRAW); bound_ = true; return true; } bool PhongShader::RenderGeometry(const geometry::Geometry &geometry, const RenderOption &option, const ViewControl &view) { if (PrepareRendering(geometry, option, view) == false) { PrintShaderWarning("Rendering failed during preparation."); return false; } glUseProgram(program_); glUniformMatrix4fv(MVP_, 1, GL_FALSE, view.GetMVPMatrix().data()); glUniformMatrix4fv(V_, 1, GL_FALSE, view.GetViewMatrix().data()); glUniformMatrix4fv(M_, 1, GL_FALSE, view.GetModelMatrix().data()); glUniformMatrix4fv(light_position_world_, 1, GL_FALSE, light_position_world_data_.data()); glUniformMatrix4fv(light_color_, 1, GL_FALSE, light_color_data_.data()); glUniform4fv(light_diffuse_power_, 1, light_diffuse_power_data_.data()); glUniform4fv(light_specular_power_, 1, light_specular_power_data_.data()); glUniform4fv(light_specular_shininess_, 1, light_specular_shininess_data_.data()); glUniform4fv(light_ambient_, 1, light_ambient_data_.data()); glEnableVertexAttribArray(vertex_position_); glBindBuffer(GL_ARRAY_BUFFER, vertex_position_buffer_); glVertexAttribPointer(vertex_position_, 3, GL_FLOAT, GL_FALSE, 0, NULL); glEnableVertexAttribArray(vertex_normal_); glBindBuffer(GL_ARRAY_BUFFER, vertex_normal_buffer_); glVertexAttribPointer(vertex_normal_, 3, GL_FLOAT, GL_FALSE, 0, NULL); glEnableVertexAttribArray(vertex_color_); glBindBuffer(GL_ARRAY_BUFFER, vertex_color_buffer_); glVertexAttribPointer(vertex_color_, 3, GL_FLOAT, GL_FALSE, 0, NULL); glDrawArrays(draw_arrays_mode_, 0, draw_arrays_size_); glDisableVertexAttribArray(vertex_position_); glDisableVertexAttribArray(vertex_normal_); glDisableVertexAttribArray(vertex_color_); return true; } void PhongShader::UnbindGeometry() { if (bound_) { glDeleteBuffers(1, &vertex_position_buffer_); glDeleteBuffers(1, &vertex_normal_buffer_); glDeleteBuffers(1, &vertex_color_buffer_); bound_ = false; } } void PhongShader::SetLighting(const ViewControl &view, const RenderOption &option) { const auto &box = view.GetBoundingBox(); light_position_world_data_.setOnes(); light_color_data_.setOnes(); for (int i = 0; i < 4; i++) { light_position_world_data_.block<3, 1>(0, i) = box.GetCenter().cast() + (float)box.GetMaxExtent() * ((float)option.light_position_relative_[i](0) * view.GetRight() + (float)option.light_position_relative_[i](1) * view.GetUp() + (float)option.light_position_relative_[i](2) * view.GetFront()); light_color_data_.block<3, 1>(0, i) = option.light_color_[i].cast(); } if (option.light_on_) { light_diffuse_power_data_ = Eigen::Vector4d(option.light_diffuse_power_).cast(); light_specular_power_data_ = Eigen::Vector4d(option.light_specular_power_).cast(); light_specular_shininess_data_ = Eigen::Vector4d(option.light_specular_shininess_) .cast(); light_ambient_data_.block<3, 1>(0, 0) = option.light_ambient_color_.cast(); light_ambient_data_(3) = 1.0f; } else { light_diffuse_power_data_ = GLHelper::GLVector4f::Zero(); light_specular_power_data_ = GLHelper::GLVector4f::Zero(); light_specular_shininess_data_ = GLHelper::GLVector4f::Ones(); light_ambient_data_ = GLHelper::GLVector4f(1.0f, 1.0f, 1.0f, 1.0f); } } bool PhongShaderForPointCloud::PrepareRendering( const geometry::Geometry &geometry, const RenderOption &option, const ViewControl &view) { if (geometry.GetGeometryType() != geometry::Geometry::GeometryType::PointCloud) { PrintShaderWarning("Rendering type is not geometry::PointCloud."); return false; } glEnable(GL_DEPTH_TEST); glDepthFunc(GLenum(option.GetGLDepthFunc())); glPointSize(GLfloat(option.point_size_)); SetLighting(view, option); return true; } bool PhongShaderForPointCloud::PrepareBinding( const geometry::Geometry &geometry, const RenderOption &option, const ViewControl &view, std::vector &points, std::vector &normals, std::vector &colors) { if (geometry.GetGeometryType() != geometry::Geometry::GeometryType::PointCloud) { PrintShaderWarning("Rendering type is not geometry::PointCloud."); return false; } const geometry::PointCloud &pointcloud = (const geometry::PointCloud &)geometry; if (pointcloud.HasPoints() == false) { PrintShaderWarning("Binding failed with empty pointcloud."); return false; } if (pointcloud.HasNormals() == false) { PrintShaderWarning("Binding failed with pointcloud with no normals."); return false; } const ColorMap &global_color_map = *GetGlobalColorMap(); points.resize(pointcloud.points_.size()); normals.resize(pointcloud.points_.size()); colors.resize(pointcloud.points_.size()); for (size_t i = 0; i < pointcloud.points_.size(); i++) { const auto &point = pointcloud.points_[i]; const auto &normal = pointcloud.normals_[i]; points[i] = point.cast(); normals[i] = normal.cast(); Eigen::Vector3d color; switch (option.point_color_option_) { case RenderOption::PointColorOption::XCoordinate: color = global_color_map.GetColor( view.GetBoundingBox().GetXPercentage(point(0))); break; case RenderOption::PointColorOption::YCoordinate: color = global_color_map.GetColor( view.GetBoundingBox().GetYPercentage(point(1))); break; case RenderOption::PointColorOption::ZCoordinate: color = global_color_map.GetColor( view.GetBoundingBox().GetZPercentage(point(2))); break; case RenderOption::PointColorOption::Color: case RenderOption::PointColorOption::Default: default: if (pointcloud.HasColors()) { color = pointcloud.colors_[i]; } else { color = global_color_map.GetColor( view.GetBoundingBox().GetZPercentage(point(2))); } break; } colors[i] = color.cast(); } draw_arrays_mode_ = GL_POINTS; draw_arrays_size_ = GLsizei(points.size()); return true; } bool PhongShaderForTriangleMesh::PrepareRendering( const geometry::Geometry &geometry, const RenderOption &option, const ViewControl &view) { if (geometry.GetGeometryType() != geometry::Geometry::GeometryType::TriangleMesh && geometry.GetGeometryType() != geometry::Geometry::GeometryType::HalfEdgeTriangleMesh) { PrintShaderWarning("Rendering type is not geometry::TriangleMesh."); return false; } if (option.mesh_show_back_face_) { glDisable(GL_CULL_FACE); } else { glEnable(GL_CULL_FACE); } glEnable(GL_DEPTH_TEST); glDepthFunc(GLenum(option.GetGLDepthFunc())); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); if (option.mesh_show_wireframe_) { glEnable(GL_POLYGON_OFFSET_FILL); glPolygonOffset(1.0, 1.0); } else { glDisable(GL_POLYGON_OFFSET_FILL); } SetLighting(view, option); return true; } bool PhongShaderForTriangleMesh::PrepareBinding( const geometry::Geometry &geometry, const RenderOption &option, const ViewControl &view, std::vector &points, std::vector &normals, std::vector &colors) { if (geometry.GetGeometryType() != geometry::Geometry::GeometryType::TriangleMesh && geometry.GetGeometryType() != geometry::Geometry::GeometryType::HalfEdgeTriangleMesh) { PrintShaderWarning("Rendering type is not geometry::TriangleMesh."); return false; } const geometry::TriangleMesh &mesh = (const geometry::TriangleMesh &)geometry; if (mesh.HasTriangles() == false) { PrintShaderWarning("Binding failed with empty triangle mesh."); return false; } if (mesh.HasTriangleNormals() == false || mesh.HasVertexNormals() == false) { PrintShaderWarning("Binding failed because mesh has no normals."); PrintShaderWarning("Call ComputeVertexNormals() before binding."); return false; } const ColorMap &global_color_map = *GetGlobalColorMap(); points.resize(mesh.triangles_.size() * 3); normals.resize(mesh.triangles_.size() * 3); colors.resize(mesh.triangles_.size() * 3); for (size_t i = 0; i < mesh.triangles_.size(); i++) { const auto &triangle = mesh.triangles_[i]; for (size_t j = 0; j < 3; j++) { size_t idx = i * 3 + j; size_t vi = triangle(j); const auto &vertex = mesh.vertices_[vi]; points[idx] = vertex.cast(); Eigen::Vector3d color; switch (option.mesh_color_option_) { case RenderOption::MeshColorOption::XCoordinate: color = global_color_map.GetColor( view.GetBoundingBox().GetXPercentage(vertex(0))); break; case RenderOption::MeshColorOption::YCoordinate: color = global_color_map.GetColor( view.GetBoundingBox().GetYPercentage(vertex(1))); break; case RenderOption::MeshColorOption::ZCoordinate: color = global_color_map.GetColor( view.GetBoundingBox().GetZPercentage(vertex(2))); break; case RenderOption::MeshColorOption::Color: if (mesh.HasVertexColors()) { color = mesh.vertex_colors_[vi]; break; } case RenderOption::MeshColorOption::Default: default: color = option.default_mesh_color_; break; } colors[idx] = color.cast(); if (option.mesh_shade_option_ == RenderOption::MeshShadeOption::FlatShade) { normals[idx] = mesh.triangle_normals_[i].cast(); } else { normals[idx] = mesh.vertex_normals_[vi].cast(); } } } draw_arrays_mode_ = GL_TRIANGLES; draw_arrays_size_ = GLsizei(points.size()); return true; } } // namespace glsl } // namespace visualization } // namespace open3d