0
\$\begingroup\$

I have found a shader that I like here: https://www.shadertoy.com/view/4dyXWy

The code for the fragment shader from the link above is:

#define DITHER          //Dithering toggle
#define QUALITY     0   //0- low, 1- medium, 2- high

#define DECAY       .974
#define EXPOSURE    .24
#if (QUALITY==2)
 #define SAMPLES    64
 #define DENSITY    .97
 #define WEIGHT     .25
#else
#if (QUALITY==1)
 #define SAMPLES    32
 #define DENSITY    .95
 #define WEIGHT     .25
#else
 #define SAMPLES    16
 #define DENSITY    .93
 #define WEIGHT     .36
#endif
#endif

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    vec2 uv = fragCoord.xy / iResolution.xy;

    vec2 coord = uv;
    vec2 lightpos = texture(iChannel0, uv).zw;

    float occ = texture(iChannel0, uv).x; //light
    float obj = texture(iChannel0, uv).y; //objects
    float dither = texture(iChannel1, fragCoord/iChannelResolution[1].xy).r;    

    vec2 dtc = (coord - lightpos) * (1. / float(SAMPLES) * DENSITY);
    float illumdecay = 1.;

    for(int i=0; i<SAMPLES; i++)
    {
        coord -= dtc;
        #ifdef DITHER
            float s = texture(iChannel0, coord+(dtc*dither)).x;
        #else
            float s = texture(iChannel0, coord).x;
        #endif
        s *= illumdecay * WEIGHT;
        occ += s;
        illumdecay *= DECAY;
    }

    fragColor = vec4(vec3(0., 0., obj*.333)+occ*EXPOSURE,1.0);
}

That is in GLSL, but I am using DirectX 11. I've been able to port it across quite easily, but I feel like I have some knowledge gaps that are making it hard for me to get correct. For example, I don't understand the difference between texture(iChannel0, uv).x and texture(iChannel0, uv).r if someone wouldn't mind explaining ( I understand sampling the colour channels but xyz while sampling?). I also do not understand the channel resolution. Is it the dimensions of the 2D texture that I'm rendering to? Or perhaps the ratio of the texture?

Here is what I have come up with so far, just the bare minimum of changes to get it working:

SamplerState g_samSampler   : register( s0 );
Texture2D g_texInfo         : register( t0 );

#define DITHER
#define DECAY       0.974f
#define EXPOSURE    0.24f
#define SAMPLES     64
#define DENSITY     0.97f
#define WEIGHT      0.25f

struct PixelInput
{
    float4 vPosition : SV_POSITION;
    float2 vTexCoord : TEXCOORD0;
};

float4 PS( PixelInput input ) : SV_TARGET
{
    float2 uv = input.vTexCoord;

    float2 coord = uv;
    float2 lightpos = g_texInfo.Sample( g_samSampler, uv ).zw; //TAKE NOTE OF THIS LINE

    float occ = g_texInfo.Sample( g_samSampler, input.vTexCoord ).g; //light
    float obj = g_texInfo.Sample( g_samSampler, input.vTexCoord ).b; //objects
    float dither = g_texInfo.Sample( g_samSampler, uv/float2( 4.f, 3.f ) ).x;  

    float2 dtc = (coord - lightpos) * (1.f / float(SAMPLES) * DENSITY);
    float illumdecay = 1.;

    for(int i=0; i<SAMPLES; i++)
    {
        coord -= dtc;

        #ifdef DITHER
            float s = g_texInfo.Sample( g_samSampler, coord+(dtc*dither) ).g;
        #else
            float s = g_texInfo.Sample( g_samSampler, input.vTexCoord ).g;
        #endif

        s *= illumdecay * WEIGHT;
        occ += s;
        illumdecay *= DECAY;
    }

    return float4(float3(0.f, 0.f, obj*.333f)+occ*EXPOSURE,1.f);
}

Please note that my green channel of the passed in texture g_texInfo stores the light texture (the white circle for the sun) and the blue channel stores the light occluders. I am doing this as a post processing effect on a screen quad. (Is this correct?) My suspicion is the line float2 lightpos = g_texInfo.Sample( g_samSampler, uv ).zw; is causing the trouble - I don't really understand this part - how can I find the position of the sun?

Here's what I'm receiving from my current shader, it appears that the light rays are being emitted in the wrong direction.

enter image description here When I shift the camera so that the sun is in the top left, it gives the correct effect, it is also occluded properly when putting an object in the way.

enter image description here

\$\endgroup\$
3
\$\begingroup\$

In GLSL (like HLSL), a vec4 (float4 in HLSL) has three different ways to access its four values. You can use (rgba), or (xyzw), or (stpq). They're all identical to each other; Asking for vector.r gets you the same value as vector.x or vector.s, so use whichever you prefer. (General best practices suggest using 'rgba' if it's a color, or 'xyzw' if it's a position in 3D/4D space, or 'stpq' if it's texture coordinates.. which amusingly we refer to as 'uv' coordinates even though you can't use either u or v to get the individual components from it. But do what makes you happy; they're all interchangeable!)

In the original code, you had this:

    vec2 lightpos = texture(iChannel0, uv).zw;

    float occ = texture(iChannel0, uv).x; //light
    float obj = texture(iChannel0, uv).y; //objects

The obvious issue I see with your port is that you have converted the above to:

    float2 lightpos = g_texInfo.Sample( g_samSampler, uv ).zw; //TAKE NOTE OF THIS LINE

    float occ = g_texInfo.Sample( g_samSampler, input.vTexCoord ).g; //light
    float obj = g_texInfo.Sample( g_samSampler, input.vTexCoord ).b; //objects

You've moved the channels around! 'zw' (which is equivalent to 'ba') is the same, but you've moved the 'x' ('r') channel to 'g', and the 'y' channel to 'b', re-using the channel you're otherwise using for the lightpos.

That's probably the source of your troubles. (along with the identical issue a few lines further down, when sampling light values inside the loop)

Regarding your other question, about iChannelResolution, that's a custom ShaderToy thing. It's a uniform variable which holds the width and height of a texture, in pixels. Here, it appears to be being used to grab the individual pixels of a texture that holds a dithering pattern. You can easily create your own version of that, but it isn't there for you by default in either GLSL or HLSL.

By contrast, iResolution (another custom ShaderToy thing) is the resolution of the destination image. By default, ShaderToy shaders receive a fragCoord input which tells them the coordinates of the pixel being rendered (measured in pixels). Dividing the fragCoord by iResolution gives you a [0,0]->[1,1] value which is useful for sampling from textures. Most ShaderToy shaders do that near the start of their code, and this one is no exception.

\$\endgroup\$

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.