wined3d: Implement vertex fog with pixel shaders

Fabian Bieler der.fabe at gmx.net
Thu Mar 15 18:09:45 CDT 2007


-------------- next part --------------
From ac554a8c77d48618196e231e7af964c9e874e222 Mon Sep 17 00:00:00 2001
From: Fabian Bieler <der.fabe at gmx.net>
Date: Thu, 15 Mar 2007 23:01:32 +0100
Subject: [PATCH] wined3d: Implement vertex fog with pixel shaders

---
 dlls/wined3d/arb_program_shader.c |   13 +++++++++++--
 dlls/wined3d/directx.c            |    5 +++--
 dlls/wined3d/drawprim.c           |    2 +-
 dlls/wined3d/glsl_shader.c        |   18 ++++++++++++++++++
 dlls/wined3d/pixelshader.c        |   32 ++++++++++++++++++++++++++++++--
 dlls/wined3d/state.c              |   27 ++++++++++++++++++++-------
 dlls/wined3d/vertexshader.c       |   13 -------------
 dlls/wined3d/wined3d_private.h    |    6 ++++--
 8 files changed, 87 insertions(+), 29 deletions(-)

diff --git a/dlls/wined3d/arb_program_shader.c b/dlls/wined3d/arb_program_shader.c
index 2e636ad..66d8864 100644
--- a/dlls/wined3d/arb_program_shader.c
+++ b/dlls/wined3d/arb_program_shader.c
@@ -120,6 +120,7 @@ void shader_arb_load_constants(
     }
 
     if (usePixelShader) {
+        float fogclamp[4];
 
         IWineD3DBaseShaderImpl* pshader = (IWineD3DBaseShaderImpl*) stateBlock->pixelShader;
 
@@ -137,6 +138,14 @@ void shader_arb_load_constants(
             float *data = (float *) &stateBlock->textureState[(int) psi->needsbumpmat][WINED3DTSS_BUMPENVMAT00];
             GL_EXTCALL(glProgramEnvParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, psi->bumpenvmatconst, data));
         }
+
+        /* Upload the parameters the which the fog coordinate is clamped [fogclamp.z, fogclamp.w].*/
+        if(stateBlock->renderState[WINED3DRS_FOGENABLE]) {
+            fogclamp[0]=0.0f;fogclamp[1]=0.0f;fogclamp[2]=0.0f;fogclamp[3]=1.0f;
+        } else {
+            fogclamp[0]=0.0f;fogclamp[1]=0.0f;fogclamp[2]=1.0f;fogclamp[3]=1.0f;
+        }
+        GL_EXTCALL(glProgramEnvParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, ARB_SHADER_PRIVCONST_FOG, fogclamp));
     }
 }
 
@@ -284,7 +293,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);
@@ -313,7 +322,7 @@ static void vshader_program_add_param(SHADER_OPCODE_ARG *arg, const DWORD param,
   IWineD3DVertexShaderImpl* This = (IWineD3DVertexShaderImpl*) arg->shader;
 
   /* oPos, oFog and oPts in D3D */
-  static const char * const hwrastout_reg_names[] = { "TMP_OUT", "TMP_FOG", "result.pointsize" };
+  static const char * const hwrastout_reg_names[] = { "TMP_OUT", "result.fogcoord", "result.pointsize" };
 
   DWORD reg = param & WINED3DSP_REGNUM_MASK;
   DWORD regtype = shader_get_regtype(param);
diff --git a/dlls/wined3d/directx.c b/dlls/wined3d/directx.c
index 7f890f5..996a604 100644
--- a/dlls/wined3d/directx.c
+++ b/dlls/wined3d/directx.c
@@ -300,9 +300,10 @@ static void select_shader_max_constants(
             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 we only have to reduce the constants by 1 for the fogclamp
+             * uniform.
              */
-            gl_info->max_pshader_constantsF = gl_info->ps_arb_constantsF;
+            gl_info->max_pshader_constantsF = gl_info->ps_arb_constantsF -1 ;
             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/glsl_shader.c b/dlls/wined3d/glsl_shader.c
index 5403ddf..e428a41 100644
--- a/dlls/wined3d/glsl_shader.c
+++ b/dlls/wined3d/glsl_shader.c
@@ -363,6 +363,21 @@ void shader_glsl_load_constants(
             checkGLcall("glUniform4fvARB");
         }
     }
+
+    /* Upload the parameters the which the fog coordinate is clamped in the pixel shader [fogclamp.z, fogclamp.w].
+     *
+     * pixel shader > 3.0 don't have this uniform, so don't upload it then.
+     */
+    pos = GL_EXTCALL(glGetUniformLocationARB(programId, "fogclamp"));
+    if( pos != -1 ) {
+        float fogclamp[4];
+        if(stateBlock->renderState[WINED3DRS_FOGENABLE]) {
+            fogclamp[0]=0.0f;fogclamp[1]=0.0f;fogclamp[2]=0.0f;fogclamp[3]=1.0f;
+        } else {
+            fogclamp[0]=0.0f;fogclamp[1]=0.0f;fogclamp[2]=1.0f;fogclamp[3]=1.0f;
+        }
+        GL_EXTCALL(glUniform4fvARB(pos, 1, fogclamp));
+    }
 }
 
 /** Generate the variable & register declarations for the GLSL output target */
@@ -403,6 +418,9 @@ void shader_generate_glsl_declarations(
     else if(reg_maps->bumpmat)
         shader_addline(buffer, "uniform vec4 bumpenvmat;\n");
 
+    if(pshader && This->baseShader.hex_version < WINED3DPS_VERSION(3,0))
+        shader_addline(buffer, "uniform vec4 fogclamp;\n");
+
     /* Declare texture samplers */ 
     for (i = 0; i < This->baseShader.limits.sampler; i++) {
         if (reg_maps->samplers[i]) {
diff --git a/dlls/wined3d/pixelshader.c b/dlls/wined3d/pixelshader.c
index dba2f34..a6994e4 100644
--- a/dlls/wined3d/pixelshader.c
+++ b/dlls/wined3d/pixelshader.c
@@ -381,6 +381,19 @@ inline static VOID IWineD3DPixelShaderImpl_GenerateShader(
             else
                 shader_addline(&buffer, "gl_FragColor = R0;\n");
         }
+
+        /* Pixel shader < 3.0 do not replace the fog stage
+         * This implements blending for vertex fog
+         * TODO: implement pixel/table fog
+         */
+        if(This->baseShader.hex_version < WINED3DPS_VERSION(3,0)) {
+            /* Some older cards like GeforceFX ones don't support multiple buffers, so also not gl_FragData */
+            if(GL_SUPPORT(ARB_DRAW_BUFFERS))
+                shader_addline(&buffer, "gl_FragData[0].xyz=mix(gl_Fog.color.xyz, gl_FragData[0].xyz, clamp(gl_FogFragCoord, fogclamp.z, fogclamp.w));\n");
+            else
+                shader_addline(&buffer, "gl_FragColor.xyz=mix(gl_Fog.color.xyz, gl_FragData.xyz, clamp(Fog, fogclamp.z, fogclamp.w));\n");
+        }
+
         shader_addline(&buffer, "}\n");
 
         TRACE("Compiling shader object %u\n", shader_obj);
@@ -407,11 +420,26 @@ inline static VOID IWineD3DPixelShaderImpl_GenerateShader(
         /* Base Declarations */
         shader_generate_arb_declarations( (IWineD3DBaseShader*) This, reg_maps, &buffer, &GLINFO_LOCATION);
 
+        /* We need a constant and two variable for fog blending */
+        shader_addline(&buffer, "PARAM fogClamp = program.env[%d];\n", ARB_SHADER_PRIVCONST_FOG);
+        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");
+        /* clamp fog coordinate and blend fog color */
+        shader_addline(&buffer, "MAX TMP_FOG.x, fragment.fogcoord.x, fogClamp.z;\n");
+        shader_addline(&buffer, "MIN TMP_FOG.x, TMP_FOG.x, fogClamp.w;\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
index 2a567b8..4efc3c7 100644
--- a/dlls/wined3d/state.c
+++ b/dlls/wined3d/state.c
@@ -726,16 +726,30 @@ 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
+     * If vertex shaders are enabled with either the fixed function rasterization pipeline
+     * or a pixel shader < 3.0, the fog stage of D3D expects the vertex shader to place the
+     * fog blending factor in the designated register where 1.0 means no fog and 0.0 mean
+     * full fog. The blending factor is clamped by the fog stage and applied, regardless of
+     * the fog mode. If a fogtablemode is specified, pixel fog accordingly to the set
+     * parameters (mode, start, end and density) is used additionally.
+     *
+     * If a pixel shader >= 3.0 is used, the programmable pipeline handles fog calculations
+     * completely.
      */
+
+    if( stateblock->renderState[WINED3DRS_FOGTABLEMODE] != WINED3DFOG_NONE
+            && use_ps(stateblock->wineD3DDevice)
+            && ((IWineD3DVertexShaderImpl *)stateblock->vertexShader)->baseShader.hex_version < WINED3DPS_VERSION(3,0)) {
+        FIXME("Implement table fog for pixel shader < 3.0\n");
+    }
+
     if (use_vs(stateblock->wineD3DDevice)
-            && ((IWineD3DVertexShaderImpl *)stateblock->vertexShader)->usesFog) {
+            && ((IWineD3DVertexShaderImpl *)stateblock->vertexShader)->usesFog
+            && !use_ps(stateblock->wineD3DDevice)) {
         glFogi(GL_FOG_MODE, GL_LINEAR);
         checkGLcall("glFogi(GL_FOG_MODE, GL_LINEAR)");
-        if (stateblock->renderState[WINED3DRS_FOGTABLEMODE] == WINED3DFOG_NONE) {
-            fogstart = 1.0;
-            fogend = 0.0;
-        }
+        fogstart = 1.0;
+        fogend = 0.0;
         context->last_was_foggy_shader = TRUE;
     }
     /* DX 7 sdk: "If both render states(vertex and table fog) are set to valid modes,
@@ -864,7 +878,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");
 }
diff --git a/dlls/wined3d/vertexshader.c b/dlls/wined3d/vertexshader.c
index 5d3a506..104890c 100644
--- a/dlls/wined3d/vertexshader.c
+++ b/dlls/wined3d/vertexshader.c
@@ -336,12 +336,6 @@ static VOID IWineD3DVertexShaderImpl_GenerateShader(
         if (This->baseShader.hex_version >= WINED3DVS_VERSION(3,0))
             vshader_glsl_output_unpack(&buffer, This->semantics_out);
 
-        /* Clamp the fog from 0 to 1 if it's used */
-        if (reg_maps->fog) {
-            This->usesFog = 1;
-            shader_addline(&buffer, "gl_FogFragCoord = clamp(gl_FogFragCoord, 0.0, 1.0);\n");
-        }
-        
         /* Write the final position.
          *
          * OpenGL coordinates specify the center of the pixel while d3d coords specify
@@ -382,17 +376,10 @@ static VOID IWineD3DVertexShaderImpl_GenerateShader(
         /* We need a constant to fixup the final position */
         shader_addline(&buffer, "PARAM posFixup = program.env[%d];\n", ARB_SHADER_PRIVCONST_POS);
 
-        if (reg_maps->fog) {
-            This->usesFog = 1;
-            shader_addline(&buffer, "TEMP TMP_FOG;\n");
-        }
 
         /* Base Shader Body */
         shader_generate_main( (IWineD3DBaseShader*) This, &buffer, reg_maps, pFunction);
 
-        /* Make sure the fog value is positive - values above 1.0 are ignored */
-        if (reg_maps->fog)
-            shader_addline(&buffer, "MAX result.fogcoord, TMP_FOG, 0.0;\n");
 
         /* Write the final position.
          *
diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
index fe2b8ad..390b967 100644
--- a/dlls/wined3d/wined3d_private.h
+++ b/dlls/wined3d/wined3d_private.h
@@ -1828,8 +1828,10 @@ inline static BOOL shader_is_comment(DWORD token) {
 /* Internally used shader constants. Applications can use constants 0 to GL_LIMITS(vshader_constantsF) - 1,
  * so upload them above that
  */
-#define ARB_SHADER_PRIVCONST_BASE GL_LIMITS(vshader_constantsF)
-#define ARB_SHADER_PRIVCONST_POS ARB_SHADER_PRIVCONST_BASE + 0
+#define ARB_VSHADER_PRIVCONST_BASE GL_LIMITS(vshader_constantsF)
+#define ARB_SHADER_PRIVCONST_POS ARB_VSHADER_PRIVCONST_BASE + 0
+#define ARB_PSHADER_PRIVCONST_BASE GL_LIMITS(pshader_constantsF)
+#define ARB_SHADER_PRIVCONST_FOG ARB_PSHADER_PRIVCONST_BASE + 0
 
 /*****************************************************************************
  * IDirect3DVertexShader implementation structure
-- 
1.4.4.1



More information about the wine-patches mailing list