Implement Reverse Z projection matrix for skin model

Signed-off-by: Dylan Schooner <dschooner05@gmail.com>
This commit is contained in:
Dylan Schooner
2025-10-31 12:20:28 -04:00
parent 2982e6e7c9
commit b488547054
5 changed files with 85 additions and 24 deletions

View File

@@ -1,6 +1,7 @@
<RCC>
<qresource prefix="/shaders">
<file>vshader.glsl</file>
<file>vshader_skin_model.glsl</file>
<file>vshader_skin_background.glsl</file>
<file>fshader.glsl</file>
</qresource>
</RCC>

View File

@@ -0,0 +1,11 @@
attribute vec4 a_position;
attribute vec2 a_texcoord;
varying vec2 v_texcoord;
void main()
{
gl_Position = a_position;
v_texcoord = a_texcoord;
}

View File

@@ -1,6 +1,12 @@
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
// https://code.qt.io/cgit/qt/qtbase.git/tree/examples/opengl/cube/vshader.glsl
// Dylan Schooner - 2025
// Modification: Implemented final Z-NDC re-inversion to compensate
// for rigid OpenGL 2.0 context forcing glClearDepth(1.0).
// This flips the high-precision Reverse Z output to the standard [0, W] range.
#ifdef GL_ES
// Set default precision to medium
precision mediump int;
@@ -20,6 +26,11 @@ void main()
// Calculate vertex position in screen space
gl_Position = mvp_matrix * model_matrix * a_position;
// Invert the z component of our Reverse Z matrix back to standard NDC
float near_z = gl_Position.z;
float w_c = gl_Position.w;
gl_Position.z = w_c - near_z;
// Pass texture coordinate to fragment shader
// Value will be automatically interpolated to fragments inside polygon faces
v_texcoord = a_texcoord;

View File

@@ -56,12 +56,19 @@ SkinOpenGLWindow::~SkinOpenGLWindow()
}
delete m_backgroundTexture;
}
if (m_program) {
if (m_program->isLinked()) {
m_program->release();
if (m_modelProgram) {
if (m_modelProgram->isLinked()) {
m_modelProgram->release();
}
m_program->removeAllShaders();
delete m_program;
m_modelProgram->removeAllShaders();
delete m_modelProgram;
}
if (m_backgroundProgram) {
if (m_backgroundProgram->isLinked()) {
m_backgroundProgram->release();
}
m_backgroundProgram->removeAllShaders();
delete m_backgroundProgram;
}
doneCurrent();
}
@@ -125,21 +132,40 @@ void SkinOpenGLWindow::initializeGL()
void SkinOpenGLWindow::initShaders()
{
m_program = new QOpenGLShaderProgram(this);
// Skin model shaders
m_modelProgram = new QOpenGLShaderProgram(this);
// Compile vertex shader
if (!m_program->addCacheableShaderFromSourceFile(QOpenGLShader::Vertex, ":/shaders/vshader.glsl"))
if (!m_modelProgram->addCacheableShaderFromSourceFile(QOpenGLShader::Vertex, ":/shaders/vshader_skin_model.glsl"))
close();
// Compile fragment shader
if (!m_program->addCacheableShaderFromSourceFile(QOpenGLShader::Fragment, ":/shaders/fshader.glsl"))
if (!m_modelProgram->addCacheableShaderFromSourceFile(QOpenGLShader::Fragment, ":/shaders/fshader.glsl"))
close();
// Link shader pipeline
if (!m_program->link())
if (!m_modelProgram->link())
close();
// Bind shader pipeline for use
if (!m_program->bind())
if (!m_modelProgram->bind())
close();
// Background shaders
m_backgroundProgram = new QOpenGLShaderProgram(this);
// Compile vertex shader
if (!m_backgroundProgram->addCacheableShaderFromSourceFile(QOpenGLShader::Vertex, ":/shaders/vshader_skin_background.glsl"))
close();
// Compile fragment shader
if (!m_backgroundProgram->addCacheableShaderFromSourceFile(QOpenGLShader::Fragment, ":/shaders/fshader.glsl"))
close();
// Link shader pipeline
if (!m_backgroundProgram->link())
close();
// Bind shader pipeline for use (verification)
if (!m_backgroundProgram->bind())
close();
}
@@ -148,13 +174,24 @@ void SkinOpenGLWindow::resizeGL(int w, int h)
// Calculate aspect ratio
qreal aspect = qreal(w) / qreal(h ? h : 1);
const qreal zNear = .1, zFar = 1000., fov = 45;
const qreal zNear = 15., fov = 45;
// Reset projection
m_projection.setToIdentity();
// Set perspective projection
m_projection.perspective(fov, aspect, zNear, zFar);
// Build the reverse z perspective projection matrix
double radians = qDegreesToRadians(fov / 2.);
double sine = std::sin(radians);
if (sine == 0)
return;
double cotan = std::cos(radians) / sine;
m_projection(0, 0) = cotan / aspect;
m_projection(1, 1) = cotan;
m_projection(2, 2) = 0.;
m_projection(3, 2) = -1.;
m_projection(2, 3) = zNear;
m_projection(3, 3) = 0.;
}
void SkinOpenGLWindow::paintGL()
@@ -172,9 +209,10 @@ void SkinOpenGLWindow::paintGL()
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
m_program->bind();
m_backgroundProgram->bind();
renderBackground();
m_backgroundProgram->release();
// Calculate model view transformation
QMatrix4x4 matrix;
float yawRad = qDegreesToRadians(m_yaw);
@@ -186,10 +224,11 @@ void SkinOpenGLWindow::paintGL()
QVector3D(0, -8, 0), QVector3D(0, 1, 0));
// Set modelview-projection matrix
m_program->setUniformValue("mvp_matrix", m_projection * matrix);
m_modelProgram->bind();
m_modelProgram->setUniformValue("mvp_matrix", m_projection * matrix);
m_scene->draw(m_program);
m_program->release();
m_scene->draw(m_modelProgram);
m_modelProgram->release();
}
void SkinOpenGLWindow::updateScene(SkinModel* skin)
@@ -246,10 +285,8 @@ void SkinOpenGLWindow::renderBackground()
glDisable(GL_DEPTH_TEST);
glDepthMask(GL_FALSE); // Disable depth buffer writing
m_backgroundTexture->bind();
QMatrix4x4 matrix;
m_program->setUniformValue("mvp_matrix", matrix);
m_program->setUniformValue("texture", 0);
m_background->draw(m_program);
m_backgroundProgram->setUniformValue("texture", 0);
m_background->draw(m_backgroundProgram);
m_backgroundTexture->release();
glDepthMask(GL_TRUE); // Re-enable depth buffer writing
glEnable(GL_DEPTH_TEST);

View File

@@ -61,7 +61,8 @@ class SkinOpenGLWindow : public QOpenGLWindow, protected QOpenGLFunctions {
void renderBackground();
private:
QOpenGLShaderProgram* m_program;
QOpenGLShaderProgram* m_modelProgram;
QOpenGLShaderProgram* m_backgroundProgram;
opengl::Scene* m_scene = nullptr;
QMatrix4x4 m_projection;