[1/7] wined3d: Implement linear fog with pixel shader

Fabian Bieler der.fabe at gmx.net
Thu Mar 22 12:19:06 CDT 2007


This patch implements linear fog in combination with pixel shaders.
In particular, this patch implements linear fog (vertex and table) with pixel 
shaders.
-------------- next part --------------
From 9abb31edc297d9c4d75fbb3f21508a3cbc243026 Mon Sep 17 00:00:00 2001
From: Fabian Bieler <der.fabe at gmx.net>
Date: Thu, 22 Mar 2007 17:32:48 +0100
Subject: [PATCH] wined3d: Implement linear fog with pixel shader

---
 dlls/wined3d/arb_program_shader.c |    2 +-
 dlls/wined3d/directx.c            |    8 ++--
 dlls/wined3d/drawprim.c           |    2 +-
 dlls/wined3d/pixelshader.c        |   36 ++++++++++++-
 dlls/wined3d/state.c              |  101 +++++++++++++++++++++++++++++++++++--
 5 files changed, 136 insertions(+), 13 deletions(-)

diff --git a/dlls/wined3d/arb_program_shader.c b/dlls/wined3d/arb_program_shader.c
index f2c58ff..d24a5f9 100644
--- a/dlls/wined3d/arb_program_shader.c
+++ b/dlls/wined3d/arb_program_shader.c
@@ -284,7 +284,7 @@ static void pshader_get_register_name(
     break;
     case WINED3DSPR_COLOROUT:
         if (reg == 0)
-            sprintf(regstr, "result.color");
+            sprintf(regstr, "TMP_COLOR");
         else {
             /* TODO: See GL_ARB_draw_buffers */
             FIXME("Unsupported write to render target %u\n", reg);
diff --git a/dlls/wined3d/directx.c b/dlls/wined3d/directx.c
index 915d938..e0dbe57 100644
--- a/dlls/wined3d/directx.c
+++ b/dlls/wined3d/directx.c
@@ -291,18 +291,18 @@ static void select_shader_max_constants(
 
     switch (ps_selected_mode) {
         case SHADER_GLSL:
-            /* Subtract the other potential uniforms from the max available (bools & ints).
+            /* Subtract the other potential uniforms from the max available (bools & ints), and 2 states for fog.
              * In theory the texbem instruction may need one more shader constant too. But lets assume
              * that a sm <= 1.3 shader does not need all the uniforms provided by a glsl-capable card,
              * and lets not take away a uniform needlessly from all other shaders.
              */
-            gl_info->max_pshader_constantsF = gl_info->ps_glsl_constantsF - (MAX_CONST_B / 4) - MAX_CONST_I;
+            gl_info->max_pshader_constantsF = gl_info->ps_glsl_constantsF - (MAX_CONST_B / 4) - MAX_CONST_I - 2;
             break;
         case SHADER_ARB:
             /* The arb shader only loads the bump mapping environment matrix into the shader if it finds
-             * a free constant to do that, so no need to reduce the number of available constants.
+             * a free constant to do that, so only reduce the number of available constants by 2 for the fog states.
              */
-            gl_info->max_pshader_constantsF = gl_info->ps_arb_constantsF;
+            gl_info->max_pshader_constantsF = gl_info->ps_arb_constantsF - 2;
             break;
         default:
             gl_info->max_pshader_constantsF = 0;
diff --git a/dlls/wined3d/drawprim.c b/dlls/wined3d/drawprim.c
index ec45543..089030b 100644
--- a/dlls/wined3d/drawprim.c
+++ b/dlls/wined3d/drawprim.c
@@ -682,7 +682,7 @@ static void drawStridedSlow(IWineD3DDevice *iface, WineDirect3DVertexStridedData
             specularColor = ptrToCoords[0];
             VTRACE(("specularColor=%lx\n", specularColor));
 
-            /* special case where the fog density is stored in the diffuse alpha channel */
+            /* special case where the fog density is stored in the specular alpha channel */
             if(This->stateBlock->renderState[WINED3DRS_FOGENABLE] &&
               (This->stateBlock->renderState[WINED3DRS_FOGVERTEXMODE] == WINED3DFOG_NONE || sd->u.s.position.dwType == WINED3DDECLTYPE_FLOAT4 )&&
               This->stateBlock->renderState[WINED3DRS_FOGTABLEMODE] == WINED3DFOG_NONE) {
diff --git a/dlls/wined3d/pixelshader.c b/dlls/wined3d/pixelshader.c
index e9a2bc5..77690c0 100644
--- a/dlls/wined3d/pixelshader.c
+++ b/dlls/wined3d/pixelshader.c
@@ -381,6 +381,21 @@ static inline VOID IWineD3DPixelShaderImpl_GenerateShader(
             else
                 shader_addline(&buffer, "gl_FragColor = R0;\n");
         }
+
+        /* Pixel shader < 3.0 do not replace the fog stage.
+         * This implements linear fog computation and blending.
+         * TODO: non linear fog
+         * NOTE: gl_Fog.start and gl_Fog.end don't hold fog start s and end e but
+         * -1/(e-s) and e/(e-s) respectively.
+         */
+        if(This->baseShader.hex_version < WINED3DPS_VERSION(3,0)) {
+            shader_addline(&buffer, "float Fog = clamp(gl_FogFragCoord * gl_Fog.start + gl_Fog.end, 0.0, 1.0);\n");
+            if(GL_SUPPORT(ARB_DRAW_BUFFERS))
+                shader_addline(&buffer, "gl_FragData[0].xyz = mix(gl_Fog.color.xyz, gl_FragData[0].xyz, Fog);\n");
+            else
+                shader_addline(&buffer, "gl_FragColor.xyz = mix(gl_Fog.color.xyz, gl_FragColor.xyz, Fog);\n");;
+        }
+
         shader_addline(&buffer, "}\n");
 
         TRACE("Compiling shader object %u\n", shader_obj);
@@ -407,11 +422,28 @@ static inline VOID IWineD3DPixelShaderImpl_GenerateShader(
         /* Base Declarations */
         shader_generate_arb_declarations( (IWineD3DBaseShader*) This, reg_maps, &buffer, &GLINFO_LOCATION);
 
+        /* We need two variables for fog blending */
+        shader_addline(&buffer, "TEMP TMP_FOG;\n");
+        if (This->baseShader.hex_version >= WINED3DPS_VERSION(2,0)) {
+            shader_addline(&buffer, "TEMP TMP_COLOR;\n");
+        }
+
         /* Base Shader Body */
         shader_generate_main( (IWineD3DBaseShader*) This, &buffer, reg_maps, pFunction);
 
-        if (This->baseShader.hex_version < WINED3DPS_VERSION(2,0))
-            shader_addline(&buffer, "MOV result.color, R0;\n");
+        /* calculate fog and blend it
+         * NOTE: state.fog.params.y and state.fog.params.z don't hold fog start s and end e but
+         * -1/(e-s) and e/(e-s) respectively.
+         */
+        shader_addline(&buffer, "MAD_SAT TMP_FOG, fragment.fogcoord, state.fog.params.y, state.fog.params.z;\n");
+        if (This->baseShader.hex_version < WINED3DPS_VERSION(2,0)) {
+            shader_addline(&buffer, "LRP result.color.rgb, TMP_FOG.x, R0, state.fog.color;\n");
+            shader_addline(&buffer, "MOV result.color.a, R0.a;\n");
+        } else {
+            shader_addline(&buffer, "LRP result.color.rgb, TMP_FOG.x, TMP_COLOR, state.fog.color;\n");
+            shader_addline(&buffer, "MOV result.color.a, TMP_COLOR.a;\n");
+        }
+
         shader_addline(&buffer, "END\n"); 
 
         /* TODO: change to resource.glObjectHandle or something like that */
diff --git a/dlls/wined3d/state.c b/dlls/wined3d/state.c
old mode 100644
new mode 100755
index aa9d0ab..bba49ee
--- a/dlls/wined3d/state.c
+++ b/dlls/wined3d/state.c
@@ -689,6 +689,8 @@ static void state_stencilwrite(DWORD state, IWineD3DStateBlockImpl *stateblock,
 static void state_fog(DWORD state, IWineD3DStateBlockImpl *stateblock, WineD3DContext *context) {
     /* TODO: Put this into the vertex type block once that is in the state table */
     BOOL fogenable = stateblock->renderState[WINED3DRS_FOGENABLE];
+    BOOL is_ps3 = use_ps(stateblock->wineD3DDevice)
+                  && ((IWineD3DPixelShaderImpl *)stateblock->pixelShader)->baseShader.hex_version >= WINED3DPS_VERSION(3,0);
     float fogstart, fogend;
 
     union {
@@ -700,6 +702,17 @@ static void state_fog(DWORD state, IWineD3DStateBlockImpl *stateblock, WineD3DCo
         /* No fog? Disable it, and we're done :-) */
         glDisable(GL_FOG);
         checkGLcall("glDisable GL_FOG");
+        if( use_ps(stateblock->wineD3DDevice)
+                && ((IWineD3DPixelShaderImpl *)stateblock->pixelShader)->baseShader.hex_version < WINED3DPS_VERSION(3,0) ) {
+            /* disable fog in the pixel shader
+             * NOTE: For pixel shader, GL_FOG_START and GL_FOG_END don't hold fog start s and end e but
+             * -1/(e-s) and e/(e-s) respectively.
+             */
+            glFogf(GL_FOG_START, 0.0f);
+            checkGLcall("glFogf(GL_FOG_START, fogstart");
+            glFogf(GL_FOG_END, 1.0f);
+            checkGLcall("glFogf(GL_FOG_END, fogend");
+        }
         return;
     }
 
@@ -726,18 +739,80 @@ static void state_fog(DWORD state, IWineD3DStateBlockImpl *stateblock, WineD3DCo
      * FOGTABLEMODE == NONE, FOGVERTEXMODE == NONE, untransformed:
      *  Linear fog with start = 255.0, end = 0.0, input comes from the specular color
      *
-     * Vertex shaders work in a simmilar way, but need more testing
+     *
+     * Rules for vertex fog with shaders:
+     *
+     * When mixing fixed function functionality with the programmable pipeline, D3D expects
+     * the fog computation to happen during transformation while openGL expects it to happen
+     * during rasterization. Also, prior to pixel shader 3.0 D3D handles fog blending after
+     * the pixel shader while openGL always expects the pixel shader to handle the blending.
+     * To solve this problem, WineD3D does:
+     * 1) implement a linear fog equation and fog blending at the end of every pre 3.0 pixel
+     * shader,
+     * and 2) disables the fog computation (in either the fixed function or programmable
+     * rasterizer) if using a vertex program.
+     *
+     *
+     * If a fogtablemode and a fogvertexmode are specified, table fog is applied (with or
+     * without shaders).
      */
+
+    if( is_ps3 ) {
+        if( !use_vs(stateblock->wineD3DDevice)
+                && stateblock->renderState[WINED3DRS_FOGTABLEMODE] == WINED3DFOG_NONE ) {
+            FIXME("Implement vertex fog for pixel shader >= 3.0 and fixed function pipeline\n");
+        }
+    }
+
     if (use_vs(stateblock->wineD3DDevice)
             && ((IWineD3DVertexShaderImpl *)stateblock->vertexShader)->usesFog) {
-        glFogi(GL_FOG_MODE, GL_LINEAR);
-        checkGLcall("glFogi(GL_FOG_MODE, GL_LINEAR)");
-        if (stateblock->renderState[WINED3DRS_FOGTABLEMODE] == WINED3DFOG_NONE) {
+        if( stateblock->renderState[WINED3DRS_FOGTABLEMODE] != WINED3DFOG_NONE ) {
+            if(!is_ps3) FIXME("Implement table fog for foggy vertex shader\n");
+            /* Disable fog */
+            fogenable = FALSE;
+        } else {
+            /* Set fog computation in the rasterizer to pass through the value (just blend it) */
+            glFogi(GL_FOG_MODE, GL_LINEAR);
+            checkGLcall("glFogi(GL_FOG_MODE, GL_LINEAR)");
             fogstart = 1.0;
             fogend = 0.0;
         }
         context->last_was_foggy_shader = TRUE;
     }
+    else if( use_ps(stateblock->wineD3DDevice) ) {
+        /* NOTE: For pixel shader, GL_FOG_START and GL_FOG_END don't hold fog start s and end e but
+         * -1/(e-s) and e/(e-s) respectively to simplify fog computation in the shader.
+         */
+        WINED3DFOGMODE mode;
+        context->last_was_foggy_shader = FALSE;
+
+        /* If both fogmodes are set use the table fog mode */
+        if(stateblock->renderState[WINED3DRS_FOGTABLEMODE] == WINED3DFOG_NONE)
+            mode = stateblock->renderState[WINED3DRS_FOGVERTEXMODE];
+        else
+            mode = stateblock->renderState[WINED3DRS_FOGTABLEMODE];
+
+        switch (mode) {
+            case WINED3DFOG_EXP:
+            case WINED3DFOG_EXP2:
+                if(!is_ps3) FIXME("Implement non linear fog for pixel shader < 3.0\n");
+                /* Disable fog */
+                fogenable = FALSE;
+                break;
+
+            case WINED3DFOG_LINEAR:
+                fogstart = -1.0f/(fogend-fogstart);
+                fogend *= -fogstart;
+                break;
+
+            case WINED3DFOG_NONE:
+                if(!is_ps3) FIXME("Implement software vertex fog for pixel shader < 3.0\n");
+                /* Disable fog */
+                fogenable = FALSE;
+                break;
+            default: FIXME("Unexpected WINED3DRS_FOGVERTEXMODE %d\n", stateblock->renderState[WINED3DRS_FOGVERTEXMODE]);
+        }
+    }
     /* DX 7 sdk: "If both render states(vertex and table fog) are set to valid modes,
      * the system will apply only pixel(=table) fog effects."
      */
@@ -854,6 +929,16 @@ static void state_fog(DWORD state, IWineD3DStateBlockImpl *stateblock, WineD3DCo
     } else {
         glDisable(GL_FOG);
         checkGLcall("glDisable GL_FOG");
+        if( use_ps(stateblock->wineD3DDevice) ) {
+            /* disable fog in the pixel shader
+             * NOTE: For pixel shader, GL_FOG_START and GL_FOG_END don't hold fog start s and end e but
+             * -1/(e-s) and e/(e-s) respectively.
+             */
+            glFogf(GL_FOG_START, 0.0f);
+            checkGLcall("glFogf(GL_FOG_START, fogstart");
+            glFogf(GL_FOG_END, 1.0f);
+            checkGLcall("glFogf(GL_FOG_END, fogend");
+        }
     }
 
     if (GL_SUPPORT(NV_FOG_DISTANCE)) {
@@ -864,7 +949,6 @@ static void state_fog(DWORD state, IWineD3DStateBlockImpl *stateblock, WineD3DCo
 static void state_fogcolor(DWORD state, IWineD3DStateBlockImpl *stateblock, WineD3DContext *context) {
     float col[4];
     D3DCOLORTOGLFLOAT4(stateblock->renderState[WINED3DRS_FOGCOLOR], col);
-    /* Set the default alpha blend color */
     glFogfv(GL_FOG_COLOR, &col[0]);
     checkGLcall("glFog GL_FOG_COLOR");
 }
@@ -2001,6 +2085,7 @@ static void pixelshader(DWORD state, IWineD3DStateBlockImpl *stateblock, WineD3D
     IWineD3DDeviceImpl *device = stateblock->wineD3DDevice;
     BOOL use_pshader = use_ps(device);
     BOOL use_vshader = use_vs(device);
+    BOOL update_fog = FALSE;
     int i;
 
     if (use_pshader) {
@@ -2014,6 +2099,7 @@ static void pixelshader(DWORD state, IWineD3DStateBlockImpl *stateblock, WineD3D
                     sampler(STATE_SAMPLER(i), stateblock, context);
                 }
             }
+            update_fog = TRUE;
         } else {
            /* Otherwise all samplers were activated by the code above in earlier draws, or by sampler()
             * if a different texture was bound. I don't have to do anything.
@@ -2031,6 +2117,8 @@ static void pixelshader(DWORD state, IWineD3DStateBlockImpl *stateblock, WineD3D
                 tex_colorop(STATE_TEXTURESTAGE(i, WINED3DTSS_COLOROP), stateblock, context);
             }
         }
+        if(context->last_was_pshader)
+            update_fog = TRUE;
     }
 
     if(!isStateDirty(context, StateTable[STATE_VSHADER].representative)) {
@@ -2041,6 +2129,9 @@ static void pixelshader(DWORD state, IWineD3DStateBlockImpl *stateblock, WineD3D
         }
     }
 
+    if(update_fog)
+        state_fog(state, stateblock, context);
+
     context->last_was_pshader = use_pshader;
 }
 
-- 
1.4.4.1



More information about the wine-patches mailing list