File Texture.cpp
File List > render > Texture.cpp
Go to the documentation of this file
#include "Texture.h"
#include "stb_image.h"
#include "macros.h"
#include "xdz_math.h"
#include <algorithm>
void Texture::_cpyInfo(const Texture& tex)
{
im_w = tex.GetW(); im_h = tex.GetH(); im_bpp = tex.GetBPP();
tex_path = tex.GetTexName(); tex_ID = tex.GetTexID();
tex_type = tex.tex_type; tex_slot_offset = tex.tex_slot_offset;
}
Texture::Texture(const std::string& texpath, TextureType tex_type, GLuint tile_type)
:tex_path(texpath), tex_type(tex_type),
im_bpp(0), im_h(0), im_w(0)
{
stbi_set_flip_vertically_on_load(0);
glGenTextures(1, &tex_ID);
glBindTexture(GL_TEXTURE_2D, tex_ID);
GLubyte* m_buffer = nullptr;
GLfloat* m_buffer_f = nullptr;
if (tex_path.find(TextureLib::root_dir) == std::string::npos)
tex_path = TextureLib::root_dir + tex_path;
auto [interlayout, layout, type, _] = Texture::ParseFormat(tex_type);
switch (tex_type)
{
case RGBA_TEXTURE:
m_buffer = stbi_load(tex_path.c_str(), &im_w, &im_h, &im_bpp, 4);
Texture::SetTexParam<GL_TEXTURE_2D>(tex_ID, GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR, tile_type, tile_type, 0, 4);
//glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, im_w, im_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, m_buffer);
glTexStorage2D(GL_TEXTURE_2D, 8, interlayout, im_w, im_h);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, im_w, im_h, layout, type, m_buffer);
#if 1
glGenerateMipmap(GL_TEXTURE_2D);
#else
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
#endif
glBindTexture(GL_TEXTURE_2D, 0);
if (m_buffer) {
stbi_image_free(m_buffer);
#ifdef _DEBUG
std::cout << "Image texture has been load successfully! [" << im_w << ":" << im_h << "]" << std::endl;
#endif
}
else {
std::cout << "Image texture FAILED" << std::endl;
_delTexture();
}
break;
case HDR_TEXTURE:
m_buffer_f = stbi_loadf(tex_path.c_str(), &im_w, &im_h, &im_bpp, 4);
Texture::SetTexParam<GL_TEXTURE_2D>(tex_ID, GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR, tile_type, tile_type, 0, 8);
//glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, im_w, im_h, 0, GL_RGBA, GL_FLOAT, m_buffer_f); //
glTexStorage2D(GL_TEXTURE_2D, 8, interlayout, im_w, im_h);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, im_w, im_h, layout, type, m_buffer_f);
#if 1
glGenerateMipmap(GL_TEXTURE_2D);
#else
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
#endif
glBindTexture(GL_TEXTURE_2D, 0);
if (m_buffer_f) {
stbi_image_free(m_buffer_f);
#ifdef _DEBUG
std::cout << "HDR texture has been load successfully! [" << im_w << ":" << im_h << "]" << std::endl;
#endif
}
else {
std::cout << "HDR texture FAILED" << std::endl;
_delTexture();
}
break;
default:
assert(false && "Unsupported texture type for file loading");
}
}
Texture::Texture()
{
}
Texture::Texture(int _w, int _h, TextureType tex_type, GLuint tile_type)
:tex_path(""), tex_type(tex_type),
im_bpp(32), im_h(_h), im_w(_w)
{
auto [interlayout, layout, type, _] = Texture::ParseFormat(tex_type);
glGenTextures(1, &tex_ID);
glBindTexture(GL_TEXTURE_2D, tex_ID);
glTexImage2D(GL_TEXTURE_2D, 0, interlayout, im_w, im_h, 0, layout, type, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, tile_type);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, tile_type);
glBindTexture(GL_TEXTURE_2D, 0);
}
Texture::Texture(int _w, int _h, GLuint _layout, const void* _ptr,
GLint _min_filter, GLint _mag_filter, GLint _wrap_s, GLint _wrap_t)
:im_w(_w), im_h(_h)
{
glGenTextures(1, &tex_ID);
glBindTexture(GL_TEXTURE_2D, tex_ID);
Texture::SetTexParam<GL_TEXTURE_2D>(tex_ID, _min_filter, _mag_filter, _wrap_s, _wrap_t, 0, 1);
switch (_layout)
{
case GL_RG8:
glTexImage2D(GL_TEXTURE_2D, 0, _layout, im_w, im_h, 0, GL_RG, GL_UNSIGNED_BYTE, _ptr);
tex_type = RG_TEXTURE;
break;
case GL_RG16F:
glTexImage2D(GL_TEXTURE_2D, 0, _layout, im_w, im_h, 0, GL_RG, GL_FLOAT, _ptr);
tex_type = RG_TEXTURE;
break;
case GL_RGB8:
glTexImage2D(GL_TEXTURE_2D, 0, _layout, im_w, im_h, 0, GL_RGB, GL_UNSIGNED_BYTE, _ptr);
tex_type = RGB_TEXTURE;
break;
case GL_RGB16F:
glTexImage2D(GL_TEXTURE_2D, 0, _layout, im_w, im_h, 0, GL_RGB, GL_FLOAT, _ptr);
tex_type = RGB_TEXTURE;
break;
case GL_RGBA8:
glTexImage2D(GL_TEXTURE_2D, 0, _layout, im_w, im_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, _ptr);
tex_type = BUFFER_TEXTURE;
break;
case GL_RGBA32F:
glTexImage2D(GL_TEXTURE_2D, 0, _layout, im_w, im_h, 0, GL_RGBA, GL_FLOAT, _ptr);
tex_type = HDR_BUFFER_TEXTURE;
break;
default:
assert(false && "WRONG LAYOUT");
}
glBindTexture(GL_TEXTURE_2D, 0);
}
Texture::Texture(int _w, int _h, GLuint _ID, TextureType _type, std::string _name)
:im_w(_w), im_h(_h), tex_ID(_ID), tex_type(_type), tex_path(_name)
{
}
Texture::Texture(int _w, int _h, TextureType _type)
: im_w(_w), im_h(_h), tex_type(_type)
{
auto [interlayout, layout, data_type, gl_type] = Texture::ParseFormat(tex_type);
glGenTextures(1, &tex_ID);
glBindTexture(gl_type, tex_ID);
switch (gl_type)
{
case GL_TEXTURE_2D:
Texture::SetTexParam<GL_TEXTURE_2D>(tex_ID, GL_LINEAR, GL_LINEAR, GL_REPEAT, GL_REPEAT);
glTexImage2D(GL_TEXTURE_2D, 0, interlayout, im_w, im_h, 0, layout, data_type, NULL);
break;
case GL_TEXTURE_CUBE_MAP:
Texture::SetTexParam<GL_TEXTURE_CUBE_MAP>(tex_ID, GL_LINEAR, GL_LINEAR, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, 0, 0, GL_CLAMP_TO_EDGE);
LOOP(6)
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, interlayout, im_w, im_w, 0, layout, data_type, NULL);
break;
}
}
Texture::Texture(const Texture& tex)
{
if (this == &tex)
return;
if (tex_ID != 0)
_delTexture();
_deepCopyFrom(tex);
}
Texture::Texture(Texture&& tex) noexcept
{
_cpyInfo(tex);
tex.tex_ID = 0;
}
Texture& Texture::operator=(const Texture& tex)
{
if (this == &tex)
return *this;
if (tex_ID != 0)
_delTexture();
_deepCopyFrom(tex);
return *this;
}
Texture& Texture::operator=(Texture&& tex)noexcept
{
if (this == &tex)
return *this;
_cpyInfo(tex);
tex.tex_ID = 0;
return *this;
}
Texture::~Texture()
{
if (tex_ID != 0)
_delTexture();
}
void Texture::_delTexture()
{
glDeleteTextures(1, &tex_ID);
tex_ID = 0;
}
void Texture::_deepCopyFrom(const Texture& tex)
{
_cpyInfo(tex);
if (tex.tex_ID == 0)
return;
auto [interlayout, layout, type, gl_type] = Texture::ParseFormat(tex_type);
if (gl_type == GL_NONE)
return;
glGenTextures(1, &tex_ID);
GLint min_filter = GL_LINEAR;
GLint mag_filter = GL_LINEAR;
GLint wrap_s = GL_REPEAT;
GLint wrap_t = GL_REPEAT;
GLint wrap_r = GL_REPEAT;
GLint base_level = 0;
GLint max_level = 0;
GLint width = 0;
GLint height = 0;
GLint depth = 1;
GLint src_internal = 0;
GLint dst_internal = 0;
GLint immutable_levels = 0;
glBindTexture(gl_type, tex.tex_ID);
glGetTexParameteriv(gl_type, GL_TEXTURE_MIN_FILTER, &min_filter);
glGetTexParameteriv(gl_type, GL_TEXTURE_MAG_FILTER, &mag_filter);
glGetTexParameteriv(gl_type, GL_TEXTURE_WRAP_S, &wrap_s);
glGetTexParameteriv(gl_type, GL_TEXTURE_WRAP_T, &wrap_t);
glGetTexParameteriv(gl_type, GL_TEXTURE_WRAP_R, &wrap_r);
glGetTexParameteriv(gl_type, GL_TEXTURE_BASE_LEVEL, &base_level);
glGetTexParameteriv(gl_type, GL_TEXTURE_MAX_LEVEL, &max_level);
glGetTexParameteriv(gl_type, GL_TEXTURE_IMMUTABLE_LEVELS, &immutable_levels);
glGetTexLevelParameteriv(gl_type, 0, GL_TEXTURE_WIDTH, &width);
glGetTexLevelParameteriv(gl_type, 0, GL_TEXTURE_HEIGHT, &height);
glGetTexLevelParameteriv(gl_type, 0, GL_TEXTURE_DEPTH, &depth);
glGetTexLevelParameteriv(gl_type, 0, GL_TEXTURE_INTERNAL_FORMAT, &src_internal);
assert(im_w == width);
assert(im_h == height);
int levels = std::min(std::max(immutable_levels, 1), max_level + 1);
glBindTexture(gl_type, tex_ID);
glTexParameteri(gl_type, GL_TEXTURE_MIN_FILTER, min_filter);
glTexParameteri(gl_type, GL_TEXTURE_MAG_FILTER, mag_filter);
glTexParameteri(gl_type, GL_TEXTURE_WRAP_S, wrap_s);
glTexParameteri(gl_type, GL_TEXTURE_WRAP_T, wrap_t);
glTexParameteri(gl_type, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(gl_type, GL_TEXTURE_MAX_LEVEL, levels - 1);
if (gl_type == GL_TEXTURE_2D) {
glTexStorage2D(gl_type, levels, interlayout, width, height);
glGetTexLevelParameteriv(gl_type, 0, GL_TEXTURE_INTERNAL_FORMAT, &dst_internal);
for (int level = 0; level < levels; ++level) {
int copy_w = std::max(1, width >> level);
int copy_h = std::max(1, height >> level);
glCopyImageSubData(
tex.tex_ID, gl_type, level,
0, 0, 0,
tex_ID, gl_type, level,
0, 0, 0,
copy_w, copy_h, 1);
}
}
else if (gl_type == GL_TEXTURE_CUBE_MAP) {
glTexParameteri(gl_type, GL_TEXTURE_WRAP_R, wrap_r);
glTexStorage2D(gl_type, levels, interlayout, width, height);
glGetTexLevelParameteriv(gl_type, 0, GL_TEXTURE_INTERNAL_FORMAT, &dst_internal);
for (int level = 0; level < levels; ++level) {
int copy_w = std::max(1, width >> level);
int copy_h = std::max(1, height >> level);
glCopyImageSubData(tex.tex_ID, gl_type, level, 0, 0, 0,
tex_ID, gl_type, level, 0, 0, 0,
copy_w, copy_h, 6);
}
}
else if (gl_type == GL_TEXTURE_2D_ARRAY) {
glTexParameteri(gl_type, GL_TEXTURE_WRAP_R, wrap_r);
glTexStorage3D(gl_type, levels, interlayout, width, height, depth);
glGetTexLevelParameteriv(gl_type, 0, GL_TEXTURE_INTERNAL_FORMAT, &dst_internal);
for (int level = 0; level < levels; ++level) {
int copy_w = std::max(1, width >> level);
int copy_h = std::max(1, height >> level);
int copy_d = std::max(1, depth >> level);
glCopyImageSubData(tex.tex_ID, gl_type, level, 0, 0, 0,
tex_ID, gl_type, level, 0, 0, 0,
copy_w, copy_h, copy_d);
}
}
glBindTexture(gl_type, 0);
}
void Texture::Resize(const glm::vec2& size)
{
Resize((GLuint)size.x, (GLuint)size.y);
}
void Texture::Resize(GLuint x, GLuint y)
{
if(im_w == (int)x && im_h == (int)y)
return;
im_w = x;
im_h = y;
auto [interlayout, layout, type, gl_type] = Texture::ParseFormat(tex_type);
glBindTexture(gl_type, tex_ID);
if (gl_type == GL_TEXTURE_2D) {
glTexImage2D(gl_type, 0, interlayout, im_w, im_h, 0, layout, type, NULL);
Texture::SetTexParam<GL_TEXTURE_2D>(tex_ID, GL_LINEAR, GL_LINEAR);
}
else if (gl_type == GL_TEXTURE_CUBE_MAP) {
for (int i = 0; i < 6; ++i)
{
glTexImage2D(
GL_TEXTURE_CUBE_MAP_POSITIVE_X + i,
0,
interlayout, // internal format
im_w,
im_h,
0,
layout, type,
nullptr
);
}
Texture::SetTexParam<GL_TEXTURE_CUBE_MAP>(tex_ID, GL_LINEAR, GL_LINEAR);
}
}
void Texture::Bind(GLuint slot) const
{
if (slot == -1)
slot = tex_type + tex_slot_offset;
Texture::BindM(tex_ID, slot, tex_type);
}
void Texture::BindC(GLuint slot /*= -1*/, GLuint read_or_write /*= GL_READ_WRITE*/, GLuint _level /*= 0*/) const
{
if (slot == -1)
slot = tex_type + tex_slot_offset;
auto [layout, _1, _2, gl_type] = Texture::ParseFormat(tex_type);
GLuint is_array = (_level != 0 || gl_type == GL_TEXTURE_CUBE_MAP) ? GL_TRUE : GL_FALSE;
glBindImageTexture(slot, tex_ID, 0, is_array, 0, read_or_write, layout);
}
void Texture::BindU(GLuint slot /*= -1*/) const
{
if (slot == -1) slot = tex_type;
glBindTextureUnit(slot, tex_ID);
}
inline void Texture::BindM(GLuint _id, GLuint _slot /*= 0*/, TextureType _type/*=RGBA_TEXTURE*/)
{
const GLuint gl_type = std::get<3>(Texture::ParseFormat(_type));
glActiveTexture(GL_TEXTURE0 + _slot);
glBindTexture(gl_type, _id);
}
void Texture::UnbindC(GLuint slot /*= -1*/, GLuint read_or_write /*= GL_READ_WRITE*/, GLuint _level /*= 0*/) const
{
if (slot == -1)
slot = tex_type + tex_slot_offset;
// other parameters are not necessary.
glBindImageTexture(slot, 0, 0, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA32F);
}
void Texture::Unbind() const
{
glBindTexture(std::get<3>(Texture::ParseFormat(tex_type)), 0);
//glActiveTexture(0);
}
inline Texture::TexStorageInfo Texture::ParseFormat(TextureType _type)
{
// https://docs.gl/gl4/glTexImage2D
switch (_type)
{
case RGBA_TEXTURE:
case BUFFER_TEXTURE:
return { GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, GL_TEXTURE_2D };
case RGB_TEXTURE:
return { GL_RGB8, GL_RGB, GL_UNSIGNED_BYTE, GL_TEXTURE_2D };
case HDR_TEXTURE:
case HDR_BUFFER_TEXTURE:
return { GL_RGBA32F, GL_RGBA, GL_FLOAT, GL_TEXTURE_2D };
case HDR_CUBE_TEXTURE:
return { GL_RGBA32F, GL_RGBA, GL_FLOAT, GL_TEXTURE_CUBE_MAP };
case FLOAT_BUFFER_TEXTURE:
case RG_TEXTURE:
return { GL_RG16F, GL_RG, GL_FLOAT, GL_TEXTURE_2D };
case LAYERED_TEXTURE:
return { GL_RGBA32F, GL_RGBA, GL_FLOAT, GL_TEXTURE_2D_ARRAY };
case LIGHTING_CACHE:
return { GL_R16F, GL_RED, GL_FLOAT, GL_TEXTURE_2D };
case DEPTH_CUBE_TEXTURE:
return { GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT, GL_TEXTURE_CUBE_MAP };
case DEPTH_TEXTURE:
return { GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT, GL_TEXTURE_2D };
default:
//assert(false && "WRONG FORMAT");
return { GL_NONE, GL_NONE , GL_NONE, GL_NONE };
}
}
template<GLuint Type>
inline void Texture::SetTexParam(GLuint _id, GLuint _fil_min, GLuint _fil_max, GLuint _warp_s /*= 0*/, GLuint _warp_t /*= 0*/, GLuint _lev_min /*= 0*/, GLuint _lev_max /*= 0*/, GLuint _warp_r /*= 0*/)
{
glBindTexture(Type, _id);
glTexParameteri(Type, GL_TEXTURE_MIN_FILTER, _fil_min);
glTexParameteri(Type, GL_TEXTURE_MAG_FILTER, _fil_max);
if (_warp_s * _warp_t != 0) {
glTexParameteri(Type, GL_TEXTURE_WRAP_S, _warp_s);
glTexParameteri(Type, GL_TEXTURE_WRAP_T, _warp_t);
}
if (_warp_r != 0)
glTexParameteri(Type, GL_TEXTURE_WRAP_T, _warp_r);
if (_lev_min + _lev_max != 0) {
glTexParameteri(Type, GL_TEXTURE_BASE_LEVEL, _lev_min);
glTexParameteri(Type, GL_TEXTURE_MAX_LEVEL, _lev_max);
}
}
#include "shaders/ComputeShader.h"
#include "LtcMatrix.h"
void Texture::GenIrradiaceConvFrom(const Texture& _Tar_Tex)
{
GenIrradianceConv(_Tar_Tex.GetTexID(), _Tar_Tex.im_w, _Tar_Tex.im_h, _Tar_Tex.tex_type);
}
void Texture::GenIBLDiffuseFrom(const Texture& _Tar_Tex, bool to_cubemap /*= false*/)
{
GenIBLDiffuse(_Tar_Tex.GetTexID(), _Tar_Tex.im_w, _Tar_Tex.im_h, _Tar_Tex.tex_type, to_cubemap);
}
void Texture::GenIBLSpecularFrom(const Texture& _Tar_Tex, bool to_cubemap /*= false*/)
{
GenIBLSpecular(_Tar_Tex.GetTexID(), _Tar_Tex.im_w, _Tar_Tex.im_h, _Tar_Tex.tex_type, to_cubemap);
}
void Texture::GenCubeMapFrom(const Texture& _Tar_Tex, int res)
{
GenCubeMap(_Tar_Tex.GetTexID(), res, _Tar_Tex.tex_type);
}
void Texture::GenERectMapFrom(const Texture& _Tar_Tex, int _w /*= 2048*/, int _h /*= 1024*/)
{
GenERectMap(_Tar_Tex.GetTexID(), _w, _h, _Tar_Tex.tex_type);
}
void Texture::ConvertDepthFrom(const Texture& _Tar_Tex)
{
ConvertDepth(_Tar_Tex.GetTexID(), _Tar_Tex.GetW(), _Tar_Tex.GetH(), _Tar_Tex.tex_type);
}
void Texture::ConvertDepthCubeFrom(const Texture& _Tar_Tex)
{
ConvertDepthCube(_Tar_Tex.GetTexID(), _Tar_Tex.GetW(), _Tar_Tex.GetH(), _Tar_Tex.tex_type);
}
void Texture::ConvertPNGFrom(const Texture& _Tar_Tex)
{
ConvertPNG(_Tar_Tex.GetTexID(), _Tar_Tex.GetW(), _Tar_Tex.GetH());
}
void Texture::FillColor(const glm::vec4 col)
{
ComputeShader& fill = ComputeShader::ImportShader("pps/Fill");
GLuint slot = -1;
auto [interlayout, layout, type, _] = Texture::ParseFormat(tex_type);
switch (interlayout)
{
case GL_RGBA32F: slot = 0; break;
case GL_RGBA8: slot = 1; break;
case GL_R16F: slot = 2; break;
default: assert(false && "unknown format");
}
BindC(slot);
fill.UseShader();
fill.SetValue("color", col);
fill.SetValue("slot", slot);
fill.RunComputeShaderSCR({ im_w, im_h }, 4);
}
void Texture::GenIrradianceConv(GLuint _tar_ID, int _tar_w, int _tar_h, TextureType _tar_type /*= HDR_TEXTURE*/)
{
assert(false && "this function has been abandoned");
}
void Texture::GenIBLSpecular(GLuint _tar_ID, int _tar_w, int _tar_h, TextureType _tar_type /*= HDR_TEXTURE*/, bool to_cubemap /*= false*/)
{
// https://learnopengl.com/PBR/IBL/Specular-IBL //
const int max_inter = 8;
GLuint ID;
glGenTextures(1, &ID); //for storage
if (to_cubemap) {
glBindTexture(GL_TEXTURE_CUBE_MAP, ID);
Texture::SetTexParam<GL_TEXTURE_CUBE_MAP>(ID, GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, 0, max_inter - 1, GL_CLAMP_TO_EDGE);
im_w = im_h = 2048; tex_type = HDR_CUBE_TEXTURE;
}
else {
glBindTexture(GL_TEXTURE_2D, ID);
Texture::SetTexParam<GL_TEXTURE_2D>(ID, GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR, GL_REPEAT, GL_MIRRORED_REPEAT, 0, max_inter - 1);
im_w = _tar_w; im_h = _tar_h; tex_type = _tar_type;
}
auto [interlayout, layout, type, _] = Texture::ParseFormat(_tar_type);
LOOP(8) {
int lod_w = im_w / (int)pow(2, i);
int lod_h = im_h / (int)pow(2, i);
lod_w -= lod_w % 4;
lod_h -= lod_h % 4;
if (to_cubemap) {
LOOP_N(6, s)
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + s, i, interlayout, lod_w, lod_h, 0, layout, type, NULL);
}
else {
glTexImage2D(GL_TEXTURE_2D, i, interlayout, lod_w, lod_h, 0, layout, type, NULL);
}
}
ComputeShader& importance_samp = ComputeShader::ImportShader(to_cubemap ? "convert/Importance_Samp_E2C" : "convert/Importance_Samp");
LOOP(8) { // 0 1 2 3 4 5 6 7
int lod_w = im_w / (int)pow(2, i);
int lod_h = im_h / (int)pow(2, i);
lod_w -= lod_w % 4;
lod_h -= lod_h % 4;
Texture::BindM(_tar_ID, _tar_type);
glBindImageTexture(tex_type + 1, ID, i, (GLuint)to_cubemap, 0, GL_WRITE_ONLY, interlayout); // HDR_TEXTURE -> HDR_TEXTURE + 1
importance_samp.UseShader();
importance_samp.SetValue("s", (float)i / (float)(max_inter - 1));
importance_samp.SetValue("max_step", 32);
importance_samp.SetValue("source", _tar_type);
importance_samp.RunComputeShader(lod_w / 4, lod_h / 4, to_cubemap ? 6 : 1);
}
_resetTexID(ID);
}
void Texture::GenIBLDiffuse(GLuint _tar_ID, int _tar_w, int _tar_h, TextureType _tar_type /*= HDR_TEXTURE*/, bool to_cubemap /*= false*/)
{
// https://learnopengl.com/PBR/IBL/Diffuse-irradiance //
GLuint ID;
glGenTextures(1, &ID); //for storage
if (to_cubemap) {
glBindTexture(GL_TEXTURE_CUBE_MAP, ID);
Texture::SetTexParam<GL_TEXTURE_CUBE_MAP>(ID, GL_LINEAR, GL_LINEAR, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, 0, 0, GL_CLAMP_TO_EDGE);
im_w = im_h = 64; tex_type = HDR_CUBE_TEXTURE;
}
else {
glBindTexture(GL_TEXTURE_2D, ID);
Texture::SetTexParam<GL_TEXTURE_2D>(ID, GL_LINEAR, GL_LINEAR, GL_REPEAT, GL_MIRRORED_REPEAT);
im_w = _tar_w / 8; im_h = _tar_h / 8; tex_type = _tar_type;
}
auto [interlayout, layout, type, _] = Texture::ParseFormat(_tar_type);
if (to_cubemap) {
LOOP_N(6, s)
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + s, 0, interlayout, im_w, im_h, 0, layout, type, NULL);
}
else {
glTexImage2D(GL_TEXTURE_2D, 0, interlayout, im_w, im_h, 0, layout, type, NULL);
}
Texture::BindM(_tar_ID, _tar_type + 1);
glBindImageTexture(tex_type, ID, 0, (GLuint)to_cubemap, 0, GL_WRITE_ONLY, interlayout);
ComputeShader& irradiance_conv = ComputeShader::ImportShader(to_cubemap ? "convert/Irradiance_Conv_E2C" : "convert/Irradiance_Conv");
irradiance_conv.UseShader();
irradiance_conv.SetValue("max_step", 64);
irradiance_conv.SetValue("source", _tar_type + 1);
irradiance_conv.RunComputeShader(im_w / 4, im_w / 4, to_cubemap ? 6 : 1);
_resetTexID(ID);
}
void Texture::GenCubeMap(GLuint _tar_ID, int _tar_res, TextureType _tar_type /*= HDR_TEXTURE*/)
{
const bool type_correct = !((_tar_type == HDR_CUBE_TEXTURE) || (_tar_type == DEPTH_CUBE_TEXTURE));
assert(type_correct && "Wrong input texture type");
// https://learnopengl.com/Advanced-OpenGL/Cubemaps
auto [interlayout, layout, type, _] = Texture::ParseFormat(_tar_type);
GLuint ID;
glGenTextures(1, &ID); //for storage
Texture::SetTexParam<GL_TEXTURE_CUBE_MAP>(ID, GL_LINEAR, GL_LINEAR, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, 0, 0, GL_CLAMP_TO_EDGE);
LOOP(6)
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, interlayout, _tar_res, _tar_res, 0, layout, type, NULL);
ComputeShader& to_cubemap = ComputeShader::ImportShader("convert/E2C", Uni("U_etangular", 1));
glBindImageTexture(0, ID, 0, GL_TRUE, 0, GL_WRITE_ONLY, interlayout);
Texture::BindM(_tar_ID, 1);
to_cubemap.UseShader();
to_cubemap.RunComputeShader(_tar_res / 4, _tar_res / 4, 6);
_resetTexID(ID);
tex_type = HDR_CUBE_TEXTURE;
im_w = im_h = _tar_res;
}
void Texture::GenERectMap(GLuint _tar_ID, int _w, int _h, TextureType _tar_type /*= HDR_TEXTURE*/)
{
const bool type_correct = (_tar_type == HDR_CUBE_TEXTURE) || (_tar_type == DEPTH_CUBE_TEXTURE);
assert(type_correct && "Wrong input texture type");
auto [interlayout, layout, type, _] = Texture::ParseFormat(HDR_CUBE_TEXTURE);
GLuint ID;
glGenTextures(1, &ID); //for storage
glBindTexture(GL_TEXTURE_2D, ID);
glTexImage2D(GL_TEXTURE_2D, 0, interlayout, _w, _h, 0, layout, type, NULL);
Texture::SetTexParam<GL_TEXTURE_2D>(ID, GL_LINEAR, GL_LINEAR, GL_MIRRORED_REPEAT, GL_MIRRORED_REPEAT);
ComputeShader& to_rectmap = ComputeShader::ImportShader("convert/C2E", Uni("U_Cube", 1));
glBindImageTexture(0, ID, 0, GL_FALSE, 0, GL_WRITE_ONLY, interlayout);
Texture::BindM(_tar_ID, 1, _tar_type);
to_rectmap.UseShader();
to_rectmap.RunComputeShader(_w / 4, _h / 4);
_resetTexID(ID);
tex_type = HDR_TEXTURE;
im_w = _w; im_h = _h;
}
void Texture::ConvertDepth(GLuint _tar_ID, int _w, int _h, TextureType _tar_type /*= DEPTH_TEXTURE*/)
{
const bool type_correct = (_tar_type == DEPTH_TEXTURE) || (_tar_type == DEPTH_CUBE_TEXTURE);
if (!type_correct) return;
auto [interlayout, layout, type, gl_type] = Texture::ParseFormat(_tar_type);
GLuint ID;
glGenTextures(1, &ID); //for storage
Texture::SetTexParam<GL_TEXTURE_2D>(ID, GL_LINEAR, GL_LINEAR, GL_MIRRORED_REPEAT, GL_MIRRORED_REPEAT);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, _w, _h, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
ComputeShader& to_texture = ComputeShader::ImportShader(gl_type == GL_TEXTURE_2D ? "convert/Depth_Texture" : "convert/Depth_Texture_C2E");
Texture::BindM(_tar_ID, 0, _tar_type);
glBindImageTexture(1, ID, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA8);
to_texture.UseShader();
to_texture.SetValue("U_depth", 0);
to_texture.RunComputeShader(_w / 4, _h / 4);
_resetTexID(ID);
tex_type = RGBA_TEXTURE;
im_w = _w; im_h = _h;
}
void Texture::ConvertDepthCube(GLuint _tar_ID, int _w, int _h, TextureType _tar_type /*= DEPTH_CUBE_TEXTURE*/)
{
if (_tar_type != DEPTH_CUBE_TEXTURE) return;
GLuint ID;
glGenTextures(1, &ID);
glBindTexture(GL_TEXTURE_CUBE_MAP, ID);
Texture::SetTexParam<GL_TEXTURE_CUBE_MAP>(ID, GL_LINEAR, GL_LINEAR, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, 0, 0, GL_CLAMP_TO_EDGE);
LOOP(6)
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGBA32F, _w, _h, 0, GL_RGBA, GL_FLOAT, nullptr);
ComputeShader& to_texture = ComputeShader::ImportShader("convert/Depth_Texture_C2C");
Texture::BindM(_tar_ID, 0, _tar_type);
glBindImageTexture(1, ID, 0, GL_TRUE, 0, GL_WRITE_ONLY, GL_RGBA32F);
to_texture.UseShader();
to_texture.SetValue("U_depth", 0);
to_texture.RunComputeShader((_w + 3) / 4, (_h + 3) / 4, 6);
_resetTexID(ID);
tex_type = HDR_CUBE_TEXTURE;
im_w = _w; im_h = _h;
}
void Texture::ConvertPNG(GLuint _tar_ID, int _w, int _h)
{
auto [interlayout, layout, type, _] = Texture::ParseFormat(RGBA_TEXTURE);
GLuint ID;
glGenTextures(1, &ID); //for storage
Texture::SetTexParam<GL_TEXTURE_2D>(ID, GL_LINEAR, GL_LINEAR, GL_MIRRORED_REPEAT, GL_MIRRORED_REPEAT);
glTexImage2D(GL_TEXTURE_2D, 0, interlayout, _w, _h, 0, layout, type, NULL);
ComputeShader& hdr_to_png = ComputeShader::ImportShader("convert/HDR2PNG");
glBindImageTexture(0, ID, 0, GL_FALSE, 0, GL_WRITE_ONLY, interlayout);
glBindImageTexture(1, _tar_ID, 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA32F);;
hdr_to_png.UseShader();
hdr_to_png.SetValue("U_HDR", 1);
hdr_to_png.RunComputeShader(_w, _h);
_resetTexID(ID);
tex_type = RGBA_TEXTURE;
im_w = _w; im_h = _h;
}
#include "stb_image_write.h"
void Texture::SaveTexture(std::string _path, bool force_png, bool force_cube) const
{
auto [_, layout, type, gl_type] = Texture::ParseFormat(tex_type);
static std::string root = "result/";
int status = -1;
stbi_flip_vertically_on_write(0);
glBindTexture(gl_type, tex_ID);
if (gl_type == GL_TEXTURE_CUBE_MAP && !force_cube) {
Texture rect_map;
if (layout == GL_DEPTH_COMPONENT) {
Texture depth_cube;
depth_cube.ConvertDepthCubeFrom(*this);
rect_map.GenERectMapFrom(depth_cube, depth_cube.GetW() * 2, depth_cube.GetH());
} else {
rect_map.GenERectMapFrom(*this, im_w * 2, im_h);
}
rect_map.SaveTexture(_path, force_png, true);
return;
}
if (layout == GL_DEPTH_COMPONENT) { // Depth Texture
if (force_png) {
static Texture depth_png;
depth_png.ConvertDepthFrom(*this);
depth_png.SaveTexture(_path, true);
return;
}
if (gl_type == GL_TEXTURE_2D) {
auto depth = std::vector<GLfloat>(im_w * im_h);
auto odata = std::vector<GLfloat>(im_w * im_h * 4);
glGetTexImage(gl_type, 0, layout, type, depth.data());
for (size_t i = 0; i < depth.size(); ++i) {
const size_t base = i * 4;
odata[base] = depth[i];
odata[base + 1] = depth[i];
odata[base + 2] = depth[i];
odata[base + 3] = 1.0f;
}
std::string outputPath = root + _path + ".hdr";
status = stbi_write_hdr(outputPath.c_str(), im_w, im_h, 4, odata.data());
}
else if (gl_type == GL_TEXTURE_CUBE_MAP) {
Texture depth_cube;
depth_cube.ConvertDepthCubeFrom(*this);
depth_cube.SaveTexture(_path, false, true);
}
else {
assert(false && "No depth map format matched");
}
}else if (type == GL_FLOAT) {
if (force_png) {
static Texture hdr_png;
hdr_png.ConvertPNGFrom(*this);
hdr_png.SaveTexture(_path);
return;
}
else if (gl_type == GL_TEXTURE_2D) {
auto odata = std::vector<GLfloat>(im_w * im_h * 4);
glGetTexImage(gl_type, 0, layout, type, odata.data());
std::string outputPath = root + _path + ".hdr";
status = stbi_write_hdr(outputPath.c_str(), im_w, im_h, 4, odata.data());
}
else if (gl_type == GL_TEXTURE_CUBE_MAP) {
auto odata = std::vector<GLfloat>(im_w * im_h * 4);
LOOP(6) {
glGetTexImage(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, layout, type, odata.data());
// e.g. outputPath = result/hdr_cube/hdr_cube_1.hdr
std::string outputPath = root + _path + "/" + _path + "_" + std::to_string(i + 1) + ".hdr";
status = stbi_write_hdr(outputPath.c_str(), im_w, im_h, 4, odata.data());
}
}
else {
assert(false && "No HDR map format matched");
}
}
else if (type == GL_UNSIGNED_BYTE) {
if (gl_type == GL_TEXTURE_2D) {
assert(type == GL_UNSIGNED_BYTE);
auto odata = std::vector<GLbyte>(im_w * im_h * 4);
glGetTexImage(gl_type, 0, layout, type, odata.data());
std::string outputPath = root + _path + ".png";
status = stbi_write_png(outputPath.c_str(), im_w, im_h, 4, odata.data(), 0);
}
else if (gl_type == GL_TEXTURE_CUBE_MAP) {
assert(type == GL_UNSIGNED_BYTE);
auto odata = std::vector<GLbyte>(im_w * im_h * 4);
LOOP(6) {
glGetTexImage(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, layout, type, odata.data());
std::string outputPath = root + _path + "/" + _path + "_" + std::to_string(i + 1) + ".png";
status = stbi_write_png(outputPath.c_str(), im_w, im_h, 4, odata.data(), 0);
}
}
else {
assert(false && "No bit map format matched");
}
}
else {
assert(false && "No texture type matched");
}
if (status == 0) {
DEBUG("failed");
}
}
void Texture::PrintTexture() const
{
auto [_, layout, type, gl_type] = Texture::ParseFormat(tex_type);
auto odata = std::vector<float>(im_w * im_h * 4);
glBindTexture(gl_type, tex_ID);
glGetTexImage(gl_type, 0, layout, type, odata.data());
int a = 0;
}
std::unordered_map<std::string, std::shared_ptr<Texture>> TextureLib::t_tex_list{};
std::string TextureLib::root_dir = "res/tex/";
Texture::TextureType TextureLib::ParseFileEXT(std::string path)
{
if (path.find(".png") != std::string::npos) return Texture::RGBA_TEXTURE;
else if (path.find(".jpg") != std::string::npos) return Texture::RGB_TEXTURE;
else if (path.find(".hdr") != std::string::npos) return Texture::HDR_TEXTURE;
return Texture::NONE_TEXTURE;
}
std::shared_ptr<Texture> TextureLib::GetTexture(const std::string& _name)
{
if (t_tex_list.find(_name) == t_tex_list.end())
return nullptr;
return t_tex_list[_name];
}
TextureLib::TextureRes TextureLib::LoadTexture(std::string _name)
{
if (t_tex_list.find(_name) != t_tex_list.end())
return t_tex_list[_name];
Texture::TextureType _type = TextureLib::ParseFileEXT(_name);
t_tex_list[_name] = std::make_shared<Texture>(_name, _type, GL_REPEAT);
return t_tex_list[_name];
}
GLuint TextureLib::GetTextureID(const std::string& _name)
{
return TextureLib::GetTexture(_name)->GetTexID();
}
void TextureLib::ResetTexLib()
{
t_tex_list.clear();
}
TextureLib::TextureRes TextureLib::Noise_2D_4x4()
{
const std::string _name = "Uni.2D.4.4";
auto result = GetTexture(_name);
if (result != nullptr)
return result;
GenNoiseTexture(UNI_2D_NOISE, 4, 4);
return t_tex_list[_name];
}
TextureLib::TextureRes TextureLib::Noise_2D_4x4xN(int n/*=6*/)
{
const std::string _name = "Uni.2D.4.4." + std::to_string(n);
auto result = GetTexture(_name);
if (result != nullptr)
return result;
GenNoiseTextures(UNI_2D_NOISE, 4, 4, n);
return t_tex_list[_name];
}
TextureLib::TextureRes TextureLib::Noise_2D_16x16xN(int n/*=6*/)
{
const std::string _name = "Uni.2D.16.16." + std::to_string(n);
auto result = GetTexture(_name);
if (result != nullptr)
return result;
GenNoiseTextures(UNI_2D_NOISE, 16, 16, n);
return t_tex_list[_name];
}
TextureLib::TextureRes TextureLib::IBL_LUT()
{
const std::string _name = "IBL_LUT";
auto result = GetTexture(_name);
if (result != nullptr)
return result;
t_tex_list[_name] = std::make_shared<Texture>("ibl_brdf_lut.png", Texture::RGBA_TEXTURE, GL_CLAMP);
return t_tex_list[_name];
}
TextureLib::TextureRes TextureLib::LTC1()
{
const std::string _name = "LTC1";
auto result = GetTexture(_name);
if (result != nullptr) return result;
t_tex_list[_name] = std::make_shared<Texture>(64, 64, GL_RGBA32F, reinterpret_cast<const void*>(LTC1_DATA), GL_LINEAR, GL_LINEAR, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE);
return t_tex_list[_name];
}
TextureLib::TextureRes TextureLib::LTC2()
{
const std::string _name = "LTC2";
auto result = GetTexture(_name);
if (result != nullptr) return result;
t_tex_list[_name] = std::make_shared<Texture>(64, 64, GL_RGBA32F, reinterpret_cast<const void*>(LTC2_DATA), GL_LINEAR, GL_LINEAR, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE);
return t_tex_list[_name];
}
void TextureLib::GenNoiseTexture(NoiseType _type, int _w, int _h)
{
GLuint id;
glGenTextures(1, &id);//for storage
glBindTexture(GL_TEXTURE_2D, id);
Texture::SetTexParam<GL_TEXTURE_2D>(id, GL_LINEAR, GL_LINEAR, GL_REPEAT, GL_REPEAT);
std::vector<glm::vec4> list(_w * _h);
LOOP(_w * _h) list[i] = xdzm::rand4();
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA32F, _w, _h);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, _w, _h, GL_RGBA, GL_FLOAT, list.data());
std::string type_name;
int dimension = 4;
switch (_type)
{
case TextureLib::NONE_NOISE:
type_name = "Non";
break;
case TextureLib::UNIFORM_NOISE:
type_name = "Uni";
break;
case TextureLib::GAUSSIAN_NOISE:
type_name = "Gau";
break;
case TextureLib::UNI_2D_NOISE:
type_name = "Uni";
dimension = 2;
break;
default:
break;
}
std::string noise_name = type_name + "." + std::to_string(dimension) + "D." + std::to_string(_w) + "." + std::to_string(_h);
TextureLib::t_tex_list[noise_name] = std::make_shared<Texture>(_w, _h, id, Texture::HDR_BUFFER_TEXTURE, noise_name);
}
void TextureLib::GenNoiseTextures(NoiseType _type, int _w, int _h, int _n)
{
GLuint id;
glGenTextures(1, &id);//for storage
glBindTexture(GL_TEXTURE_2D_ARRAY, id);
Texture::SetTexParam<GL_TEXTURE_2D_ARRAY>(id, GL_LINEAR, GL_LINEAR, GL_REPEAT, GL_REPEAT, 0, _n);
glTexStorage3D(GL_TEXTURE_2D_ARRAY, 1, GL_RGBA32F, _w, _h, _n);
LOOP_N(_n, level) {
std::vector<glm::vec4> list(_w * _h);
LOOP(_w * _h) list[i] = xdzm::rand4();
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, level, _w, _h, 1, GL_RGBA, GL_FLOAT, (float*)list.data());
}
std::string type_name;
int dimension = 4;
switch (_type)
{
case TextureLib::NONE_NOISE:
type_name = "Non";
break;
case TextureLib::UNIFORM_NOISE:
type_name = "Uni";
break;
case TextureLib::GAUSSIAN_NOISE:
type_name = "Gau";
break;
case TextureLib::UNI_2D_NOISE:
type_name = "Uni";
dimension = 2;
break;
default:
break;
}
std::string noise_name = type_name + "." + std::to_string(dimension) + "D." + std::to_string(_w) + "." + std::to_string(_h) + "." + std::to_string(_n);
TextureLib::t_tex_list[noise_name] = std::make_shared<Texture>(_w, _h, id, Texture::LAYERED_TEXTURE, noise_name);
}