Files
UnrealEngine/Engine/Source/ThirdParty/MaterialX/MaterialX-1.38.10/source/MaterialXRenderGlsl/GLFramebuffer.cpp
2025-05-18 13:04:45 +08:00

187 lines
6.1 KiB
C++

//
// Copyright Contributors to the MaterialX Project
// SPDX-License-Identifier: Apache-2.0
//
#include <MaterialXRenderGlsl/GLFramebuffer.h>
#include <MaterialXRenderGlsl/GlslProgram.h>
#include <MaterialXRenderGlsl/GlslRenderer.h>
#include <MaterialXRenderGlsl/GLTextureHandler.h>
#include <MaterialXRenderGlsl/External/Glad/glad.h>
MATERIALX_NAMESPACE_BEGIN
//
// GLFramebuffer methods
//
GLFramebufferPtr GLFramebuffer::create(unsigned int width, unsigned int height, unsigned channelCount, Image::BaseType baseType)
{
return GLFramebufferPtr(new GLFramebuffer(width, height, channelCount, baseType));
}
GLFramebuffer::GLFramebuffer(unsigned int width, unsigned int height, unsigned int channelCount, Image::BaseType baseType) :
_width(width),
_height(height),
_channelCount(channelCount),
_baseType(baseType),
_encodeSrgb(false),
_framebuffer(0),
_colorTexture(0),
_depthTexture(0)
{
if (!glGenFramebuffers)
{
gladLoadGL();
}
// Convert texture format to OpenGL.
int glType, glFormat, glInternalFormat;
GLTextureHandler::mapTextureFormatToGL(baseType, channelCount, true, glType, glFormat, glInternalFormat);
// Create and bind framebuffer.
glGenFramebuffers(1, &_framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, _framebuffer);
// Create the offscreen color target and attach to the framebuffer.
glGenTextures(1, &_colorTexture);
glBindTexture(GL_TEXTURE_2D, _colorTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, glInternalFormat, _width, _height, 0, glFormat, glType, nullptr);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _colorTexture, 0);
// Create the offscreen depth target and attach to the framebuffer.
glGenTextures(1, &_depthTexture);
glBindTexture(GL_TEXTURE_2D, _depthTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, _width, _height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, _depthTexture, 0);
glBindTexture(GL_TEXTURE_2D, GlslProgram::UNDEFINED_OPENGL_RESOURCE_ID);
glDrawBuffer(GL_NONE);
// Validate the framebuffer.
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE)
{
glBindFramebuffer(GL_FRAMEBUFFER, GlslProgram::UNDEFINED_OPENGL_RESOURCE_ID);
glDeleteFramebuffers(1, &_framebuffer);
_framebuffer = GlslProgram::UNDEFINED_OPENGL_RESOURCE_ID;
string errorMessage;
switch (status)
{
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
errorMessage = "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT";
break;
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
errorMessage = "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT";
break;
case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
errorMessage = "GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER";
break;
case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
errorMessage = "GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER";
break;
case GL_FRAMEBUFFER_UNSUPPORTED:
errorMessage = "GL_FRAMEBUFFER_UNSUPPORTED";
break;
case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE:
errorMessage = "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE";
break;
case GL_FRAMEBUFFER_UNDEFINED:
errorMessage = "GL_FRAMEBUFFER_UNDEFINED";
break;
case GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS:
errorMessage = "GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS";
break;
default:
errorMessage = std::to_string(status);
break;
}
throw ExceptionRenderError("Frame buffer object setup failed: " + errorMessage);
}
// Unbind on cleanup
glBindFramebuffer(GL_FRAMEBUFFER, GlslProgram::UNDEFINED_OPENGL_RESOURCE_ID);
}
GLFramebuffer::~GLFramebuffer()
{
if (_framebuffer)
{
glBindFramebuffer(GL_FRAMEBUFFER, GlslProgram::UNDEFINED_OPENGL_RESOURCE_ID);
glDeleteTextures(1, &_colorTexture);
glDeleteTextures(1, &_depthTexture);
glDeleteFramebuffers(1, &_framebuffer);
}
}
void GLFramebuffer::bind()
{
if (!_framebuffer)
{
throw ExceptionRenderError("No framebuffer exists to bind");
}
glBindFramebuffer(GL_FRAMEBUFFER, _framebuffer);
GLenum colorList[1] = { GL_COLOR_ATTACHMENT0 };
glDrawBuffers(1, colorList);
if (_encodeSrgb)
{
glEnable(GL_FRAMEBUFFER_SRGB);
}
else
{
glDisable(GL_FRAMEBUFFER_SRGB);
}
glViewport(0, 0, _width, _height);
}
void GLFramebuffer::unbind()
{
glBindFramebuffer(GL_FRAMEBUFFER, GlslProgram::UNDEFINED_OPENGL_RESOURCE_ID);
glDrawBuffer(GL_NONE);
}
ImagePtr GLFramebuffer::getColorImage(ImagePtr image)
{
if (!image)
{
image = Image::create(_width, _height, _channelCount, _baseType);
image->createResourceBuffer();
}
int glType, glFormat, glInternalFormat;
GLTextureHandler::mapTextureFormatToGL(_baseType, _channelCount, false, glType, glFormat, glInternalFormat);
bind();
glPixelStorei(GL_PACK_ALIGNMENT, 1);
glReadBuffer(GL_COLOR_ATTACHMENT0);
glReadPixels(0, 0, image->getWidth(), image->getHeight(), glFormat, glType, image->getResourceBuffer());
unbind();
return image;
}
void GLFramebuffer::blit()
{
glBindFramebuffer(GL_READ_FRAMEBUFFER, _framebuffer);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glDrawBuffer(GL_BACK);
glBlitFramebuffer(0, 0, _width, _height, 0, 0, _width, _height,
GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT, GL_NEAREST);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
MATERIALX_NAMESPACE_END