效果见图:
shader 代码修改如下, 主要看 USE_MASK_UVY 关键字部分修改:
// Copyright (c) 2017-2020 Xiamen Yaji Software Co., Ltd.
CCEffect %{techniques:- name: opaquepasses:- vert: unlit-vs:vertfrag: unlit-fs:fragproperties: &propsmainTexture: { 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 compabilitymask_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: &migsproperties:mainColor: { formerlySerializedAs: color }- &planar-shadowvert: planar-shadow-vs:vertfrag: planar-shadow-fs:fragphase: planar-shadowpropertyIndex: 0depthStencilState:depthTest: truedepthWrite: falsestencilTestFront: truestencilFuncFront: not_equalstencilPassOpFront: replacestencilRef: 0x80 # only use the leftmost bitstencilReadMask: 0x80stencilWriteMask: 0x80blendState:targets:- blend: trueblendSrc: src_alphablendDst: one_minus_src_alphablendDstAlpha: one_minus_src_alpha- &deferred-forwardvert: unlit-vs:vertfrag: unlit-fs:fragphase: deferred-forwardpropertyIndex: 0- name: transparentpasses:- vert: unlit-vs:vertfrag: unlit-fs:fragdepthStencilState: &d1depthTest: truedepthWrite: falseblendState: &b1targets:- blend: trueblendSrc: src_alphablendDst: one_minus_src_alphablendDstAlpha: one_minus_src_alphaproperties: *propsmigrations: *migs- *planar-shadow- &deferred-forward-transparentvert: unlit-vs:vertfrag: unlit-fs:fragphase: deferred-forwardpropertyIndex: 0migrations: *migsdepthStencilState: *d1blendState: *b1- name: addpasses:- vert: unlit-vs:vertfrag: unlit-fs:fragrasterizerState: &r1 { cullMode: none }depthStencilState: *d1blendState: &b2targets:- blend: trueblendSrc: src_alphablendDst: oneblendSrcAlpha: src_alphablendDstAlpha: oneproperties: *propsmigrations: *migs- &deferred-forward-addvert: unlit-vs:vertfrag: unlit-fs:fragphase: deferred-forwardrasterizerState: *r1depthStencilState: *d1blendState: *b2propertyIndex: 0migrations: *migs- name: alpha-blendpasses:- vert: unlit-vs:vertfrag: unlit-fs:fragrasterizerState: *r1depthStencilState: *d1blendState: &b3targets:- blend: trueblendSrc: src_alphablendDst: one_minus_src_alphablendSrcAlpha: src_alphablendDstAlpha: one_minus_src_alphaproperties: *propsmigrations: *migs- &deferred-forward-alpha-blendvert: unlit-vs:vertfrag: unlit-fs:fragphase: deferred-forwardrasterizerState: *r1depthStencilState: *d1blendState: *b3propertyIndex: 0migrations: *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_COLORin lowp vec4 a_color;out lowp vec4 v_color;#endif#if USE_TEXTUREout vec2 v_uv;uniform TexCoords {vec4 tilingOffset;};#endifvec4 vert () {vec4 position;CCVertInput(position);mat4 matWorld;CCGetWorldMatrix(matWorld);#if USE_TEXTUREv_uv = a_texCoord * tilingOffset.xy + tilingOffset.zw;#if SAMPLE_FROM_RTCC_HANDLE_RT_SAMPLE_FLIP(v_uv);#endif#endif#if USE_VERTEX_COLORv_color = a_color;#endifCC_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_TEXTUREin vec2 v_uv;uniform sampler2D mainTexture;#endifuniform Constant {vec4 mainColor;vec4 colorScaleAndCutoff;vec4 mask; // USE_MASK_UVY};#if USE_VERTEX_COLORin 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); // smoothstepreturn x;}#endifvec4 frag () {vec4 o = mainColor;o.rgb *= colorScaleAndCutoff.xyz;#if USE_VERTEX_COLORo.rgb *= SRGBToLinear(v_color.rgb);//use linearo.a *= v_color.a;#endif#if USE_TEXTUREvec4 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_TESTif (o.ALPHA_TEST_CHANNEL < colorScaleAndCutoff.w) discard;#endifCC_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 Spacemat4 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内的渐变显示, 超过这个距离的不显示: