You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
199 lines
4.9 KiB
199 lines
4.9 KiB
|
1 week ago
|
// 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 <builtin/uniforms/cc-global>
|
||
|
|
|
||
|
|
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 <legacy/output>
|
||
|
|
|
||
|
|
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;
|
||
|
|
}
|
||
|
|
}%
|