Jump to content

shader raindrop from vehicle


Recommended Posts

Hello dear forum members, I am deeply interested in a persistent question regarding my shader for the effect of raindrops flowing down the sides of the car, as well as the effect of raindrops (spots) on the surface of the car (roof, bumper).

I’ve been struggling with these issues for a long time and still can’t figure out how to solve my two problems. I hope someone can help me and explain to a beginner in HLSL how to properly implement the shader.

  1. The first problem is that I don’t understand how to make the raindrops flow down the different sides of the car. Essentially, I have a single texture applied to the car, and I can't figure out how to split it into multiple segments and adapt it to flow around the sides of the car. (I also need to account for the unique features of each car model so that the flow looks realistic on each vehicle.)

  2. The second problem is the effect of tiny raindrops on the roof, hood, and bumper of the car (the generation of small droplets that gradually appear and disappear). The issue is that I’m trying to apply both effects in one shader, but the problem is that either only one of them works under certain settings, or nothing works at all. I can't get both effects to work harmoniously together.

I’ll also provide an example of the Lua script and the textures used for the shader.

I really hope for your help. I've been spending too much time on this, and I can't seem to find a solution.


 

float4x4 gWorld : WORLD;
float4x4 gView : VIEW;
float4x4 gProjection : PROJECTION;
float gTime : TIME;

// Rain parameters
float gDropSpeed = 0.03; // Speed of droplet flow
float2 noiseScale = float2(0.2, 0.8); // Noise texture scale
float3 lightDirection = float3(1.0, 1.0, -1.0); // Light direction
float gDropSpread = 0.3; // Droplet spread
float gDropDepth = 0.3; // Droplet depth

texture gTexture; // Main car texture
texture texRain; // Raindrop texture
texture texNormalRain; // Raindrop normals
texture texNoise; // Noise texture

// Variables for controlling the effect
float gAlpha = 0.01; // Alpha channel for the car
float rainAlpha = 1.8; // Alpha channel for raindrops

sampler2D gTextureSampler = sampler_state
{
    Texture = ( gTexture );
    MinFilter = Linear;
    MagFilter = Linear;
    AddressU = Wrap;
    AddressV = Wrap;
};

sampler2D texRainSampler = sampler_state
{
    Texture = ( texRain );
    MinFilter = Linear;
    MagFilter = Linear;
    AddressU = Wrap;
    AddressV = Wrap;
};

sampler2D texNormalRainSampler = sampler_state
{
    Texture = ( texNormalRain );
    MinFilter = Linear;
    MagFilter = Linear;
    AddressU = Wrap;
    AddressV = Wrap;
};

sampler2D texNoiseSampler = sampler_state
{
    Texture = ( texNoise );
    MinFilter = Linear;
    MagFilter = Linear;
    AddressU = Wrap;
    AddressV = Wrap;
};

struct VertexShaderInput
{
    float4 Position : POSITION; // Using standard POSITION semantics for the vertex shader
    float2 TextureCoordinate : TEXCOORD0; // Passing texture coordinates
};

struct PixelShaderInput
{
    float4 Position : POSITION; // Passing position as float4 for proper operation
    float3 WorldPos : TEXCOORD1; // Passing world coordinates for calculations
    float2 TextureCoordinate : TEXCOORD0; // Passing texture coordinates
};

PixelShaderInput VertexShaderFunction(VertexShaderInput input)
{
    PixelShaderInput output;
    
    // Position in world coordinates
    float4 worldPos = mul(input.Position, gWorld);
    
    // Storing position for further projection
    output.Position = mul(mul(input.Position, gWorld), gView); 
    output.Position = mul(output.Position, gProjection); // Applying projection
    
    // Storing world coordinates (for droplet flow)
    output.WorldPos = worldPos.xyz; 
    
    // Passing texture coordinates
    output.TextureCoordinate = input.TextureCoordinate;
    
    return output;
}

float4 PixelShaderFunction(PixelShaderInput output) : COLOR0
{
    float2 textureCoordinate = output.TextureCoordinate;

    // Main car texture
    float4 baseColor = tex2D(gTextureSampler, textureCoordinate);
    baseColor.rgb = max(baseColor.rgb, 0.1); // Brightening dark areas

    // Getting noise for random movement
    float noise = tex2D(texNoiseSampler, textureCoordinate * noiseScale).r;

    // World coordinates to determine droplet direction
    float3 worldPos = output.WorldPos; // Using world coordinates

    // Determining droplet direction based on normals and world position
    float3 normal = normalize(output.WorldPos); // Normal to the surface

    // Checking direction by normals to determine which side of the car we're on
    if (abs(normal.x) > abs(normal.y) && abs(normal.x) > abs(normal.z)) {
        // Side surfaces
        textureCoordinate.y += gDropSpeed * gTime; // Flowing down the sides
    } else if (normal.y > 0.5) {
        // Top surface (roof)
        textureCoordinate.y -= gDropSpeed * gTime; // Droplets flow down
    } else {
        // Other sides (front and back)
        textureCoordinate.x -= gDropSpeed * gTime; // Optionally add droplet flow forward/backward
    }
    
    // Reducing random offset for more precise flow
    float2 offset = gDropSpread * (noise * 4.0 - 0.5) * float2(1.0, 2.5); // Using float2 for offset
    textureCoordinate -= offset; // Applying deviation in both axes

    // Raindrop texture
    float4 rainColor = tex2D(texRainSampler, textureCoordinate);

    // Adjusting droplets to the car color
    rainColor.rgb = baseColor.rgb * 0.8 + float3(0.1, 0.1, 0.1); // Adding body tint to droplets

    rainColor.a = rainAlpha; // Alpha channel of droplets

    // Normals for droplets
    float4 tempRainNormal = tex2D(texNormalRainSampler, textureCoordinate); // Explicitly using float4
    float3 rainNormal = normalize(tempRainNormal.rgb * 2.5 - 0.75); // Normalizing normals

    // Lighting on droplets
    float diffuse = saturate(dot(normalize(rainNormal), normalize(lightDirection))); // Correct lighting

    // Combining colors
    float4 finalColor = lerp(baseColor, rainColor, rainAlpha * diffuse);
    finalColor.a = lerp(gAlpha, rainAlpha, diffuse * 0.9);

    // Adding droplet depth
    finalColor.rgb += gDropDepth * 0.9; // Depth effect of droplets

    float3 v = float3(0.0, 0.0, 0.0);
    float Tr2 = gTime * 11000.0;
    float2 n = tex2D(texNoiseSampler, textureCoordinate * 0.5).rg;

    for (float r = 4.0; r > 0.0; r--)  // Reducing the number of iterations
    {
        float2 x = 2100.0 * r * 0.015;
        float2 p = 6.28 * textureCoordinate * x + (n - 0.5) * 2.0;
        float4 d = tex2D(texNoiseSampler, round(textureCoordinate * x - 0.25) / x);
        float t = (sin(p.x) + sin(p.y)) * max(0.0, 1.0 - frac(Tr2 * (d.b + 0.1) + d.g) * 2.0);

        if (d.r < (2.0 - r) * 0.08 && t > 0.5)
        {
            v += normalize(float3(-cos(p.x), lerp(1.2, 2.0, t - 0.5), 0.0));
        }
    }

    finalColor.rgb += v * rainAlpha;

    return finalColor;
}

technique Replace
{
    pass P0
    {
        DepthBias = -0.0001;
        AlphaBlendEnable = TRUE;
        SrcBlend = SRCALPHA;
        DestBlend = INVSRCALPHA;
        VertexShader = compile vs_1_1 VertexShaderFunction();
        PixelShader = compile ps_3_0 PixelShaderFunction();
    }
}


LUA CODE:

 

local shader = dxCreateShader("fx/shader.fx", 2, 300, true, "vehicle");


local zapylyayushiesyaDetali = {
	"remap_body",
};


local rainTexture = dxCreateTexture("assets/RainCar.png")
local normalRainTexture = dxCreateTexture("assets/RainNormal.png")
local noiseTexture = dxCreateTexture("assets/enbnoise.png")


dxSetShaderValue(shader, "texRain", rainTexture)
dxSetShaderValue(shader, "texNormalRain", normalRainTexture)
dxSetShaderValue(shader, "texNoise", noiseTexture)


-- Применение шейдера к машине
local vehicle = getPedOccupiedVehicle(localPlayer)
if vehicle then
    --engineApplyShaderToWorldTexture(shader, "vehiclegrunge256", vehicle)
    for _, name in ipairs(zapylyayushiesyaDetali) do
        engineRemoveShaderFromWorldTexture(shader, name, localPlayer.vehicle)
        engineApplyShaderToWorldTexture(shader, name, localPlayer.vehicle)
    end
end


renderTarget = dxCreateRenderTarget(512, 512, true)
addEventHandler("onClientRender", root, function()
    iprint(shader)
    if shader then
        if renderTarget then
            
            dxSetRenderTarget(renderTarget, true) 
            dxDrawImage(0, 0, 512, 512, shader) 
            dxSetRenderTarget() 
            
            
            dxDrawImage(50, 50, 256, 256, renderTarget)
        end
    end
end)

 

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...