SD3.图片仿PS投影Shader
大约 5 分钟
/**
PS图层样式投影效果
@param {UVs} texture coordinate
@param {Texture} texture object
@param {TextureSize} 纹理大小(x, y)
@param {ShadowRGBA} 投影颜色与不透明度
@param {ShadowRotate} 投影角度
@param {ShadowLength} 投影距离
@param {ShadowSize} 投影大小
@param {BorderThreshold} 边界UVs阈值(左, 上, 右, 下)
*/
float4 Shadow(float2 UVs, Texture2D Texture, float2 TextureSize, float4 ShadowRGBA, float ShadowRotate, half ShadowLength, half ShadowSize, float4 BorderThreshold=0.001) {
const float PI = acos(-1);
// 单位像素
float2 TexturePixel = 1 / TextureSize;
// 角度
float Angle = 360 * ShadowRotate;
// 弧度
float Degrees = Angle / 180 * PI;
// 阴影反向方位(单位向量)
float2 Direction = TexturePixel * float2(cos(Degrees), sin(Degrees));
class Function {
Texture2D Texture;
SamplerState TextureSampler;
float4 ShadowRGBA;
float2 Position;
float BorderThresholdLeft;
float BorderThresholdTop;
float BorderThresholdRight;
float BorderThresholdBottom;
float PI;
float2 TexturePixel;
// 阴影颜色
float3 ShadowColor(float3 Color) {
// 如果需要与颜色混合,在此修改返回值,如下式 正片叠底。
// return this.ShadowRGBA.rgb * Color;
return this.ShadowRGBA.rgb;
}
// 混合
float3 Blend(float3 base, float3 blend, float alpha) {
// 如果使用了混合模式,把 blend 按混合公式计算一次,如下式 正片叠底。
// blend = base * blend;
return lerp(base, blend, alpha);
}
// 纹理采样
float4 TextureSample(float2 UVs) {
// 如果需要 alpha 通道反向,在此修改。
return Texture2DSampleLevel(this.Texture, this.TextureSampler, UVs, 0).xyzw;
}
float4 GetShadowRGBA(float2 UVs) {
// 当前像素点 RGBA
float4 TextureRGBA = this.TextureSample(UVs);
// 阴影反向方位 UVs
float2 PositionUVs = UVs + this.Position;
// 阴影反向方位 UVs 超出了 0 - 1 的范围则不计算
if (PositionUVs.x < this.BorderThresholdLeft || PositionUVs.x > this.BorderThresholdRight || PositionUVs.y < this.BorderThresholdTop || PositionUVs.y > this.BorderThresholdBottom) {
return TextureRGBA;
}
// 阴影反向方位像素点RGBA
float4 PositionRGBA = this.TextureSample(PositionUVs);
// 阴影透明度
float ShadowOpacity = PositionRGBA.a * this.ShadowRGBA.a;
if (ShadowOpacity <= 0) {
return TextureRGBA;
}
// 当前像素点混合后的结果色
// this.ShadowRGBA.rgb 为 base 固有色
// TextureRGBA.rgb 为 blend 固有色
// TextureRGBA.a 为 alpha
float3 ShadowBlendColor = this.Blend(this.ShadowColor(PositionRGBA.rgb) * ShadowOpacity, TextureRGBA.rgb, TextureRGBA.a);
// 当前像素点混合后的透明度
float ShadowBlendOpacity = ShadowOpacity + TextureRGBA.a - ShadowOpacity * TextureRGBA.a;
// 当前像素点混合后的RGBA
return float4(ShadowBlendColor / ShadowBlendOpacity, ShadowBlendOpacity);
}
float Calculate1DGaussian(float x) {
return exp(-0.5 * pow(this.PI * x, 2));
}
float4 GetShadowSizeRGBA(float2 UVs, half ShadowSize) {
// 当前像素点 RGBA
float4 TextureRGBA = this.TextureSample(UVs);
// 投影大小范围内像素颜色累加
float4 RGBASum = float4(0, 0, 0, 0);
// 投影大小范围内像素的权重
float WeightSum = 0;
for (half x = -ShadowSize; x <= ShadowSize; x++) {
for (half y = -ShadowSize; y <= ShadowSize; y++) {
float Weight = this.Calculate1DGaussian(x / ShadowSize) * this.Calculate1DGaussian(y / ShadowSize);
WeightSum += Weight;
// 阴影偏移 UVs
float2 OffsetUVs = UVs + float2(x, y) * this.TexturePixel + this.Position;
if (OffsetUVs.x < this.BorderThresholdLeft || OffsetUVs.x > this.BorderThresholdRight || OffsetUVs.y < this.BorderThresholdTop || OffsetUVs.y > this.BorderThresholdBottom) {
continue;
}
// 阴影偏移像素点 RGBA
float4 OffsetRGBA = this.TextureSample(OffsetUVs);
// 阴影透明度
float Opacity = this.ShadowRGBA.a * OffsetRGBA.a;
if (Opacity <= 0) {
continue;
}
// 阴影结果色
float4 RGBA = float4(this.ShadowColor(OffsetRGBA.rgb), Opacity);
RGBASum += RGBA * Weight;
}
}
// 模糊后的 RGBA
float4 BlurRGBA = RGBASum / WeightSum;
// 当前像素点混合后的结果色
float3 Color = this.Blend(BlurRGBA.rgb * BlurRGBA.a, TextureRGBA.rgb, TextureRGBA.a);
// 当前像素点混合后的透明度
float Opacity = BlurRGBA.a + TextureRGBA.a - BlurRGBA.a * TextureRGBA.a;
// 当前像素点混合后的RGBA
return float4(Color / Opacity, Opacity);
}
}; // 注意要加分号
// Function func;
// func.Texture = Texture;
// func.TextureSampler = TextureSampler;
// func.ShadowRGBA = ShadowRGBA;
// func.Position = ShadowLength * Direction;
// func.BorderThresholdLeft = BorderThreshold.x;
// func.BorderThresholdTop = BorderThreshold.y;
// func.BorderThresholdRight = 1 - BorderThreshold.z;
// func.BorderThresholdBottom = 1 - BorderThreshold.w;
// func.PI = PI;
// func.TexturePixel = TexturePixel;
Function func = { Texture, TextureSampler, ShadowRGBA, ShadowLength * Direction, BorderThreshold.x, BorderThreshold.y, 1 - BorderThreshold.z, 1 - BorderThreshold.w, PI, TexturePixel };
if (ShadowSize < 1) {
return func.GetShadowRGBA(UVs);
}
return func.GetShadowSizeRGBA(UVs, round(ShadowSize));
}
参考自csdn
/**
PS图层样式投影效果
@param {UVs} texture coordinate
@param {Texture} texture object
@param {TextureSize} 纹理大小(x, y)
@param {ShadowRGBA} 投影颜色与不透明度
@param {ShadowRotate} 投影角度
@param {ShadowLength} 投影距离
@param {ShadowSize} 投影大小
@param {BorderThreshold} 边界UVs阈值(左, 上, 右, 下)
*/
const float PI = acos(-1);
// 单位像素
float2 TexturePixel = 1 / TextureSize;
// 角度
float Angle = 360 * ShadowRotate;
// 弧度
float Degrees = Angle / 180 * PI;
// 阴影反向方位(单位向量)
float2 Direction = TexturePixel * float2(cos(Degrees), sin(Degrees));
class Function {
Texture2D Texture;
SamplerState TextureSampler;
float4 ShadowRGBA;
float2 Position;
float BorderThresholdLeft;
float BorderThresholdTop;
float BorderThresholdRight;
float BorderThresholdBottom;
float PI;
float2 TexturePixel;
// 阴影颜色
float3 ShadowColor(float3 Color) {
// 如果需要与颜色混合,在此修改返回值,如下式 正片叠底。
// return this.ShadowRGBA.rgb * Color;
return this.ShadowRGBA.rgb;
}
// 混合
float3 Blend(float3 base, float3 blend, float alpha) {
// 如果使用了混合模式,把 blend 按混合公式计算一次,如下式 正片叠底。
// blend = base * blend;
return lerp(base, blend, alpha);
}
// 纹理采样
float4 TextureSample(float2 UVs) {
// 如果需要 alpha 通道反向,在此修改。
return Texture2DSampleLevel(this.Texture, this.TextureSampler, UVs, 0).xyzw;
}
float4 GetShadowRGBA(float2 UVs) {
// 当前像素点 RGBA
float4 TextureRGBA = this.TextureSample(UVs);
// 阴影反向方位 UVs
float2 PositionUVs = UVs + this.Position;
// 阴影反向方位 UVs 超出了 0 - 1 的范围则不计算
if (PositionUVs.x < this.BorderThresholdLeft || PositionUVs.x > this.BorderThresholdRight || PositionUVs.y < this.BorderThresholdTop || PositionUVs.y > this.BorderThresholdBottom) {
return TextureRGBA;
}
// 阴影反向方位像素点RGBA
float4 PositionRGBA = this.TextureSample(PositionUVs);
// 阴影透明度
float ShadowOpacity = PositionRGBA.a * this.ShadowRGBA.a;
if (ShadowOpacity <= 0) {
return TextureRGBA;
}
// 当前像素点混合后的结果色
// this.ShadowRGBA.rgb 为 base 固有色
// TextureRGBA.rgb 为 blend 固有色
// TextureRGBA.a 为 alpha
float3 ShadowBlendColor = this.Blend(this.ShadowColor(PositionRGBA.rgb) * ShadowOpacity, TextureRGBA.rgb, TextureRGBA.a);
// 当前像素点混合后的透明度
float ShadowBlendOpacity = ShadowOpacity + TextureRGBA.a - ShadowOpacity * TextureRGBA.a;
// 当前像素点混合后的RGBA
return float4(ShadowBlendColor / ShadowBlendOpacity, ShadowBlendOpacity);
}
float Calculate1DGaussian(float x) {
return exp(-0.5 * pow(this.PI * x, 2));
}
float4 GetShadowSizeRGBA(float2 UVs, half ShadowSize) {
// 当前像素点 RGBA
float4 TextureRGBA = this.TextureSample(UVs);
// 投影大小范围内像素颜色累加
float4 RGBASum = float4(0, 0, 0, 0);
// 投影大小范围内像素的权重
float WeightSum = 0;
for (half x = -ShadowSize; x <= ShadowSize; x++) {
for (half y = -ShadowSize; y <= ShadowSize; y++) {
float Weight = this.Calculate1DGaussian(x / ShadowSize) * this.Calculate1DGaussian(y / ShadowSize);
WeightSum += Weight;
// 阴影偏移 UVs
float2 OffsetUVs = UVs + float2(x, y) * this.TexturePixel + this.Position;
if (OffsetUVs.x < this.BorderThresholdLeft || OffsetUVs.x > this.BorderThresholdRight || OffsetUVs.y < this.BorderThresholdTop || OffsetUVs.y > this.BorderThresholdBottom) {
continue;
}
// 阴影偏移像素点 RGBA
float4 OffsetRGBA = this.TextureSample(OffsetUVs);
// 阴影透明度
float Opacity = this.ShadowRGBA.a * OffsetRGBA.a;
if (Opacity <= 0) {
continue;
}
// 阴影结果色
float4 RGBA = float4(this.ShadowColor(OffsetRGBA.rgb), Opacity);
RGBASum += RGBA * Weight;
}
}
// 模糊后的 RGBA
float4 BlurRGBA = RGBASum / WeightSum;
// 当前像素点混合后的结果色
float3 Color = this.Blend(BlurRGBA.rgb * BlurRGBA.a, TextureRGBA.rgb, TextureRGBA.a);
// 当前像素点混合后的透明度
float Opacity = BlurRGBA.a + TextureRGBA.a - BlurRGBA.a * TextureRGBA.a;
// 当前像素点混合后的RGBA
return float4(Color / Opacity, Opacity);
}
}; // 注意要加分号
// Function func;
// func.Texture = Texture;
// func.TextureSampler = TextureSampler;
// func.ShadowRGBA = ShadowRGBA;
// func.Position = ShadowLength * Direction;
// func.BorderThresholdLeft = BorderThreshold.x;
// func.BorderThresholdTop = BorderThreshold.y;
// func.BorderThresholdRight = 1 - BorderThreshold.z;
// func.BorderThresholdBottom = 1 - BorderThreshold.w;
// func.PI = PI;
// func.TexturePixel = TexturePixel;
Function func = { Texture, TextureSampler, ShadowRGBA, ShadowLength * Direction, BorderThreshold.x, BorderThreshold.y, 1 - BorderThreshold.z, 1 - BorderThreshold.w, PI, TexturePixel };
if (ShadowSize < 1) {
return func.GetShadowRGBA(UVs);
}
return func.GetShadowSizeRGBA(UVs, round(ShadowSize));