You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

738 lines
19 KiB

// License: Apache 2.0. See LICENSE file in root directory.
// Copyright(c) 2020 Intel Corporation. All Rights Reserved.
#include "opengl3.h"
#include <stdexcept> // runtime_error
#include <sstream>
#include <assert.h>
#include <glad/glad.h>
using namespace rs2;
int vbo::convert_type(vbo_type type)
{
switch (type) {
case vbo_type::array_buffer: return GL_ARRAY_BUFFER;
case vbo_type::element_array_buffer: return GL_ELEMENT_ARRAY_BUFFER;
default: throw std::runtime_error("Not supported VBO type!");
}
}
vbo::vbo(vbo_type type)
: _type(type)
{
glGenBuffers(1, &_id);
}
void vbo::bind()
{
glBindBuffer(convert_type(_type), _id);
}
void vbo::unbind()
{
glBindBuffer(convert_type(_type), 0);
}
void vbo::upload(int attribute, const float* xyz, int size, int count, bool dynamic)
{
assert(_type == vbo_type::array_buffer);
bind();
glBufferData(convert_type(_type), count * size * sizeof(float), xyz,
dynamic ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW);
glVertexAttribPointer(attribute, size, GL_FLOAT, GL_FALSE, 0, 0);
_size = count;
check_gl_error();
unbind();
}
void vbo::upload(const int3* indx, int count)
{
assert(_type == vbo_type::element_array_buffer);
bind();
glBufferData(convert_type(_type), count * sizeof(int3), indx, GL_STATIC_DRAW);
check_gl_error();
_size = count;
}
void vbo::draw_points()
{
assert(_type == vbo_type::array_buffer);
bind();
glDrawArrays(GL_POINTS, 0, _size);
check_gl_error();
unbind();
}
void vbo::draw_triangles()
{
assert(_type == vbo_type::array_buffer);
bind();
glDrawArrays(GL_TRIANGLES, 0, _size);
check_gl_error();
unbind();
}
void vbo::draw_indexed_triangles()
{
assert(_type == vbo_type::element_array_buffer);
glDrawElements(GL_TRIANGLES, _size * (sizeof(int3) / sizeof(int)), GL_UNSIGNED_INT, 0);
check_gl_error();
}
vbo::vbo(vbo&& other)
: _id(other._id), _type(other._type), _size(other._size)
{
other._id = 0;
}
vbo::~vbo()
{
if (_id) glDeleteBuffers(1, &_id);
}
vao::vao(const float3* vert, const float2* uvs, const float3* normals,
const float3* tangents, int vert_count, const int3* indx, int indx_count)
: _vertexes(vbo_type::array_buffer),
_uvs(vbo_type::array_buffer),
_indexes(vbo_type::element_array_buffer),
_tangents(vbo_type::array_buffer),
_vertex_count(vert_count)
{
glGenVertexArrays(1, &_id);
check_gl_error();
bind();
_indexes.upload(indx, indx_count);
_vertexes.upload(0, (float*)vert, 3, vert_count, true);
if (normals) _normals.upload(2, (float*)normals, 3, vert_count);
if (tangents) _tangents.upload(3, (float*)tangents, 3, vert_count);
if (uvs) _uvs.upload(1, (float*)uvs, 2, vert_count);
unbind();
}
std::unique_ptr<vao> vao::create(const obj_mesh& mesh)
{
return std::unique_ptr<vao>(new vao(mesh.positions.data(),
mesh.uvs.data(),
mesh.normals.data(),
mesh.tangents.data(),
int(mesh.positions.size()),
mesh.indexes.data(),
int(mesh.indexes.size())));
}
vao::vao(vao&& other)
: _id(other._id),
_indexes(std::move(other._indexes)),
_vertexes(std::move(other._vertexes)),
_uvs(std::move(other._uvs)),
_normals(std::move(other._normals)),
_tangents(std::move(other._tangents))
{
other._id = 0;
}
vao::~vao()
{
if (_id) glDeleteVertexArrays(1, &_id);
}
void vao::bind()
{
glBindVertexArray(_id);
check_gl_error();
}
void vao::unbind()
{
glBindVertexArray(0);
}
void vao::draw_points()
{
bind();
glEnableVertexAttribArray(0); // vertex
if (_uvs.size()) glEnableVertexAttribArray(1); // uv
if (_normals.size()) glEnableVertexAttribArray(2); // normals
if (_tangents.size()) glEnableVertexAttribArray(3); // tangents
check_gl_error();
_vertexes.draw_points();
check_gl_error();
glDisableVertexAttribArray(0);
if (_uvs.size()) glDisableVertexAttribArray(1);
if (_normals.size()) glDisableVertexAttribArray(2);
if (_tangents.size()) glDisableVertexAttribArray(3);
check_gl_error();
unbind();
}
void vao::draw()
{
bind();
glEnableVertexAttribArray(0); // vertex
if (_uvs.size()) glEnableVertexAttribArray(1); // uv
if (_normals.size()) glEnableVertexAttribArray(2); // normals
if (_tangents.size()) glEnableVertexAttribArray(3); // tangents
check_gl_error();
_indexes.draw_indexed_triangles();
check_gl_error();
glDisableVertexAttribArray(0);
if (_uvs.size()) glDisableVertexAttribArray(1);
if (_normals.size()) glDisableVertexAttribArray(2);
if (_tangents.size()) glDisableVertexAttribArray(3);
check_gl_error();
unbind();
}
void vao::update_positions(const float3* vert)
{
_vertexes.upload(0, (float*)vert, 3, _vertex_count, true);
}
static const char* vertex_shader_text =
"#version 110\n"
"attribute vec3 position;\n"
"attribute vec2 textureCoords;\n"
"varying vec2 textCoords;\n"
"uniform vec2 elementPosition;\n"
"uniform vec2 elementScale;\n"
"void main(void)\n"
"{\n"
" gl_Position = vec4(position * vec3(elementScale, 1.0) + vec3(elementPosition, 0.0), 1.0);\n"
" textCoords = textureCoords;\n"
"}";
static const char* splash_shader_text =
"#version 110\n"
"varying vec2 textCoords;\n"
"uniform sampler2D textureSampler;\n"
"uniform float opacity;\n"
"uniform vec2 rayOrigin;\n"
"uniform float power;\n"
"void main(void) {\n"
" vec4 FragColor = texture2D(textureSampler, textCoords);\n"
" int samples = 120;\n"
" vec2 delta = vec2(textCoords - rayOrigin);\n"
" delta *= 1.0 / float(samples) * 0.99;"
" vec2 coord = textCoords;\n"
" float illuminationDecay = power;\n"
" for(int i=0; i < samples ; i++)\n"
" {\n"
" coord -= delta;\n"
" vec4 texel = texture2D(textureSampler, coord);\n"
" texel *= illuminationDecay * 0.4;\n"
" texel.x *= 80.0 / 255.0;\n"
" texel.y *= 99.0 / 255.0;\n"
" texel.z *= 115.0 / 255.0;\n"
" FragColor += texel;\n"
" illuminationDecay *= power;\n"
" }\n"
" FragColor = clamp(FragColor, 0.0, 1.0);\n"
" gl_FragColor = vec4(FragColor.xyz, opacity);\n"
"}";
static const char* fragment_shader_text =
"#version 110\n"
"varying vec2 textCoords;\n"
"uniform sampler2D textureSampler;\n"
"uniform float opacity;\n"
"void main(void) {\n"
" vec2 tex = vec2(textCoords.x, 1.0 - textCoords.y);\n"
" vec4 color = texture2D(textureSampler, tex);\n"
" gl_FragColor = vec4(color.xyz, opacity);\n"
"}";
using namespace rs2;
texture_2d_shader::texture_2d_shader(std::unique_ptr<shader_program> shader)
: _shader(std::move(shader))
{
init();
}
const char* texture_2d_shader::default_vertex_shader()
{
return vertex_shader_text;
}
texture_2d_shader::texture_2d_shader()
{
_shader = shader_program::load(
vertex_shader_text,
fragment_shader_text, "position", "textureCoords");
init();
}
void visualizer_2d::draw_texture(uint32_t tex, float opacity)
{
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
tex_2d_shader->begin();
tex_2d_shader->set_opacity(opacity);
tex_2d_shader->end();
draw_texture({ 0.f, 0.f }, { 1.f, 1.f }, tex);
glDisable(GL_BLEND);
check_gl_error();
}
void visualizer_2d::draw_texture(uint32_t tex1, uint32_t tex2, float opacity)
{
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
tex_2d_shader->begin();
tex_2d_shader->set_opacity(opacity);
tex_2d_shader->end();
draw_texture({ 0.f, 0.f }, { 1.0f, 1.0f }, tex1, tex2);
glDisable(GL_BLEND);
}
void texture_2d_shader::set_position_and_scale(
const float2& position,
const float2& scale)
{
_shader->load_uniform(_position_location, position);
_shader->load_uniform(_scale_location, scale);
}
void splash_screen_shader::set_ray_center(const float2& center)
{
_shader->load_uniform(_rays_location, center);
}
void splash_screen_shader::set_power(float power)
{
_shader->load_uniform(_power_location, power);
}
void texture_2d_shader::init()
{
_position_location = _shader->get_uniform_location("elementPosition");
_scale_location = _shader->get_uniform_location("elementScale");
_opacity_location = _shader->get_uniform_location("opacity");
auto texture0_sampler_location = _shader->get_uniform_location("textureSampler");
_shader->begin();
_shader->load_uniform(texture0_sampler_location, 0);
set_opacity(1.f);
_shader->end();
}
splash_screen_shader::splash_screen_shader()
: texture_2d_shader(shader_program::load(
vertex_shader_text,
splash_shader_text, "position", "textureCoords"))
{
_rays_location = _shader->get_uniform_location("rayOrigin");
_power_location = _shader->get_uniform_location("power");
}
void texture_2d_shader::begin() { _shader->begin(); }
void texture_2d_shader::end() { _shader->end(); }
void texture_2d_shader::set_opacity(float opacity)
{
_shader->load_uniform(_opacity_location, opacity);
check_gl_error();
}
void texture_visualizer::draw(texture_2d_shader& shader, uint32_t tex)
{
shader.begin();
shader.set_position_and_scale(_position, _scale);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex);
_geometry->draw();
glBindTexture(GL_TEXTURE_2D, 0);
shader.end();
check_gl_error();
}
void texture_visualizer::draw(texture_2d_shader& shader, uint32_t tex1, uint32_t tex2)
{
shader.begin();
shader.set_position_and_scale(_position, _scale);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex1);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, tex2);
_geometry->draw();
glBindTexture(GL_TEXTURE_2D, 0);
shader.end();
}
obj_mesh texture_visualizer::create_mesh()
{
obj_mesh res;
res.positions.reserve(4);
res.positions.emplace_back(float3{ -1.f, -1.f, 0.f });
res.positions.emplace_back(float3{ 1.f, -1.f, 0.f });
res.positions.emplace_back(float3{ 1.f, 1.f, 0.f });
res.positions.emplace_back(float3{ -1.f, 1.f, 0.f });
res.uvs.reserve(4);
res.uvs.emplace_back(float2{ 0.f, 1.f });
res.uvs.emplace_back(float2{ 1.f, 1.f });
res.uvs.emplace_back(float2{ 1.f, 0.f });
res.uvs.emplace_back(float2{ 0.f, 0.f });
res.indexes.reserve(2);
res.indexes.emplace_back(int3{ 0, 1, 2 });
res.indexes.emplace_back(int3{ 2, 3, 0 });
return res;
}
#include <iostream>
using namespace rs2;
int shader_program::get_uniform_location(const std::string& name)
{
return glGetUniformLocation(_id, name.c_str());
}
void shader_program::load_uniform(int location, int value)
{
glUniform1i(location, value);
check_gl_error();
}
void shader_program::load_uniform(int location, float value)
{
glUniform1f(location, value);
check_gl_error();
}
void shader_program::load_uniform(int location, bool value)
{
load_uniform(location, value ? 1.f : 0.f);
check_gl_error();
}
void shader_program::load_uniform(int location, const float3& vec)
{
glUniform3f(location, vec.x, vec.y, vec.z);
check_gl_error();
}
void shader_program::load_uniform(int location, const float2& vec)
{
glUniform2f(location, vec.x, vec.y);
check_gl_error();
}
void shader_program::load_uniform(int location, const matrix4& matrix)
{
glUniformMatrix4fv(location, 1, GL_FALSE, (float*)&matrix);
check_gl_error();
}
void shader_program::bind_attribute(int attr, const std::string& name)
{
glBindAttribLocation(_id, attr, name.c_str());
check_gl_error();
}
shader::shader(const std::string& shader_code, shader_type type)
{
auto lambda = [&]() {
switch (type)
{
case shader_type::vertex: return GL_VERTEX_SHADER;
case shader_type::fragment: return GL_FRAGMENT_SHADER;
default:
throw std::runtime_error("Unknown shader type!");
}
};
const auto gl_type = lambda();
GLuint shader_id = glCreateShader(gl_type);
char const * source_ptr = shader_code.c_str();
int length = int(shader_code.size());
glShaderSource(shader_id, 1, &source_ptr, &length);
glCompileShader(shader_id);
GLint result;
int log_length;
glGetShaderiv(shader_id, GL_COMPILE_STATUS, &result);
glGetShaderiv(shader_id, GL_INFO_LOG_LENGTH, &log_length);
if ((result == GL_FALSE) && (log_length > 0)) {
std::vector<char> error_message(log_length + 1);
glGetShaderInfoLog(shader_id, log_length, NULL, &error_message[0]);
std::string error(&error_message[0]);
std::cerr << error;
glDeleteShader(shader_id);
throw std::runtime_error(error);
}
check_gl_error();
_id = shader_id;
}
shader::~shader()
{
glDeleteShader(_id);
}
shader_program::shader_program()
{
GLuint program_id = glCreateProgram();
check_gl_error();
_id = program_id;
}
shader_program::~shader_program()
{
glUseProgram(0);
glDeleteProgram(_id);
}
void shader_program::attach(const shader& shader)
{
_shaders.push_back(&shader);
}
void shader_program::link()
{
for (auto ps : _shaders)
{
glAttachShader(_id, ps->get_id());
}
glLinkProgram(_id);
GLint result;
int log_length;
glGetProgramiv(_id, GL_LINK_STATUS, &result);
glGetProgramiv(_id, GL_INFO_LOG_LENGTH, &log_length);
if ((result == GL_FALSE) && (log_length > 0)) {
std::vector<char> error_message(log_length + 1);
glGetProgramInfoLog(_id, log_length, NULL, &error_message[0]);
std::string error(&error_message[0]);
std::cerr << error;
for (auto ps : _shaders)
{
glDetachShader(_id, ps->get_id());
}
throw std::runtime_error(error);
}
glValidateProgram(_id);
glGetProgramiv(_id, GL_VALIDATE_STATUS, &result);
glGetProgramiv(_id, GL_INFO_LOG_LENGTH, &log_length);
if ((result == GL_FALSE) && (log_length > 0)) {
std::vector<char> error_message(log_length + 1);
glGetProgramInfoLog(_id, log_length, NULL, &error_message[0]);
std::string error(&error_message[0]);
std::cerr << error;
for (auto ps : _shaders)
{
glDetachShader(_id, ps->get_id());
}
throw std::runtime_error(error);
}
for (auto ps : _shaders)
{
glDetachShader(_id, ps->get_id());
}
_shaders.clear();
check_gl_error();
}
void shader_program::begin() const
{
glUseProgram(_id);
check_gl_error();
}
void shader_program::end() const
{
glUseProgram(0);
}
std::unique_ptr<shader_program> shader_program::load(
const std::string& vertex_shader,
const std::string& fragment_shader,
const char* input0,
const char* input1,
const char* output0,
const char* output1)
{
std::unique_ptr<shader_program> res(new shader_program());
shader vertex(vertex_shader, shader_type::vertex);
shader fragment(fragment_shader, shader_type::fragment);
res->attach(vertex);
res->attach(fragment);
if (input0) glBindAttribLocation(res->get_id(), 0, input0);
if (input1) glBindAttribLocation(res->get_id(), 1, input1);
check_gl_error();
if (output0) glBindFragDataLocation(res->get_id(), 0, output0);
if (output1) glBindFragDataLocation(res->get_id(), 1, output1);
check_gl_error();
res->link();
return std::move(res);
}
fbo::fbo(int w, int h) : _w(w), _h(h)
{
glGenFramebuffers(1, &_id);
check_gl_error();
glBindFramebuffer(GL_FRAMEBUFFER, _id);
check_gl_error();
glDrawBuffer(GL_COLOR_ATTACHMENT0);
check_gl_error();
}
void fbo::createTextureAttachment(uint32_t handle)
{
glBindTexture(GL_TEXTURE_2D, handle);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, _w, _h, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
// avoids from repetition of the texture at the edges of the viewport
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, handle, 0);
glBindTexture(GL_TEXTURE_2D, 0);
check_gl_error();
}
void fbo::createDepthTextureAttachment(uint32_t handle)
{
glBindTexture(GL_TEXTURE_2D, handle);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32, _w, _h, 0, GL_DEPTH_COMPONENT, GL_FLOAT, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, handle, 0);
glBindTexture(GL_TEXTURE_2D, 0);
check_gl_error();
}
void fbo::bind()
{
glGetIntegerv(GL_VIEWPORT, _viewport);
glBindTexture(GL_TEXTURE_2D, 0);
check_gl_error();
glBindFramebuffer(GL_FRAMEBUFFER, _id);
check_gl_error();
glViewport(0, 0, _w, _h);
check_gl_error();
}
void fbo::unbind()
{
glBindFramebuffer(GL_FRAMEBUFFER, 0);
check_gl_error();
glViewport(_viewport[0], _viewport[1], _viewport[2], _viewport[3]);
check_gl_error();
}
void fbo::createDepthBufferAttachment()
{
if (_db) glDeleteRenderbuffers(1, &_db);
glGenRenderbuffers(1, &_db);
glBindRenderbuffer(GL_RENDERBUFFER, _db);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, _w, _h);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, _db);
check_gl_error();
}
fbo::~fbo()
{
glDeleteRenderbuffers(1, &_db);
glDeleteFramebuffers(1, &_id);
}
std::string fbo::get_status()
{
std::string res = "UNKNOWN";
bind();
auto s = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (s == GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT) res = "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT";
else if (s == 0x8CD9) res = "GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS";
else if (s == GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT) res = "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT";
else if (s == GL_FRAMEBUFFER_UNSUPPORTED) res = "GL_FRAMEBUFFER_UNSUPPORTED";
else if (s == GL_FRAMEBUFFER_COMPLETE) res = "GL_FRAMEBUFFER_COMPLETE";
unbind();
return res;
}
void _check_gl_error(const char *file, int line)
{
GLenum err (glGetError());
std::stringstream ss;
bool has_errors = false;
while (err != GL_NO_ERROR)
{
std::string error;
switch (err)
{
case GL_INVALID_OPERATION: error="INVALID_OPERATION"; break;
case GL_INVALID_ENUM: error="INVALID_ENUM"; break;
case GL_INVALID_VALUE: error="INVALID_VALUE"; break;
case GL_OUT_OF_MEMORY: error="OUT_OF_MEMORY"; break;
case GL_INVALID_FRAMEBUFFER_OPERATION: error="INVALID_FRAMEBUFFER_OPERATION"; break;
default: error="Unknown"; break;
}
ss << "GL_" << error.c_str() << " - " << file << ":" << line << "\n";
err = glGetError();
has_errors = true;
}
if (has_errors)
{
auto error = ss.str();
throw std::runtime_error(error);
}
}
void clear_gl_errors()
{
GLenum err(glGetError());
while (err != GL_NO_ERROR)
{
err = glGetError();
}
}