Skip to content

Instantly share code, notes, and snippets.

@Xynonners
Last active April 25, 2024 12:16
Show Gist options
  • Save Xynonners/ddf30a5215a59db5690f6420621f0623 to your computer and use it in GitHub Desktop.
Save Xynonners/ddf30a5215a59db5690f6420621f0623 to your computer and use it in GitHub Desktop.
gdshader HHAA implementation (spatial/depth AA)
shader_type spatial;
render_mode unshaded;
void vertex() {
POSITION = vec4(VERTEX, 1.0);
}
uniform sampler2D BackBufferTex : hint_screen_texture, repeat_disable, filter_nearest;
uniform sampler2D DepthBufferTex : source_color, hint_depth_texture, filter_nearest;
uniform float luma_threshold: hint_range(0, 1) = 0.375;
uniform float low_threshold: hint_range(0, 1) = 0.05;
uniform float high_threshold: hint_range(0, 1) = 0.2;
uniform float depth_threshold: hint_range(0, 1) = 0.1;
uniform bool cosine_blending = true;
// Constants for offsets
const ivec2 OffSW = ivec2(-1, 1);
const ivec2 OffSE = ivec2(1, 1);
const ivec2 OffNE = ivec2(1, -1);
const ivec2 OffNW = ivec2(-1, -1);
// Function to calculate luminance
float getLuma(vec3 color) {
return dot(color, vec3(0.299, 0.587, 0.114));
}
// Sampling functions
vec3 sampleColor(vec2 p) {
return texture(BackBufferTex, p).rgb;
}
// Function to sample luminance at an offset
float sampleLumaOff(vec2 uv, ivec2 offset, vec2 texSize) {
return getLuma(texture(BackBufferTex, uv + vec2(offset) / texSize).rgb);
}
// Sobel operator kernels
const mat3 sobel_kernel_x = mat3(vec3(-1, 0, 1),
vec3(-2, 0, 2),
vec3(-1, 0, 1));
const mat3 sobel_kernel_y = mat3(vec3(-1, -2, -1),
vec3(0, 0, 0),
vec3(1, 2, 1));
// Function to apply Sobel filter
vec2 applySobel(vec2 uv, vec2 texSize) {
float gx = 0.0;
float gy = 0.0;
for (int x = -1; x <= 1; x++) {
for (int y = -1; y <= 1; y++) {
vec3 sampleColor = texture(BackBufferTex, uv + vec2(float(x), float(y)) / texSize).rgb;
float luma = getLuma(sampleColor);
gx += luma * sobel_kernel_x[x + 1][y + 1];
gy += luma * sobel_kernel_y[x + 1][y + 1];
}
}
return vec2(gx, gy);
}
// Non-maximum suppression
bool isLocalMaximum(vec2 uv, vec2 gradient, vec2 texSize) {
float edgeStrength = length(gradient);
float angle = atan(gradient.y, gradient.x);
vec2 n = normalize(vec2(cos(angle), sin(angle)));
float luma1 = length(applySobel(uv + n / texSize, texSize));
float luma2 = length(applySobel(uv - n / texSize, texSize));
return edgeStrength >= luma1 && edgeStrength >= luma2;
}
// Gaussian kernel (5x5 approximation)
const float gaussian_kernel[25] = float[25](
1.0, 4.0, 7.0, 4.0, 1.0,
4.0, 16.0, 26.0, 16.0, 4.0,
7.0, 26.0, 41.0, 26.0, 7.0,
4.0, 16.0, 26.0, 16.0, 4.0,
1.0, 4.0, 7.0, 4.0, 1.0
);
// Function to apply Gaussian blur
vec3 applyGaussianBlur(vec2 uv, vec2 texSize) {
vec3 blurredColor = vec3(0.0);
float totalWeight = 0.0;
for (int x = -2; x <= 2; x++) {
for (int y = -2; y <= 2; y++) {
vec2 offset = vec2(float(x), float(y)) / texSize;
vec3 sampleColor = texture(BackBufferTex, uv + offset).rgb;
float weight = gaussian_kernel[(x + 2)*(y + 2)];
blurredColor += sampleColor * weight;
totalWeight += weight;
}
}
return blurredColor / totalWeight;
}
// Laplacian kernel
const mat3 laplacian_kernel = mat3(vec3(0, 1, 0),
vec3(1, -4, 1),
vec3(0, 1, 0));
// Function to apply Laplacian filter to blurred image
float applyLaplacianToBlurred(vec2 uv, vec2 texSize, vec3 blurredColor) {
float laplacianValue = 0.0;
for (int x = -1; x <= 1; x++) {
for (int y = -1; y <= 1; y++) {
vec2 offset = vec2(float(x), float(y)) / texSize;
vec3 sampleColor = texture(BackBufferTex, uv + offset).rgb;
if (x == 0 && y == 0) {
sampleColor = blurredColor; // Use the blurred color for the center pixel
}
laplacianValue += getLuma(sampleColor) * laplacian_kernel[x + 1][y + 1];
}
}
return laplacianValue;
}
float calculateLocalContrast(vec3 neighborhoodColors[9]) {
float centerLuma = getLuma(neighborhoodColors[4]); // Center pixel luminance
float averageLuma = 0.0;
for (int i = 0; i < 9; i++) {
if (i != 4) { // Exclude center pixel
averageLuma += getLuma(neighborhoodColors[i]);
}
}
averageLuma /= 8.0;
return abs(centerLuma - averageLuma);
}
float calculateColorVariance(vec3 neighborhoodColors[9]) {
vec3 meanColor = vec3(0.0);
for (int i = 0; i < 9; i++) {
meanColor += neighborhoodColors[i];
}
meanColor /= 9.0;
float variance = 0.0;
for (int i = 0; i < 9; i++) {
variance += distance(meanColor, neighborhoodColors[i]);
}
variance /= 9.0;
return variance;
}
// Function to linearize depth value
float linearizeDepth(float depth, vec2 uv, mat4 inv_projection_matrix) {
vec3 ndc = vec3(uv * 2.0 - 1.0, depth);
vec4 view = inv_projection_matrix * vec4(ndc, 1.0);
view.xyz /= view.w;
return -view.z;
}
// Function to calculate depth difference
float getDepthDifference(vec2 uv, vec2 texSize, mat4 inv_projection_matrix) {
float centerDepth = linearizeDepth(texture(DepthBufferTex, uv).x, uv, inv_projection_matrix);
float maxDepthDifference = 0.0;
for (int x = -1; x <= 1; x++) {
for (int y = -1; y <= 1; y++) {
if (x == 0 && y == 0) continue; // Skip the center pixel
vec2 offset = vec2(float(x), float(y)) / texSize;
float neighborDepth = linearizeDepth(texture(DepthBufferTex, uv + offset).x, uv + offset, inv_projection_matrix);
maxDepthDifference = max(maxDepthDifference, abs(centerDepth - neighborDepth));
}
}
return maxDepthDifference;
}
// Cosine blend between two colors
vec3 cosineBlend(vec3 color1, vec3 color2, float factor) {
float cosFactor = smoothstep(0.0, 1.0, factor);
return mix(color1, color2, cosFactor);
}
// Simple noise function for dithering
float noise(vec2 co){
return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
}
void fragment() {
vec2 texSize = vec2(textureSize(BackBufferTex, 0));
vec2 RCP2 = 2.0 / texSize;
vec2 uv = FRAGCOORD.xy / texSize;
// Luminance-based edge detection
vec4 lumaA;
lumaA.x = sampleLumaOff(uv, OffSW, texSize);
lumaA.y = sampleLumaOff(uv, OffSE, texSize);
lumaA.z = sampleLumaOff(uv, OffNE, texSize);
lumaA.w = sampleLumaOff(uv, OffNW, texSize);
float gradientSWNE = lumaA.x - lumaA.z;
float gradientSENW = lumaA.y - lumaA.w;
vec2 dir = vec2(gradientSWNE + gradientSENW, gradientSWNE - gradientSENW);
vec2 dirM = abs(dir);
float lumaMax = max(max(lumaA.x, lumaA.y), max(lumaA.z, lumaA.w));
float localLumaFactor = lumaMax * 0.5 + 0.5;
float localThres = luma_threshold * localLumaFactor;
bool luminanceEdge = abs(dirM.x - dirM.y) >= localThres;
float dirMMin = min(dirM.x, dirM.y);
vec2 offM = clamp(vec2(0.0625) * dirM / dirMMin, 0.0, 1.0);
vec2 offMult = RCP2 * sign(dir);
float offMMax = max(offM.x, offM.y);
if (abs(offMMax - 1.0) < 0.0001) {
bool horSpan = abs(offM.x - 1.0) < 0.0001;
bool negSpan = horSpan ? offMult.x < 0.0 : offMult.y < 0.0;
bool sowSpan = horSpan == negSpan;
vec2 uvC = uv;
if (horSpan) uvC.x += 2.0 * offMult.x;
if (!horSpan) uvC.y += 2.0 * offMult.y;
vec4 lumaAC = lumaA;
if (sowSpan) lumaAC.x = sampleLumaOff(uvC, OffSW, texSize);
if (!negSpan) lumaAC.y = sampleLumaOff(uvC, OffSE, texSize);
if (!sowSpan) lumaAC.z = sampleLumaOff(uvC, OffNE, texSize);
if (negSpan) lumaAC.w = sampleLumaOff(uvC, OffNW, texSize);
float gradientSWNEC = lumaAC.x - lumaAC.z;
float gradientSENWC = lumaAC.y - lumaAC.w;
vec2 dirC = vec2(gradientSWNEC + gradientSENWC, gradientSWNEC - gradientSENWC);
if (!horSpan) dirC = dirC.yx;
bool passC = abs(dirC.x) > 2.0 * abs(dirC.y);
if (passC) offMult *= 2.0;
}
// Apply Sobel filter to get gradient for Canny edge detection
vec2 gradient = applySobel(uv, texSize);
// Non-maximum suppression for Canny
bool isCannyEdge = isLocalMaximum(uv, gradient, texSize);
// Simple thresholding for Canny edge detection
float edgeStrength = length(gradient);
bool isStrongCannyEdge = edgeStrength > high_threshold;
bool isWeakCannyEdge = edgeStrength > low_threshold;
// Pseudo hysteresis for Canny
bool hasStrongNeighbor = false;
if (isWeakCannyEdge && !isStrongCannyEdge) {
for (int x = -1; x <= 1; x++) {
for (int y = -1; y <= 1; y++) {
if (x == 0 && y == 0) continue; // Skip the center pixel
vec2 neighborUV = uv + vec2(float(x), float(y)) / texSize;
vec2 neighborGradient = applySobel(neighborUV, texSize);
float neighborStrength = length(neighborGradient);
if (neighborStrength > high_threshold) {
hasStrongNeighbor = true;
break;
}
}
if (hasStrongNeighbor) break;
}
}
// Apply Gaussian blur
vec3 blurredColor = applyGaussianBlur(uv, texSize);
// Apply Laplacian filter to the blurred image
float laplacianValue = applyLaplacianToBlurred(uv, texSize, blurredColor);
// Check for zero-crossings in Laplacian (edge detection)
bool isLoGEdge = laplacianValue < 0.0;
// Combine edge detections: Luminance-based, Canny, and LoG
bool isEdge = luminanceEdge || isCannyEdge || isLoGEdge || hasStrongNeighbor;
// Blend colors based on combined edge detection
vec3 originalColor = texture(BackBufferTex, uv).rgb;
vec3 finalColor;
if (isEdge) {
// Collect neighborhood colors
vec3 neighborhoodColors[9];
int index = 0;
for (int x = -1; x <= 1; x++) {
for (int y = -1; y <= 1; y++) {
vec2 offset = vec2(float(x), float(y)) / texSize;
neighborhoodColors[index++] = texture(BackBufferTex, uv + offset).rgb;
}
}
// Calculate depth difference for depth-aware blending
float depthDifference = getDepthDifference(uv, texSize, INV_PROJECTION_MATRIX);
// Advanced blending logic
vec2 offset = offM * offMult;
vec3 rgbM = sampleColor(uv);
vec3 rgbN = sampleColor(uv - offset);
vec3 rgbP = sampleColor(uv + offset);
// Calculate edge strength and other factors for adaptive blending
float edgeStrength = length(gradient);
float localContrast = calculateLocalContrast(neighborhoodColors);
float colorVariance = calculateColorVariance(neighborhoodColors);
// Modulate blend factor based on depth difference
float depthBlendFactor = 1.0 - smoothstep(0.0, depth_threshold, depthDifference);
// Combine factors for final blend factor
float adaptiveBlendFactor = smoothstep(0.0, 1.0, edgeStrength) * (1.0 - localContrast) * (1.0 - colorVariance) * depthBlendFactor;
vec3 rgbR;
if (cosine_blending) {
// Blend based on edge strength, local contrast, and color variance
vec3 blendedEdgeColor = cosineBlend(rgbN, rgbP, 0.5); // Smooth blend of neighboring colors
rgbR = cosineBlend(rgbM, blendedEdgeColor, adaptiveBlendFactor * 0.3 + 0.4);
} else {
// Blend based on combined factors
vec3 blendedEdgeColor = mix(rgbN, rgbP, 0.5); // Average of neighboring colors
rgbR = mix(rgbM, blendedEdgeColor, adaptiveBlendFactor * 0.3 + 0.4);
}
// Luminance check
float lumaR = getLuma(rgbR);
float lumaAMin = min(min(lumaA.x, lumaA.y), min(lumaA.z, lumaA.w));
float lumaAMax = max(max(lumaA.x, lumaA.y), max(lumaA.z, lumaA.w));
bool outOfRange = (lumaR < lumaAMin) || (lumaR > lumaAMax);
if (!outOfRange) {
finalColor = rgbR;
} else {
discard;
}
} else {
discard;
}
ALBEDO = finalColor;
// Apply dithering
//float dither = noise(uv * texSize); // Generate noise based on UV coordinates
//ALBEDO += (dither - 0.5) / 255.0; // Apply a small color offset
}
shader_type canvas_item;
uniform sampler2D BackBufferTex : hint_screen_texture, repeat_disable;
uniform float luma_threshold: hint_range(0, 1) = 0.375;
uniform float low_threshold: hint_range(0, 1) = 0.05;
uniform float high_threshold: hint_range(0, 1) = 0.2;
uniform bool cosine_blending = true;
// Constants for offsets
const ivec2 OffSW = ivec2(-1, 1);
const ivec2 OffSE = ivec2(1, 1);
const ivec2 OffNE = ivec2(1, -1);
const ivec2 OffNW = ivec2(-1, -1);
// Function to calculate luminance
float getLuma(vec3 color) {
return dot(color, vec3(0.299, 0.587, 0.114));
}
// Sampling functions
vec3 sampleColor(vec2 p) {
return texture(BackBufferTex, p).rgb;
}
// Function to sample luminance at an offset
float sampleLumaOff(vec2 uv, ivec2 offset, vec2 texSize) {
return getLuma(texture(BackBufferTex, uv + vec2(offset) / texSize).rgb);
}
// Sobel operator kernels
const mat3 sobel_kernel_x = mat3(vec3(-1, 0, 1),
vec3(-2, 0, 2),
vec3(-1, 0, 1));
const mat3 sobel_kernel_y = mat3(vec3(-1, -2, -1),
vec3(0, 0, 0),
vec3(1, 2, 1));
// Function to apply Sobel filter
vec2 applySobel(vec2 uv, vec2 texSize) {
float gx = 0.0;
float gy = 0.0;
for (int x = -1; x <= 1; x++) {
for (int y = -1; y <= 1; y++) {
vec3 sampleColor = texture(BackBufferTex, uv + vec2(float(x), float(y)) / texSize).rgb;
float luma = getLuma(sampleColor);
gx += luma * sobel_kernel_x[x + 1][y + 1];
gy += luma * sobel_kernel_y[x + 1][y + 1];
}
}
return vec2(gx, gy);
}
// Non-maximum suppression
bool isLocalMaximum(vec2 uv, vec2 gradient, vec2 texSize) {
float edgeStrength = length(gradient);
float angle = atan(gradient.y, gradient.x);
vec2 n = normalize(vec2(cos(angle), sin(angle)));
float luma1 = length(applySobel(uv + n / texSize, texSize));
float luma2 = length(applySobel(uv - n / texSize, texSize));
return edgeStrength >= luma1 && edgeStrength >= luma2;
}
// Gaussian kernel (5x5 approximation)
const float gaussian_kernel[25] = float[25](
1.0, 4.0, 7.0, 4.0, 1.0,
4.0, 16.0, 26.0, 16.0, 4.0,
7.0, 26.0, 41.0, 26.0, 7.0,
4.0, 16.0, 26.0, 16.0, 4.0,
1.0, 4.0, 7.0, 4.0, 1.0
);
// Function to apply Gaussian blur
vec3 applyGaussianBlur(vec2 uv, vec2 texSize) {
vec3 blurredColor = vec3(0.0);
float totalWeight = 0.0;
for (int x = -2; x <= 2; x++) {
for (int y = -2; y <= 2; y++) {
vec2 offset = vec2(float(x), float(y)) / texSize;
vec3 sampleColor = texture(BackBufferTex, uv + offset).rgb;
float weight = gaussian_kernel[(x + 2)*(y + 2)];
blurredColor += sampleColor * weight;
totalWeight += weight;
}
}
return blurredColor / totalWeight;
}
// Laplacian kernel
const mat3 laplacian_kernel = mat3(vec3(0, 1, 0),
vec3(1, -4, 1),
vec3(0, 1, 0));
// Function to apply Laplacian filter to blurred image
float applyLaplacianToBlurred(vec2 uv, vec2 texSize, vec3 blurredColor) {
float laplacianValue = 0.0;
for (int x = -1; x <= 1; x++) {
for (int y = -1; y <= 1; y++) {
vec2 offset = vec2(float(x), float(y)) / texSize;
vec3 sampleColor = texture(BackBufferTex, uv + offset).rgb;
if (x == 0 && y == 0) {
sampleColor = blurredColor; // Use the blurred color for the center pixel
}
laplacianValue += getLuma(sampleColor) * laplacian_kernel[x + 1][y + 1];
}
}
return laplacianValue;
}
float calculateLocalContrast(vec3 neighborhoodColors[9]) {
float centerLuma = getLuma(neighborhoodColors[4]); // Center pixel luminance
float averageLuma = 0.0;
for (int i = 0; i < 9; i++) {
if (i != 4) { // Exclude center pixel
averageLuma += getLuma(neighborhoodColors[i]);
}
}
averageLuma /= 8.0;
return abs(centerLuma - averageLuma);
}
float calculateColorVariance(vec3 neighborhoodColors[9]) {
vec3 meanColor = vec3(0.0);
for (int i = 0; i < 9; i++) {
meanColor += neighborhoodColors[i];
}
meanColor /= 9.0;
float variance = 0.0;
for (int i = 0; i < 9; i++) {
variance += distance(meanColor, neighborhoodColors[i]);
}
variance /= 9.0;
return variance;
}
// Cosine blend between two colors
vec3 cosineBlend(vec3 color1, vec3 color2, float factor) {
float cosFactor = smoothstep(0.0, 1.0, factor);
return mix(color1, color2, cosFactor);
}
// Simple noise function for dithering
float noise(vec2 co){
return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
}
void fragment() {
vec2 texSize = vec2(textureSize(BackBufferTex, 0));
vec2 RCP2 = 2.0 / texSize;
vec2 uv = FRAGCOORD.xy / texSize;
// Luminance-based edge detection
vec4 lumaA;
lumaA.x = sampleLumaOff(uv, OffSW, texSize);
lumaA.y = sampleLumaOff(uv, OffSE, texSize);
lumaA.z = sampleLumaOff(uv, OffNE, texSize);
lumaA.w = sampleLumaOff(uv, OffNW, texSize);
float gradientSWNE = lumaA.x - lumaA.z;
float gradientSENW = lumaA.y - lumaA.w;
vec2 dir = vec2(gradientSWNE + gradientSENW, gradientSWNE - gradientSENW);
vec2 dirM = abs(dir);
float lumaMax = max(max(lumaA.x, lumaA.y), max(lumaA.z, lumaA.w));
float localLumaFactor = lumaMax * 0.5 + 0.5;
float localThres = luma_threshold * localLumaFactor;
bool luminanceEdge = abs(dirM.x - dirM.y) >= localThres;
float dirMMin = min(dirM.x, dirM.y);
vec2 offM = clamp(vec2(0.0625) * dirM / dirMMin, 0.0, 1.0);
vec2 offMult = RCP2 * sign(dir);
float offMMax = max(offM.x, offM.y);
if (abs(offMMax - 1.0) < 0.0001) {
bool horSpan = abs(offM.x - 1.0) < 0.0001;
bool negSpan = horSpan ? offMult.x < 0.0 : offMult.y < 0.0;
bool sowSpan = horSpan == negSpan;
vec2 uvC = uv;
if (horSpan) uvC.x += 2.0 * offMult.x;
if (!horSpan) uvC.y += 2.0 * offMult.y;
vec4 lumaAC = lumaA;
if (sowSpan) lumaAC.x = sampleLumaOff(uvC, OffSW, texSize);
if (!negSpan) lumaAC.y = sampleLumaOff(uvC, OffSE, texSize);
if (!sowSpan) lumaAC.z = sampleLumaOff(uvC, OffNE, texSize);
if (negSpan) lumaAC.w = sampleLumaOff(uvC, OffNW, texSize);
float gradientSWNEC = lumaAC.x - lumaAC.z;
float gradientSENWC = lumaAC.y - lumaAC.w;
vec2 dirC = vec2(gradientSWNEC + gradientSENWC, gradientSWNEC - gradientSENWC);
if (!horSpan) dirC = dirC.yx;
bool passC = abs(dirC.x) > 2.0 * abs(dirC.y);
if (passC) offMult *= 2.0;
}
// Apply Sobel filter to get gradient for Canny edge detection
vec2 gradient = applySobel(uv, texSize);
// Non-maximum suppression for Canny
bool isCannyEdge = isLocalMaximum(uv, gradient, texSize);
// Simple thresholding for Canny edge detection
float edgeStrength = length(gradient);
bool isStrongCannyEdge = edgeStrength > high_threshold;
bool isWeakCannyEdge = edgeStrength > low_threshold;
// Pseudo hysteresis for Canny
bool hasStrongNeighbor = false;
if (isWeakCannyEdge && !isStrongCannyEdge) {
for (int x = -1; x <= 1; x++) {
for (int y = -1; y <= 1; y++) {
if (x == 0 && y == 0) continue; // Skip the center pixel
vec2 neighborUV = uv + vec2(float(x), float(y)) / texSize;
vec2 neighborGradient = applySobel(neighborUV, texSize);
float neighborStrength = length(neighborGradient);
if (neighborStrength > high_threshold) {
hasStrongNeighbor = true;
break;
}
}
if (hasStrongNeighbor) break;
}
}
// Apply Gaussian blur
vec3 blurredColor = applyGaussianBlur(uv, texSize);
// Apply Laplacian filter to the blurred image
float laplacianValue = applyLaplacianToBlurred(uv, texSize, blurredColor);
// Check for zero-crossings in Laplacian (edge detection)
bool isLoGEdge = laplacianValue < 0.0;
// Combine edge detections: Luminance-based, Canny, and LoG
bool isEdge = luminanceEdge || isCannyEdge || isLoGEdge || hasStrongNeighbor;
// Blend colors based on combined edge detection
vec3 originalColor = texture(BackBufferTex, uv).rgb;
vec3 finalColor;
if (isEdge) {
// Collect neighborhood colors before offset
vec3 neighborhoodColors[9];
int index = 0;
for (int x = -1; x <= 1; x++) {
for (int y = -1; y <= 1; y++) {
vec2 offset = vec2(float(x), float(y)) / texSize;
neighborhoodColors[index++] = texture(BackBufferTex, uv + offset).rgb;
}
}
// Advanced blending logic
vec2 offset = offM * offMult;
vec3 rgbM = sampleColor(uv);
vec3 rgbN = sampleColor(uv - offset);
vec3 rgbP = sampleColor(uv + offset);
// Calculate edge strength for adaptive blending
float edgeStrength = length(gradient);
float localContrast = calculateLocalContrast(neighborhoodColors);
float colorVariance = calculateColorVariance(neighborhoodColors);
float adaptiveBlendFactor = smoothstep(0.0, 1.0, edgeStrength) * (1.0 - localContrast) * (1.0 - colorVariance);
vec3 rgbR;
if (cosine_blending) {
// Blend based on edge strength, local contrast, and color variance
vec3 blendedEdgeColor = cosineBlend(rgbN, rgbP, 0.5); // Smooth blend of neighboring colors
rgbR = cosineBlend(rgbM, blendedEdgeColor, adaptiveBlendFactor * 0.3 + 0.4);
} else {
// Blend based on combined factors
vec3 blendedEdgeColor = mix(rgbN, rgbP, 0.5); // Average of neighboring colors
rgbR = mix(rgbM, blendedEdgeColor, adaptiveBlendFactor * 0.3 + 0.4);
}
// Luminance check
float lumaR = getLuma(rgbR);
float lumaAMin = min(min(lumaA.x, lumaA.y), min(lumaA.z, lumaA.w));
float lumaAMax = max(max(lumaA.x, lumaA.y), max(lumaA.z, lumaA.w));
bool outOfRange = (lumaR < lumaAMin) || (lumaR > lumaAMax);
if (!outOfRange) {
finalColor = rgbR;
} else {
discard;
}
} else {
discard;
}
COLOR = vec4(finalColor, 1.0);
// Apply dithering
//float dither = noise(uv * texSize); // Generate noise based on UV coordinates
//COLOR.rgb += (dither - 0.5) / 255.0; // Apply a small color offset
}
[gd_scene load_steps=6 format=3 uid="uid://y2but6np0htd"]
[ext_resource type="Shader" path="res://HHAA-D.gdshader" id="1_bbn3p"]
[ext_resource type="Shader" path="res://HHAA-S.gdshader" id="2_24bkf"]
[sub_resource type="ShaderMaterial" id="ShaderMaterial_j20el"]
render_priority = -128
shader = ExtResource("1_bbn3p")
shader_parameter/luma_threshold = 0.375
shader_parameter/low_threshold = 0.05
shader_parameter/high_threshold = 0.2
shader_parameter/depth_threshold = 0.1
shader_parameter/cosine_blending = true
[sub_resource type="QuadMesh" id="QuadMesh_6r3u8"]
material = SubResource("ShaderMaterial_j20el")
flip_faces = true
size = Vector2(2, 2)
[sub_resource type="ShaderMaterial" id="ShaderMaterial_lutf1"]
shader = ExtResource("2_24bkf")
shader_parameter/luma_threshold = 0.375
shader_parameter/low_threshold = 0.05
shader_parameter/high_threshold = 0.2
shader_parameter/cosine_blending = true
[node name="HHAA-D" type="MeshInstance3D"]
extra_cull_margin = 16384.0
mesh = SubResource("QuadMesh_6r3u8")
[node name="HHAA-S" type="ColorRect" parent="."]
material = SubResource("ShaderMaterial_lutf1")
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
@Xynonners
Copy link
Author

to use, stack D&S, put D in 2x2 flip faces QuadMesh with as much Extra Cull Margin as possible - as child of Camera3D, then put S in full rect ColorRect as child of D.

@Xynonners
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment