4 ms·
Figured out how to do Multi-Frame style Anti-Aliasing for a much lower performance hit, looks nearly as good in motion: void mainImage( out vec4 fragColor, i
by entropicdrifter 9y ago
Figured out how to do Multi-Frame style Anti-Aliasing for a much lower performance hit, looks nearly as good in motion:
void mainImage( out vec4 fragColor, in vec2 fragCoord ) {
float h1 = (hash(time + fragCoord.x) + hash(time + fragCoord.y)) / 4.0;
float h2 = (hash(time - fragCoord.x) + hash(time -fragCoord.y)) / 4.0;
vec3 c1 = getColor(fragCoord + vec2(h1, h2));
fragColor = vec4(c1, 1.0);
}
Or if you want it to look pretty much exactly as good as 4x MSAA (which is what you're doing) for a little more than half the cost:
void mainImage( out vec4 fragColor, in vec2 fragCoord ) {
float h1 = (hash(time + fragCoord.x) + hash(time + fragCoord.y)) / 4.0;
float h2 = (hash(time - fragCoord.x) + hash(time -fragCoord.y)) / 4.0;
vec3 c1 = getColor(fragCoord + vec2(h1, h2));
vec3 c2 = getColor(fragCoord + vec2(-h1, -h2));
fragColor = vec4((c1 + c2)/2., 1.0);
}
EDIT: Changed per suggestions from reply
Edit2:
Found another way to cut down on cost with even less sacrifice of quality (no under-water feeling while holding still), you can do a 2x version of the original 4x method, but flip-flop the x coordinates every frame, like so:
void mainImage( out vec4 fragColor, in vec2 fragCoord ) {
bool even = mod(float(iFrame), 2.) == 0.;
float x;
float y = 0.25;
if(even) {
x = -0.25;
}
else {
x = 0.25;
}
vec3 c1 = getColor(fragCoord + vec2(x, y));
vec3 c2 = getColor(fragCoord + vec2(-x, -y));
fragColor = vec4((c1 + c2)/2., 1.0);
}
- mkhalil 9y agoshould the last line of the second function be fragColor = vec4(c1, c2)? I just see an unused c2.
- entropicdrifter 9y agoThanks! Fixed it.
- gpm 9y agoThis (edit: the first one) works way better than I expected! Your h1 and h2 are always the same since hash is deterministic when I assume you want them to be distinct, and they're in the range [-1, 1] instead of [0.5, -0.5] which I think makes more sense. I'm also not sure why you made r instead of just hashing fragCoord directly? I also think adding time to the hash makes it look better, though if you stand still it's somewhat nauseating (switch all time variables in getColor to 0.0). Anyways I ended up with the following which I think looks great :) float h1 = (hash(time + fragCoord.x) + hash(time + fragCoord.y)) / 4.0; float h2 = (hash(time - fragCoord.x) + hash(time -fragCoord.y)) / 4.0;
- entropicdrifter 9y agoHash is pseudorandom. It amplifies the rounding error of the sine function, as explained here: https://stackoverflow.com/a/4275343 https://stackoverflow.com/a/4275343 The reason I didn't use fragCoord was because I wasn't sure it would be within range, though thinking about it, I can't really see why it wouldn't be since before the engine was using it directly (d'oh). Anyways I did the mod 9 and divide by 10 to ensure it was in the range of [0, 0.8999999...]. The idea there was I got initial the impression (though I didn't really dig) that the hash function generates a point between x and y of the vector. Reviewing, it seems this was a silly notion. I think you're right, now that I'm looking at it again. Your way of getting h1 and h2 is much more efficient.
- rzzzt 9y agoMy attempt at temporal anti-aliasing: 1) Add the editor tab for "Buffer A"; move all code with the getColor modification above to this editor; use the following mainImage method: #define SCALE_FACTOR 1.0 #define BLUR_FACTOR 0.95 void mainImage( out vec4 fragColor, in vec2 fragCoord ) { float sum = floor(fragCoord.x / SCALE_FACTOR) + floor(fragCoord.y / SCALE_FACTOR); bool evenSum = mod(sum, 2.0) < 0.0001; bool evenFrame = (iFrame % 2) == 0; vec2 texCoord = fragCoord.xy / iResolution.xy; vec3 texColor = texture(iChannel0, texCoord).rgb; if (evenSum == evenFrame) { fragColor = vec4(mix(texColor, getColor(fragCoord), BLUR_FACTOR), 1.0); } else { fragColor = vec4(texColor * BLUR_FACTOR, 1.0); } } 2) The "Image" tab should only render the contents of iChannel0 on the display: void mainImage( out vec4 fragColor, in vec2 fragCoord ) { vec2 texCoord = fragCoord.xy / iResolution.xy; fragColor = texture(iChannel0, texCoord); } 3) Assign Buffer A as the input/output for iChannel0. This should do the ray-casting part at effectively half resolution in an alternating checkerboard pattern, blending the previous contents of the buffer with the current output.
- entropicdrifter 9y agoBrilliant! This was my first time playing around with GLSL and I wasn't sure how to implement the buffer to do the blending.
- entropicdrifter 9y agoSo I decided to try combining the way you did it with what I did before, and I came up with this method, which is effectively equivalent to the 4x AA in the top-level comment: void mainImage( out vec4 fragColor, in vec2 fragCoord ) { bool evenFrame = (iFrame % 2) == 0; float x; float y = 0.25; if(evenFrame) { x = 0.25; } else { x = -0.25; } vec3 c1 = getColor(fragCoord + vec2(x, y)); vec3 c2 = getColor(fragCoord + vec2(-x, -y)); vec2 texCoord = fragCoord.xy / iResolution.xy; vec3 texColor = texture(iChannel0, texCoord).rgb; fragColor = vec4(mix(texColor, (c1+c2), 0.5), 1.0); }