diff --git a/GPU/Common/SoftwareTransformCommon.cpp b/GPU/Common/SoftwareTransformCommon.cpp index 7fade324e6..cdbd78cf34 100644 --- a/GPU/Common/SoftwareTransformCommon.cpp +++ b/GPU/Common/SoftwareTransformCommon.cpp @@ -211,6 +211,7 @@ void SoftwareTransform( // The w of uv is also never used (hardcoded to 1.0.) } } else { + // Okay, need to actually perform the full transform. for (int index = 0; index < maxIndex; index++) { reader.Goto(index); @@ -401,6 +402,9 @@ void SoftwareTransform( } transformed[index].color0_32 = c0.ToRGBA(); transformed[index].color1_32 = c1.ToRGBA(); + + // The multiplication by the projection matrix is still performed in the vertex shader. + // So is vertex depth rounding, to simulate the 16-bit depth buffer. } } diff --git a/GPU/Directx9/GPU_DX9.cpp b/GPU/Directx9/GPU_DX9.cpp index 42502240fe..12ff6c44d6 100644 --- a/GPU/Directx9/GPU_DX9.cpp +++ b/GPU/Directx9/GPU_DX9.cpp @@ -889,7 +889,7 @@ void DIRECTX9_GPU::Execute_ViewportType(u32 op, u32 diff) { switch (op >> 24) { case GE_CMD_VIEWPORTZ1: case GE_CMD_VIEWPORTZ2: - shaderManager_->DirtyUniform(DIRTY_PROJMATRIX); + shaderManager_->DirtyUniform(DIRTY_PROJMATRIX | DIRTY_DEPTHRANGE); break; } } diff --git a/GPU/Directx9/ShaderManagerDX9.cpp b/GPU/Directx9/ShaderManagerDX9.cpp index 989befb2ec..0ee849b940 100644 --- a/GPU/Directx9/ShaderManagerDX9.cpp +++ b/GPU/Directx9/ShaderManagerDX9.cpp @@ -176,6 +176,10 @@ void ShaderManagerDX9::VSSetColorUniform3(int creg, u32 color) { pD3Ddevice->SetVertexShaderConstantF(creg, col, 1); } +void ShaderManagerDX9::VSSetFloatUniform4(int creg, float data[4]) { + pD3Ddevice->SetVertexShaderConstantF(creg, data, 1); +} + void ShaderManagerDX9::VSSetFloat24Uniform3(int creg, const u32 data[3]) { const u32 col[4] = { data[0] >> 8, data[1] >> 8, data[2] >> 8, 0 @@ -475,6 +479,26 @@ void ShaderManagerDX9::VSUpdateUniforms(int dirtyUniforms) { VSSetFloatArray(CONST_VS_UVSCALEOFFSET, uvscaleoff, 4); } + if (dirtyUniforms & DIRTY_DEPTHRANGE) { + float viewZScale = gstate.getViewportZScale(); + float viewZCenter = gstate.getViewportZCenter(); + + // Adjust for D3D projection matrix. We got squashed up to only 0-1, so we multiply + // the scale factor by 2, and add an offset. + // Given the way we do the rounding, the integer part of the offset is probably mostly irrelevant as we cancel + // it afterwards anyway. + viewZScale *= 2.0f; + viewZCenter -= 32767.5f; + float viewZInvScale; + if (viewZScale != 0.0) { + viewZInvScale = 1.0f / viewZScale; + } else { + viewZInvScale = 0.0; + } + + float data[4] = { viewZScale, viewZCenter, viewZCenter, viewZInvScale }; + VSSetFloatUniform4(CONST_VS_DEPTHRANGE, data); + } // Lighting if (dirtyUniforms & DIRTY_AMBIENT) { VSSetColorUniform3Alpha(CONST_VS_AMBIENT, gstate.ambientcolor, gstate.getAmbientA()); diff --git a/GPU/Directx9/ShaderManagerDX9.h b/GPU/Directx9/ShaderManagerDX9.h index 7ad723aac4..fde7fee71d 100644 --- a/GPU/Directx9/ShaderManagerDX9.h +++ b/GPU/Directx9/ShaderManagerDX9.h @@ -57,6 +57,8 @@ enum { DIRTY_UVSCALEOFFSET = (1 << 18), // this will be dirtied ALL THE TIME... maybe we'll need to do "last value with this shader compares" DIRTY_TEXCLAMP = (1 << 19), + DIRTY_DEPTHRANGE = (1 << 20), + DIRTY_WORLDMATRIX = (1 << 21), DIRTY_VIEWMATRIX = (1 << 22), // Maybe we'll fold this into projmatrix eventually DIRTY_TEXMATRIX = (1 << 23), @@ -143,6 +145,7 @@ private: void VSSetFloat(int creg, float value); void VSSetFloatArray(int creg, const float *value, int count); void VSSetFloat24Uniform3(int creg, const u32 data[3]); + void VSSetFloatUniform4(int creg, float data[4]); void Clear(); diff --git a/GPU/Directx9/StateMappingDX9.cpp b/GPU/Directx9/StateMappingDX9.cpp index 65aefcc292..e671a2df8b 100644 --- a/GPU/Directx9/StateMappingDX9.cpp +++ b/GPU/Directx9/StateMappingDX9.cpp @@ -745,10 +745,12 @@ void TransformDrawEngineDX9::ApplyDrawState(int prim) { float zScale = gstate.getViewportZScale() / 65535.0f; float zCenter = gstate.getViewportZCenter() / 65535.0f; - // Note - we lose the sign of the zscale here. Although I suppose we still keep it in gstate_c.vpDepth... - float depthRangeMin = zCenter - fabsf(zScale); - float depthRangeMax = zCenter + fabsf(zScale); + // Note - We lose the sign of the zscale here. But we keep it in gstate_c.vpDepth. + // That variable is only check for sign later so the multiplication by 2 isn't really necessary. + // It's unclear why we need this Z offset of 1 to match OpenGL, but this checks out in multiple games. + float depthRangeMin = zCenter - fabsf(zScale) - 0.5f/65535.0f; + float depthRangeMax = zCenter + fabsf(zScale) - 0.5f/65535.0f; gstate_c.vpDepth = zScale * 2; // D3D doesn't like viewports partially outside the target, so we diff --git a/GPU/Directx9/VertexShaderGeneratorDX9.cpp b/GPU/Directx9/VertexShaderGeneratorDX9.cpp index 9106d1a2f6..dba65ed8cc 100644 --- a/GPU/Directx9/VertexShaderGeneratorDX9.cpp +++ b/GPU/Directx9/VertexShaderGeneratorDX9.cpp @@ -226,6 +226,11 @@ void GenerateVertexShaderDX9(int prim, char *buffer, bool useHWTransform) { } } + if (!gstate.isModeThrough()) { + WRITE(p, "float4 u_depthRange : register(c%i);\n", CONST_VS_DEPTHRANGE); + } + + // And the "varyings". if (useHWTransform) { WRITE(p, "struct VS_IN { \n"); if (vertTypeIsSkinningEnabled(vertType)) { @@ -279,6 +284,20 @@ void GenerateVertexShaderDX9(int prim, char *buffer, bool useHWTransform) { } WRITE(p, "};\n"); + // Confirmed: Through mode gets through exactly the same in GL and D3D in Phantasy Star: Text is 38023.0 in the test scene. + + if (!gstate.isModeThrough()) { + // Apply the projection and viewport to get the Z buffer value, floor to integer, undo the viewport and projection. + // Not completely sure this is 100% right under DX9 as the Z range is different... + WRITE(p, "\nfloat4 depthRoundZVP(float4 v) {\n"); + WRITE(p, " float z = v.z / v.w;\n"); + WRITE(p, " z = z * u_depthRange.x + u_depthRange.y;\n"); + WRITE(p, " z = floor(z);\n"); + WRITE(p, " z = (z - u_depthRange.z) * u_depthRange.w;\n"); + WRITE(p, " return float4(v.x, v.y, z * v.w, v.w);\n"); + WRITE(p, "}\n\n"); + } + WRITE(p, "VS_OUT main(VS_IN In) {\n"); WRITE(p, " VS_OUT Out = (VS_OUT)0; \n"); if (!useHWTransform) { @@ -309,7 +328,7 @@ void GenerateVertexShaderDX9(int prim, char *buffer, bool useHWTransform) { if (gstate.isModeThrough()) { WRITE(p, " Out.gl_Position = mul(float4(In.position.xyz, 1.0), u_proj_through);\n"); } else { - WRITE(p, " Out.gl_Position = mul(float4(In.position.xyz, 1.0), u_proj);\n"); + WRITE(p, " Out.gl_Position = depthRoundZVP(mul(float4(In.position.xyz, 1.0), u_proj));\n"); } } else { // Step 1: World Transform / Skinning @@ -398,7 +417,7 @@ void GenerateVertexShaderDX9(int prim, char *buffer, bool useHWTransform) { WRITE(p, " float4 viewPos = float4(mul(float4(worldpos, 1.0), u_view), 1.0);\n"); // Final view and projection transforms. - WRITE(p, " Out.gl_Position = mul(viewPos, u_proj);\n"); + WRITE(p, " Out.gl_Position = depthRoundZVP(mul(viewPos, u_proj));\n"); // TODO: Declare variables for dots for shade mapping if needed. diff --git a/GPU/Directx9/VertexShaderGeneratorDX9.h b/GPU/Directx9/VertexShaderGeneratorDX9.h index b708003daf..2b29ad03af 100644 --- a/GPU/Directx9/VertexShaderGeneratorDX9.h +++ b/GPU/Directx9/VertexShaderGeneratorDX9.h @@ -87,5 +87,6 @@ int TranslateNumBonesDX9(int bones); #define CONST_VS_LIGHTDIFFUSE 71 #define CONST_VS_LIGHTSPECULAR 75 #define CONST_VS_LIGHTAMBIENT 79 +#define CONST_VS_DEPTHRANGE 83 }; diff --git a/GPU/GLES/GLES_GPU.cpp b/GPU/GLES/GLES_GPU.cpp index 8cc3be391e..41a2616281 100644 --- a/GPU/GLES/GLES_GPU.cpp +++ b/GPU/GLES/GLES_GPU.cpp @@ -77,8 +77,8 @@ static const CommandTableEntry commandTable[] = { {GE_CMD_FOG2, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTEONCHANGE, DIRTY_FOGCOEF, &GLES_GPU::Execute_FogCoef}, // Should these maybe flush? - {GE_CMD_MINZ, FLAG_FLUSHBEFOREONCHANGE}, - {GE_CMD_MAXZ, FLAG_FLUSHBEFOREONCHANGE}, + {GE_CMD_MINZ, FLAG_FLUSHBEFOREONCHANGE, DIRTY_DEPTHRANGE}, + {GE_CMD_MAXZ, FLAG_FLUSHBEFOREONCHANGE, DIRTY_DEPTHRANGE}, // Changes that dirty texture scaling. {GE_CMD_TEXMAPMODE, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTEONCHANGE, DIRTY_UVSCALEOFFSET, &GLES_GPU::Execute_TexMapMode}, @@ -206,8 +206,8 @@ static const CommandTableEntry commandTable[] = { {GE_CMD_VIEWPORTY1, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTEONCHANGE, 0, &GLES_GPU::Execute_ViewportType}, {GE_CMD_VIEWPORTX2, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTEONCHANGE, 0, &GLES_GPU::Execute_ViewportType}, {GE_CMD_VIEWPORTY2, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTEONCHANGE, 0, &GLES_GPU::Execute_ViewportType}, - {GE_CMD_VIEWPORTZ1, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTEONCHANGE, 0, &GLES_GPU::Execute_ViewportType}, - {GE_CMD_VIEWPORTZ2, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTEONCHANGE, 0, &GLES_GPU::Execute_ViewportType}, + {GE_CMD_VIEWPORTZ1, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTEONCHANGE, DIRTY_DEPTHRANGE, &GLES_GPU::Execute_ViewportZType}, + {GE_CMD_VIEWPORTZ2, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTEONCHANGE, DIRTY_DEPTHRANGE, &GLES_GPU::Execute_ViewportZType}, // Region {GE_CMD_REGION1, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTEONCHANGE, 0, &GLES_GPU::Execute_Region}, @@ -1014,6 +1014,12 @@ void GLES_GPU::Execute_ViewportType(u32 op, u32 diff) { gstate_c.textureChanged |= TEXCHANGE_PARAMSONLY; } +void GLES_GPU::Execute_ViewportZType(u32 op, u32 diff) { + gstate_c.framebufChanged = true; + gstate_c.textureChanged |= TEXCHANGE_PARAMSONLY; + shaderManager_->DirtyUniform(DIRTY_DEPTHRANGE); +} + void GLES_GPU::Execute_TexScaleU(u32 op, u32 diff) { gstate_c.uv.uScale = getFloat24(op); shaderManager_->DirtyUniform(DIRTY_UVSCALEOFFSET); diff --git a/GPU/GLES/GLES_GPU.h b/GPU/GLES/GLES_GPU.h index 711f34c726..052b409e96 100644 --- a/GPU/GLES/GLES_GPU.h +++ b/GPU/GLES/GLES_GPU.h @@ -101,6 +101,7 @@ public: void Execute_Scissor(u32 op, u32 diff); void Execute_FramebufType(u32 op, u32 diff); void Execute_ViewportType(u32 op, u32 diff); + void Execute_ViewportZType(u32 op, u32 diff); void Execute_TexScaleU(u32 op, u32 diff); void Execute_TexScaleV(u32 op, u32 diff); void Execute_TexOffsetU(u32 op, u32 diff); diff --git a/GPU/GLES/ShaderManager.cpp b/GPU/GLES/ShaderManager.cpp index f8825545cc..3216572d45 100644 --- a/GPU/GLES/ShaderManager.cpp +++ b/GPU/GLES/ShaderManager.cpp @@ -167,6 +167,7 @@ LinkedShader::LinkedShader(Shader *vs, Shader *fs, u32 vertType, bool useHWTrans numBones = TranslateNumBones(vertTypeGetNumBoneWeights(vertType)); else numBones = 0; + u_depthRange = glGetUniformLocation(program, "u_depthRange"); #ifdef USE_BONE_ARRAY u_bone = glGetUniformLocation(program, "u_bone"); @@ -233,6 +234,8 @@ LinkedShader::LinkedShader(Shader *vs, Shader *fs, u32 vertType, bool useHWTrans if (u_texmtx != -1) availableUniforms |= DIRTY_TEXMATRIX; if (u_stencilReplaceValue != -1) availableUniforms |= DIRTY_STENCILREPLACEVALUE; if (u_blendFixA != -1 || u_blendFixB != -1 || u_fbotexSize != -1) availableUniforms |= DIRTY_SHADERBLEND; + if (u_depthRange != -1) + availableUniforms |= DIRTY_DEPTHRANGE; // Looping up to numBones lets us avoid checking u_bone[i] #ifdef USE_BONE_ARRAY @@ -343,6 +346,10 @@ static void SetFloat24Uniform3(int uniform, const u32 data[3]) { glUniform3fv(uniform, 1, (const GLfloat *)&col[0]); } +static void SetFloatUniform4(int uniform, float data[4]) { + glUniform4fv(uniform, 1, data); +} + static void SetMatrix4x3(int uniform, const float *m4x3) { float m4x4[16]; ConvertMatrix4x3To4x4(m4x4, m4x3); @@ -442,7 +449,7 @@ void LinkedShader::UpdateUniforms(u32 vertType) { if (dirty & DIRTY_PROJTHROUGHMATRIX) { Matrix4x4 proj_through; - proj_through.setOrtho(0.0f, gstate_c.curRTWidth, gstate_c.curRTHeight, 0, 0, 1); + proj_through.setOrtho(0.0f, gstate_c.curRTWidth, gstate_c.curRTHeight, 0, 0.0f, 1.0f); glUniformMatrix4fv(u_proj_through, 1, GL_FALSE, proj_through.getReadPtr()); } if (dirty & DIRTY_TEXENV) { @@ -547,7 +554,7 @@ void LinkedShader::UpdateUniforms(u32 vertType) { glUniform4fv(u_uvscaleoffset, 1, uvscaleoff); } - if (dirty & DIRTY_TEXCLAMP) { + if ((dirty & DIRTY_TEXCLAMP) && u_texclamp != -1) { const float invW = 1.0f / (float)gstate_c.curTextureWidth; const float invH = 1.0f / (float)gstate_c.curTextureHeight; const int w = gstate.getTextureWidth(0); @@ -582,6 +589,18 @@ void LinkedShader::UpdateUniforms(u32 vertType) { if (dirty & DIRTY_TEXMATRIX) { SetMatrix4x3(u_texmtx, gstate.tgenMatrix); } + if ((dirty & DIRTY_DEPTHRANGE) && u_depthRange != -1) { + float viewZScale = gstate.getViewportZScale(); + float viewZCenter = gstate.getViewportZCenter(); + float viewZInvScale; + if (viewZScale != 0.0) { + viewZInvScale = 1.0f / viewZScale; + } else { + viewZInvScale = 0.0; + } + float data[4] = { viewZScale, viewZCenter, viewZCenter, viewZInvScale }; + SetFloatUniform4(u_depthRange, data); + } if (dirty & DIRTY_STENCILREPLACEVALUE) { glUniform1f(u_stencilReplaceValue, (float)gstate.getStencilTestRef() * (1.0f / 255.0f)); diff --git a/GPU/GLES/ShaderManager.h b/GPU/GLES/ShaderManager.h index 80469c87f0..ff2d29f50c 100644 --- a/GPU/GLES/ShaderManager.h +++ b/GPU/GLES/ShaderManager.h @@ -88,6 +88,8 @@ public: int u_view; int u_texmtx; int u_world; + int u_depthRange; // x,y = viewport xscale/xcenter. z,w=clipping minz/maxz (?) + #ifdef USE_BONE_ARRAY int u_bone; // array, size is numBones #else @@ -132,10 +134,10 @@ public: enum { DIRTY_PROJMATRIX = (1 << 0), DIRTY_PROJTHROUGHMATRIX = (1 << 1), - DIRTY_FOGCOLOR = (1 << 2), - DIRTY_FOGCOEF = (1 << 3), - DIRTY_TEXENV = (1 << 4), - DIRTY_ALPHACOLORREF = (1 << 5), + DIRTY_FOGCOLOR = (1 << 2), + DIRTY_FOGCOEF = (1 << 3), + DIRTY_TEXENV = (1 << 4), + DIRTY_ALPHACOLORREF = (1 << 5), // 1 << 6 is free! Wait, not anymore... DIRTY_STENCILREPLACEVALUE = (1 << 6), @@ -155,7 +157,10 @@ enum { DIRTY_SHADERBLEND = (1 << 17), // Used only for in-shader blending. DIRTY_UVSCALEOFFSET = (1 << 18), // this will be dirtied ALL THE TIME... maybe we'll need to do "last value with this shader compares" + + // Texclamp is fairly rare so let's share it's bit with DIRTY_DEPTHRANGE. DIRTY_TEXCLAMP = (1 << 19), + DIRTY_DEPTHRANGE = (1 << 19), DIRTY_WORLDMATRIX = (1 << 21), DIRTY_VIEWMATRIX = (1 << 22), // Maybe we'll fold this into projmatrix eventually diff --git a/GPU/GLES/StateMapping.cpp b/GPU/GLES/StateMapping.cpp index 4a44f63dda..30aa49ebe4 100644 --- a/GPU/GLES/StateMapping.cpp +++ b/GPU/GLES/StateMapping.cpp @@ -871,11 +871,11 @@ void TransformDrawEngine::ApplyDrawState(int prim) { glstate.viewport.set(left, bottom, right - left, top - bottom); - float zScale = gstate.getViewportZScale() * (1.0f / 65535.0f); - float zCenter = gstate.getViewportZCenter() * (1.0f / 65535.0f); + float zScale = gstate.getViewportZScale(); + float zCenter = gstate.getViewportZCenter(); float depthRangeMin = zCenter - zScale; float depthRangeMax = zCenter + zScale; - glstate.depthRange.set(depthRangeMin, depthRangeMax); + glstate.depthRange.set(depthRangeMin * (1.0f / 65535.0f), depthRangeMax * (1.0f / 65535.0f)); #ifndef MOBILE_DEVICE float minz = gstate.getDepthRangeMin() * (1.0f / 65535.0f); diff --git a/GPU/GLES/VertexShaderGenerator.cpp b/GPU/GLES/VertexShaderGenerator.cpp index acf8ae11d0..379043ca0b 100644 --- a/GPU/GLES/VertexShaderGenerator.cpp +++ b/GPU/GLES/VertexShaderGenerator.cpp @@ -152,6 +152,33 @@ enum DoLightComputation { LIGHT_FULL, }; + +// Depth range and viewport +// +// After the multiplication with the projection matrix, we have a 4D vector in clip space. +// In OpenGL, Z is from -1 to 1, while in D3D, Z is from 0 to 1. +// PSP appears to use the OpenGL convention. As Z is from -1 to 1, and the viewport is represented +// by a center and a scale, to find the final Z value, all we need to do is to multiply by ZScale and +// add ZCenter - these are properly scaled to directly give a Z value in [0, 65535]. +// +// z = vec.z * ViewportZScale + ViewportZCenter; +// +// That will give us the final value between 0 and 65535, which we can simply floor to simulate +// the limited precision of the PSP's depth buffer. Then we convert it back: +// z = floor(z); +// +// vec.z = (z - ViewportZCenter) / ViewportZScale; +// +// Now, the regular machinery will take over and do the calculation again. +// +// All this above is for full transform mode. +// In through mode, the Z coordinate just goes straight through and there is no perspective division. +// We simulate this of course with pretty much an identity matrix. Rounding Z becomes very easy. +// +// TODO: Skip all this if we can actually get a 16-bit depth buffer along with stencil, which +// is a bit of a rare configuration, although quite common on mobile. + + void GenerateVertexShader(int prim, u32 vertType, char *buffer, bool useHWTransform) { char *p = buffer; @@ -214,6 +241,7 @@ void GenerateVertexShader(int prim, u32 vertType, char *buffer, bool useHWTransf boneWeightDecl = boneWeightInDecl; } + bool lmode = gstate.isUsingSecondaryColor() && gstate.isLightingEnabled() && !gstate.isModeThrough(); bool doTexture = gstate.isTextureMapEnabled() && !gstate.isModeClear(); bool doTextureProjection = gstate.getUVGenMode() == GE_TEXMAP_TEXTURE_MATRIX; @@ -336,6 +364,10 @@ void GenerateVertexShader(int prim, u32 vertType, char *buffer, bool useHWTransf WRITE(p, "uniform highp vec2 u_fogcoef;\n"); } + if (!gstate.isModeThrough()) { + WRITE(p, "uniform highp vec4 u_depthRange;\n"); + } + WRITE(p, "%s %s lowp vec4 v_color0;\n", shading, varying); if (lmode) { WRITE(p, "%s %s lowp vec3 v_color1;\n", shading, varying); @@ -357,6 +389,18 @@ void GenerateVertexShader(int prim, u32 vertType, char *buffer, bool useHWTransf } } + // See comment above this function (GenerateVertexShader). + if (!gstate.isModeThrough()) { + // Apply the projection and viewport to get the Z buffer value, floor to integer, undo the viewport and projection. + WRITE(p, "\nvec4 depthRoundZVP(vec4 v) {\n"); + WRITE(p, " float z = v.z / v.w;\n"); + WRITE(p, " z = z * u_depthRange.x + u_depthRange.y;\n"); + WRITE(p, " z = floor(z);\n"); + WRITE(p, " z = (z - u_depthRange.z) * u_depthRange.w;\n"); + WRITE(p, " return vec4(v.x, v.y, z * v.w, v.w);\n"); + WRITE(p, "}\n\n"); + } + WRITE(p, "void main() {\n"); if (!useHWTransform) { @@ -383,7 +427,8 @@ void GenerateVertexShader(int prim, u32 vertType, char *buffer, bool useHWTransf if (gstate.isModeThrough()) { WRITE(p, " gl_Position = u_proj_through * vec4(position.xyz, 1.0);\n"); } else { - WRITE(p, " gl_Position = u_proj * vec4(position.xyz, 1.0);\n"); + // The viewport is used in this case, so need to compensate for that. + WRITE(p, " gl_Position = depthRoundZVP(u_proj * vec4(position.xyz, 1.0));\n"); } } else { // Step 1: World Transform / Skinning @@ -476,7 +521,7 @@ void GenerateVertexShader(int prim, u32 vertType, char *buffer, bool useHWTransf WRITE(p, " vec4 viewPos = u_view * vec4(worldpos, 1.0);\n"); // Final view and projection transforms. - WRITE(p, " gl_Position = u_proj * viewPos;\n"); + WRITE(p, " gl_Position = depthRoundZVP(u_proj * viewPos);\n"); // TODO: Declare variables for dots for shade mapping if needed.