Jump to content

Applying a dynamic depth bias to a shader possible?


Einheit-101

Recommended Posts

Hello Community!

I have literally zero hope of someone helping me since no one has a clue about how shaders work (including me), but i am using the open-source cswp water shader from Sam@ke and i want to enable zWriting in this shader to make it work together with dynamic lighting + SSAO. Now i get some issues with zFighting and because i am not fully stupid i added depthBias 0.0002 to the shader. This fixes the zFighting but it also causes extreme visual artifacts if the camera is further away from the water pixels. The solution would be to calculate a dynamic depthBias value that has a high value (0.0002 or something like that) if the pixel is close to the camera and a very low value (0.0000001 or something like that) if the pixel is far away from the camera. 
Since @Sam@ke and @Ren_712 seem to have abandoned MTA for now, i couldnt get help from them. The following stuff are screenshots and the full cswp water shader with my modifications. I would be glad if someone came up with an idea.

No depth bias = zFighting: https://i.imgur.com/BoAmqaB.png
High depth bias value = zFighting fixed https://i.imgur.com/IQYtl7S.png

But issues start happening with distance to the water pixels: https://i.imgur.com/sbBr1Zn.png
At even more distance issues are completely obvious: https://i.imgur.com/AkT337k.png

 

//--CSWP water shader by Sam@ke, its free to use but keep his name in mind
//-- Include some common stuff
#include "mta-helper.fx"

float Time : Time;
texture skyBoxTexture1;
texture skyBoxTexture2;
float3 skyRotate = float3(0, 0, 0);
float fadeValue = 0;
texture causticTexture;
texture reflectionTexture;
texture refractionTexture;
texture normalTexture;

float flowSpeed = 0.0;
float reflectScale = 0.0;
float refractScale = 0.0;
float reflectionStrength = 0.0;
float refractionStrength = 0.0;
float causticStrength = 0.0;
float4 waterColor = float4(0.0, 0.0, 0.0, 0.0);
float waterAlpha = 0.0;
float waterBrightness = 1.0;
float3 sunPos = float3(0, 0, 0);
float4 sunColor = float4(0.0, 0.0, 0.0, 0.0);
float specularSize = 4;
float waterShiningPower = 1;
float fogStart = 50;
float fogEnd = 550;

///////////////////
// SAMPLE STATES //
///////////////////

sampler CausticSampler = sampler_state
{
    Texture = <causticTexture>;
	MinFilter = Linear;
    MagFilter = Linear;
    MipFilter = Linear;
    AddressU = Wrap;
    AddressV = Wrap;
};

sampler ReflectionSampler = sampler_state
{
    Texture = <reflectionTexture>;
	MinFilter = Linear;
    MagFilter = Linear;
    MipFilter = Linear;
    AddressU = Mirror;
    AddressV = Mirror;
};

sampler RefractionSampler = sampler_state
{
    Texture = <refractionTexture>;
	MinFilter = Linear;
    MagFilter = Linear;
    MipFilter = Linear;
    AddressU = Mirror;
    AddressV = Mirror;
};

sampler NormalSampler = sampler_state
{
    Texture = <normalTexture>;
	MinFilter = Linear;
    MagFilter = Linear;
    MipFilter = Linear;
    AddressU = Wrap;
    AddressV = Wrap;
};

samplerCUBE SkyCubeSampler1 = sampler_state
{
    Texture = <skyBoxTexture1>;
	MinFilter = Linear;
    MagFilter = Linear;
    MipFilter = Linear;
	AddressU = Clamp;
    AddressV = Clamp;
};

samplerCUBE SkyCubeSampler2 = sampler_state
{
    Texture = <skyBoxTexture2>;
	MinFilter = Linear;
    MagFilter = Linear;
    MipFilter = Linear;
	AddressU = Clamp;
    AddressV = Clamp;
};

float3x3 eulRotate(float3 Rotate)
{
    float cosX,sinX;
    float cosY,sinY;
    float cosZ,sinZ;

    sincos(Rotate.x,sinX,cosX);
    sincos(-Rotate.y,sinY,cosY);
    sincos(Rotate.z,sinZ,cosZ);

//Euler extrinsic rotations 
//http://www.vectoralgebra.info/eulermatrix.html


		float3x3 rot = float3x3(
		cosY * cosZ + sinX * sinY * sinZ, -cosX * sinZ,  sinX * cosY * sinZ - sinY * cosZ,
		cosY * sinZ - sinX * sinY * cosZ,  cosX * cosZ, -sinY * sinZ - sinX * cosY * cosZ,
		cosX * sinY,                       sinX,         cosX * cosY
	);

return rot;	
}

struct VertexInputType
{
    float3 position : POSITION;
	float3 normal : NORMAL0;
    float2 textureCoords : TEXCOORD0;
};

struct PixelInputType
{
    float4 position : POSITION;
    float2 textureCoords : TEXCOORD0;
	float4 reflectionPosition : TEXCOORD1;
    float4 refractionPosition : TEXCOORD2;
	float3 skyTextureCoordinate : TEXCOORD3;
	float Depth : TEXCOORD4;
	float3 worldPosition : TEXCOORD5;
	float3 lightDirection : TEXCOORD6;
	float3 worldNormal : TEXCOORD7;
  
};

////////////////////////////////////////////////////////////////////////////////
// Vertex Shader
////////////////////////////////////////////////////////////////////////////////
PixelInputType WaterVertexShader(VertexInputType input)
{
    PixelInputType output;
    matrix reflectProjection;
    matrix refractProjection;

    // Calculate the position of the vertex against the world, view, and projection matrices.
    output.position = MTACalcScreenPosition(input.position);
	output.worldPosition = MTACalcWorldPosition(input.position);
	output.lightDirection = normalize(gCameraPosition - sunPos);
	output.worldNormal = MTACalcWorldNormal(input.normal);
    
    // Store the texture coordinates for the pixel shader.
    output.textureCoords = input.textureCoords;
	
	// Create the view projection world matrix for reflection.
    reflectProjection = mul(gWorldViewProjection, gWorld);
    reflectProjection = mul(gWorld, reflectProjection);
   
    // Calculate the input position against the refractProjection matrix.
    output.reflectionPosition = mul(float4(input.position, 1), reflectProjection);

    // Create the view projection world matrix for refraction.
    refractProjection = mul(gWorldViewProjection, gWorld);
    refractProjection = mul(gWorld, refractProjection);
   
    // Calculate the input position against the refractProjection matrix.
    output.refractionPosition = mul(float4(input.position, 1), refractProjection);
	
	float4 vertexPosition = mul(float4(input.position, 1), gWorld);
	
	// compute the eye vector 
    float4 eyeVector = vertexPosition - gViewInverse[3]; 			
    output.skyTextureCoordinate = mul(eulRotate(skyRotate), eyeVector.xyz);
	
	output.Depth = output.position.z;

    return output;
}



////////////////////////////////////////////////////////////////////////////////
// Pixel Shader
////////////////////////////////////////////////////////////////////////////////
float4 WaterPixelShader(PixelInputType input) : COLOR0
{
    float2 refractTexCoord;
	float2 reflectTexCoord;
	float3 skyBoxReflectTexCoord;
	float3 skyBoxTexCoord;
    float4 normalMap;
    float3 normal;
    float4 reflectionColor;
    float4 refractionColor;
	float4 causticColor;
	float4 skyColor;
    float4 color;
	float timer = (Time/12) * flowSpeed;

    // Move the position the water normal is sampled from to simulate moving water.	
	
    // Calculate the projected refraction texture coordinates.
	reflectTexCoord.x = input.reflectionPosition.x / input.reflectionPosition.w / 2.0f + 0.5f;
    reflectTexCoord.y = -input.reflectionPosition.y / input.reflectionPosition.w / 2.0f + 0.5f;
    refractTexCoord.x = input.refractionPosition.x / input.refractionPosition.w / 2.0f + 0.5f;
    refractTexCoord.y = -input.refractionPosition.y / input.refractionPosition.w / 2.0f + 0.5f;
	skyBoxReflectTexCoord.x = -input.skyTextureCoordinate.x / 1 / 2.0f + 0.5f;
	skyBoxReflectTexCoord.y = -input.skyTextureCoordinate.y / 1 / 2.0f + 0.5f;
	skyBoxReflectTexCoord.z = input.skyTextureCoordinate.z / 1 / 2.0f + 0.5f;
    // Sample the normal from the normal map texture.
	float2 NormalTex = input.textureCoords;
	NormalTex.y = NormalTex.y + timer;
    normalMap = tex2D(NormalSampler, NormalTex);

    // Expand the range of the normal from (0,1) to (-1,+1).
    normal = (normalMap.xyz * 2.0f) - 1.0f;

    // Re-position the texture coordinate sampling position by the normal map value to simulate the rippling wave effect.
	reflectTexCoord = reflectTexCoord + (normal.xy * reflectScale);
    refractTexCoord = refractTexCoord + (normal.xy * refractScale);
	skyBoxReflectTexCoord = skyBoxReflectTexCoord + (normal * reflectScale);
    // Sample the texture pixels from the textures using the updated texture coordinates.
	float2 CausticTex = input.textureCoords;
	CausticTex.y = CausticTex.y  + (sin(CausticTex.y * 10) * 0.01) + timer;
    causticColor = tex2D(CausticSampler, CausticTex);
	
	float4 skyColor1 = texCUBE(SkyCubeSampler1, 1 - skyBoxReflectTexCoord.yzx);
	float4 skyColor2 = texCUBE(SkyCubeSampler2, 1 - skyBoxReflectTexCoord.yzx);	
	float4 finalSkyColor = (skyColor2 * fadeValue) + (skyColor1 * (1 - fadeValue));
	
	reflectionColor = tex2D(ReflectionSampler, reflectTexCoord) * reflectionStrength;
	refractionColor = tex2D(RefractionSampler, refractTexCoord) * refractionStrength;

	// Using Blinn half angle modification for performance over correctness
    float3 lightRange = normalize(normalize(gCameraPosition - input.worldPosition) - input.lightDirection);
    float specularLight = pow(saturate(dot(lightRange, normal)), specularSize * 2);
	float4 specularColor = float4(sunColor.rgb * specularLight, 1);
	specularColor += pow(saturate(dot(lightRange, input.worldNormal)), specularSize / 2) / 2;
	specularColor *= normalMap.g * normalMap.g;
	
    // Combine the reflection and refraction results for the final color.
    color = lerp(reflectionColor * reflectionStrength, refractionColor * refractionStrength, 2.0f);
	color *= causticColor * causticStrength;
    color += finalSkyColor * waterColor * reflectionStrength;
	
	float distanceFog = saturate((input.Depth - fogStart)/(fogEnd - fogStart));
	float4 finalColor = lerp(float4(color.rgb, 1), float4(reflectionColor.rgb/2, 1), distanceFog);
	finalColor.rgb += specularColor.rgb * normalMap * waterShiningPower;
	finalColor.rgb *= waterBrightness;
	finalColor.a *= waterAlpha;
	
	return finalColor;
}

////////////////////////////////////////////////////////////////////////////////
// Technique
////////////////////////////////////////////////////////////////////////////////
technique WaterTechnique
{
    pass pass0
    {
		ZEnable = true;
		ZWriteEnable = true;
		ZFunc = 4;
		DepthBias = 0.0002;
		SlopeScaleDepthBias = 5;
		VertexShader = compile vs_3_0 WaterVertexShader();
        PixelShader = compile ps_3_0 WaterPixelShader();
    }
}

// Fallback
technique fallback
{
    pass P0
    {
        // Just draw normally
    }
}

 

Link to comment

I "solved" the problem by using no depth bias at all (lol) and i simply applied shader_soft_particles to all other effects that interfere with water (boatwake1, coronaringa). This solution isnt really perfect but it seems okay....
Ren712 seems to disable depthBias entirely and it looks like he uses a code that manipulates pixel depth in the pixel shader function, fading out the color output depending on his custom calculated depth values:

By looking at his soft_particles resource he has this as input:

texture gDepthBuffer : DEPTHBUFFER;

And this sampler where he "samples" the depth buffer (i still dont know what a sampler really is and what this does)

sampler SamplerDepth = sampler_state
{
    Texture     = (gDepthBuffer);
    AddressU    = Clamp;
    AddressV    = Clamp;
};

He then has this pixel shader function where he calls the function "FetchDepthBufferValue" and he fades the output depending on his own depth:

float4 PixelShaderFunctionDB(PSInput PS) : COLOR0
{
    float2 TexCoordProj = PS.TexCoordProj.xy / PS.TexCoordProj.z;
    TexCoordProj += float2( 0.0006, 0.0009 );
    float BufferValue = FetchDepthBufferValue( TexCoordProj );
    float depth = Linearize( BufferValue );
    float fade = saturate( ( depth - ( PS.DistFromCam + fDistAdd ) * fDistMult ) * fDepthSpread);
    float4 color = tex2D( Sampler0 ,PS.TexCoord);
    color *= PS.Diffuse;
    color.a *= fade;
    return color;
}

This is the FetchDepthBufferValue function, i have not even remotely a clue how this thing works but i guess it "returns" the depth of a given depth buffer coordinate,
i guess this "tex2D" thing "converts" the depth buffer into a "picture" that can be used to do some fancy math with it:

float FetchDepthBufferValue( float2 uv )
{
    float4 texel = tex2D(SamplerDepth, uv);
#if IS_DEPTHBUFFER_RAWZ
    float3 rawval = floor(255.0 * texel.arg + 0.5);
    float3 valueScaler = float3(0.996093809371817670572857294849, 0.0038909914428586627756752238080039, 1.5199185323666651467481343000015e-5);
    return dot(rawval, valueScaler / 255.0);
#else
    return texel.r;
#endif
}

 

Enabling zWrite in a water shader does actually solve many GTA issues, for example effects (bubbles...) are no longer drawn in front of the water and the game recognizes the water surface as something physical. But given all these zfighting issues, now i know why they decided to do so....

Edited by Einheit-101
Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...