Help to understand Pixelate effect - graphics

I'm new a HLSL and I'm trying to understand a pixelate sample. However, I haven't been able to find a reference about how a couple of operations are. Here is the shader example:
//--------------------------------------------------------------------------------------
//
// WPF ShaderEffect HLSL -- PixelateEffect
//
//--------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------
// Shader constant register mappings (scalars - float, double, Point, Color, Point3D, etc.)
//-----------------------------------------------------------------------------------------
float HorizontalPixelCounts : register(C0);
float VerticalPixelCounts : register(C1);
//--------------------------------------------------------------------------------------
// Sampler Inputs (Brushes, including ImplicitInput)
//--------------------------------------------------------------------------------------
sampler2D implicitInputSampler : register(S0);
//--------------------------------------------------------------------------------------
// Pixel Shader
//--------------------------------------------------------------------------------------
float4 main(float2 uv : TEXCOORD) : COLOR
{
float2 brickCounts = { HorizontalPixelCounts, VerticalPixelCounts };
float2 brickSize = 1.0 / brickCounts;
// Offset every other row of bricks
float2 offsetuv = uv;
bool oddRow = floor(offsetuv.y / brickSize.y) % 2.0 >= 1.0;
if (oddRow)
{
offsetuv.x += brickSize.x / 2.0;
}
float2 brickNum = floor(offsetuv / brickSize);
float2 centerOfBrick = brickNum * brickSize + brickSize / 2;
float4 color = tex2D(implicitInputSampler, centerOfBrick);
return color;
}
I haven't been able to understand what computation is happening in:
float2 brickNum = floor(offsetuv / brickSize);
I'm not sure what how to compute the division between the two vectors, and also I don't know how to compute the floor of a vector. (I'm assuming that division of two float2 returns a float2).
Any idea?

HLSL operators and functions often work with structures like float2 which has an x and y.
The division inside the floor returns a float2 where the x and y are the result of dividing the x with x and y with y. And floor will return a float2 where the x and y of the result are the floored value of the x and y of the input (the result of the division).
The same is true for float3 and other similar structures.

Related

Upscaling using color interpolation for lighting?

I'm writing a lighting system for 2D games using a rather common method of 2D radiosity. The idea is to generate a JFA voronoi of the game scene (black, alpha = 1.0 for occluders and color, alpha = 1.0 for emitters) and generate an SDF from the JFA. Next you raymarch every pixel on screen for N rays with M max steps on the SDF with random angle offsets for each pixel. You then sample the emitter/occluder surface at the end point of each ray, step back into empty space and sample again for light emitted in the nearest empty space. This gives you a nice result as seen below:
That isn't the problem, it works great. The problem is efficiency. The idea behind fixing this is to render the GI at 1/N sample size (width/N, height/N) and then upscale the GI using interpolation. As I've done below:
This is the problem. The upscaling I've accomplished using weighted color-interpolation, but it produces these nasty results near occluders:
Here's the full shader:
The uniforms passed are the GI downsampled texture (in_GIField), Scene (emitters/occluders only) Texture (gm_basetexture), Signed Distance Field (in_SDField), Resolution (in_Screen) and the Downsample ratio (in_Sample).
/*
UPSCALING SHADER:
Find the nearest 4 boundign samples to the current pixel (xyDelta & xyShift)
Calculate all of the sample's weights based on whether they're marchable or source pixels.
Final perform a composite weighted interpolation for the current pixel to the nearest 4 samples.
*/
varying vec2 in_Coord;
uniform float in_Sample;
uniform vec2 in_Screen;
uniform sampler2D in_GIField;
uniform sampler2D in_SDField;
#define TPI 9.4247779607693797153879301498385
#define PI 3.1415926535897932384626433832795
#define TAU 6.2831853071795864769252867665590
#define EPSILON 0.001 // floating point precision check
#define dot(f) dot(f,f) // shorthand dot of a single float
float ATAN2(float yy, float xx) { return mod(atan(yy, xx), TAU); }
float DIRECT(vec2 v1, vec2 v2) { vec2 v3 = v2 - v1; return ATAN2(-v3.y, v3.x); }
float DIFFERENCE(float src, float dst) { return mod(dst - src + TPI, TAU) - PI; }
float V2_F16(vec2 v) { return v.x + (v.y / 255.0); }
float VMAX(vec3 v) { return max(v.r, max(v.g, v.b)); }
vec2 SAMPLEXY(vec2 xycoord) { return (floor(xycoord / in_Sample) * in_Sample) + (in_Sample*0.5); }
vec3 TONEMAP(vec3 color, float dist) { return color * (1.0 / (1.0 + dot(dist / min(in_Screen.x, in_Screen.y)))); }
float TESTMARCH(vec2 pix, vec2 end) {
float aspect = in_Screen.x / in_Screen.y,
dst = distance(pix, end);
vec2 dir = normalize((end*in_Screen) - (pix*in_Screen)) / in_Screen;
for(float i = 0.0; i < in_Sample; i += 1.0) {
vec2 test = vec2(pix.x * aspect, pix.y) + (dir * (i/in_Screen));
test.x /= aspect;
vec4 sourceCol = texture2D(gm_BaseTexture, test);
float source = max(sourceCol.r, max(sourceCol.g, sourceCol.b));
if (source < EPSILON && sourceCol.a > 0.0) return 0.0;
}
return 1.0;
}
vec3 WCOMPOSITE(vec3 colors[4], float weights[4], vec2 uv) {
// (uv * A * B) + (B * (1.0 - A)) //0, 2, 1, 3
float weightA = (uv.y * weights[0] * weights[2]) + (weights[2] * (1.0 - weights[0])),
weightB = (uv.y * weights[1] * weights[3]) + (weights[3] * (1.0 - weights[1]));
vec3 colorA = mix(colors[0], colors[2], weightA),
colorB = mix(colors[1], colors[3], weightB);
return mix(colorA, colorB, uv.x);
}
void main() {
vec2 xyCoord = in_Coord * in_Screen;
vec2 xyLight = SAMPLEXY(xyCoord);
vec2 xyDelta = sign(sign(xyCoord - xyLight) - 1.0);
vec2 xyShift[4];
xyShift[0] = vec2(0.,0.) + xyDelta;
xyShift[1] = vec2(1.,0.) + xyDelta;
xyShift[2] = vec2(0.,1.) + xyDelta;
xyShift[3] = vec2(1.,1.) + xyDelta;
vec2 xyField[4]; vec3 xyColor[4]; float notSource[4]; float xyWghts[4];
for(int i = 0; i < 4; i++) {
xyField[i] = (xyLight + (xyShift[i] * in_Sample)) * (1.0/in_Screen);
xyColor[i] = texture2D(in_GIField, xyField[i]).rgb;
notSource[i] = 1.0 - sign(texture2D(gm_BaseTexture, xyField[i]).a);
xyWghts[i] = TESTMARCH(in_Coord, xyField[i]) * sign(VMAX(xyColor[i])) * notSource[i];
}
vec2 uvCoord = mod(xyCoord-xyLight, in_Sample) * (1.0/in_Sample);
vec3 xyFinal = WCOMPOSITE(xyColor, xyWghts, uvCoord);
vec4 xySource = texture2D(gm_BaseTexture, in_Coord);
float isSource = sign(xySource.a);
gl_FragColor = vec4((isSource * xySource.rgb) + ((1.0-isSource) * xyFinal), 1.0);
}
EDIT: This DOES produce the intended result in empty space, but ends up with nasty artifacting near emitters and occluders. I tried to solve this in the for-loop in the main function by weighting out the emitter/occluder (source pixels in the scene texture) colors, but this isn't working.
See shader code attached (Shadertoy). I noticed that the weighting function will actually produce some colors with a weight of 0 (as expected as originally written). I currently don't have a solution for how to remove colors from the interpolation process entirely.
Full Source Code
Full Color Shader Code

Converting X, Z coords to RGB using GLSL shaders

I have a Three js scene that contains a 100x100 plane centred at the origin (ie. min coord: (-50,-50), max coord: (50,50)). I am trying to have the plane appear as a colour wheel by using the x and z coords in a custom glsl shader. Using this guide (see HSB in polar coordinates, towards the bottom of the page) I have gotten my
Shader Code with Three.js Scene
but it is not quite right.
I have played around tweaking all the variables that make sense to me, but as you can see in the screenshot the colours change twice as often as what they should. My math intuition says just divide the angle by 2 but when I tried that it was completely incorrect.
I know the solution is very simple but I have tried for a couple hours and I haven't got it.
How do I turn my shader that I currently have into one that makes exactly 1 full colour rotation in 2pi radians?
EDIT: here is the relevant shader code in plain text
varying vec3 vColor;
const float PI = 3.1415926535897932384626433832795;
uniform float delta;
uniform float scale;
uniform float size;
vec3 hsb2rgb( in vec3 c ){
vec3 rgb = clamp(abs(mod(c.x*6.0+vec3(0.0,4.0,2.0),
6.0)-3.0)-1.0,
0.0,
1.0 );
rgb = rgb*rgb*(3.0-2.0*rgb);
return c.z * mix( vec3(1.0), rgb, c.y);
}
void main()
{
vec4 worldPosition = modelMatrix * vec4(position, 1.0);
float r = 0.875;
float g = 0.875;
float b = 0.875;
if (worldPosition.y > 0.06 || worldPosition.y < -0.06) {
vec2 toCenter = vec2(0.5) - vec2((worldPosition.z+50.0)/100.0, (worldPosition.x+50.0)/100.0);
float angle = atan(worldPosition.z/worldPosition.x);
float radius = length(toCenter) * 2.0;
vColor = hsb2rgb(vec3((angle/(PI))+0.5,radius,1.0));
} else {
vColor = vec3(r,g,b);
}
vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);
gl_PointSize = size * (scale/length(mvPosition.xyz));
gl_Position = projectionMatrix * mvPosition;
}
I have discovered that the guide I was following was incorrect. I wasn't thinking about my math properly but I now know what the problem was.
atan has a range from -PI/2 to PI/2 which only accounts for half of a circle. When worldPosition.x is negative atan will not return the correct angle since it is out of range of the function. The angle needs to be adjusted based on what quadrant it is in the plane.
Q1: do nothing
Q2: add PI to the angle
Q3: add PI to the angle
Q4: add 2PI to the angle
After this normalize the angle (divide by 2PI) then pass it to the hsb2rgb function.

Using 2D metaballs to draw an outline with a constant thickness

I'm apply the concept of metaballs to a game I'm making in order to show that the player has selected a few ships, like so http://prntscr.com/klgktf
However, my goal is to keep a constant thickness of this outline, and that's not what I'm getting with the current code.
I'm using a GLSL shader to do this, and I pass to the fragmentation shader a uniform array of positions for the ships (u_metaballs).
Vertex shader:
#version 120
void main() {
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}
Fragmentation shader:
#version 120
uniform vec2 u_metaballs[128];
void main() {
float intensity = 0;
for(int i = 0; i < 128 && u_metaballs[i].x != 0; i++){
float r = length(u_metaballs[i] - gl_FragCoord.xy);
intensity += 1 / r;
}
gl_FragColor = vec4(0, 0, 0, 0);
if(intensity > .2 && intensity < .21)
gl_FragColor = vec4(.5, 1, .7, .2);
}
I've tried playing around with the intensity ranges, and even changing 1 / r to 10000 / (r ^ 4) which (although it makes no sense) helps a bit, though it does not fix the problem.
Any help or suggestions would be greatly appreciated.
after some more taught it is doable even in single pass ... you just compute the distance to nearest metaball and if less or equal to the boundary thickness render fragment otherwise discard it ... Here example (assuming single quad <-1,+1> is rendered covering whole screen):
Vertex:
// Vertex
varying vec2 pos; // fragment position in world space
void main()
{
pos=gl_Vertex.xy;
gl_Position=ftransform();
}
Fragment:
// Fragment
#version 120
varying vec2 pos;
const float r=0.3; // metabal radius
const float w=0.02; // border line thickness
uniform vec2 u_metaballs[5]=
{
vec2(-0.25,-0.25),
vec2(+0.25,-0.25),
vec2( 0.00,+0.05),
vec2(+0.30,+0.35),
vec2(-1000.1,-1000.1), // end of metaballs
};
void main()
{
int i;
float d;
// d = min distance to any metaball
for (d=r+r+w+w,i=0;u_metaballs[i].x>-1000.0;i++)
d=min(d,length(pos-u_metaballs[i].xy));
// if outside range ignore fragment
if ((d<r)||(d>r+w)) discard;
// otherwise render it
gl_FragColor=vec4(1.0,1.0,1.0,1.0);
}
Preview:

Shadow mapping with wrong results (HLSL, Shader model 3.0)

(Sorry for my bad English.)
I'm new to Stack Overflow and writing a 3D game application with MS Visual C++ 2015 compiler, Direct3D 9 and HLSL(Shader model 3.0).
I've implemented a deferred rendering logic with 4 render target textures.
I stored depth values of pixels in a render target texture and created a shadow map. Here are the results. (All meshes have black color because the meshes have small size and close to the camera. The far plane distance value is 1000.0f.)
The depth texture and the shadow map.
I rendered a full screen quad with shadow mapping shaders and outputted shadows with red color to confirm the shader is working correctly.
But, It seems that the shaders output wrong results. The shadow map texture output repeats on the mesh surfaces.
https://www.youtube.com/watch?v=1URGgoCR6Zc
Here is the shadow mapping vertex shader to draw the quad.
struct VsInput {
float4 position : POSITION0;
};
struct VsOutput {
float4 position : POSITION0;
float4 cameraViewRay : TEXCOORD0;
};
float4x4 matInverseCameraViewProjection;
float4 cameraWorldPosition;
float farDistance;
VsOutput vs_main(VsInput input) {
VsOutput output = (VsOutput)0;
output.position = input.position;
output.cameraViewRay = mul(float4(input.position.xy, 1.0f, 1.0f) * farDistance, matInverseCameraViewProjection);
output.cameraViewRay /= output.cameraViewRay.w;
output.cameraViewRay.xyz -= cameraWorldPosition.xyz;
return output;
}
And here is the shadow mapping pixel shader to draw the quad.
struct PsInput {
float2 screenPosition : VPOS;
float4 viewRay : TEXCOORD0;
};
struct PsOutput {
float4 color : COLOR0;
};
texture depthMap;
texture shadowMap;
sampler depthMapSampler = sampler_state {
Texture = (depthMap);
AddressU = CLAMP;
AddressV = CLAMP;
MagFilter = POINT;
MinFilter = POINT;
MipFilter = POINT;
};
sampler shadowMapSampler = sampler_state {
Texture = (shadowMap);
AddressU = CLAMP;
AddressV = CLAMP;
MagFilter = POINT;
MinFilter = POINT;
MipFilter = POINT;
};
//float4x4 matCameraView;
float4x4 matLightView;
float4x4 matLightProjection;
float4 cameraWorldPosition;
float4 lightWorldPosition;
float2 halfPixel;
float epsilon;
float farDistance;
PsOutput ps_main(PsInput input) {
PsOutput output = (PsOutput)0;
output.color.a = 1.0f;
//Reconstruct the world position using the view-space linear depth value.
float2 textureUv = input.screenPosition * halfPixel * 2.0f - halfPixel;
float viewDepth = tex2D(depthMapSampler, textureUv).r;
float3 eye = input.viewRay.xyz * viewDepth;
float4 worldPosition = float4((eye + cameraWorldPosition.xyz), 1.0f);
//Test if the reconstructed world position has right coordinate values.
//output.color = mul(worldPosition, matCameraView).z / farDistance;
float4 positionInLightView = mul(worldPosition, matLightView);
float lightDepth = positionInLightView.z / farDistance;
float4 positionInLightProjection = mul(positionInLightView, matLightProjection);
positionInLightProjection /= positionInLightProjection.w;
//If-statement doesn't work???
float condition = positionInLightProjection.x >= -1.0f;
condition *= positionInLightProjection.x <= 1.0f;
condition *= positionInLightProjection.y >= -1.0f;
condition *= positionInLightProjection.y <= 1.0f;
condition *= positionInLightProjection.z >= 0.0f;
condition *= positionInLightProjection.z <= 1.0f;
condition *= positionInLightProjection.w > 0.0f;
float2 shadowMapUv = float2(
positionInLightProjection.x * 0.5f + 0.5f,
-positionInLightProjection.y * 0.5f + 0.5f
);
//If-statement doesn't work???
float condition2 = shadowMapUv.x >= 0.0f;
condition2 *= shadowMapUv.x <= 1.0f;
condition2 *= shadowMapUv.y >= 0.0f;
condition2 *= shadowMapUv.y <= 1.0f;
float viewDepthInShadowMap = tex2D(
shadowMapSampler,
shadowMapUv
).r;
output.color.r = lightDepth > viewDepthInShadowMap + epsilon;
output.color.r *= condition;
output.color.r *= condition2;
return output;
}
It seems that the uv for the shadow map has some wrong values, but i can't figure out what's the real problem.
Many thanks for any help.
EDIT : I've updated the shader codes. I decided to use view-space linear depth and confirmed that the world position has right value. I really don't understand why the shadow map coordinate values have wrong values...
It really looks like you are using a wrong bias. Google up "Shadow Acne" and you should find your answer to your problem. Also the resolution of the shadowmap could be a problem.
I found the solution.
The first problem was that the render target texture had wrong texture format. I should have used D3DFMT_R32F. (I had used D3DFMT_A8R8G8B8.)
And i added these lines in my shadow mapping pixel shader.
//Reconstruct the world position using the view-space linear depth value.
float2 textureUv = input.screenPosition * halfPixel * 2.0f - halfPixel;
float4 viewPosition = float4(input.viewRay.xyz * tex2D(depthMapSampler, textureUv).r, 1.0f);
float4 worldPosition = mul(viewPosition, matInverseCameraView);
...
//If-statement doesn't work???
float condition = positionInLightProjection.x >= -1.0f;
condition *= positionInLightProjection.x <= 1.0f;
condition *= positionInLightProjection.y >= -1.0f;
condition *= positionInLightProjection.y <= 1.0f;
condition *= positionInLightProjection.z >= 0.0f;
condition *= positionInLightProjection.z <= 1.0f;
condition *= viewPosition.z < farDistance;
The last line was the key and solved my second problem. The 'farDistance' is the far plane distance of the camera frustum. I'm still trying to understand why that is needed.
You can use saturate to clamp the positionInLightProjection and compare it against the unsaturated variable. This way you can verify that positionInLightProjection is within 0..1.
if ((saturate(positionInLightProjection.x) == positionInLightProjection.x) && (saturate(positionInLightProjection.y) == positionInLightProjection.y)) {
// we are in the view of light
// todo: compare depth values from shadow map and current scene depth
} else {
// this is shadow for sure!
}

Is there a faked antialiasing algorithm using the depth buffer?

Lately I implemented the FXAA algorithm into my OpenGL application. I haven't understand this algorithm completely by now but I know that it uses contrast data of the final image to selectively apply blurring. As a post processing effect that makes sense. B since I use deferred shading in my application I already have a depth texture of the scene. Using that it might be much easier and more precise to find edges for applying blur there.
So is there a known antialiasing algorithm using the depth texture instead of the final image to find the edges? By fakes I mean an antialiasing algorithm based on a pixel basis instead of a vertex basis.
After some research I found out that my idea is widely used already in deferred renderers. I decided to post this answer because I came up with my own implementation which I want to share with the community.
Based on the gradient changes of the depth and the angle changes of the normals, there is blurring applied to the pixel.
// GLSL fragment shader
#version 330
in vec2 coord;
out vec4 image;
uniform sampler2D image_tex;
uniform sampler2D position_tex;
uniform sampler2D normal_tex;
uniform vec2 frameBufSize;
void depth(out float value, in vec2 offset)
{
value = texture2D(position_tex, coord + offset / frameBufSize).z / 1000.0f;
}
void normal(out vec3 value, in vec2 offset)
{
value = texture2D(normal_tex, coord + offset / frameBufSize).xyz;
}
void main()
{
// depth
float dc, dn, ds, de, dw;
depth(dc, vec2( 0, 0));
depth(dn, vec2( 0, +1));
depth(ds, vec2( 0, -1));
depth(de, vec2(+1, 0));
depth(dw, vec2(-1, 0));
float dvertical = abs(dc - ((dn + ds) / 2));
float dhorizontal = abs(dc - ((de + dw) / 2));
float damount = 1000 * (dvertical + dhorizontal);
// normals
vec3 nc, nn, ns, ne, nw;
normal(nc, vec2( 0, 0));
normal(nn, vec2( 0, +1));
normal(ns, vec2( 0, -1));
normal(ne, vec2(+1, 0));
normal(nw, vec2(-1, 0));
float nvertical = dot(vec3(1), abs(nc - ((nn + ns) / 2.0)));
float nhorizontal = dot(vec3(1), abs(nc - ((ne + nw) / 2.0)));
float namount = 50 * (nvertical + nhorizontal);
// blur
const int radius = 1;
vec3 blur = vec3(0);
int n = 0;
for(float u = -radius; u <= +radius; ++u)
for(float v = -radius; v <= +radius; ++v)
{
blur += texture2D(image_tex, coord + vec2(u, v) / frameBufSize).rgb;
n++;
}
blur /= n;
// result
float amount = mix(damount, namount, 0.5);
vec3 color = texture2D(image_tex, coord).rgb;
image = vec4(mix(color, blur, min(amount, 0.75)), 1.0);
}
For comparison, this is the scene without any anti-aliasing.
This is the result with anti-aliasing applied.
You may need to view the images at their full resolution to judge the effect. In my view the result is adequate for the simple implementation. The best thing is that there are nearly no jagged artifacts when the camera moves.

Resources