效果见图:
shader 代码修改如下, 主要看 USE_MASK_UVY 关键字部分修改:
// Copyright (c) 2017-2020 Xiamen Yaji Software Co., Ltd.
CCEffect %{
techniques:
- name: opaque
passes:
- vert: unlit-vs:vert
frag: unlit-fs:frag
properties: &props
mainTexture: { value: grey }
tilingOffset: { value: [1, 1, 0, 0] }
mainColor: { value: [1, 1, 1, 1], linear: true, editor: { type: color } }
colorScale: { value: [1, 1, 1], target: colorScaleAndCutoff.xyz }
alphaThreshold: { value: 0.5, target: colorScaleAndCutoff.w, editor: { parent: USE_ALPHA_TEST } }
color: { target: mainColor, linear: true, editor: { visible: false } } # backward compability
mask_uvpos: { value: 0.5, target: mask.x, editor: { parent: USE_MASK_UVY } } # uv 采样中心位置
mask_width: { value: 0.1, target: mask.y, editor: { parent: USE_MASK_UVY, range: [0, 1] } } # uv 采样上下宽度范围
mask_lerpwidth: { value: 0.05, target: mask.z, editor: { parent: USE_MASK_UVY , range: [0, 1]} } # uv 采样边缘平滑宽度
mask_alpha_strength: { value: 1, target: mask.w, editor: { parent: USE_MASK_UVY } } # uv 采样透明度强度
migrations: &migs
properties:
mainColor: { formerlySerializedAs: color }
- &planar-shadow
vert: planar-shadow-vs:vert
frag: planar-shadow-fs:frag
phase: planar-shadow
propertyIndex: 0
depthStencilState:
depthTest: true
depthWrite: false
stencilTestFront: true
stencilFuncFront: not_equal
stencilPassOpFront: replace
stencilRef: 0x80 # only use the leftmost bit
stencilReadMask: 0x80
stencilWriteMask: 0x80
blendState:
targets:
- blend: true
blendSrc: src_alpha
blendDst: one_minus_src_alpha
blendDstAlpha: one_minus_src_alpha
- &deferred-forward
vert: unlit-vs:vert
frag: unlit-fs:frag
phase: deferred-forward
propertyIndex: 0
- name: transparent
passes:
- vert: unlit-vs:vert
frag: unlit-fs:frag
depthStencilState: &d1
depthTest: true
depthWrite: false
blendState: &b1
targets:
- blend: true
blendSrc: src_alpha
blendDst: one_minus_src_alpha
blendDstAlpha: one_minus_src_alpha
properties: *props
migrations: *migs
- *planar-shadow
- &deferred-forward-transparent
vert: unlit-vs:vert
frag: unlit-fs:frag
phase: deferred-forward
propertyIndex: 0
migrations: *migs
depthStencilState: *d1
blendState: *b1
- name: add
passes:
- vert: unlit-vs:vert
frag: unlit-fs:frag
rasterizerState: &r1 { cullMode: none }
depthStencilState: *d1
blendState: &b2
targets:
- blend: true
blendSrc: src_alpha
blendDst: one
blendSrcAlpha: src_alpha
blendDstAlpha: one
properties: *props
migrations: *migs
- &deferred-forward-add
vert: unlit-vs:vert
frag: unlit-fs:frag
phase: deferred-forward
rasterizerState: *r1
depthStencilState: *d1
blendState: *b2
propertyIndex: 0
migrations: *migs
- name: alpha-blend
passes:
- vert: unlit-vs:vert
frag: unlit-fs:frag
rasterizerState: *r1
depthStencilState: *d1
blendState: &b3
targets:
- blend: true
blendSrc: src_alpha
blendDst: one_minus_src_alpha
blendSrcAlpha: src_alpha
blendDstAlpha: one_minus_src_alpha
properties: *props
migrations: *migs
- &deferred-forward-alpha-blend
vert: unlit-vs:vert
frag: unlit-fs:frag
phase: deferred-forward
rasterizerState: *r1
depthStencilState: *d1
blendState: *b3
propertyIndex: 0
migrations: *migs
}%
CCProgram unlit-vs %{
precision highp float;
#include <legacy/input>
#include <builtin/uniforms/cc-global>
#include <legacy/decode-base>
#include <legacy/local-batch>
#include <legacy/input>
#include <legacy/fog-vs>
#if USE_VERTEX_COLOR
in lowp vec4 a_color;
out lowp vec4 v_color;
#endif
#if USE_TEXTURE
out vec2 v_uv;
uniform TexCoords {
vec4 tilingOffset;
};
#endif
vec4 vert () {
vec4 position;
CCVertInput(position);
mat4 matWorld;
CCGetWorldMatrix(matWorld);
#if USE_TEXTURE
v_uv = a_texCoord * tilingOffset.xy + tilingOffset.zw;
#if SAMPLE_FROM_RT
CC_HANDLE_RT_SAMPLE_FLIP(v_uv);
#endif
#endif
#if USE_VERTEX_COLOR
v_color = a_color;
#endif
CC_TRANSFER_FOG(matWorld * position);
return cc_matProj * (cc_matView * matWorld) * position;
}
}%
CCProgram unlit-fs %{
precision highp float;
#include <legacy/output-standard>
#include <legacy/fog-fs>
#if USE_ALPHA_TEST
#pragma define-meta ALPHA_TEST_CHANNEL options([a, r, g, b])
#endif
#if USE_TEXTURE
in vec2 v_uv;
uniform sampler2D mainTexture;
#endif
uniform Constant {
vec4 mainColor;
vec4 colorScaleAndCutoff;
vec4 mask; // USE_MASK_UVY
};
#if USE_VERTEX_COLOR
in lowp vec4 v_color;
#endif
#if USE_MASK_UVY
// float LerpMask(float uv_y, vec4 mask) {
// float d = abs(uv_y - mask.x); // uv y 与中心点的距离
// float a = mask.w;
// float d2 = d - mask.y; // 距离小于 mask.y(mask宽度范围) 则显示全值
// if(d2 > 0.0) {
// if(d2 <= mask.z && mask.z > 0.0) {
// a *= (1.0 - (d2 /mask.z)); // 距离大于 mask.y 且 小于 mask.z(mask边缘平滑宽度) 则开始衰减
// } else {
// a = 0.0;
// }
// }
// return a;
// }
float LinearStep(float t1, float t2, float x)
{
x = clamp((x-t1)/(t2-t1),0.0,1.0);
// return x*x*(3-2*x); // smoothstep
return x;
}
#endif
vec4 frag () {
vec4 o = mainColor;
o.rgb *= colorScaleAndCutoff.xyz;
#if USE_VERTEX_COLOR
o.rgb *= SRGBToLinear(v_color.rgb);//use linear
o.a *= v_color.a;
#endif
#if USE_TEXTURE
vec4 texColor = texture(mainTexture, v_uv);
texColor.rgb = SRGBToLinear(texColor.rgb);
o *= texColor;
#endif
#if USE_MASK_UVY
// o.a *= LerpMask(v_uv.y, mask);
o.a *= LinearStep(mask.z, 0.0, abs(v_uv.y - mask.x) - mask.y) * mask.w;
#endif
#if USE_ALPHA_TEST
if (o.ALPHA_TEST_CHANNEL < colorScaleAndCutoff.w) discard;
#endif
CC_APPLY_FOG(o);
return CCFragOutput(o);
}
}%
CCProgram planar-shadow-vs %{
precision highp float;
#include <legacy/input>
#include <builtin/uniforms/cc-global>
#include <legacy/decode-base>
#include <legacy/local-batch>
#include <builtin/uniforms/cc-shadow>
#include <common/lighting/functions>
out float v_dist;
vec4 vert () {
vec4 position;
CCVertInput(position);
// World Space
mat4 matWorld, matWorldIT;
CCGetWorldMatrixFull(matWorld, matWorldIT);
vec3 worldPos = (matWorld * position).xyz;
vec4 shadowPos = CalculatePlanarShadowPos(worldPos, cc_cameraPos.xyz, cc_mainLitDir.xyz, cc_planarNDInfo);
position = CalculatePlanarShadowClipPos(shadowPos, cc_cameraPos.xyz, cc_matView, cc_matProj, cc_nearFar, cc_shadowWHPBInfo.w);
v_dist = shadowPos.w;
return position;
}
}%
CCProgram planar-shadow-fs %{
precision highp float;
#include <builtin/uniforms/cc-shadow>
#include <legacy/output>
in float v_dist;
vec4 frag () {
if(v_dist < 0.0)
discard;
return CCFragOutput(cc_shadowColor);
}
}%
即加了4个参数:
mask_uvpos: { value: 0.5, target: mask.x, editor: { parent: USE_MASK_UVY } } # uv 采样中心位置
mask_width: { value: 0.1, target: mask.y, editor: { parent: USE_MASK_UVY, range: [0, 1] } } # uv 采样上下宽度范围
mask_lerpwidth: { value: 0.05, target: mask.z, editor: { parent: USE_MASK_UVY , range: [0, 1]} } # uv 采样边缘平滑宽度
mask_alpha_strength: { value: 1, target: mask.w, editor: { parent: USE_MASK_UVY } } # uv 采样透明度强度
核心算法就一句:
o.a *= smoothstep(mask.z, 0.0, abs(v_uv.y - mask.x) - mask.y) * mask.w;
感觉平滑过渡反而不好,所以自己实现了一个函数 LinearStep:
o.a *= LinearStep(mask.z, 0.0, abs(v_uv.y - mask.x) - mask.y) * mask.w;
注释掉的 LerpMask 是等价这个算法的,只不过用了 if else来实现的。
算法思路如下: 靠近 pos width内的 全部显示, 接着lerpwidth内的渐变显示, 超过这个距离的不显示: