[2/2] wined3d: Take abs() of vertex z coordinate as FFP fog coordinate (try 3)

Joachim Priesner joachim.priesner at web.de
Mon Nov 3 12:55:46 CST 2014


Supersedes 107334.

Take the absolute value of vertex.z as fog coordinate instead of just
the z coordinate. This fixes e.g. fog for applications that use
right-handed projection matrices.

Also test the clamp behavior of the oFog vertex shader output.

Tested on openSuse 13.1 and Windows 8.1 (VMware Player)

Try 3 which fixes the issues pointed out by Henri Verbeet.
---
 dlls/d3d8/tests/visual.c   | 305 +++++++++++++++++++++++++++++++++++++
 dlls/d3d9/tests/visual.c   | 370 +++++++++++++++++++++++++++++++++++++++++++++
 dlls/ddraw/tests/ddraw7.c  | 161 ++++++++++++++++++++
 dlls/wined3d/glsl_shader.c |   2 +-
 4 files changed, 837 insertions(+), 1 deletion(-)

diff --git a/dlls/d3d8/tests/visual.c b/dlls/d3d8/tests/visual.c
index 163110c..8727d0e 100644
--- a/dlls/d3d8/tests/visual.c
+++ b/dlls/d3d8/tests/visual.c
@@ -696,6 +696,310 @@ done:
     DestroyWindow(window);
 }
 
+/* This test tests fog in combination with negative vertex z coordinates. */
+static void fog_negative_z_test(void)
+{
+    enum
+    {
+        C_ALPHA_0x00  = 0x00000000,
+        C_CLEAR       = 0xffff00ff,
+        C_FOGGED      = 0x0000ff00,
+        C_HALF_FOGGED = 0x00808000,
+        C_UNFOGGED    = 0x00ff0000,
+        C_CLAMPED_FOG = 0xdeadbeef /* triggers special codepath when used as middle_color */
+    };
+
+    /* Fill the null-shader entry with the FVF (SetVertexShader is "overloaded" on d3d8). */
+    DWORD vertex_shader[3] = {D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_SPECULAR, 0, 0};
+    DWORD pixel_shader[2] = {0, 0};
+    IDirect3D8 *d3d;
+    IDirect3DDevice8 *device;
+    BOOL has_vs_support, has_ps_support, has_table_fog_support;
+    unsigned int i, ps, y;
+    D3DCOLOR color, expected_middle_color;
+    ULONG refcount;
+    D3DCAPS8 caps;
+    HWND window;
+    HRESULT hr;
+
+    /* All vertex shaders expect the projection matrix in registers c0...c3. */
+
+    /* Basic vertex shader without fog computation ("non foggy") */
+    static const DWORD vertex_shader_code1[] =
+    {
+        0xfffe0100,                                                             /* vs_1_0             */
+        /* output.Pos = mul(Projection, input.Pos) */
+        0x00000005, 0x800f0000, 0x90000000, 0xa0e40000,                         /* mul r0, v0.x, c0   */
+        0x00000005, 0x800f0001, 0x90550000, 0xa0e40001,                         /* mul r1, v0.y, c1   */
+        0x00000002, 0x800f0000, 0x80e40000, 0x80e40001,                         /* add r0, r0, r1     */
+        0x00000005, 0x800f0001, 0x90aa0000, 0xa0e40002,                         /* mul r1, v0.z, c2   */
+        0x00000002, 0x800f0000, 0x80e40000, 0x80e40001,                         /* add r0, r0, r1     */
+        0x00000005, 0x800f0001, 0x90ff0000, 0xa0e40003,                         /* mul r1, v0.w, c3   */
+        0x00000002, 0xc00f0000, 0x80e40000, 0x80e40001,                         /* add oPos, r0, r1   */
+        /* output.Color = input.Color */
+        0x00000001, 0xd00f0000, 0x90e40001,                                     /* mov oD0, v1        */
+        0x0000ffff,                                                             /* END                */
+    };
+
+    /* Basic vertex shader with fog computation ("foggy"). Outputs the z coordinate of the vertex
+     * as its fog intensity value. */
+    static const DWORD vertex_shader_code2[] =
+    {
+        0xfffe0100,                                                             /* vs_1_0             */
+        /* output.Pos = mul(Projection, input.Pos) */
+        0x00000005, 0x800f0000, 0x90000000, 0xa0e40000,                         /* mul r0, v0.x, c0   */
+        0x00000005, 0x800f0001, 0x90550000, 0xa0e40001,                         /* mul r1, v0.y, c1   */
+        0x00000002, 0x800f0000, 0x80e40000, 0x80e40001,                         /* add r0, r0, r1     */
+        0x00000005, 0x800f0001, 0x90aa0000, 0xa0e40002,                         /* mul r1, v0.z, c2   */
+        0x00000002, 0x800f0000, 0x80e40000, 0x80e40001,                         /* add r0, r0, r1     */
+        0x00000005, 0x800f0001, 0x90ff0000, 0xa0e40003,                         /* mul r1, v0.w, c3   */
+        0x00000002, 0xc00f0000, 0x80e40000, 0x80e40001,                         /* add oPos, r0, r1   */
+        /* output.Color = input.Color */
+        0x00000001, 0xd00f0000, 0x90e40001,                                     /* mov oD0, v1        */
+        /* output.Fog = input.Pos.z */
+        0x00000001, 0xc00f0001, 0x90aa0000,                                     /* mov oFog, v0.z     */
+        0x0000ffff,                                                             /* END                */
+    };
+
+    /* Basic pixel shader */
+    static const DWORD pixel_shader_code[] =
+    {
+        0xffff0101,                                                             /* ps_1_1     */
+        0x00000001, 0x800f0000, 0x90e40000,                                     /* mov r0, v0 */
+        0x0000ffff
+    };
+
+    static const struct
+    {
+        struct vec3 position;
+        DWORD diffuse;
+        DWORD specular;
+    }
+    top_quad[] =
+    {
+        {{-1.0f, -1.0f, -1.0f}, C_UNFOGGED, C_ALPHA_0x00},
+        {{-1.0f,  0.0f, -1.0f}, C_UNFOGGED, C_ALPHA_0x00},
+        {{ 1.0f, -1.0f,  1.0f}, C_UNFOGGED, C_ALPHA_0x00},
+        {{ 1.0f,  0.0f,  1.0f}, C_UNFOGGED, C_ALPHA_0x00},
+    },
+    bottom_quad_1[] =
+    {
+        {{-1.0f,  0.0f, -1.0f}, C_UNFOGGED, C_ALPHA_0x00},
+        {{-1.0f,  1.0f, -1.0f}, C_UNFOGGED, C_ALPHA_0x00},
+        {{ 0.0f,  0.0f,  0.0f}, C_UNFOGGED, C_ALPHA_0x00},
+        {{ 0.0f,  1.0f,  0.0f}, C_UNFOGGED, C_ALPHA_0x00},
+    },
+    bottom_quad_2[] =
+    {
+        {{ 0.0f,  0.0f,  0.0f}, C_UNFOGGED, C_ALPHA_0x00},
+        {{ 0.0f,  1.0f,  0.0f}, C_UNFOGGED, C_ALPHA_0x00},
+        {{ 1.0f,  0.0f,  1.0f}, C_UNFOGGED, C_ALPHA_0x00},
+        {{ 1.0f,  1.0f,  1.0f}, C_UNFOGGED, C_ALPHA_0x00},
+    };
+    static const DWORD vertex_decl[] =
+    {
+        D3DVSD_STREAM(0),
+        D3DVSD_REG(0, D3DVSDT_FLOAT3),   /* position, v0 */
+        D3DVSD_REG(1, D3DVSDT_D3DCOLOR), /* diffuse color, v1 */
+        D3DVSD_REG(2, D3DVSDT_D3DCOLOR), /* specular color, v2 */
+        D3DVSD_END()
+    };
+    static const struct
+    {
+        int vshader;
+        D3DFOGMODE vfog;
+        D3DFOGMODE tfog;
+        DWORD color_left;
+        DWORD color_middle_top;
+        DWORD color_middle_bottom;
+        DWORD color_right;
+    }
+    test_data[] =
+    {
+        /* Using z-based fog, the bottom quad should have a gradient UNFOGGED->FOGGED
+         * for table fog, and be completely fogged for vertex fog.
+         * When the fog coordinate returned by the vertex shader is used instead,
+         * the result is a gradient FOGGED->UNFOGGED. */
+
+        /* No vertex shader */
+        {0, D3DFOG_NONE,   D3DFOG_LINEAR, C_UNFOGGED, C_HALF_FOGGED, C_HALF_FOGGED, C_FOGGED},
+        {0, D3DFOG_LINEAR, D3DFOG_LINEAR, C_UNFOGGED, C_HALF_FOGGED, C_HALF_FOGGED, C_FOGGED},
+        {0, D3DFOG_LINEAR, D3DFOG_NONE,   C_FOGGED,   C_UNFOGGED,    C_FOGGED,      C_FOGGED},
+        {0, D3DFOG_NONE,   D3DFOG_NONE,   C_FOGGED,   C_FOGGED,      C_FOGGED,      C_FOGGED},
+
+        /* Vertex shader without vertex fog computation */
+        {1, D3DFOG_NONE,   D3DFOG_LINEAR, C_UNFOGGED, C_HALF_FOGGED, C_HALF_FOGGED, C_FOGGED},
+        {1, D3DFOG_LINEAR, D3DFOG_LINEAR, C_UNFOGGED, C_HALF_FOGGED, C_HALF_FOGGED, C_FOGGED},
+        {1, D3DFOG_LINEAR, D3DFOG_NONE,   C_FOGGED,   C_FOGGED,      C_FOGGED,      C_FOGGED},
+        {1, D3DFOG_NONE,   D3DFOG_NONE,   C_FOGGED,   C_FOGGED,      C_FOGGED,      C_FOGGED},
+
+        /* Vertex shader with vertex fog computation.
+         * As drivers inconsistently clamp or don't clamp the oFog output of vertex shaders,
+         * the special value C_CLAMPED_FOG allows both C_HALF_FOGGED (clamped) and C_FOGGED. */
+        {2, D3DFOG_NONE,   D3DFOG_LINEAR, C_UNFOGGED, C_HALF_FOGGED, C_HALF_FOGGED, C_FOGGED},
+        {2, D3DFOG_LINEAR, D3DFOG_LINEAR, C_UNFOGGED, C_HALF_FOGGED, C_HALF_FOGGED, C_FOGGED},
+        {2, D3DFOG_LINEAR, D3DFOG_NONE,   C_FOGGED,   C_FOGGED,      C_CLAMPED_FOG, C_UNFOGGED},
+        {2, D3DFOG_NONE,   D3DFOG_NONE,   C_FOGGED,   C_FOGGED,      C_CLAMPED_FOG, C_UNFOGGED},
+    };
+    /* Affine projection matrix for z-near = -1, z-far = 1. */
+    static const D3DMATRIX proj_mat =
+    {{{
+        1.0f, 0.0f, 0.0f, 0.0f,
+        0.0f, 1.0f, 0.0f, 0.0f,
+        0.0f, 0.0f, 0.5f, 0.0f,
+        0.0f, 0.0f, 0.5f, 1.0f
+    }}};
+
+    window = CreateWindowA("static", "d3d8_test", WS_OVERLAPPEDWINDOW | WS_VISIBLE,
+            0, 0, 640, 480, NULL, NULL, NULL, NULL);
+    d3d = Direct3DCreate8(D3D_SDK_VERSION);
+    ok(!!d3d, "Failed to create a D3D object\n");
+    if (!(device = create_device(d3d, window, window, TRUE)))
+    {
+        skip("Failed to create a D3D device, skipping tests\n");
+        goto done;
+    }
+
+    hr = IDirect3DDevice8_GetDeviceCaps(device, &caps);
+    ok(SUCCEEDED(hr), "Failed to get device caps (%#x)\n", hr);
+
+    has_table_fog_support = caps.RasterCaps & D3DPRASTERCAPS_FOGTABLE;
+    if (!has_table_fog_support)
+        skip("No table fog support, skipping some fog tests\n");
+
+    has_ps_support = caps.PixelShaderVersion >= D3DPS_VERSION(1, 1);
+    if (has_ps_support)
+    {
+        hr = IDirect3DDevice8_CreatePixelShader(device, pixel_shader_code, &pixel_shader[1]);
+        ok(SUCCEEDED(hr), "CreatePixelShader failed (%#x)\n", hr);
+    }
+    else
+    {
+        skip("No ps_1_1 support, skipping some fog tests\n");
+    }
+
+    has_vs_support = caps.VertexShaderVersion >= D3DVS_VERSION(1, 0);
+    if (has_vs_support)
+    {
+        hr = IDirect3DDevice8_CreateVertexShader(device, vertex_decl, vertex_shader_code1, &vertex_shader[1], 0);
+        ok(SUCCEEDED(hr), "CreateVertexShader failed (%#x)\n", hr);
+        hr = IDirect3DDevice8_CreateVertexShader(device, vertex_decl, vertex_shader_code2, &vertex_shader[2], 0);
+        ok(SUCCEEDED(hr), "CreateVertexShader failed (%#x)\n", hr);
+
+        hr = IDirect3DDevice8_SetVertexShaderConstant(device, 0, &proj_mat, 4);
+        ok(SUCCEEDED(hr), "SetVertexShaderConstant (projection matrix) failed (%#x)\n", hr);
+    }
+    else
+    {
+        skip("No vs_1_0 support, skipping some fog tests\n");
+    }
+
+    /* Setup initial states: No depth test, no lighting, fog on, fog color */
+    hr = IDirect3DDevice8_SetRenderState(device, D3DRS_ZENABLE, D3DZB_FALSE);
+    ok(SUCCEEDED(hr), "Turning off depth test failed (%#x)\n", hr);
+    hr = IDirect3DDevice8_SetRenderState(device, D3DRS_LIGHTING, FALSE);
+    ok(SUCCEEDED(hr), "Turning off lighting failed (%#x)\n", hr);
+    hr = IDirect3DDevice8_SetRenderState(device, D3DRS_FOGENABLE, TRUE);
+    ok(SUCCEEDED(hr), "Turning on fog calculations failed (%#x)\n", hr);
+    hr = IDirect3DDevice8_SetRenderState(device, D3DRS_FOGCOLOR, C_FOGGED);
+    ok(SUCCEEDED(hr), "Setting fog color failed (%#x)\n", hr);
+
+    hr = IDirect3DDevice8_SetTransform(device, D3DTS_PROJECTION, &proj_mat);
+    ok(SUCCEEDED(hr), "Failed to set projection transform (%#x)\n", hr);
+
+    for (i = 0; i < sizeof(test_data)/sizeof(test_data[0]); i++)
+    {
+        if (test_data[i].vshader != 0 && !vertex_shader[test_data[i].vshader])
+            continue;
+        if (test_data[i].tfog != D3DFOG_NONE && !has_table_fog_support)
+            continue;
+
+        hr = IDirect3DDevice8_SetVertexShader(device, vertex_shader[test_data[i].vshader]);
+        ok(SUCCEEDED(hr), "SetVertexShader failed (%#x)\n", hr);
+
+        for (ps = 0; ps < sizeof(pixel_shader)/sizeof(pixel_shader[0]); ps++)
+        {
+            if (ps != 0 && !pixel_shader[ps])
+                continue;
+
+            if (has_ps_support)
+            {
+                hr = IDirect3DDevice8_SetPixelShader(device, pixel_shader[ps]);
+                ok(SUCCEEDED(hr), "SetPixelShader failed (%#x)\n", hr);
+            }
+
+            hr = IDirect3DDevice8_SetRenderState(device, D3DRS_FOGVERTEXMODE, test_data[i].vfog);
+            ok(SUCCEEDED(hr), "Setting fog vertex mode to %d failed (%#x)\n", test_data[i].vfog, hr);
+            hr = IDirect3DDevice8_SetRenderState(device, D3DRS_FOGTABLEMODE, test_data[i].tfog);
+            ok(SUCCEEDED(hr), "Setting fog table mode to %d failed (%#x)\n", test_data[i].tfog, hr);
+
+            hr = IDirect3DDevice8_Clear(device, 0, NULL, D3DCLEAR_TARGET, C_CLEAR, 1.0f, 0);
+            ok(SUCCEEDED(hr), "Clear failed (%#x)\n", hr);
+            hr = IDirect3DDevice8_BeginScene(device);
+            ok(SUCCEEDED(hr), "BeginScene failed (%#x)\n", hr);
+            hr = IDirect3DDevice8_DrawPrimitiveUP(
+                    device, D3DPT_TRIANGLESTRIP, 2, &top_quad[0], sizeof(top_quad[0]));
+            ok(SUCCEEDED(hr), "DrawPrimitiveUP failed (%#x)\n", hr);
+            hr = IDirect3DDevice8_DrawPrimitiveUP(
+                    device, D3DPT_TRIANGLESTRIP, 2, &bottom_quad_1[0], sizeof(bottom_quad_1[0]));
+            ok(SUCCEEDED(hr), "DrawPrimitiveUP failed (%#x)\n", hr);
+            hr = IDirect3DDevice8_DrawPrimitiveUP(
+                    device, D3DPT_TRIANGLESTRIP, 2, &bottom_quad_2[0], sizeof(bottom_quad_2[0]));
+            ok(SUCCEEDED(hr), "DrawPrimitiveUP failed (%#x)\n", hr);
+            hr = IDirect3DDevice8_EndScene(device);
+            ok(SUCCEEDED(hr), "EndScene failed (%#x)\n", hr);
+
+            /* Use 5% tolerance on the colors since there may be a gradient
+             * between left and right vertices. */
+            for (y = 120; y <= 360; y += 240)
+            {
+                color = getPixelColor(device, 2, y);
+                ok(color_match(color, test_data[i].color_left, 13),
+                        "fog vs%i ps%i fvm%i ftm%i, y=%i: got left color %08x, expected %08x+-5%%\n",
+                        test_data[i].vshader, ps, test_data[i].vfog, test_data[i].tfog, y,
+                        color, test_data[i].color_left);
+                color = getPixelColor(device, 320, y);
+                expected_middle_color =
+                        y == 120 ? test_data[i].color_middle_top : test_data[i].color_middle_bottom;
+                if (expected_middle_color == C_CLAMPED_FOG)
+                {
+                    ok(color_match(color, C_HALF_FOGGED, 13) || color_match(color, C_FOGGED, 13),
+                            "fog vs%i ps%i fvm%i ftm%i, y=%i: got middle color %08x, "
+                            "expected %08x+-5%% or %08x+-5%%\n",
+                            test_data[i].vshader, ps, test_data[i].vfog, test_data[i].tfog, y,
+                            color, C_HALF_FOGGED, C_FOGGED);
+                }
+                else
+                {
+                    ok(color_match(color, expected_middle_color, 13),
+                            "fog vs%i ps%i fvm%i ftm%i, y=%i: got middle color %08x, expected %08x+-5%%\n",
+                            test_data[i].vshader, ps, test_data[i].vfog, test_data[i].tfog, y,
+                            color, expected_middle_color);
+                }
+                color = getPixelColor(device, 638, y);
+                ok(color_match(color, test_data[i].color_right, 13),
+                        "fog vs%i ps%i fvm%i ftm%i, y=%i: got right color %08x, expected %08x+-5%%\n",
+                        test_data[i].vshader, ps, test_data[i].vfog, test_data[i].tfog, y,
+                        color, test_data[i].color_right);
+            }
+
+            hr = IDirect3DDevice8_Present(device, NULL, NULL, NULL, NULL);
+            ok(SUCCEEDED(hr), "Present failed (%#x)\n", hr);
+        }
+    }
+    for (i = 0; i < sizeof(vertex_shader)/sizeof(vertex_shader[0]); i++)
+        if (vertex_shader[i])
+            IDirect3DDevice8_DeleteVertexShader(device, vertex_shader[i]);
+    for (i = 0; i < sizeof(pixel_shader)/sizeof(pixel_shader[0]); i++)
+        if (pixel_shader[i])
+            IDirect3DDevice8_DeletePixelShader(device, pixel_shader[i]);
+    refcount = IDirect3DDevice8_Release(device);
+    ok(!refcount, "Device has %u references left\n", refcount);
+done:
+    IDirect3D8_Release(d3d);
+    DestroyWindow(window);
+}
+
 /* This tests fog in combination with shaders.
  * What's tested: linear fog (vertex and table) with pixel shader
  *                linear table fog with non foggy vertex shader
@@ -5092,6 +5396,7 @@ START_TEST(visual)
     offscreen_test();
     alpha_test();
     test_scalar_instructions();
+    fog_negative_z_test();
     fog_with_shader_test();
     cnd_test();
     p8_texture_test();
diff --git a/dlls/d3d9/tests/visual.c b/dlls/d3d9/tests/visual.c
index 117c8eb..d0b73f6 100644
--- a/dlls/d3d9/tests/visual.c
+++ b/dlls/d3d9/tests/visual.c
@@ -1825,6 +1825,375 @@ done:
     DestroyWindow(window);
 }
 
+/* This test tests fog in combination with negative vertex z coordinates. */
+static void fog_negative_z_test(void)
+{
+    enum
+    {
+        C_ALPHA_0x00  = 0x00000000,
+        C_CLEAR       = 0xffff00ff,
+        C_FOGGED      = 0x0000ff00,
+        C_HALF_FOGGED = 0x00808000,
+        C_UNFOGGED    = 0x00ff0000,
+        C_CLAMPED_FOG = 0xdeadbeef /* triggers special codepath when used as middle_color */
+    };
+
+    IDirect3DVertexShader9 *vertex_shader[4] = {NULL, NULL, NULL, NULL};
+    IDirect3DPixelShader9 *pixel_shader[3] = {NULL, NULL, NULL};
+    IDirect3DVertexDeclaration9 *vertex_declaration = NULL;
+    IDirect3DDevice9 *device;
+    BOOL has_vs_support, has_ps_support, has_table_fog_support, is_fog_vertex_clamped;
+    unsigned int i, ps, y;
+    IDirect3D9 *d3d;
+    ULONG refcount;
+    D3DCAPS9 caps;
+    DWORD color, expected_middle_color;
+    HWND window;
+    HRESULT hr;
+
+    /* All vertex shaders expect the projection matrix in registers c0...c3. */
+
+    /* Basic vertex shader without fog computation ("non foggy") */
+    static const DWORD vertex_shader_code1[] =
+    {
+        0xfffe0101,                                                             /* vs_1_1             */
+        0x0000001f, 0x80000000, 0x900f0000,                                     /* dcl_position0 v0   */
+        0x0000001f, 0x8000000a, 0x900f0001,                                     /* dcl_color0 v1      */
+        /* output.Pos = mul(Projection, input.Pos) */
+        0x00000005, 0x800f0000, 0x90000000, 0xa0e40000,                         /* mul r0, v0.x, c0   */
+        0x00000005, 0x800f0001, 0x90550000, 0xa0e40001,                         /* mul r1, v0.y, c1   */
+        0x00000002, 0x800f0000, 0x80e40000, 0x80e40001,                         /* add r0, r0, r1     */
+        0x00000005, 0x800f0001, 0x90aa0000, 0xa0e40002,                         /* mul r1, v0.z, c2   */
+        0x00000002, 0x800f0000, 0x80e40000, 0x80e40001,                         /* add r0, r0, r1     */
+        0x00000005, 0x800f0001, 0x90ff0000, 0xa0e40003,                         /* mul r1, v0.w, c3   */
+        0x00000002, 0xc00f0000, 0x80e40000, 0x80e40001,                         /* add oPos, r0, r1   */
+        /* output.Color = input.Color */
+        0x00000001, 0xd00f0000, 0x90e40001,                                     /* mov oD0, v1        */
+        0x0000ffff,                                                             /* END                */
+    };
+
+    /* Basic vertex shader with fog computation ("foggy"). Outputs the z coordinate of the vertex
+     * as its fog intensity value. */
+    static const DWORD vertex_shader_code2[] =
+    {
+        0xfffe0101,                                                             /* vs_1_1             */
+        0x0000001f, 0x80000000, 0x900f0000,                                     /* dcl_position0 v0   */
+        0x0000001f, 0x8000000a, 0x900f0001,                                     /* dcl_color0 v1      */
+        /* output.Pos = mul(Projection, input.Pos) */
+        0x00000005, 0x800f0000, 0x90000000, 0xa0e40000,                         /* mul r0, v0.x, c0   */
+        0x00000005, 0x800f0001, 0x90550000, 0xa0e40001,                         /* mul r1, v0.y, c1   */
+        0x00000002, 0x800f0000, 0x80e40000, 0x80e40001,                         /* add r0, r0, r1     */
+        0x00000005, 0x800f0001, 0x90aa0000, 0xa0e40002,                         /* mul r1, v0.z, c2   */
+        0x00000002, 0x800f0000, 0x80e40000, 0x80e40001,                         /* add r0, r0, r1     */
+        0x00000005, 0x800f0001, 0x90ff0000, 0xa0e40003,                         /* mul r1, v0.w, c3   */
+        0x00000002, 0xc00f0000, 0x80e40000, 0x80e40001,                         /* add oPos, r0, r1   */
+        /* output.Color = input.Color */
+        0x00000001, 0xd00f0000, 0x90e40001,                                     /* mov oD0, v1        */
+        /* output.Fog = input.Pos.z */
+        0x00000001, 0xc00f0001, 0x90aa0000,                                     /* mov oFog, v0.z     */
+        0x0000ffff,                                                             /* END                */
+    };
+
+    /* Basic vertex shader with fog computation ("foggy"), vs_2_0 */
+    static const DWORD vertex_shader_code3[] =
+    {
+        0xfffe0200,                                                             /* vs_2_0             */
+        0x0200001f, 0x80000000, 0x900f0000,                                     /* dcl_position0 v0   */
+        0x0200001f, 0x8000000a, 0x900f0001,                                     /* dcl_color0 v1      */
+        /* output.Pos = mul(Projection, input.Pos) */
+        0x03000005, 0x800f0000, 0x90000000, 0xa0e40000,                         /* mul r0, v0.x, c0   */
+        0x03000005, 0x800f0001, 0x90550000, 0xa0e40001,                         /* mul r1, v0.y, c1   */
+        0x03000002, 0x800f0000, 0x80e40000, 0x80e40001,                         /* add r0, r0, r1     */
+        0x03000005, 0x800f0001, 0x90aa0000, 0xa0e40002,                         /* mul r1, v0.z, c2   */
+        0x03000002, 0x800f0000, 0x80e40000, 0x80e40001,                         /* add r0, r0, r1     */
+        0x03000005, 0x800f0001, 0x90ff0000, 0xa0e40003,                         /* mul r1, v0.w, c3   */
+        0x03000002, 0xc00f0000, 0x80e40000, 0x80e40001,                         /* add oPos, r0, r1   */
+        /* output.Color = input.Color */
+        0x02000001, 0xd00f0000, 0x90e40001,                                     /* mov oD0, v1        */
+        /* output.Fog = input.Pos.z */
+        0x02000001, 0xc00f0001, 0x90aa0000,                                     /* mov oFog, v0.z     */
+        0x0000ffff,                                                             /* END                */
+    };
+
+    /* Basic pixel shader */
+    static const DWORD pixel_shader_code[] =
+    {
+        0xffff0101,                                                             /* ps_1_1     */
+        0x00000001, 0x800f0000, 0x90e40000,                                     /* mov r0, v0 */
+        0x0000ffff
+    };
+    static const DWORD pixel_shader_code2[] =
+    {
+        0xffff0200,                                                             /* ps_2_0      */
+        0x0200001f, 0x80000000, 0x900f0000,                                     /* dcl v0      */
+        0x02000001, 0x800f0800, 0x90e40000,                                     /* mov oC0, v0 */
+        0x0000ffff
+    };
+    static const struct
+    {
+        struct vec3 position;
+        D3DCOLOR diffuse;
+        D3DCOLOR specular;
+    }
+    top_quad[] =
+    {
+        {{-1.0f, -1.0f, -1.0f}, C_UNFOGGED, C_ALPHA_0x00},
+        {{-1.0f,  0.0f, -1.0f}, C_UNFOGGED, C_ALPHA_0x00},
+        {{ 1.0f, -1.0f,  1.0f}, C_UNFOGGED, C_ALPHA_0x00},
+        {{ 1.0f,  0.0f,  1.0f}, C_UNFOGGED, C_ALPHA_0x00},
+    },
+    bottom_quad_1[] =
+    {
+        {{-1.0f,  0.0f, -1.0f}, C_UNFOGGED, C_ALPHA_0x00},
+        {{-1.0f,  1.0f, -1.0f}, C_UNFOGGED, C_ALPHA_0x00},
+        {{ 0.0f,  0.0f,  0.0f}, C_UNFOGGED, C_ALPHA_0x00},
+        {{ 0.0f,  1.0f,  0.0f}, C_UNFOGGED, C_ALPHA_0x00},
+    },
+    bottom_quad_2[] =
+    {
+        {{ 0.0f,  0.0f,  0.0f}, C_UNFOGGED, C_ALPHA_0x00},
+        {{ 0.0f,  1.0f,  0.0f}, C_UNFOGGED, C_ALPHA_0x00},
+        {{ 1.0f,  0.0f,  1.0f}, C_UNFOGGED, C_ALPHA_0x00},
+        {{ 1.0f,  1.0f,  1.0f}, C_UNFOGGED, C_ALPHA_0x00},
+    };
+
+    static const D3DVERTEXELEMENT9 decl_elements[] =
+    {
+        {0,  0, D3DDECLTYPE_FLOAT3,   D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
+        {0, 12, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR,    0}, /* diffuse */
+        {0, 16, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR,    1}, /* specular */
+        D3DDECL_END()
+    };
+    static const struct
+    {
+        int vshader;
+        D3DFOGMODE vfog;
+        D3DFOGMODE tfog;
+        DWORD color_left;
+        DWORD color_middle_top;
+        DWORD color_middle_bottom;
+        DWORD color_right;
+    }
+    test_data[] =
+    {
+        /* Using z-based fog, the bottom quad should have a gradient UNFOGGED->FOGGED
+         * for table fog, and be completely fogged for vertex fog.
+         * When the fog coordinate returned by the vertex shader is used instead,
+         * the result is a gradient FOGGED->UNFOGGED in the right half of the screen.
+         *
+         * C_CLAMPED_FOG will be replaced by the correct expected value based on
+         * the value of D3DPMISCCAPS_FOGVERTEXCLAMPED. If the driver clamps the oFog
+         * output of vertex shaders, it should be C_HALF_FOGGED, else C_FOGGED. */
+
+        /* No vertex shader */
+        {0, D3DFOG_NONE,   D3DFOG_LINEAR, C_UNFOGGED, C_HALF_FOGGED, C_HALF_FOGGED, C_FOGGED},
+        {0, D3DFOG_LINEAR, D3DFOG_LINEAR, C_UNFOGGED, C_HALF_FOGGED, C_HALF_FOGGED, C_FOGGED},
+        {0, D3DFOG_LINEAR, D3DFOG_NONE,   C_FOGGED,   C_UNFOGGED,    C_FOGGED,      C_FOGGED},
+        {0, D3DFOG_NONE,   D3DFOG_NONE,   C_FOGGED,   C_FOGGED,      C_FOGGED,      C_FOGGED},
+
+        /* Vertex shader without vertex fog computation */
+        {1, D3DFOG_NONE,   D3DFOG_LINEAR, C_UNFOGGED, C_HALF_FOGGED, C_HALF_FOGGED, C_FOGGED},
+        {1, D3DFOG_LINEAR, D3DFOG_LINEAR, C_UNFOGGED, C_HALF_FOGGED, C_HALF_FOGGED, C_FOGGED},
+        {1, D3DFOG_LINEAR, D3DFOG_NONE,   C_FOGGED,   C_FOGGED,      C_FOGGED,      C_FOGGED},
+        {1, D3DFOG_NONE,   D3DFOG_NONE,   C_FOGGED,   C_FOGGED,      C_FOGGED,      C_FOGGED},
+
+        /* Vertex shader vs_1_1 with vertex fog computation */
+        {2, D3DFOG_NONE,   D3DFOG_LINEAR, C_UNFOGGED, C_HALF_FOGGED, C_HALF_FOGGED, C_FOGGED},
+        {2, D3DFOG_LINEAR, D3DFOG_LINEAR, C_UNFOGGED, C_HALF_FOGGED, C_HALF_FOGGED, C_FOGGED},
+        {2, D3DFOG_LINEAR, D3DFOG_NONE,   C_FOGGED,   C_FOGGED,      C_CLAMPED_FOG, C_UNFOGGED},
+        {2, D3DFOG_NONE,   D3DFOG_NONE,   C_FOGGED,   C_FOGGED,      C_CLAMPED_FOG, C_UNFOGGED},
+
+        /* Vertex shader vs_2_0 with vertex fog computation */
+        {3, D3DFOG_NONE,   D3DFOG_LINEAR, C_UNFOGGED, C_HALF_FOGGED, C_HALF_FOGGED, C_FOGGED},
+        {3, D3DFOG_LINEAR, D3DFOG_LINEAR, C_UNFOGGED, C_HALF_FOGGED, C_HALF_FOGGED, C_FOGGED},
+        {3, D3DFOG_LINEAR, D3DFOG_NONE,   C_FOGGED,   C_FOGGED,      C_CLAMPED_FOG, C_UNFOGGED},
+        {3, D3DFOG_NONE,   D3DFOG_NONE,   C_FOGGED,   C_FOGGED,      C_CLAMPED_FOG, C_UNFOGGED},
+    };
+    /* Affine projection matrix for z-near = -1, z-far = 1. */
+    static const D3DMATRIX proj_mat =
+    {{{
+        1.0f, 0.0f, 0.0f, 0.0f,
+        0.0f, 1.0f, 0.0f, 0.0f,
+        0.0f, 0.0f, 0.5f, 0.0f,
+        0.0f, 0.0f, 0.5f, 1.0f,
+    }}};
+
+    window = CreateWindowA("static", "d3d9_test", WS_OVERLAPPEDWINDOW | WS_VISIBLE,
+            0, 0, 640, 480, NULL, NULL, NULL, NULL);
+    d3d = Direct3DCreate9(D3D_SDK_VERSION);
+    ok(!!d3d, "Failed to create a D3D object\n");
+    if (!(device = create_device(d3d, window, window, TRUE)))
+    {
+        skip("Failed to create a D3D device, skipping tests\n");
+        goto done;
+    }
+
+    hr = IDirect3DDevice9_GetDeviceCaps(device, &caps);
+    ok(SUCCEEDED(hr), "Failed to get device caps (%#x)\n", hr);
+
+    has_table_fog_support = caps.RasterCaps & D3DPRASTERCAPS_FOGTABLE;
+    if (!has_table_fog_support)
+        skip("No table fog support, skipping some fog tests\n");
+
+    has_vs_support = caps.VertexShaderVersion >= D3DVS_VERSION(1, 1);
+    if (has_vs_support)
+    {
+        hr = IDirect3DDevice9_CreateVertexShader(device, vertex_shader_code1, &vertex_shader[1]);
+        ok(SUCCEEDED(hr), "CreateVertexShader failed (%#x)\n", hr);
+        hr = IDirect3DDevice9_CreateVertexShader(device, vertex_shader_code2, &vertex_shader[2]);
+        ok(SUCCEEDED(hr), "CreateVertexShader failed (%#x)\n", hr);
+
+        if (caps.VertexShaderVersion >= D3DVS_VERSION(2, 0))
+        {
+            hr = IDirect3DDevice9_CreateVertexShader(device, vertex_shader_code3, &vertex_shader[3]);
+            ok(SUCCEEDED(hr), "CreateVertexShader failed (%#x)\n", hr);
+        }
+        else
+        {
+            skip("No vs_2_0 support, skipping some fog tests\n");
+        }
+
+        hr = IDirect3DDevice9_SetVertexShaderConstantF(device, 0, (const float*)&proj_mat, 4);
+        ok(SUCCEEDED(hr), "SetVertexShaderConstantF (projection matrix) failed (%#x)\n", hr);
+    }
+    else
+    {
+        skip("No vs_1_1 support, skipping some fog tests\n");
+    }
+
+    has_ps_support = caps.PixelShaderVersion >= D3DPS_VERSION(1, 1);
+    if (has_ps_support)
+    {
+        hr = IDirect3DDevice9_CreatePixelShader(device, pixel_shader_code, &pixel_shader[1]);
+        ok(SUCCEEDED(hr), "CreatePixelShader failed (%#x)\n", hr);
+
+        if (caps.PixelShaderVersion >= D3DPS_VERSION(2, 0))
+        {
+            hr = IDirect3DDevice9_CreatePixelShader(device, pixel_shader_code2, &pixel_shader[2]);
+            ok(SUCCEEDED(hr), "CreatePixelShader failed (%#x)\n", hr);
+        }
+        else
+        {
+            skip("No ps_2_0 support, skipping some fog tests\n");
+        }
+    }
+    else
+    {
+        skip("No ps_1_1 support, skipping some fog tests\n");
+    }
+
+    is_fog_vertex_clamped = caps.PrimitiveMiscCaps & D3DPMISCCAPS_FOGVERTEXCLAMPED;
+
+    hr = IDirect3DDevice9_CreateVertexDeclaration(device, decl_elements, &vertex_declaration);
+    ok(SUCCEEDED(hr), "CreateVertexDeclaration failed (%#x)\n", hr);
+
+    /* Setup initial states: No depth test, no lighting, fog on, fog color */
+    hr = IDirect3DDevice9_SetRenderState(device, D3DRS_ZENABLE, D3DZB_FALSE);
+    ok(SUCCEEDED(hr), "Turning off depth test failed (%#x)\n", hr);
+    hr = IDirect3DDevice9_SetRenderState(device, D3DRS_LIGHTING, FALSE);
+    ok(SUCCEEDED(hr), "Turning off lighting failed (%#x)\n", hr);
+    hr = IDirect3DDevice9_SetRenderState(device, D3DRS_FOGENABLE, TRUE);
+    ok(SUCCEEDED(hr), "Turning on fog calculations failed (%#x)\n", hr);
+    hr = IDirect3DDevice9_SetRenderState(device, D3DRS_FOGCOLOR, C_FOGGED);
+    ok(SUCCEEDED(hr), "Setting fog color failed (%#x)\n", hr);
+    hr = IDirect3DDevice9_SetVertexDeclaration(device, vertex_declaration);
+    ok(SUCCEEDED(hr), "SetVertexDeclaration failed (%#x)\n", hr);
+
+    hr = IDirect3DDevice9_SetTransform(device, D3DTS_PROJECTION, &proj_mat);
+    ok(SUCCEEDED(hr), "Failed to set projection transform (%#x)\n", hr);
+
+    for (i = 0; i < sizeof(test_data)/sizeof(test_data[0]); i++)
+    {
+        if (test_data[i].vshader != 0 && !vertex_shader[test_data[i].vshader])
+            continue;
+        if (test_data[i].tfog != D3DFOG_NONE && !has_table_fog_support)
+            continue;
+
+        if (has_vs_support)
+        {
+            hr = IDirect3DDevice9_SetVertexShader(device, vertex_shader[test_data[i].vshader]);
+            ok(SUCCEEDED(hr), "SetVertexShader failed (%#x)\n", hr);
+        }
+
+        for (ps = 0; ps < sizeof(pixel_shader)/sizeof(pixel_shader[0]); ps++)
+        {
+            if (ps != 0 && !pixel_shader[ps])
+                continue;
+
+            if (has_ps_support)
+            {
+                hr = IDirect3DDevice9_SetPixelShader(device, pixel_shader[ps]);
+                ok(SUCCEEDED(hr), "SetPixelShader failed (%#x)\n", hr);
+            }
+
+            hr = IDirect3DDevice9_SetRenderState(device, D3DRS_FOGVERTEXMODE, test_data[i].vfog);
+            ok(SUCCEEDED(hr), "Setting fog vertex mode to %d failed (%#x)\n", test_data[i].vfog, hr);
+            hr = IDirect3DDevice9_SetRenderState(device, D3DRS_FOGTABLEMODE, test_data[i].tfog);
+            ok(SUCCEEDED(hr), "Setting fog table mode to %d failed (%#x)\n", test_data[i].tfog, hr);
+
+            hr = IDirect3DDevice9_Clear(device, 0, NULL, D3DCLEAR_TARGET, C_CLEAR, 1.0f, 0);
+            ok(SUCCEEDED(hr), "Clear failed (%#x)\n", hr);
+            hr = IDirect3DDevice9_BeginScene(device);
+            ok(SUCCEEDED(hr), "BeginScene failed (%#x)\n", hr);
+            hr = IDirect3DDevice9_DrawPrimitiveUP(
+                    device, D3DPT_TRIANGLESTRIP, 2, &top_quad[0], sizeof(top_quad[0]));
+            ok(SUCCEEDED(hr), "DrawPrimitiveUP failed (%#x)\n", hr);
+            hr = IDirect3DDevice9_DrawPrimitiveUP(
+                    device, D3DPT_TRIANGLESTRIP, 2, &bottom_quad_1[0], sizeof(bottom_quad_1[0]));
+            ok(SUCCEEDED(hr), "DrawPrimitiveUP failed (%#x)\n", hr);
+            hr = IDirect3DDevice9_DrawPrimitiveUP(
+                    device, D3DPT_TRIANGLESTRIP, 2, &bottom_quad_2[0], sizeof(bottom_quad_2[0]));
+            ok(SUCCEEDED(hr), "DrawPrimitiveUP failed (%#x)\n", hr);
+            hr = IDirect3DDevice9_EndScene(device);
+            ok(SUCCEEDED(hr), "EndScene failed (%#x)\n", hr);
+
+            /* Use 5% tolerance on the colors since there may be a gradient
+             * between left and right vertices. */
+            for (y = 120; y <= 360; y += 240)
+            {
+                color = getPixelColor(device, 2, y);
+                ok(color_match(color, test_data[i].color_left, 13),
+                        "fog vs%i ps%i fvm%i ftm%i, y=%i: got left color %08x, expected %08x+-5%%\n",
+                        test_data[i].vshader, ps, test_data[i].vfog, test_data[i].tfog, y,
+                        color, test_data[i].color_left);
+                color = getPixelColor(device, 320, y);
+                expected_middle_color =
+                        y == 120 ? test_data[i].color_middle_top : test_data[i].color_middle_bottom;
+                if (expected_middle_color == C_CLAMPED_FOG)
+                {
+                    expected_middle_color = is_fog_vertex_clamped ? C_HALF_FOGGED : C_FOGGED;
+                }
+                ok(color_match(color, expected_middle_color, 13),
+                        "fog vs%i ps%i fvm%i ftm%i, y=%i: got middle color %08x, expected %08x+-5%%\n",
+                        test_data[i].vshader, ps, test_data[i].vfog, test_data[i].tfog, y,
+                        color, expected_middle_color);
+                color = getPixelColor(device, 638, y);
+                ok(color_match(color, test_data[i].color_right, 13),
+                        "fog vs%i ps%i fvm%i ftm%i, y=%i: got right color %08x, expected %08x+-5%%\n",
+                        test_data[i].vshader, ps, test_data[i].vfog, test_data[i].tfog, y,
+                        color, test_data[i].color_right);
+
+            }
+
+            hr = IDirect3DDevice9_Present(device, NULL, NULL, NULL, NULL);
+            ok(SUCCEEDED(hr), "Present failed (%#x)\n", hr);
+        }
+    }
+
+    for (i = 0; i < sizeof(vertex_shader)/sizeof(vertex_shader[0]); i++)
+        if (vertex_shader[i])
+            IDirect3DVertexShader9_Release(vertex_shader[i]);
+    for (i = 0; i < sizeof(pixel_shader)/sizeof(pixel_shader[0]); i++)
+        if (pixel_shader[i])
+            IDirect3DPixelShader9_Release(pixel_shader[i]);
+    IDirect3DVertexDeclaration9_Release(vertex_declaration);
+    refcount = IDirect3DDevice9_Release(device);
+    ok(!refcount, "Device has %u references left\n", refcount);
+done:
+    IDirect3D9_Release(d3d);
+    DestroyWindow(window);
+}
+
 /* This test tests fog in combination with shaders.
  * What's tested: linear fog (vertex and table) with pixel shader
  *                linear table fog with non foggy vertex shader
@@ -16729,6 +17098,7 @@ START_TEST(visual)
     test_vshader_input();
     test_vshader_float16();
     stream_test();
+    fog_negative_z_test();
     fog_with_shader_test();
     texbem_test();
     texdepth_test();
diff --git a/dlls/ddraw/tests/ddraw7.c b/dlls/ddraw/tests/ddraw7.c
index ec50fb8..4e999f7 100644
--- a/dlls/ddraw/tests/ddraw7.c
+++ b/dlls/ddraw/tests/ddraw7.c
@@ -3384,6 +3384,166 @@ static void test_fog_special(void)
     DestroyWindow(window);
 }
 
+/* This test tests fog in combination with negative vertex z coordinates. */
+static void test_fog_negative_z(void)
+{
+    enum
+    {
+        C_ALPHA_0x00  = 0x00000000,
+        C_CLEAR       = 0xffff00ff,
+        C_FOGGED      = 0x0000ff00,
+        C_HALF_FOGGED = 0x00808000,
+        C_UNFOGGED    = 0x00ff0000
+    };
+
+    D3DCOLOR color, expected_middle_color;
+    HRESULT hr;
+    ULONG refcount;
+    BOOL has_table_fog_support;
+    unsigned int i, y;
+    HWND window;
+    IDirect3DDevice7 *device;
+    IDirectDrawSurface7 *rt;
+    D3DDEVICEDESC7 caps;
+
+    static struct
+    {
+        struct vec3 position;
+        D3DCOLOR diffuse;
+        D3DCOLOR specular;
+    }
+    top_quad[] =
+    {
+        {{-1.0f,  0.0f, -1.0f}, C_UNFOGGED, C_ALPHA_0x00},
+        {{ 1.0f,  0.0f,  1.0f}, C_UNFOGGED, C_ALPHA_0x00},
+        {{-1.0f, -1.0f, -1.0f}, C_UNFOGGED, C_ALPHA_0x00},
+        {{ 1.0f, -1.0f,  1.0f}, C_UNFOGGED, C_ALPHA_0x00},
+    },
+    bottom_quad_1[] =
+    {
+        {{-1.0f,  1.0f, -1.0f}, C_UNFOGGED, C_ALPHA_0x00},
+        {{ 0.0f,  1.0f,  0.0f}, C_UNFOGGED, C_ALPHA_0x00},
+        {{-1.0f,  0.0f, -1.0f}, C_UNFOGGED, C_ALPHA_0x00},
+        {{ 0.0f,  0.0f,  0.0f}, C_UNFOGGED, C_ALPHA_0x00},
+    },
+    bottom_quad_2[] =
+    {
+        {{ 0.0f,  1.0f,  0.0f}, C_UNFOGGED, C_ALPHA_0x00},
+        {{ 1.0f,  1.0f,  1.0f}, C_UNFOGGED, C_ALPHA_0x00},
+        {{ 0.0f,  0.0f,  0.0f}, C_UNFOGGED, C_ALPHA_0x00},
+        {{ 1.0f,  0.0f,  1.0f}, C_UNFOGGED, C_ALPHA_0x00},
+    };
+    static const struct
+    {
+        DWORD vertexmode, tablemode;
+        D3DCOLOR color_left, color_middle_top, color_middle_bottom, color_right;
+    }
+    test_data[] =
+    {
+        {D3DFOG_NONE,   D3DFOG_LINEAR, C_UNFOGGED, C_HALF_FOGGED, C_HALF_FOGGED, C_FOGGED},
+        {D3DFOG_LINEAR, D3DFOG_LINEAR, C_UNFOGGED, C_HALF_FOGGED, C_HALF_FOGGED, C_FOGGED},
+        {D3DFOG_LINEAR, D3DFOG_NONE,   C_FOGGED,   C_UNFOGGED,    C_FOGGED,      C_FOGGED},
+        {D3DFOG_NONE,   D3DFOG_NONE,   C_FOGGED,   C_FOGGED,      C_FOGGED,      C_FOGGED},
+    };
+    /* Affine projection matrix for z-near = -1, z-far = 1. */
+    static D3DMATRIX proj_mat =
+    {
+        1.0f, 0.0f, 0.0f, 0.0f,
+        0.0f, 1.0f, 0.0f, 0.0f,
+        0.0f, 0.0f, 0.5f, 0.0f,
+        0.0f, 0.0f, 0.5f, 1.0f
+    };
+
+    window = CreateWindowA("static", "ddraw_test", WS_OVERLAPPEDWINDOW,
+            0, 0, 640, 480, 0, 0, 0, 0);
+
+    if (!(device = create_device(window, DDSCL_NORMAL)))
+    {
+        skip("Failed to create a 3D device, skipping test.\n");
+        DestroyWindow(window);
+        return;
+    }
+
+    memset(&caps, 0, sizeof(caps));
+    hr = IDirect3DDevice7_GetCaps(device, &caps);
+    ok(SUCCEEDED(hr), "IDirect3DDevice7_GetCaps failed, hr %#x.\n", hr);
+
+    has_table_fog_support = caps.dpcTriCaps.dwRasterCaps & D3DPRASTERCAPS_FOGTABLE;
+    if (!has_table_fog_support)
+        skip("No table fog support, skipping some fog tests.\n");
+
+    hr = IDirect3DDevice7_GetRenderTarget(device, &rt);
+    ok(SUCCEEDED(hr), "Failed to get render target, hr %#x.\n", hr);
+
+    hr = IDirect3DDevice7_SetRenderState(device, D3DRENDERSTATE_FOGENABLE, TRUE);
+    ok(SUCCEEDED(hr), "Failed to enable fog, hr %#x.\n", hr);
+    hr = IDirect3DDevice7_SetRenderState(device, D3DRENDERSTATE_FOGCOLOR, C_FOGGED);
+    ok(SUCCEEDED(hr), "Failed to set fog color, hr %#x.\n", hr);
+    hr = IDirect3DDevice7_SetRenderState(device, D3DRENDERSTATE_LIGHTING, FALSE);
+    ok(SUCCEEDED(hr), "Failed to disable lighting, hr %#x.\n", hr);
+    hr = IDirect3DDevice7_SetRenderState(device, D3DRENDERSTATE_ZENABLE, D3DZB_FALSE);
+    ok(SUCCEEDED(hr), "Failed to disable depth test, hr %#x.\n", hr);
+
+    hr = IDirect3DDevice7_SetTransform(device, D3DTRANSFORMSTATE_PROJECTION, &proj_mat);
+    ok(SUCCEEDED(hr), "Failed to set projection transform, hr %#x.\n", hr);
+
+    for (i = 0; i < sizeof(test_data)/sizeof(test_data[0]); i++)
+    {
+        if (test_data[i].tablemode != D3DFOG_NONE && !has_table_fog_support)
+            continue;
+
+        hr = IDirect3DDevice7_Clear(device, 0, NULL, D3DCLEAR_TARGET, C_CLEAR, 1.0f, 0);
+        ok(SUCCEEDED(hr), "Failed to clear render target, i=%d, hr %#x.\n", i, hr);
+
+        hr = IDirect3DDevice7_SetRenderState(device, D3DRENDERSTATE_FOGVERTEXMODE, test_data[i].vertexmode);
+        ok(SUCCEEDED(hr), "Failed to set fog vertex mode, i=%d, hr %#x.\n", i, hr);
+        hr = IDirect3DDevice7_SetRenderState(device, D3DRENDERSTATE_FOGTABLEMODE, test_data[i].tablemode);
+        ok(SUCCEEDED(hr), "Failed to set fog table mode, i=%d, hr %#x.\n", i, hr);
+
+        hr = IDirect3DDevice7_BeginScene(device);
+        ok(SUCCEEDED(hr), "Failed to begin scene, i=%d, hr %#x.\n", i, hr);
+        hr = IDirect3DDevice7_DrawPrimitive(device, D3DPT_TRIANGLESTRIP,
+                D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_SPECULAR, top_quad, 4, 0);
+        ok(SUCCEEDED(hr), "Failed to draw, i=%d, hr %#x.\n", i, hr);
+        hr = IDirect3DDevice7_DrawPrimitive(device, D3DPT_TRIANGLESTRIP,
+                D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_SPECULAR, bottom_quad_1, 4, 0);
+        ok(SUCCEEDED(hr), "Failed to draw, i=%d, hr %#x.\n", i, hr);
+        hr = IDirect3DDevice7_DrawPrimitive(device, D3DPT_TRIANGLESTRIP,
+                D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_SPECULAR, bottom_quad_2, 4, 0);
+        ok(SUCCEEDED(hr), "Failed to draw, i=%d, hr %#x.\n", i, hr);
+        hr = IDirect3DDevice7_EndScene(device);
+        ok(SUCCEEDED(hr), "Failed to end scene, i=%d, hr %#x.\n", i, hr);
+
+        /* Use 5% tolerance on the colors since there may be a gradient
+         * between left and right vertices. */
+        for (y = 120; y <= 360; y += 240)
+        {
+            color = get_surface_color(rt, 2, y);
+            ok(compare_color(color, test_data[i].color_left, 13),
+                    "fog fvm%i ftm%i y=%i: got left color %08x, expected %08x+-5%%.\n",
+                    test_data[i].vertexmode, test_data[i].tablemode, color, y,
+                    test_data[i].color_left);
+            color = get_surface_color(rt, 320, y);
+            expected_middle_color =
+                    y == 120 ? test_data[i].color_middle_top : test_data[i].color_middle_bottom;
+            ok(compare_color(color, expected_middle_color, 13),
+                    "fog fvm%i ftm%i y=%i: got middle color %08x, expected %08x+-5%%.\n",
+                    test_data[i].vertexmode, test_data[i].tablemode, y,
+                    color, expected_middle_color);
+            color = get_surface_color(rt, 638, y);
+            ok(compare_color(color, test_data[i].color_right, 13),
+                    "fog fvm%i ftm%i y=%i: got right color %08x, expected %08x+-5%%.\n",
+                    test_data[i].vertexmode, test_data[i].tablemode, y,
+                    color, test_data[i].color_right);
+        }
+    }
+
+    IDirectDrawSurface7_Release(rt);
+    refcount = IDirect3DDevice7_Release(device);
+    ok(!refcount, "Device has %u references left.\n", refcount);
+    DestroyWindow(window);
+}
+
 static void test_lighting_interface_versions(void)
 {
     IDirect3DDevice7 *device;
@@ -7671,6 +7831,7 @@ START_TEST(ddraw7)
     test_clear_rect_count();
     test_coop_level_versions();
     test_fog_special();
+    test_fog_negative_z();
     test_lighting_interface_versions();
     test_coop_level_activateapp();
     test_texturemanage();
diff --git a/dlls/wined3d/glsl_shader.c b/dlls/wined3d/glsl_shader.c
index 7379ba2..907e895 100644
--- a/dlls/wined3d/glsl_shader.c
+++ b/dlls/wined3d/glsl_shader.c
@@ -5005,7 +5005,7 @@ static GLhandleARB shader_glsl_generate_ffp_vertex_shader(struct wined3d_shader_
                 /* Need to undo the [0.0 - 1.0] -> [-1.0 - 1.0] transformation from D3D to GL coordinates. */
                 shader_addline(buffer, "gl_FogFragCoord = gl_Position.z * 0.5 + 0.5;\n");
             else
-                shader_addline(buffer, "gl_FogFragCoord = ec_pos.z;\n");
+                shader_addline(buffer, "gl_FogFragCoord = abs(ec_pos.z);\n");
             break;
 
         default:
-- 
1.8.4.5




More information about the wine-patches mailing list