0
\$\begingroup\$

I have a simple shader, converted from GLSL to HLSL. This is the original: https://www.shadertoy.com/view/tdKSWG

The problem is that there is a very visible diagonal line artifact going from UV(1,1) to UV(0,0) and this artifact is not visible in the GLSL original.

Ripples pinched along the diagonal line Speckled line along diagonal, "echo" ripples off right edge of grey area

No artifacts visible when ripples are started in any other screen position:

Ripple not near the edge or diagonal, undistorted, with no speckles or echoes

The sampler AddressU is set to "Wrap" and in shadertoy it is set to "Repeat", it should do the same but the waves still get reflected at the screen border (It works fine in GLSL)...

Does anyone know how to fix this?

This is my HLSL version:

//-- Include some common stuff
#include "mta-helper.fx"

float STRENGTH = 5.0;
float MODIFIER = 0.91;
float STEP = 2.6;
float FADESTRENGTH = 20;
float2 iResolution = {1024,512};
float3 iMouse = {0,0,0};
texture buffer;

sampler myself = sampler_state
{
    Texture = (buffer);
    MinFilter = Linear;
    MagFilter = Linear;
    MipFilter = Linear;
    AddressU = Wrap;
    AddressV = Mirror;
};

//------------------------------------------------------------------------------------------
// Structure of data sent to the vertex shader
//------------------------------------------------------------------------------------------
struct VSInput
{
    float4 Position : POSITION0;
    float4 Diffuse : COLOR0;
    float2 TexCoord : TEXCOORD0;
};

//------------------------------------------------------------------------------------------
// Structure of data sent to the pixel shader ( from the vertex shader )
//------------------------------------------------------------------------------------------
struct PSInput
{
    float4 Position : POSITION0;
    float4 Diffuse : COLOR0;
    float2 TexCoord: TEXCOORD0;
};

//------------------------------------------------------------------------------------------
// VertexShaderFunction
//------------------------------------------------------------------------------------------
PSInput VertexShaderFunction(VSInput VS)
{
    PSInput PS = (PSInput)0;

    // Calculate screen pos of vertex
    PS.Position = MTACalcScreenPosition(VS.Position);

    // Pass through color and tex coord
    PS.TexCoord = VS.TexCoord;
    //PS.TexCoord.y -= 0.5;
    PS.Diffuse = VS.Diffuse;
    return PS;
}

float4 psFunction(PSInput PS) : COLOR0
{

    float4 color = tex2D(myself, PS.TexCoord);
    float value = 0;

    float3 direction = float3(STEP, STEP, 0) / iResolution.xyy;

    float s1 = tex2D(myself, PS.TexCoord - direction.zy).r;
    float s2 = tex2D(myself, PS.TexCoord - direction.xz).r;
    float s3 = tex2D(myself, PS.TexCoord + direction.xz).r;
    float s4 = tex2D(myself, PS.TexCoord + direction.zy).r;

    if (length(PS.TexCoord - iMouse.xy) < 0.005 && iMouse.z == 1) {
        value = STRENGTH * smoothstep(3, 0.5, length(iMouse.xy - PS.TexCoord));
    }

    value += -(color.g - 0.5) * 2 + (s1 + s2 + s3 + s4 - 2);
    value *= MODIFIER;
    value = value * 0.5 + 0.5;

    float alphaFade = saturate(FADESTRENGTH * PS.TexCoord.y) * saturate(FADESTRENGTH * (1 - PS.TexCoord.y));
    return float4(value, color.r, value, alphaFade);
}

technique shield
{
    pass P0
    {
        VertexShader = compile vs_3_0 VertexShaderFunction();
        PixelShader  = compile ps_3_0 psFunction();
    }
}
\$\endgroup\$
7
  • \$\begingroup\$ Are you writing to the same texture you're reading, or ping-ponging between two buffers? Is it safe to assume you're rendering this as a quad made of two triangles, where the triangle seam runs along the diagonal line where you see the artifacts? \$\endgroup\$
    – DMGregory
    Nov 27 '19 at 22:08
  • \$\begingroup\$ I am drawing to a render target that is 800 pixels in width and 450 pixels in height. But it doesnt matter if i draw that quad or if i draw the shader directly to the screen. Its not made out of Triangles. And that rendertarget is also read. \$\endgroup\$ Nov 28 '19 at 13:05
  • \$\begingroup\$ ... I investigated a bit more but i cant find a single issue, everything seems to be absolutely correct but the HLSL shader thinks that this diagonal line is a screen border. \$\endgroup\$ Nov 28 '19 at 14:27
  • \$\begingroup\$ Can we see the host code? \$\endgroup\$ Nov 28 '19 at 15:55
  • \$\begingroup\$ Do you mean the script that controls the shader? It is a very, very simple Lua script. pastebin.com/Wk6YZc88 \$\endgroup\$ Nov 28 '19 at 17:53
1
\$\begingroup\$

This guy had the same problem, he needed to ping-pong between 2 textures for the alternating of read/write during rendering. I did that now, AND IT WORKS. Its impossible to use one and the same renderTarget in one and the same frame for reading AND writing, you have to use 2 renderTargets. One for reading and one for writing. And in the next frame you have to swap them.

https://community.khronos.org/t/wave-ripple-fragment-shader-working-but-with-strange-side-effects/76194/3

Here is the full code with my (new) ripples effect that is actually working fine:

--Lua script code
local w, h = guiGetScreenSize()
local shaders = {}
local bufferIndex = 0
local qualityX = 1000 -- Quality of render target
local qualityY = 500

function createShader()
local shadergroup = {dxCreateShader("buffer.fx", 0, 100, false, "object"), dxCreateRenderTarget(qualityX, qualityY), dxCreateRenderTarget(qualityX, qualityY)}
if shadergroup[1] and shadergroup[2] and shadergroup[3] then
    dxSetShaderValue(shadergroup[1], "texelSize", {1/qualityX, 1/qualityY, qualityX, qualityY})
    table.insert(shaders, shadergroup)
else
    if isElement(shadergroup[1]) then destroyElement(shadergroup[1]) end
    if isElement(shadergroup[2]) then destroyElement(shadergroup[2]) end
    if isElement(shadergroup[3]) then destroyElement(shadergroup[3]) end
end
end
addEventHandler("onClientResourceStart", resourceRoot, createShader)

function update()
local mouseX, mouseY, click = 0, 0, 0
if getKeyState("mouse1") and mouseX then
    click, mouseX, mouseY = 1, getCursorPosition()
end
for i, shadergroup in pairs(shaders) do
    if shadergroup[1] and shadergroup[2] and shadergroup[3] then

    --set renderTarget to A for writing, give shader renderTarget B for reading
    --next frame set renderTarget to B for writing, give shader renderTarget A for reading

        bufferIndex = (bufferIndex + 1) % 2 -- this swaps between 0 and 1 each frame

        local readTarget = shadergroup[2]
        local writeTarget = shadergroup[3]
        if bufferIndex == 0 then
            readTarget = shadergroup[3]
            writeTarget = shadergroup[2]
        end

        dxSetShaderValue(shadergroup[1], "iMouse", {mouseX * qualityX, mouseY * qualityY, click}) -- pass current mouse state
        dxSetShaderValue(shadergroup[1], "buffer", readTarget) -- pass current render target for reading color to the shader

        dxSetRenderTarget(writeTarget) -- set the new writing render target
        dxDrawImage(0, 0, qualityX, qualityY, shadergroup[1]) -- draw the shader to the render target
        dxSetRenderTarget()

        dxDrawImage(0, 0, 800, 400, writeTarget) -- draw final image

    end
end
end
addEventHandler("onClientRender", root, update) -- execute code each frame

//Shader code, PS 2.0 required
static const float FADE_SPEED = 0.99; // how fast should the ripples fade out
static const float MOUSE_RADIUS = 5.0; // how large is the ripple that a mouse click creates
static const int BORDER_FADE_STRENGTH = 20; // strength of fading-out at the screen border

float4 texelSize = {0.001, 0.002, 1000, 500}; // texelSize = 1/width, 1/height, width, height
float3 iMouse = {0,0,0}; // x, y mouse position in pixels, z = 1 -> click
texture buffer;

float4x4 gWorld : WORLD;
float4x4 gView : VIEW;
float4x4 gProjection : PROJECTION;

sampler readTarget = sampler_state
{
    Texture = (buffer);
    AddressU = Wrap; // Wrap = ripples will come out of the other screen side
    AddressV = Mirror; // Mirror = ripples will bounce off the screen border
};

//------------------------------------------------------------------------------------------
// Structure of data sent to the vertex shader
//------------------------------------------------------------------------------------------
struct VSInput
{
    float4 Position : POSITION0;
    float2 TexCoord : TEXCOORD0;
};

//------------------------------------------------------------------------------------------
// Structure of data sent to the pixel shader ( from the vertex shader )
//------------------------------------------------------------------------------------------
struct PSInput
{
    float4 Position : POSITION0;
    float2 TexCoord: TEXCOORD0;
};

//------------------------------------------------------------------------------------------
// VertexShaderFunction
//------------------------------------------------------------------------------------------
PSInput VertexShaderFunction(VSInput VS)
{
    PSInput PS = (PSInput)0;

    // Calculate screen pos of vertex
    float4 posWorld = mul(VS.Position, gWorld);
    float4 posWorldView = mul(posWorld, gView);
    PS.Position = mul(posWorldView, gProjection);

    // Pass through color and TexCoord
    PS.TexCoord = VS.TexCoord; // PS.TexCoord = texture coordinates in a range of 0-1
    return PS;
}

float3 fetch(float2 coord, float2 offset){
    return tex2D(readTarget, coord + offset).rgb;
}

float4 psFunction(PSInput PS) : COLOR0
{
    float weight = (fetch(PS.TexCoord, float2(0, texelSize.y)).r // expand the ripple in all 4 directions
            + fetch(PS.TexCoord, float2(0, -texelSize.y)).r
            + fetch(PS.TexCoord, float2(texelSize.x, 0)).r
            + fetch(PS.TexCoord, float2(-texelSize.x, 0)).r) / 2;
    float t = FADE_SPEED * (weight - fetch(PS.TexCoord, float2(0, 0)).g);

    if (iMouse.z == 1 && length(iMouse.xy - PS.TexCoord * texelSize.zw) <= MOUSE_RADIUS) // add the ability to add ripples by clicking
    {
        t = 1;
    }

    float4 finalColor = float4(t, fetch(PS.TexCoord, float2(0, 0)).r, 0, 1); // dont forget to set alpha to a value greater than 0 or you wont see anything

    return finalColor;
}

technique ripple
{
    pass P0
    {
        VertexShader = compile vs_2_0 VertexShaderFunction();
        PixelShader  = compile ps_2_0 psFunction();
    }
}

Screenshot:

Simple ripples created with mouse

\$\endgroup\$
3
  • \$\begingroup\$ You might want to mark this answer as accepted :) \$\endgroup\$
    – Vaillancourt
    Oct 28 '20 at 2:35
  • \$\begingroup\$ That little hook? \$\endgroup\$ Oct 29 '20 at 8:34
  • \$\begingroup\$ Yep! Thanks! This way, everyone will see that this is the way to solve the issue! \$\endgroup\$
    – Vaillancourt
    Oct 29 '20 at 10:20

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.