跳至主要內容

SD3.图片仿PS投影Shader

Mr.Si大约 5 分钟unreal

/**
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));
}

参考自csdnopen in new window

头像
UE5.2中会报错,去掉函数定义即可
/**
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));