wined3d: Implement linear fog with pixel shader.
This commit is contained in:
parent
67813450b9
commit
7cedd56d24
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue