OpenGL Renderer: Add true support for the depth-equals test by properly emulating the tolerance value. This fixes many longstanding bugs related to missing polygons, Z-fighting glitches, and other misc. graphical glitches in many games.
This commit is contained in:
parent
2a1aaf727e
commit
47a71941ac
|
@ -323,6 +323,7 @@ static const char *GeometryFragShader_100 = {"\
|
|||
uniform bool texSingleBitAlpha;\n\
|
||||
\n\
|
||||
uniform bool polyDrawShadow;\n\
|
||||
uniform int polyDepthOffsetMode;\n\
|
||||
\n\
|
||||
void main()\n\
|
||||
{\n\
|
||||
|
@ -331,8 +332,9 @@ static const char *GeometryFragShader_100 = {"\
|
|||
vec4 newFogAttributes = vec4(0.0, 0.0, 0.0, 0.0);\n\
|
||||
\n\
|
||||
float vertW = (vtxPosition.w == 0.0) ? 0.00000001 : vtxPosition.w;\n\
|
||||
float depthOffset = (polyDepthOffsetMode == 0) ? 0.0 : ((polyDepthOffsetMode == 1) ? -512.0 : 512.0);\n\
|
||||
// hack: when using z-depth, drop some LSBs so that the overworld map in Dragon Quest IV shows up correctly\n\
|
||||
float newFragDepthValue = (stateUseWDepth) ? floor(vtxPosition.w * 4096.0) / 16777215.0 : (floor(clamp(((vtxPosition.z/vertW) * 0.5 + 0.5), 0.0, 1.0) * 32767.0) * 512.0) / 16777215.0;\n\
|
||||
float newFragDepthValue = (stateUseWDepth) ? clamp( ( floor(vtxPosition.w * 4096.0) + depthOffset ) / 16777215.0, 0.0, 1.0 ) : clamp( ( (floor(clamp(((vtxPosition.z/vertW) * 0.5 + 0.5), 0.0, 1.0) * 32767.0) * 512.0) + depthOffset ) / 16777215.0, 0.0, 1.0 );\n\
|
||||
\n\
|
||||
if ((polyMode != 3) || polyDrawShadow)\n\
|
||||
{\n\
|
||||
|
@ -1625,23 +1627,16 @@ size_t OpenGLRenderer::DrawPolygonsForIndexRange(const POLYLIST *polyList, const
|
|||
{
|
||||
if (DRAWMODE != OGLPolyDrawMode_ZeroAlphaPass)
|
||||
{
|
||||
this->DrawShadowPolygon(polyPrimitive, vertIndexCount, indexBufferPtr, thePoly.attribute.TranslucentDepthWrite_Enable, thePoly.isTranslucent(), thePoly.attribute.PolygonID);
|
||||
this->DrawShadowPolygon(polyPrimitive, vertIndexCount, indexBufferPtr, thePoly.attribute.DepthEqualTest_Enable, thePoly.attribute.TranslucentDepthWrite_Enable, (DRAWMODE == OGLPolyDrawMode_DrawTranslucentPolys), thePoly.attribute.PolygonID);
|
||||
}
|
||||
}
|
||||
else if ( (thePoly.texParam.PackedFormat == TEXMODE_A3I5) || (thePoly.texParam.PackedFormat == TEXMODE_A5I3) )
|
||||
{
|
||||
if (DRAWMODE == OGLPolyDrawMode_ZeroAlphaPass)
|
||||
{
|
||||
this->DrawAlphaTexturePolygon<false>(polyPrimitive, vertIndexCount, indexBufferPtr, thePoly.attribute.TranslucentDepthWrite_Enable, thePoly.isTranslucent(), thePoly.isWireframe() || thePoly.isOpaque());
|
||||
}
|
||||
else
|
||||
{
|
||||
this->DrawAlphaTexturePolygon<true>(polyPrimitive, vertIndexCount, indexBufferPtr, thePoly.attribute.TranslucentDepthWrite_Enable, thePoly.isTranslucent(), thePoly.isWireframe() || thePoly.isOpaque());
|
||||
}
|
||||
this->DrawAlphaTexturePolygon<DRAWMODE>(polyPrimitive, vertIndexCount, indexBufferPtr, thePoly.attribute.DepthEqualTest_Enable, thePoly.attribute.TranslucentDepthWrite_Enable, thePoly.isWireframe() || thePoly.isOpaque(), thePoly.attribute.PolygonID);
|
||||
}
|
||||
else
|
||||
{
|
||||
glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr);
|
||||
this->DrawOtherPolygon<DRAWMODE>(polyPrimitive, vertIndexCount, indexBufferPtr, thePoly.attribute.DepthEqualTest_Enable, thePoly.attribute.TranslucentDepthWrite_Enable, thePoly.attribute.PolygonID);
|
||||
}
|
||||
|
||||
indexBufferPtr += vertIndexCount;
|
||||
|
@ -1652,44 +1647,145 @@ size_t OpenGLRenderer::DrawPolygonsForIndexRange(const POLYLIST *polyList, const
|
|||
return indexOffset;
|
||||
}
|
||||
|
||||
template <bool WILLUPDATESTENCILBUFFER>
|
||||
Render3DError OpenGLRenderer::DrawAlphaTexturePolygon(const GLenum polyPrimitive, const GLsizei vertIndexCount, const GLushort *indexBufferPtr, const bool enableAlphaDepthWrite, const bool isTranslucent, const bool canHaveOpaqueFragments)
|
||||
template <OGLPolyDrawMode DRAWMODE>
|
||||
Render3DError OpenGLRenderer::DrawAlphaTexturePolygon(const GLenum polyPrimitive, const GLsizei vertIndexCount, const GLushort *indexBufferPtr, const bool performDepthEqualTest, const bool enableAlphaDepthWrite, const bool canHaveOpaqueFragments, const u8 opaquePolyID)
|
||||
{
|
||||
const OGLRenderRef &OGLRef = *this->ref;
|
||||
|
||||
if (this->isShaderSupported)
|
||||
{
|
||||
const OGLRenderRef &OGLRef = *this->ref;
|
||||
|
||||
if (isTranslucent)
|
||||
if ((DRAWMODE != OGLPolyDrawMode_ZeroAlphaPass) && performDepthEqualTest)
|
||||
{
|
||||
// Draw the translucent fragments.
|
||||
glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr);
|
||||
|
||||
// Draw the opaque fragments if they might exist.
|
||||
if (canHaveOpaqueFragments)
|
||||
if (DRAWMODE == OGLPolyDrawMode_DrawTranslucentPolys)
|
||||
{
|
||||
if (WILLUPDATESTENCILBUFFER)
|
||||
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
|
||||
glDepthMask(GL_FALSE);
|
||||
|
||||
// Use the stencil buffer to determine which fragments pass the lower-side tolerance.
|
||||
glUniform1i(OGLRef.uniformPolyDepthOffsetMode, 1);
|
||||
glDepthFunc(GL_LEQUAL);
|
||||
glStencilFunc(GL_ALWAYS, 0x80, 0x80);
|
||||
glStencilOp(GL_ZERO, GL_ZERO, GL_REPLACE);
|
||||
glStencilMask(0x80);
|
||||
|
||||
glUniform1i(OGLRef.uniformTexDrawOpaque, GL_FALSE);
|
||||
glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr);
|
||||
glUniform1i(OGLRef.uniformTexDrawOpaque, GL_TRUE);
|
||||
glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr);
|
||||
|
||||
// Use the stencil buffer to determine which fragments pass the higher-side tolerance.
|
||||
glUniform1i(OGLRef.uniformPolyDepthOffsetMode, 2);
|
||||
glDepthFunc(GL_GEQUAL);
|
||||
glStencilFunc(GL_EQUAL, 0x80, 0x80);
|
||||
glStencilOp(GL_ZERO, GL_ZERO, GL_KEEP);
|
||||
glStencilMask(0x80);
|
||||
|
||||
glUniform1i(OGLRef.uniformTexDrawOpaque, GL_FALSE);
|
||||
glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr);
|
||||
glUniform1i(OGLRef.uniformTexDrawOpaque, GL_TRUE);
|
||||
glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr);
|
||||
|
||||
// Set up the actual drawing of the polygon, using the mask within the stencil buffer to determine which fragments should pass.
|
||||
glUniform1i(OGLRef.uniformPolyDepthOffsetMode, 0);
|
||||
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
||||
glDepthFunc(GL_ALWAYS);
|
||||
glStencilFunc(GL_EQUAL, 0x80 | opaquePolyID, 0x80);
|
||||
glStencilMask(0x7F); // Drawing non-shadow polygons will implicitly reset the shadow volume mask.
|
||||
|
||||
// Draw the translucent fragments.
|
||||
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
|
||||
glDepthMask((enableAlphaDepthWrite) ? GL_TRUE : GL_FALSE);
|
||||
|
||||
glUniform1i(OGLRef.uniformTexDrawOpaque, GL_FALSE);
|
||||
glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr);
|
||||
|
||||
// Draw the opaque fragments if they might exist.
|
||||
if (canHaveOpaqueFragments)
|
||||
{
|
||||
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
|
||||
glDepthMask(GL_TRUE);
|
||||
}
|
||||
|
||||
glUniform1i(OGLRef.uniformTexDrawOpaque, GL_TRUE);
|
||||
glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr);
|
||||
glUniform1i(OGLRef.uniformTexDrawOpaque, GL_FALSE);
|
||||
|
||||
if (WILLUPDATESTENCILBUFFER)
|
||||
{
|
||||
|
||||
glUniform1i(OGLRef.uniformTexDrawOpaque, GL_TRUE);
|
||||
glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr);
|
||||
glUniform1i(OGLRef.uniformTexDrawOpaque, GL_FALSE);
|
||||
|
||||
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
|
||||
glDepthMask((enableAlphaDepthWrite) ? GL_TRUE : GL_FALSE);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
|
||||
glDepthMask(GL_FALSE);
|
||||
|
||||
glUniform1i(OGLRef.uniformTexDrawOpaque, GL_TRUE);
|
||||
|
||||
// Use the stencil buffer to determine which fragments pass the lower-side tolerance.
|
||||
glUniform1i(OGLRef.uniformPolyDepthOffsetMode, 1);
|
||||
glDepthFunc(GL_LEQUAL);
|
||||
glStencilFunc(GL_ALWAYS, 0x80, 0x80);
|
||||
glStencilOp(GL_ZERO, GL_ZERO, GL_REPLACE);
|
||||
glStencilMask(0x80);
|
||||
glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr);
|
||||
|
||||
// Use the stencil buffer to determine which fragments pass the higher-side tolerance.
|
||||
glUniform1i(OGLRef.uniformPolyDepthOffsetMode, 2);
|
||||
glDepthFunc(GL_GEQUAL);
|
||||
glStencilFunc(GL_EQUAL, 0x80, 0x80);
|
||||
glStencilOp(GL_ZERO, GL_ZERO, GL_KEEP);
|
||||
glStencilMask(0x80);
|
||||
glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr);
|
||||
|
||||
// Set up the actual drawing of the polygon, using the mask within the stencil buffer to determine which fragments should pass.
|
||||
glUniform1i(OGLRef.uniformPolyDepthOffsetMode, 0);
|
||||
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
||||
glDepthFunc(GL_ALWAYS);
|
||||
glStencilFunc(GL_EQUAL, 0x80 | opaquePolyID, 0x80);
|
||||
glStencilMask(0x7F); // Drawing non-shadow polygons will implicitly reset the shadow volume mask.
|
||||
|
||||
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
|
||||
glDepthMask(GL_TRUE);
|
||||
|
||||
// Draw the polygon as completely opaque.
|
||||
glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr);
|
||||
|
||||
glUniform1i(OGLRef.uniformTexDrawOpaque, GL_FALSE);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Draw the polygon as completely opaque.
|
||||
glUniform1i(OGLRef.uniformTexDrawOpaque, GL_TRUE);
|
||||
glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr);
|
||||
glUniform1i(OGLRef.uniformTexDrawOpaque, GL_FALSE);
|
||||
if (DRAWMODE != OGLPolyDrawMode_DrawOpaquePolys)
|
||||
{
|
||||
// Draw the translucent fragments.
|
||||
glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr);
|
||||
|
||||
// Draw the opaque fragments if they might exist.
|
||||
if (canHaveOpaqueFragments)
|
||||
{
|
||||
if (DRAWMODE != OGLPolyDrawMode_ZeroAlphaPass)
|
||||
{
|
||||
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
|
||||
glDepthMask(GL_TRUE);
|
||||
}
|
||||
|
||||
glUniform1i(OGLRef.uniformTexDrawOpaque, GL_TRUE);
|
||||
glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr);
|
||||
glUniform1i(OGLRef.uniformTexDrawOpaque, GL_FALSE);
|
||||
|
||||
if (DRAWMODE != OGLPolyDrawMode_ZeroAlphaPass)
|
||||
{
|
||||
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
|
||||
glDepthMask((enableAlphaDepthWrite) ? GL_TRUE : GL_FALSE);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Draw the polygon as completely opaque.
|
||||
glUniform1i(OGLRef.uniformTexDrawOpaque, GL_TRUE);
|
||||
glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr);
|
||||
glUniform1i(OGLRef.uniformTexDrawOpaque, GL_FALSE);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -1700,6 +1796,51 @@ Render3DError OpenGLRenderer::DrawAlphaTexturePolygon(const GLenum polyPrimitive
|
|||
return OGLERROR_NOERR;
|
||||
}
|
||||
|
||||
template <OGLPolyDrawMode DRAWMODE>
|
||||
Render3DError OpenGLRenderer::DrawOtherPolygon(const GLenum polyPrimitive, const GLsizei vertIndexCount, const GLushort *indexBufferPtr, const bool performDepthEqualTest, const bool enableAlphaDepthWrite, const u8 opaquePolyID)
|
||||
{
|
||||
OGLRenderRef &OGLRef = *this->ref;
|
||||
|
||||
if ((DRAWMODE != OGLPolyDrawMode_ZeroAlphaPass) && performDepthEqualTest && this->isShaderSupported)
|
||||
{
|
||||
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
|
||||
glDepthMask(GL_FALSE);
|
||||
|
||||
// Use the stencil buffer to determine which fragments pass the lower-side tolerance.
|
||||
glUniform1i(OGLRef.uniformPolyDepthOffsetMode, 1);
|
||||
glDepthFunc(GL_LEQUAL);
|
||||
glStencilFunc(GL_ALWAYS, 0x80, 0x80);
|
||||
glStencilOp(GL_ZERO, GL_ZERO, GL_REPLACE);
|
||||
glStencilMask(0x80);
|
||||
glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr);
|
||||
|
||||
// Use the stencil buffer to determine which fragments pass the higher-side tolerance.
|
||||
glUniform1i(OGLRef.uniformPolyDepthOffsetMode, 2);
|
||||
glDepthFunc(GL_GEQUAL);
|
||||
glStencilFunc(GL_EQUAL, 0x80, 0x80);
|
||||
glStencilOp(GL_ZERO, GL_ZERO, GL_KEEP);
|
||||
glStencilMask(0x80);
|
||||
glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr);
|
||||
|
||||
// Draw the polygon using the mask within the stencil buffer to determine which fragments should pass.
|
||||
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
||||
glDepthMask(((DRAWMODE == OGLPolyDrawMode_DrawOpaquePolys) || enableAlphaDepthWrite) ? GL_TRUE : GL_FALSE);
|
||||
|
||||
glUniform1i(OGLRef.uniformPolyDepthOffsetMode, 0);
|
||||
glDepthFunc(GL_ALWAYS);
|
||||
glStencilFunc(GL_EQUAL, 0x80 | opaquePolyID, 0x80);
|
||||
glStencilOp(GL_KEEP, GL_KEEP, (DRAWMODE == OGLPolyDrawMode_DrawTranslucentPolys) ? GL_KEEP : GL_REPLACE);
|
||||
glStencilMask(0x7F); // Drawing non-shadow polygons will implicitly reset the shadow volume mask.
|
||||
glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr);
|
||||
}
|
||||
else
|
||||
{
|
||||
glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr);
|
||||
}
|
||||
|
||||
return OGLERROR_NOERR;
|
||||
}
|
||||
|
||||
OpenGLRenderer_1_2::~OpenGLRenderer_1_2()
|
||||
{
|
||||
glFinish();
|
||||
|
@ -2058,6 +2199,7 @@ Render3DError OpenGLRenderer_1_2::InitGeometryProgramShaderLocations()
|
|||
|
||||
OGLRef.uniformTexDrawOpaque = glGetUniformLocation(OGLRef.programGeometryID, "texDrawOpaque");
|
||||
OGLRef.uniformPolyDrawShadow = glGetUniformLocation(OGLRef.programGeometryID, "polyDrawShadow");
|
||||
OGLRef.uniformPolyDepthOffsetMode = glGetUniformLocation(OGLRef.programGeometryID, "polyDepthOffsetMode");
|
||||
|
||||
return OGLERROR_NOERR;
|
||||
}
|
||||
|
@ -4099,8 +4241,7 @@ void OpenGLRenderer_1_2::SetPolygonIndex(const size_t index)
|
|||
Render3DError OpenGLRenderer_1_2::SetupPolygon(const POLY &thePoly, bool treatAsTranslucent, bool willChangeStencilBuffer)
|
||||
{
|
||||
// Set up depth test mode
|
||||
static const GLenum oglDepthFunc[2] = {GL_LESS, GL_EQUAL};
|
||||
glDepthFunc(oglDepthFunc[thePoly.attribute.DepthEqualTest_Enable]);
|
||||
glDepthFunc((thePoly.attribute.DepthEqualTest_Enable) ? GL_EQUAL : GL_LESS);
|
||||
|
||||
// Set up culling mode
|
||||
static const GLenum oglCullingMode[4] = {GL_FRONT_AND_BACK, GL_FRONT, GL_BACK, 0};
|
||||
|
@ -4166,6 +4307,7 @@ Render3DError OpenGLRenderer_1_2::SetupPolygon(const POLY &thePoly, bool treatAs
|
|||
glUniform1i(OGLRef.uniformPolyID, thePoly.attribute.PolygonID);
|
||||
glUniform1i(OGLRef.uniformPolyIsWireframe, (thePoly.isWireframe()) ? GL_TRUE : GL_FALSE);
|
||||
glUniform1i(OGLRef.uniformPolySetNewDepthForTranslucent, (thePoly.attribute.TranslucentDepthWrite_Enable) ? GL_TRUE : GL_FALSE);
|
||||
glUniform1i(OGLRef.uniformPolyDepthOffsetMode, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -4259,8 +4401,10 @@ Render3DError OpenGLRenderer_1_2::SetupViewport(const u32 viewportValue)
|
|||
return OGLERROR_NOERR;
|
||||
}
|
||||
|
||||
Render3DError OpenGLRenderer_1_2::DrawShadowPolygon(const GLenum polyPrimitive, const GLsizei vertIndexCount, const GLushort *indexBufferPtr, const bool enableAlphaDepthWrite, const bool isTranslucent, const u8 opaquePolyID)
|
||||
Render3DError OpenGLRenderer_1_2::DrawShadowPolygon(const GLenum polyPrimitive, const GLsizei vertIndexCount, const GLushort *indexBufferPtr, const bool performDepthEqualTest, const bool enableAlphaDepthWrite, const bool isTranslucent, const u8 opaquePolyID)
|
||||
{
|
||||
OGLRenderRef &OGLRef = *this->ref;
|
||||
|
||||
// Shadow polygons are actually drawn over the course of multiple passes.
|
||||
// Note that the 1st and 2nd passes are performed using states from SetupPolygon().
|
||||
//
|
||||
|
@ -4275,28 +4419,99 @@ Render3DError OpenGLRenderer_1_2::DrawShadowPolygon(const GLenum polyPrimitive,
|
|||
// what the NDS does at this point. In OpenGL, this pass is used only to update the
|
||||
// stencil buffer for the polygon ID check, checking bits 0x3F for the polygon ID,
|
||||
// and clearing bit 6 (0x40) if this check fails. Color and depth writes are disabled
|
||||
//
|
||||
// 3rd pass (emulator driven): This pass only occurs when the shadow polygon is opaque.
|
||||
// Since opaque polygons need to update their polygon IDs, we update only the stencil
|
||||
// buffer with the polygon ID. Color and depth writes are disabled for this pass.
|
||||
// for this pass.
|
||||
//
|
||||
// 3rd pass (emulator driven): Use stencil buffer bit 6 (0x40) for the shadow volume
|
||||
// 4th pass (emulator driven): Use stencil buffer bit 6 (0x40) for the shadow volume
|
||||
// mask and render the shadow polygons only within the mask. Color writes are always
|
||||
// enabled and depth writes are enabled if the shadow polygon is opaque or if transparent
|
||||
// polygon depth writes are enabled.
|
||||
//
|
||||
// 4th pass (emulator driven): This pass only occurs when the shadow polygon is opaque.
|
||||
// Since opaque polygons need to update their polygon IDs, we update only the stencil
|
||||
// buffer with the polygon ID. Color and depth values are disabled for this pass.
|
||||
|
||||
// 1st pass: Create the shadow volume.
|
||||
if (opaquePolyID == 0)
|
||||
{
|
||||
glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr);
|
||||
if (performDepthEqualTest && this->isShaderSupported)
|
||||
{
|
||||
// Use the stencil buffer to determine which fragments pass the lower-side tolerance.
|
||||
glUniform1i(OGLRef.uniformPolyDepthOffsetMode, 1);
|
||||
glDepthFunc(GL_LEQUAL);
|
||||
glStencilFunc(GL_ALWAYS, 0x80, 0x80);
|
||||
glStencilOp(GL_ZERO, GL_ZERO, GL_REPLACE);
|
||||
glStencilMask(0x80);
|
||||
glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr);
|
||||
|
||||
// Use the stencil buffer to determine which fragments pass the higher-side tolerance.
|
||||
glUniform1i(OGLRef.uniformPolyDepthOffsetMode, 2);
|
||||
glDepthFunc(GL_GEQUAL);
|
||||
glStencilFunc(GL_EQUAL, 0x80, 0x80);
|
||||
glStencilOp(GL_ZERO, GL_ZERO, GL_KEEP);
|
||||
glStencilMask(0x80);
|
||||
glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr);
|
||||
|
||||
// Draw the polygon using the mask within the stencil buffer to determine which fragments should pass.
|
||||
glUniform1i(OGLRef.uniformPolyDepthOffsetMode, 0);
|
||||
glDepthFunc(GL_ALWAYS);
|
||||
glStencilFunc(GL_EQUAL, 0xC0, 0x80);
|
||||
glStencilOp(GL_KEEP, GL_REPLACE, GL_KEEP);
|
||||
glStencilMask(0x40);
|
||||
glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr);
|
||||
}
|
||||
else
|
||||
{
|
||||
glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr);
|
||||
}
|
||||
|
||||
return OGLERROR_NOERR;
|
||||
}
|
||||
|
||||
// 2nd pass: Do the polygon ID check.
|
||||
if (performDepthEqualTest && this->isShaderSupported)
|
||||
{
|
||||
// Use the stencil buffer to determine which fragments pass the lower-side tolerance.
|
||||
glUniform1i(OGLRef.uniformPolyDepthOffsetMode, 1);
|
||||
glDepthFunc(GL_LEQUAL);
|
||||
glStencilFunc(GL_ALWAYS, 0x80, 0x80);
|
||||
glStencilOp(GL_ZERO, GL_ZERO, GL_REPLACE);
|
||||
glStencilMask(0x80);
|
||||
glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr);
|
||||
|
||||
// Use the stencil buffer to determine which fragments pass the higher-side tolerance.
|
||||
glUniform1i(OGLRef.uniformPolyDepthOffsetMode, 2);
|
||||
glDepthFunc(GL_GEQUAL);
|
||||
glStencilFunc(GL_EQUAL, 0x80, 0x80);
|
||||
glStencilOp(GL_ZERO, GL_ZERO, GL_KEEP);
|
||||
glStencilMask(0x80);
|
||||
glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr);
|
||||
|
||||
// Check both the depth-equals test bit (bit 7) and shadow volume mask bit (bit 6).
|
||||
// Fragments that fail this stencil test are removed from the shadow volume mask.
|
||||
glUniform1i(OGLRef.uniformPolyDepthOffsetMode, 0);
|
||||
glDepthFunc(GL_ALWAYS);
|
||||
glStencilFunc(GL_EQUAL, 0xC0, 0xC0);
|
||||
glStencilOp(GL_ZERO, GL_KEEP, GL_KEEP);
|
||||
glStencilMask(0x40);
|
||||
glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr);
|
||||
|
||||
// Set up the polygon ID check as normal.
|
||||
glStencilFunc(GL_NOTEQUAL, opaquePolyID, 0x3F);
|
||||
glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr);
|
||||
}
|
||||
|
||||
glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr);
|
||||
|
||||
// 3rd pass: Draw the shadow polygon.
|
||||
// 3rd pass: Update the polygon IDs in the stencil buffer if the shadow polygons are opaque.
|
||||
if (!isTranslucent)
|
||||
{
|
||||
glStencilFunc(GL_EQUAL, 0x40 | opaquePolyID, 0x40);
|
||||
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
|
||||
glStencilMask(0x3F);
|
||||
glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr);
|
||||
}
|
||||
|
||||
// 4th pass: Draw the shadow polygon.
|
||||
glStencilFunc(GL_EQUAL, 0x40, 0x40);
|
||||
// Technically, a depth-fail result should also clear the shadow volume mask, but
|
||||
// Mario Kart DS draws shadow polygons better when it doesn't clear bits on depth-fail.
|
||||
|
@ -4308,8 +4523,6 @@ Render3DError OpenGLRenderer_1_2::DrawShadowPolygon(const GLenum polyPrimitive,
|
|||
|
||||
if (this->isShaderSupported)
|
||||
{
|
||||
const OGLRenderRef &OGLRef = *this->ref;
|
||||
|
||||
glUniform1i(OGLRef.uniformPolyDrawShadow, GL_TRUE);
|
||||
glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr);
|
||||
glUniform1i(OGLRef.uniformPolyDrawShadow, GL_FALSE);
|
||||
|
@ -4320,21 +4533,12 @@ Render3DError OpenGLRenderer_1_2::DrawShadowPolygon(const GLenum polyPrimitive,
|
|||
}
|
||||
|
||||
// Reset the OpenGL states back to their original shadow polygon states.
|
||||
glStencilFunc(GL_NOTEQUAL, opaquePolyID, 0x3F);
|
||||
glStencilOp(GL_ZERO, GL_KEEP, GL_KEEP);
|
||||
glStencilMask(0x40);
|
||||
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
|
||||
glDepthMask(GL_FALSE);
|
||||
|
||||
// 4th pass: Update the polygon IDs in the stencil buffer if the shadow polygons are opaque.
|
||||
if (!isTranslucent)
|
||||
{
|
||||
glStencilFunc(GL_ALWAYS, opaquePolyID, 0x3F);
|
||||
glStencilMask(0x3F);
|
||||
glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr);
|
||||
}
|
||||
|
||||
glStencilFunc(GL_NOTEQUAL, opaquePolyID, 0x3F);
|
||||
glStencilMask(0x40);
|
||||
|
||||
return OGLERROR_NOERR;
|
||||
}
|
||||
|
||||
|
|
|
@ -523,6 +523,7 @@ struct OGLRenderRef
|
|||
GLint uniformTexDrawOpaque;
|
||||
|
||||
GLint uniformPolyStateIndex;
|
||||
GLint uniformPolyDepthOffsetMode;
|
||||
GLint uniformPolyDrawShadow;
|
||||
|
||||
GLuint texToonTableID;
|
||||
|
@ -658,7 +659,8 @@ protected:
|
|||
Render3DError FlushFramebuffer(const FragmentColor *__restrict srcFramebuffer, FragmentColor *__restrict dstFramebufferMain, u16 *__restrict dstFramebuffer16);
|
||||
OpenGLTexture* GetLoadedTextureFromPolygon(const POLY &thePoly, bool enableTexturing);
|
||||
template<OGLPolyDrawMode DRAWMODE> size_t DrawPolygonsForIndexRange(const POLYLIST *polyList, const INDEXLIST *indexList, size_t firstIndex, size_t lastIndex, size_t &indexOffset, POLYGON_ATTR &lastPolyAttr);
|
||||
template<bool WILLUPDATESTENCILBUFFER> Render3DError DrawAlphaTexturePolygon(const GLenum polyPrimitive, const GLsizei vertIndexCount, const GLushort *indexBufferPtr, const bool enableAlphaDepthWrite, const bool isTranslucent, const bool canHaveOpaqueFragments);
|
||||
template<OGLPolyDrawMode DRAWMODE> Render3DError DrawAlphaTexturePolygon(const GLenum polyPrimitive, const GLsizei vertIndexCount, const GLushort *indexBufferPtr, const bool performDepthEqualTest, const bool enableAlphaDepthWrite, const bool canHaveOpaqueFragments, const u8 opaquePolyID);
|
||||
template<OGLPolyDrawMode DRAWMODE> Render3DError DrawOtherPolygon(const GLenum polyPrimitive, const GLsizei vertIndexCount, const GLushort *indexBufferPtr, const bool performDepthEqualTest, const bool enableAlphaDepthWrite, const u8 opaquePolyID);
|
||||
|
||||
// OpenGL-specific methods
|
||||
virtual Render3DError CreateVBOs() = 0;
|
||||
|
@ -706,7 +708,7 @@ protected:
|
|||
virtual Render3DError DownsampleFBO() = 0;
|
||||
virtual Render3DError ReadBackPixels() = 0;
|
||||
|
||||
virtual Render3DError DrawShadowPolygon(const GLenum polyPrimitive, const GLsizei vertIndexCount, const GLushort *indexBufferPtr, const bool enableAlphaDepthWrite, const bool isTranslucent, const u8 opaquePolyID) = 0;
|
||||
virtual Render3DError DrawShadowPolygon(const GLenum polyPrimitive, const GLsizei vertIndexCount, const GLushort *indexBufferPtr, const bool performDepthEqualTest, const bool enableAlphaDepthWrite, const bool isTranslucent, const u8 opaquePolyID) = 0;
|
||||
virtual void SetPolygonIndex(const size_t index) = 0;
|
||||
virtual Render3DError SetupPolygon(const POLY &thePoly, bool treatAsTranslucent, bool willChangeStencilBuffer) = 0;
|
||||
|
||||
|
@ -792,7 +794,7 @@ protected:
|
|||
virtual Render3DError SetupTexture(const POLY &thePoly, size_t polyRenderIndex);
|
||||
virtual Render3DError SetupViewport(const u32 viewportValue);
|
||||
|
||||
virtual Render3DError DrawShadowPolygon(const GLenum polyPrimitive, const GLsizei vertIndexCount, const GLushort *indexBufferPtr, const bool enableAlphaDepthWrite, const bool isTranslucent, const u8 opaquePolyID);
|
||||
virtual Render3DError DrawShadowPolygon(const GLenum polyPrimitive, const GLsizei vertIndexCount, const GLushort *indexBufferPtr, const bool performDepthEqualTest, const bool enableAlphaDepthWrite, const bool isTranslucent, const u8 opaquePolyID);
|
||||
|
||||
public:
|
||||
~OpenGLRenderer_1_2();
|
||||
|
|
|
@ -191,6 +191,7 @@ static const char *GeometryFragShader_150 = {"\
|
|||
uniform bool texDrawOpaque;\n\
|
||||
uniform bool polyDrawShadow;\n\
|
||||
uniform int polyIndex;\n\
|
||||
uniform int polyDepthOffsetMode;\n\
|
||||
\n\
|
||||
out vec4 outFragColor;\n\
|
||||
out vec4 outPolyID;\n\
|
||||
|
@ -203,8 +204,9 @@ static const char *GeometryFragShader_150 = {"\
|
|||
vec4 newFogAttributes = vec4(0.0, 0.0, 0.0, 0.0);\n\
|
||||
\n\
|
||||
float vertW = (vtxPosition.w == 0.0) ? 0.00000001 : vtxPosition.w;\n\
|
||||
float depthOffset = (polyDepthOffsetMode == 0) ? 0.0 : ((polyDepthOffsetMode == 1) ? -512.0 : 512.0);\n\
|
||||
// hack: when using z-depth, drop some LSBs so that the overworld map in Dragon Quest IV shows up correctly\n\
|
||||
float newFragDepthValue = (state.useWDepth) ? floor(vtxPosition.w * 4096.0) / 16777215.0 : (floor(clamp(((vtxPosition.z/vertW) * 0.5 + 0.5), 0.0, 1.0) * 32767.0) * 512.0) / 16777215.0;\n\
|
||||
float newFragDepthValue = (state.useWDepth) ? clamp( ( floor(vtxPosition.w * 4096.0) + depthOffset ) / 16777215.0, 0.0, 1.0 ) : clamp( ( (floor(clamp(((vtxPosition.z/vertW) * 0.5 + 0.5), 0.0, 1.0) * 32767.0) * 512.0) + depthOffset ) / 16777215.0, 0.0, 1.0 );\n\
|
||||
\n\
|
||||
if ((polyMode != 3u) || polyDrawShadow)\n\
|
||||
{\n\
|
||||
|
@ -1237,6 +1239,7 @@ Render3DError OpenGLRenderer_3_2::InitGeometryProgramShaderLocations()
|
|||
OGLRef.uniformTexDrawOpaque = glGetUniformLocation(OGLRef.programGeometryID, "texDrawOpaque");
|
||||
OGLRef.uniformPolyDrawShadow = glGetUniformLocation(OGLRef.programGeometryID, "polyDrawShadow");
|
||||
OGLRef.uniformPolyStateIndex = glGetUniformLocation(OGLRef.programGeometryID, "polyIndex");
|
||||
OGLRef.uniformPolyDepthOffsetMode = glGetUniformLocation(OGLRef.programGeometryID, "polyDepthOffsetMode");
|
||||
|
||||
return OGLERROR_NOERR;
|
||||
}
|
||||
|
@ -1858,9 +1861,11 @@ void OpenGLRenderer_3_2::SetPolygonIndex(const size_t index)
|
|||
|
||||
Render3DError OpenGLRenderer_3_2::SetupPolygon(const POLY &thePoly, bool treatAsTranslucent, bool willChangeStencilBuffer)
|
||||
{
|
||||
OGLRenderRef &OGLRef = *this->ref;
|
||||
|
||||
// Set up depth test mode
|
||||
static const GLenum oglDepthFunc[2] = {GL_LESS, GL_EQUAL};
|
||||
glDepthFunc(oglDepthFunc[thePoly.attribute.DepthEqualTest_Enable]);
|
||||
glDepthFunc((thePoly.attribute.DepthEqualTest_Enable) ? GL_EQUAL : GL_LESS);
|
||||
glUniform1i(OGLRef.uniformPolyDepthOffsetMode, 0);
|
||||
|
||||
// Set up culling mode
|
||||
static const GLenum oglCullingMode[4] = {GL_FRONT_AND_BACK, GL_FRONT, GL_BACK, 0};
|
||||
|
|
Loading…
Reference in New Issue