// Copyright (c) 2017-2020 Xiamen Yaji Software Co., Ltd. CCEffect %{ techniques: - passes: - vert: sprite-vs:vert frag: sprite-fs:frag depthStencilState: depthTest: false depthWrite: false blendState: targets: - blend: true blendSrc: src_alpha blendDst: one_minus_src_alpha blendDstAlpha: one_minus_src_alpha rasterizerState: cullMode: none properties: edgeColor: { value: [1.0, 1.0, 1.0, 1.0] } textureSize: { value: [512.0, 512.0] } edgeWidth: { value: 50.0 } alphaThreshold: { value: 0.5 } }% CCProgram sprite-vs %{ precision highp float; #include #if USE_LOCAL #include #endif #if SAMPLE_FROM_RT #include #endif in vec3 a_position; in vec2 a_texCoord; in vec4 a_color; out vec4 color; out vec2 uv0; vec4 vert () { vec4 pos = vec4(a_position, 1); #if USE_LOCAL pos = cc_matWorld * pos; #endif #if USE_PIXEL_ALIGNMENT pos = cc_matView * pos; pos.xyz = floor(pos.xyz); pos = cc_matProj * pos; #else pos = cc_matViewProj * pos; #endif uv0 = a_texCoord; #if SAMPLE_FROM_RT CC_HANDLE_RT_SAMPLE_FLIP(uv0); #endif color = a_color; return pos; } }% CCProgram sprite-fs %{ precision highp float; #include #include #define MAX_EDGE_WIDTH 50.0 // 最大边缘宽度(可根据需求调整) in vec4 color; #if USE_TEXTURE in vec2 uv0; #pragma builtin(local) layout(set = 2, binding = 12) uniform sampler2D cc_spriteTexture; #endif uniform EdgeUniforms { vec4 edgeColor; vec2 textureSize; float edgeWidth; }; vec4 frag () { vec4 o = vec4(1, 1, 1, 1); #if USE_TEXTURE o *= CCSampleWithAlphaSeparated(cc_spriteTexture, uv0); // Edge detection based on alpha channel vec2 texelSize = 1.0 / textureSize; float currentAlpha = o.a; // Sample surrounding pixels to detect edge float edgeDetected = 0.0; if (currentAlpha > 0.01) { // Check if we're at the edge by sampling neighboring pixels float maxDistance = min(edgeWidth, MAX_EDGE_WIDTH); bool isEdge = false; // Check boundary conditions first (image edges) vec2 pixelPos = uv0 * textureSize; float distToLeft = pixelPos.x; float distToRight = textureSize.x - pixelPos.x; float distToTop = pixelPos.y; float distToBottom = textureSize.y - pixelPos.y; float minDistToBorder = min(min(distToLeft, distToRight), min(distToTop, distToBottom)); if (minDistToBorder <= maxDistance) { isEdge = true; edgeDetected = max(edgeDetected, 1.0 - (minDistToBorder / maxDistance)); } // Also check for alpha transitions if (!isEdge) { // 改为固定范围循环(-MAX_EDGE_WIDTH 到 MAX_EDGE_WIDTH) for (float x = -MAX_EDGE_WIDTH; x <= MAX_EDGE_WIDTH; x += 1.0) { for (float y = -MAX_EDGE_WIDTH; y <= MAX_EDGE_WIDTH; y += 1.0) { if (x == 0.0 && y == 0.0) continue; vec2 sampleUV = uv0 + vec2(x, y) * texelSize; float sampleAlpha = CCSampleWithAlphaSeparated(cc_spriteTexture, sampleUV).a; // If we find a transparent pixel nearby, we're at an edge if (sampleAlpha < 0.01) { float distance = length(vec2(x, y)); if (distance <= maxDistance) { float edgeStrength = 1.0 - (distance / maxDistance); edgeDetected = max(edgeDetected, edgeStrength); isEdge = true; } } } } } } // Apply edge effect if (edgeDetected > 0.0) { o.rgb = mix(o.rgb, edgeColor.rgb, edgeDetected * edgeColor.a); } #endif o *= color; ALPHA_TEST(o); return o; } }%