// chenshw23 // cocos store : https://store.cocos.com/dashboard/detail/8073 // 修改说明:增加了边缘虚化功能 (edgeBlurRadius) CCEffect %{ techniques: - passes: - vert: lightScene-vs:vert frag: lightScene-fs:frag depthStencilState: depthTest: false depthWrite: false blendState: targets: - blend: true blendSrc: src_alpha blendDst: one_minus_src_alpha rasterizerState: cullMode: none properties: wh_ratio: { value: 2.22222 } lights: { value: [-1, 0.5, -1, 0.05]} occluders: { value: [0.0, 0.0, 0.0, 0.0]} lightShadow: { value: [0.0, 0.0, 0.0, 0.0]} edgeBlurRadius: { value: 0.1 } }% CCProgram lightScene-vs %{ precision highp float; #include in vec3 a_position; in vec2 a_texCoord; in vec4 a_color; out vec4 v_color; out vec2 v_uv; vec4 vert() { vec4 pos = vec4(a_position, 1); pos = cc_matViewProj * pos; v_uv = a_texCoord; v_color = a_color; return pos; } }% CCProgram lightScene-fs %{ precision highp float; #include in vec2 v_uv; in vec4 v_color; uniform sampler2D mainTexture; #pragma define light_max_num 30 #pragma define occluder_max_num 200 #pragma define PI 3.14159265359 uniform Constant { vec4 lights[light_max_num]; vec4 lightShadow[light_max_num]; vec4 occluders[occluder_max_num]; float wh_ratio; float edgeBlurRadius; }; bool lineSegmentIntersectOptimized(vec2 p1, vec2 p2, vec2 p3, vec2 p4) { if (max(p1.x, p2.x) < min(p3.x, p4.x) || max(p3.x, p4.x) < min(p1.x, p2.x) || max(p1.y, p2.y) < min(p3.y, p4.y) || max(p3.y, p4.y) < min(p1.y, p2.y)) { return false; } vec2 a = p2 - p1; vec2 b = p4 - p3; vec2 c = p3 - p1; float denom = a.x * b.y - a.y * b.x; if (abs(denom) < 1e-6) { return false; } float t = (c.x * b.y - c.y * b.x) / denom; float u = (c.x * a.y - c.y * a.x) / denom; return (t >= 0.0 && t <= 1.0 && u >= 0.0 && u <= 1.0); } bool isLightOccluder(vec2 lightPos) { bool isCross = false; vec2 bPos = vec2(0.0); int nextNum = 0; bool isLast = false; for (int o_i = 0; o_i < occluder_max_num; o_i++) { if (o_i == nextNum) { if (occluders[o_i].x <= 0.0) { break; } isLast = occluders[o_i].y <= 0.0; bPos = occluders[o_i].zw; nextNum = o_i + int(occluders[o_i].x) + 1; } else { vec2 p_prev = (o_i == 0) ? bPos : occluders[o_i-1].zw; // 简单保护 // 原逻辑还原开始 isCross = lineSegmentIntersectOptimized(v_uv, lightPos, occluders[o_i-1].zw, occluders[o_i].xy); if (isCross) {break;} if (o_i+1 != nextNum || isLast) { isCross = lineSegmentIntersectOptimized(v_uv, lightPos, occluders[o_i].xy, occluders[o_i].zw); if (isCross) {break;} } if (o_i+1 == nextNum) { isCross = lineSegmentIntersectOptimized(v_uv, lightPos, isLast ? occluders[o_i].zw : occluders[o_i].xy, bPos); if (isCross) {break;} } // 原逻辑还原结束 } } return isCross; } vec4 frag() { vec4 col = v_color; // 1. 原有的光照计算逻辑 for (int c_i = 0; c_i < light_max_num; c_i++) { if (lights[c_i].x <= -1.0) { break; } vec2 v2 = v_uv - lights[c_i].xy; v2.x *= wh_ratio; float dis = length(v2); if (dis <= (lights[c_i].z + lights[c_i].w)) { bool isCross = false; #if CALC_OCCLUDER if (lightShadow[c_i].x > 0.5) { isCross = isLightOccluder(lights[c_i].xy); } #endif if (!isCross) { if (dis <= lights[c_i].z) { col.a = 0.0; break; } else if (lights[c_i].w > 0.0 && dis <= (lights[c_i].z + lights[c_i].w)) { float t = smoothstep(0.0, 1.0, (dis - lights[c_i].z) / lights[c_i].w); col.a -= (1.0-t); } if (col.a <= 0.0) { col.a = 0.0; break; } } } } // 如果光照已经让透明度为 0,直接返回,无需计算边缘 if (col.a <= 0.0) { col.a = 0.0; return col; } // 2. 新增:边缘虚化逻辑 if (edgeBlurRadius > 0.0) { // 计算当前像素距离四边的距离 (UV 范围 0.0 ~ 1.0) float distLeft = v_uv.x; float distRight = 1.0 - v_uv.x; float distBottom = v_uv.y; float distTop = 1.0 - v_uv.y; // 取最小距离 float minDist = min(min(distLeft, distRight), min(distBottom, distTop)); // 如果距离小于虚化半径,进行平滑过渡 if (minDist < edgeBlurRadius) { // 使用 smoothstep 产生柔和的渐变 // 当 minDist 为 0 时,factor 为 0 (完全透明) // 当 minDist 为 edgeBlurRadius 时,factor 为 1 (不透明) float factor = smoothstep(0.0, edgeBlurRadius, minDist); col.a *= factor; } } // 最终钳制透明度 if (col.a <= 0.0) { col.a = 0.0; } return col; } }%