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:
rogerman 2017-10-10 11:25:16 -07:00
parent 2a1aaf727e
commit 47a71941ac
3 changed files with 274 additions and 63 deletions

View File

@ -323,6 +323,7 @@ static const char *GeometryFragShader_100 = {"\
uniform bool texSingleBitAlpha;\n\ uniform bool texSingleBitAlpha;\n\
\n\ \n\
uniform bool polyDrawShadow;\n\ uniform bool polyDrawShadow;\n\
uniform int polyDepthOffsetMode;\n\
\n\ \n\
void main()\n\ void main()\n\
{\n\ {\n\
@ -331,8 +332,9 @@ static const char *GeometryFragShader_100 = {"\
vec4 newFogAttributes = vec4(0.0, 0.0, 0.0, 0.0);\n\ vec4 newFogAttributes = vec4(0.0, 0.0, 0.0, 0.0);\n\
\n\ \n\
float vertW = (vtxPosition.w == 0.0) ? 0.00000001 : vtxPosition.w;\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\ // 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\ \n\
if ((polyMode != 3) || polyDrawShadow)\n\ if ((polyMode != 3) || polyDrawShadow)\n\
{\n\ {\n\
@ -1625,23 +1627,16 @@ size_t OpenGLRenderer::DrawPolygonsForIndexRange(const POLYLIST *polyList, const
{ {
if (DRAWMODE != OGLPolyDrawMode_ZeroAlphaPass) 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) ) else if ( (thePoly.texParam.PackedFormat == TEXMODE_A3I5) || (thePoly.texParam.PackedFormat == TEXMODE_A5I3) )
{ {
if (DRAWMODE == OGLPolyDrawMode_ZeroAlphaPass) this->DrawAlphaTexturePolygon<DRAWMODE>(polyPrimitive, vertIndexCount, indexBufferPtr, thePoly.attribute.DepthEqualTest_Enable, thePoly.attribute.TranslucentDepthWrite_Enable, thePoly.isWireframe() || thePoly.isOpaque(), thePoly.attribute.PolygonID);
{
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());
}
} }
else 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; indexBufferPtr += vertIndexCount;
@ -1652,44 +1647,145 @@ size_t OpenGLRenderer::DrawPolygonsForIndexRange(const POLYLIST *polyList, const
return indexOffset; return indexOffset;
} }
template <bool WILLUPDATESTENCILBUFFER> template <OGLPolyDrawMode DRAWMODE>
Render3DError OpenGLRenderer::DrawAlphaTexturePolygon(const GLenum polyPrimitive, const GLsizei vertIndexCount, const GLushort *indexBufferPtr, const bool enableAlphaDepthWrite, const bool isTranslucent, const bool canHaveOpaqueFragments) 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) if (this->isShaderSupported)
{ {
const OGLRenderRef &OGLRef = *this->ref; if ((DRAWMODE != OGLPolyDrawMode_ZeroAlphaPass) && performDepthEqualTest)
if (isTranslucent)
{ {
// Draw the translucent fragments. if (DRAWMODE == OGLPolyDrawMode_DrawTranslucentPolys)
glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr);
// Draw the opaque fragments if they might exist.
if (canHaveOpaqueFragments)
{ {
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); glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
glDepthMask(GL_TRUE); glDepthMask(GL_TRUE);
}
glUniform1i(OGLRef.uniformTexDrawOpaque, GL_TRUE); glUniform1i(OGLRef.uniformTexDrawOpaque, GL_TRUE);
glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr); glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr);
glUniform1i(OGLRef.uniformTexDrawOpaque, GL_FALSE); glUniform1i(OGLRef.uniformTexDrawOpaque, GL_FALSE);
if (WILLUPDATESTENCILBUFFER)
{
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
glDepthMask((enableAlphaDepthWrite) ? GL_TRUE : GL_FALSE); 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 else
{ {
// Draw the polygon as completely opaque. if (DRAWMODE != OGLPolyDrawMode_DrawOpaquePolys)
glUniform1i(OGLRef.uniformTexDrawOpaque, GL_TRUE); {
glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr); // Draw the translucent fragments.
glUniform1i(OGLRef.uniformTexDrawOpaque, GL_FALSE); 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 else
@ -1700,6 +1796,51 @@ Render3DError OpenGLRenderer::DrawAlphaTexturePolygon(const GLenum polyPrimitive
return OGLERROR_NOERR; 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() OpenGLRenderer_1_2::~OpenGLRenderer_1_2()
{ {
glFinish(); glFinish();
@ -2058,6 +2199,7 @@ Render3DError OpenGLRenderer_1_2::InitGeometryProgramShaderLocations()
OGLRef.uniformTexDrawOpaque = glGetUniformLocation(OGLRef.programGeometryID, "texDrawOpaque"); OGLRef.uniformTexDrawOpaque = glGetUniformLocation(OGLRef.programGeometryID, "texDrawOpaque");
OGLRef.uniformPolyDrawShadow = glGetUniformLocation(OGLRef.programGeometryID, "polyDrawShadow"); OGLRef.uniformPolyDrawShadow = glGetUniformLocation(OGLRef.programGeometryID, "polyDrawShadow");
OGLRef.uniformPolyDepthOffsetMode = glGetUniformLocation(OGLRef.programGeometryID, "polyDepthOffsetMode");
return OGLERROR_NOERR; 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) Render3DError OpenGLRenderer_1_2::SetupPolygon(const POLY &thePoly, bool treatAsTranslucent, bool willChangeStencilBuffer)
{ {
// Set up depth test mode // Set up depth test mode
static const GLenum oglDepthFunc[2] = {GL_LESS, GL_EQUAL}; glDepthFunc((thePoly.attribute.DepthEqualTest_Enable) ? GL_EQUAL : GL_LESS);
glDepthFunc(oglDepthFunc[thePoly.attribute.DepthEqualTest_Enable]);
// Set up culling mode // Set up culling mode
static const GLenum oglCullingMode[4] = {GL_FRONT_AND_BACK, GL_FRONT, GL_BACK, 0}; 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.uniformPolyID, thePoly.attribute.PolygonID);
glUniform1i(OGLRef.uniformPolyIsWireframe, (thePoly.isWireframe()) ? GL_TRUE : GL_FALSE); glUniform1i(OGLRef.uniformPolyIsWireframe, (thePoly.isWireframe()) ? GL_TRUE : GL_FALSE);
glUniform1i(OGLRef.uniformPolySetNewDepthForTranslucent, (thePoly.attribute.TranslucentDepthWrite_Enable) ? GL_TRUE : GL_FALSE); glUniform1i(OGLRef.uniformPolySetNewDepthForTranslucent, (thePoly.attribute.TranslucentDepthWrite_Enable) ? GL_TRUE : GL_FALSE);
glUniform1i(OGLRef.uniformPolyDepthOffsetMode, 0);
} }
else else
{ {
@ -4259,8 +4401,10 @@ Render3DError OpenGLRenderer_1_2::SetupViewport(const u32 viewportValue)
return OGLERROR_NOERR; 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. // Shadow polygons are actually drawn over the course of multiple passes.
// Note that the 1st and 2nd passes are performed using states from SetupPolygon(). // 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 // 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, // 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 // 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. // 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 // 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 // enabled and depth writes are enabled if the shadow polygon is opaque or if transparent
// polygon depth writes are enabled. // 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. // 1st pass: Create the shadow volume.
if (opaquePolyID == 0) 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; return OGLERROR_NOERR;
} }
// 2nd pass: Do the polygon ID check. // 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); 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); glStencilFunc(GL_EQUAL, 0x40, 0x40);
// Technically, a depth-fail result should also clear the shadow volume mask, but // 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. // 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) if (this->isShaderSupported)
{ {
const OGLRenderRef &OGLRef = *this->ref;
glUniform1i(OGLRef.uniformPolyDrawShadow, GL_TRUE); glUniform1i(OGLRef.uniformPolyDrawShadow, GL_TRUE);
glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr); glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr);
glUniform1i(OGLRef.uniformPolyDrawShadow, GL_FALSE); 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. // Reset the OpenGL states back to their original shadow polygon states.
glStencilFunc(GL_NOTEQUAL, opaquePolyID, 0x3F);
glStencilOp(GL_ZERO, GL_KEEP, GL_KEEP); glStencilOp(GL_ZERO, GL_KEEP, GL_KEEP);
glStencilMask(0x40);
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
glDepthMask(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; return OGLERROR_NOERR;
} }

View File

@ -523,6 +523,7 @@ struct OGLRenderRef
GLint uniformTexDrawOpaque; GLint uniformTexDrawOpaque;
GLint uniformPolyStateIndex; GLint uniformPolyStateIndex;
GLint uniformPolyDepthOffsetMode;
GLint uniformPolyDrawShadow; GLint uniformPolyDrawShadow;
GLuint texToonTableID; GLuint texToonTableID;
@ -658,7 +659,8 @@ protected:
Render3DError FlushFramebuffer(const FragmentColor *__restrict srcFramebuffer, FragmentColor *__restrict dstFramebufferMain, u16 *__restrict dstFramebuffer16); Render3DError FlushFramebuffer(const FragmentColor *__restrict srcFramebuffer, FragmentColor *__restrict dstFramebufferMain, u16 *__restrict dstFramebuffer16);
OpenGLTexture* GetLoadedTextureFromPolygon(const POLY &thePoly, bool enableTexturing); 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<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 // OpenGL-specific methods
virtual Render3DError CreateVBOs() = 0; virtual Render3DError CreateVBOs() = 0;
@ -706,7 +708,7 @@ protected:
virtual Render3DError DownsampleFBO() = 0; virtual Render3DError DownsampleFBO() = 0;
virtual Render3DError ReadBackPixels() = 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 void SetPolygonIndex(const size_t index) = 0;
virtual Render3DError SetupPolygon(const POLY &thePoly, bool treatAsTranslucent, bool willChangeStencilBuffer) = 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 SetupTexture(const POLY &thePoly, size_t polyRenderIndex);
virtual Render3DError SetupViewport(const u32 viewportValue); 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: public:
~OpenGLRenderer_1_2(); ~OpenGLRenderer_1_2();

View File

@ -191,6 +191,7 @@ static const char *GeometryFragShader_150 = {"\
uniform bool texDrawOpaque;\n\ uniform bool texDrawOpaque;\n\
uniform bool polyDrawShadow;\n\ uniform bool polyDrawShadow;\n\
uniform int polyIndex;\n\ uniform int polyIndex;\n\
uniform int polyDepthOffsetMode;\n\
\n\ \n\
out vec4 outFragColor;\n\ out vec4 outFragColor;\n\
out vec4 outPolyID;\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\ vec4 newFogAttributes = vec4(0.0, 0.0, 0.0, 0.0);\n\
\n\ \n\
float vertW = (vtxPosition.w == 0.0) ? 0.00000001 : vtxPosition.w;\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\ // 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\ \n\
if ((polyMode != 3u) || polyDrawShadow)\n\ if ((polyMode != 3u) || polyDrawShadow)\n\
{\n\ {\n\
@ -1237,6 +1239,7 @@ Render3DError OpenGLRenderer_3_2::InitGeometryProgramShaderLocations()
OGLRef.uniformTexDrawOpaque = glGetUniformLocation(OGLRef.programGeometryID, "texDrawOpaque"); OGLRef.uniformTexDrawOpaque = glGetUniformLocation(OGLRef.programGeometryID, "texDrawOpaque");
OGLRef.uniformPolyDrawShadow = glGetUniformLocation(OGLRef.programGeometryID, "polyDrawShadow"); OGLRef.uniformPolyDrawShadow = glGetUniformLocation(OGLRef.programGeometryID, "polyDrawShadow");
OGLRef.uniformPolyStateIndex = glGetUniformLocation(OGLRef.programGeometryID, "polyIndex"); OGLRef.uniformPolyStateIndex = glGetUniformLocation(OGLRef.programGeometryID, "polyIndex");
OGLRef.uniformPolyDepthOffsetMode = glGetUniformLocation(OGLRef.programGeometryID, "polyDepthOffsetMode");
return OGLERROR_NOERR; 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) Render3DError OpenGLRenderer_3_2::SetupPolygon(const POLY &thePoly, bool treatAsTranslucent, bool willChangeStencilBuffer)
{ {
OGLRenderRef &OGLRef = *this->ref;
// Set up depth test mode // Set up depth test mode
static const GLenum oglDepthFunc[2] = {GL_LESS, GL_EQUAL}; glDepthFunc((thePoly.attribute.DepthEqualTest_Enable) ? GL_EQUAL : GL_LESS);
glDepthFunc(oglDepthFunc[thePoly.attribute.DepthEqualTest_Enable]); glUniform1i(OGLRef.uniformPolyDepthOffsetMode, 0);
// Set up culling mode // Set up culling mode
static const GLenum oglCullingMode[4] = {GL_FRONT_AND_BACK, GL_FRONT, GL_BACK, 0}; static const GLenum oglCullingMode[4] = {GL_FRONT_AND_BACK, GL_FRONT, GL_BACK, 0};