Files
vimix/Shader.cpp

288 lines
8.9 KiB
C++

/*
* This file is part of vimix - video live mixer
*
* **Copyright** (C) 2020-2021 Bruno Herbelin <bruno.herbelin@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
**/
#include <fstream>
#include <sstream>
#include <iostream>
#include <chrono>
#include <ctime>
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <glm/gtc/type_ptr.hpp>
#define GLM_ENABLE_EXPERIMENTAL
#include <glm/gtx/string_cast.hpp>
#include "Resource.h"
#include "FrameBuffer.h"
#include "Log.h"
#include "Visitor.h"
#include "BaseToolkit.h"
#include "RenderingManager.h"
#include "Shader.h"
// Globals
ShadingProgram *ShadingProgram::currentProgram_ = nullptr;
ShadingProgram simpleShadingProgram("shaders/simple.vs", "shaders/simple.fs");
// Blending presets for matching with Shader::BlendModes:
GLenum blending_equation[9] = { GL_FUNC_ADD, // normal
GL_FUNC_ADD, // screen
GL_FUNC_REVERSE_SUBTRACT, // subtract
GL_FUNC_ADD, // multiply
GL_FUNC_ADD, // soft light
GL_FUNC_ADD, // hard light
GL_FUNC_REVERSE_SUBTRACT, // soft subtract
GL_MAX, // lighten only
GL_FUNC_ADD};
GLenum blending_source_function[9] = { GL_ONE, // normal
GL_ONE, // screen
GL_SRC_COLOR, // subtract (can be GL_ONE)
GL_DST_COLOR, // multiply : src x dst color
GL_DST_COLOR, // soft light : src x dst color
GL_SRC_COLOR, // hard light : src x src color
GL_DST_COLOR, // soft subtract
GL_ONE, // lighten only
GL_ONE};
GLenum blending_destination_function[9] = {GL_ONE_MINUS_SRC_ALPHA,// normal
GL_ONE, // screen
GL_ONE, // subtract
GL_ONE_MINUS_SRC_ALPHA, // multiply
GL_ONE, // soft light
GL_ONE, // hard light
GL_ONE, // soft subtract
GL_ONE, // lighten only
GL_ZERO};
ShadingProgram::ShadingProgram(const std::string& vertex_file, const std::string& fragment_file) :
vertex_id_(0), fragment_id_(0), id_(0), vertex_file_(vertex_file), fragment_file_(fragment_file)
{
}
void ShadingProgram::init()
{
vertex_code_ = Resource::getText(vertex_file_);
fragment_code_ = Resource::getText(fragment_file_);
compile();
link();
}
bool ShadingProgram::initialized()
{
return (id_ != 0);
}
void ShadingProgram::compile()
{
const char* vcode = vertex_code_.c_str();
vertex_id_ = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex_id_, 1, &vcode, NULL);
glCompileShader(vertex_id_);
const char* fcode = fragment_code_.c_str();
fragment_id_ = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragment_id_, 1, &fcode, NULL);
glCompileShader(fragment_id_);
checkCompileErr();
}
void ShadingProgram::link()
{
id_ = glCreateProgram();
glAttachShader(id_, vertex_id_);
glAttachShader(id_, fragment_id_);
glLinkProgram(id_);
checkLinkingErr();
glUseProgram(id_);
glUniform1i(glGetUniformLocation(id_, "iChannel0"), 0);
glUniform1i(glGetUniformLocation(id_, "iChannel1"), 1);
glUseProgram(0);
glDeleteShader(vertex_id_);
glDeleteShader(fragment_id_);
}
void ShadingProgram::use()
{
if (currentProgram_ == nullptr || currentProgram_ != this)
{
currentProgram_ = this;
glUseProgram(id_);
}
}
void ShadingProgram::enduse()
{
glUseProgram(0);
currentProgram_ = nullptr ;
}
template<>
void ShadingProgram::setUniform<int>(const std::string& name, int val) {
glUniform1i(glGetUniformLocation(id_, name.c_str()), val);
}
template<>
void ShadingProgram::setUniform<bool>(const std::string& name, bool val) {
glUniform1i(glGetUniformLocation(id_, name.c_str()), val);
}
template<>
void ShadingProgram::setUniform<float>(const std::string& name, float val) {
glUniform1f(glGetUniformLocation(id_, name.c_str()), val);
}
template<>
void ShadingProgram::setUniform<float>(const std::string& name, float val1, float val2) {
glUniform2f(glGetUniformLocation(id_, name.c_str()), val1, val2);
}
template<>
void ShadingProgram::setUniform<float>(const std::string& name, float val1, float val2, float val3) {
glUniform3f(glGetUniformLocation(id_, name.c_str()), val1, val2, val3);
}
template<>
void ShadingProgram::setUniform<glm::vec2>(const std::string& name, glm::vec2 val) {
glm::vec2 v(val);
glUniform2fv(glGetUniformLocation(id_, name.c_str()), 1, glm::value_ptr(v));
}
template<>
void ShadingProgram::setUniform<glm::vec3>(const std::string& name, glm::vec3 val) {
glm::vec3 v(val);
glUniform3fv(glGetUniformLocation(id_, name.c_str()), 1, glm::value_ptr(v));
}
template<>
void ShadingProgram::setUniform<glm::vec4>(const std::string& name, glm::vec4 val) {
glm::vec4 v(val);
glUniform4fv(glGetUniformLocation(id_, name.c_str()), 1, glm::value_ptr(v));
}
template<>
void ShadingProgram::setUniform<glm::mat4>(const std::string& name, glm::mat4 val) {
glm::mat4 m(val);
glUniformMatrix4fv(glGetUniformLocation(id_, name.c_str()), 1, GL_FALSE, glm::value_ptr(m));
}
// template<>
// void ShadingProgram::setUniform<float*>(const std::string& name, float* val) {
// glUniformMatrix4fv(glGetUniformLocation(id_, name.c_str()), 1, GL_FALSE, val);
// }
void ShadingProgram::checkCompileErr()
{
int success;
char infoLog[1024];
glGetShaderiv(vertex_id_, GL_COMPILE_STATUS, &success);
if (!success) {
glGetShaderInfoLog(vertex_id_, 1024, NULL, infoLog);
Log::Warning("Error compiling Vertex ShadingProgram:\n%s", infoLog);
}
glGetShaderiv(fragment_id_, GL_COMPILE_STATUS, &success);
if (!success) {
glGetShaderInfoLog(fragment_id_, 1024, NULL, infoLog);
Log::Warning("Error compiling Fragment ShadingProgram:\n%s", infoLog);
}
}
void ShadingProgram::checkLinkingErr()
{
int success;
glGetProgramiv(id_, GL_LINK_STATUS, &success);
if (!success) {
char infoLog[1024];
glGetProgramInfoLog(id_, 1024, NULL, infoLog);
Log::Warning("Error linking ShadingProgram:\n%s", infoLog);
}
}
bool Shader::force_blending_opacity = false;
Shader::Shader() : blending(BLEND_OPACITY)
{
// create unique id
id_ = BaseToolkit::uniqueId();
program_ = &simpleShadingProgram;
Shader::reset();
}
void Shader::copy(Shader const& S)
{
color = S.color;
blending = S.blending;
iTransform = S.iTransform;
}
void Shader::accept(Visitor& v) {
v.visit(*this);
}
void Shader::use()
{
// initialization on first use
if (!program_->initialized())
program_->init();
// Use program
program_->use();
// set uniforms
program_->setUniform("projection", projection);
program_->setUniform("modelview", modelview);
program_->setUniform("iTransform", iTransform);
program_->setUniform("color", color);
glm::vec3 iResolution = glm::vec3( Rendering::manager().currentAttrib().viewport, 0.f);
program_->setUniform("iResolution", iResolution);
// Blending Function
if (force_blending_opacity) {
glEnable(GL_BLEND);
glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD);
glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
}
else if ( blending < BLEND_NONE ) {
glEnable(GL_BLEND);
glBlendEquationSeparate(blending_equation[blending], GL_FUNC_ADD);
glBlendFuncSeparate(blending_source_function[blending], blending_destination_function[blending], GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
}
else
glDisable(GL_BLEND);
}
void Shader::reset()
{
projection = glm::identity<glm::mat4>();
modelview = glm::identity<glm::mat4>();
iTransform = glm::identity<glm::mat4>();
color = glm::vec4(1.f, 1.f, 1.f, 1.f);
blending = BLEND_OPACITY;
}